diff --git a/3rdparty/ymfm/GeneralInfo.md b/3rdparty/ymfm/GeneralInfo.md index c9c402c106f..52cdbf5e81b 100644 --- a/3rdparty/ymfm/GeneralInfo.md +++ b/3rdparty/ymfm/GeneralInfo.md @@ -19,7 +19,7 @@ The Yamaha FM chips can be broadly categoried into families: * OPL (YM3526) * OPL2 (YM3812) * OPLL (YM2413, YM2423, YMF281, DS1001, and others) - * OPL3 (YMF262) + * OPL3 (YMF262, YMF289B) * OPL4 (YMF278) Additionally, several lesser-documented variants exist exclusively in the employ of Yamaha synthesizers: @@ -242,9 +242,9 @@ some details on the OPN family: chip ID: | YM2203 | YM2608 | YMF288 | YM2610 | YM2610B | YM2612 | YM3438 | YMF276 | ---------:|:------:|:------:|:------:|:------:|:-------:|:------:|:------:|:------:| -aka: | OPN | OPNA | OPN3 | OPNB | OPNB2 | OPN2 | OPN2C | OPN2L | +aka: | OPN | OPNA | OPN3L | OPNB | OPNB2 | OPN2 | OPN2C | OPN2L | FM: | 3 | 6 | 6 | 4 | 6 | 6 | 6 | 6 | -AY-8910: | 3 | 3 | 3 | 3 | 3 | - | - | - | +AY-8910: | 3 | 1 | 1 | 1 | 1 | - | - | - | ADPCM-A: | - | 6 int | 6 int | 6 ext | 6 ext | - | - | - | ADPCM-B: | - | 1 ext | - | 1 ext | 1 ext | - | - | - | DAC: | no | no | no | no | no | yes | yes | yes | @@ -252,7 +252,7 @@ output: | 10.3fp | 16-bit | 16-bit | 16-bit | 16-bit | 9-bit | 9-bit | 16-b summing: | adder | adder | adder | adder | adder | muxer | muxer | adder | * FM represents the number of FM channels available. -* AY-8910 represents the number of AY-8910-compatible channels that are built in. +* AY-8910 represents the number of AY-8910-compatible outputs. * ADPCM-A represents the number of internal/external ADPCM-A channels present. * ADPCM-B represents the number of internal/external ADPCM-B channels present. * DAC indicates if a directly-accessible DAC output exists, replacing one channel. @@ -261,15 +261,15 @@ summing: | adder | adder | adder | adder | adder | muxer | muxer | add OPL has a similar trove of chip variants: -chip ID: | YM3526 | Y8950 | YM3812 | YM2413 | YMF262 | YMF278B | -------------:|:------:|:-------:|:------:|:------:|:------:|:-------:| -aka: | OPL |MSX-AUDIO| OPL2 | OPLL | OPL3 | OPL4 | -FM: | 9 | 9 | 9 | 9 | 18 | 18 | -ADPCM-B: | - | 1 ext | - | - | - | - | -wavetable: | - | - | - | - | - | 24 | -instruments: | no | no | no | yes | no | no | -output: | 10.3fp | 10.3fp | 10.3fp | 9-bit | 16-bit | 16-bit | -summing: | adder | adder | adder | muxer | adder | adder | +chip ID: | YM3526 | Y8950 | YM3812 | YM2413 | YMF262 | YMF289B | YMF278B | +------------:|:------:|:-------:|:------:|:------:|:------:|:-------:|:-------:| +aka: | OPL |MSX-AUDIO| OPL2 | OPLL | OPL3 | OPL3L | OPL4 | +FM: | 9 | 9 | 9 | 9 | 18 | 18 | 18 | +ADPCM-B: | - | 1 ext | - | - | - | - | - | +wavetable: | - | - | - | - | - | - | 24 | +instruments: | no | no | no | yes | no | no | no | +output: | 10.3fp | 10.3fp | 10.3fp | 9-bit | 16-bit | 16-bit | 16-bit | +summing: | adder | adder | adder | muxer | adder | adder | adder | * FM represents the number of FM channels available. * ADPCM-B represents the number of external ADPCM-B channels present. diff --git a/3rdparty/ymfm/README.md b/3rdparty/ymfm/README.md index a7f16d6660a..86b7dd446f9 100644 --- a/3rdparty/ymfm/README.md +++ b/3rdparty/ymfm/README.md @@ -23,12 +23,14 @@ Currently, support is present for the following chips (organized by header file) * YM2612 (OPN2) * YM3438 (OPN2C) * YMF276 (OPN2L) + * YMF288 (OPN3L) * ymfm_opl.h: * YM3526 (OPL) * Y8950 (MSX-Audio) * YM3812 (OPL2) * YMF262 (OPL3) - * YMF278B (OPL4) -- partial (only the FM side) + * YMF289B (OPL3L) + * YMF278B (OPL4) * YM2413 (OPLL) * YM2423 (OPLL-X) * YMF281 (OPLLP) @@ -38,6 +40,11 @@ Currently, support is present for the following chips (organized by header file) * ymfm_opz.h: * YM2414 (OPZ) -- preliminary +There are some obviously-related chips that also are on my horizon but have no implementation as yet: + +* YMW-258-F 'GEW8' (aka Sega 315-5560 aka Sega Multi-PCM) +* YMF271 (OPX) + ## History These cores were originally written during the summer and fall of 2020 as part of the [MAME](https://mamedev.org/) project. diff --git a/3rdparty/ymfm/examples/vgmrender/vgmrender.cpp b/3rdparty/ymfm/examples/vgmrender/vgmrender.cpp index 97f650c6a94..7f99c5abb8d 100644 --- a/3rdparty/ymfm/examples/vgmrender/vgmrender.cpp +++ b/3rdparty/ymfm/examples/vgmrender/vgmrender.cpp @@ -5,15 +5,15 @@ // // Compile with: // -// g++ --std=c++17 -I../../src vgmrender.cpp em_inflate.cpp ../../src/ymfm_opl.cpp ../../src/ymfm_opm.cpp ../../src/ymfm_opn.cpp ../../src/ymfm_adpcm.cpp ../../src/ymfm_ssg.cpp -o vgmrender.exe +// g++ --std=c++14 -I../../src vgmrender.cpp em_inflate.cpp ../../src/ymfm_opl.cpp ../../src/ymfm_opm.cpp ../../src/ymfm_opn.cpp ../../src/ymfm_adpcm.cpp ../../src/ymfm_pcm.cpp ../../src/ymfm_ssg.cpp -o vgmrender.exe // // or: // -// clang --std=c++17 -I../../src vgmrender.cpp em_inflate.cpp ../../src/ymfm_opl.cpp ../../src/ymfm_opm.cpp ../../src/ymfm_opn.cpp ../../src/ymfm_adpcm.cpp ../../src/ymfm_ssg.cpp -o vgmrender.exe +// clang --std=c++14 -I../../src vgmrender.cpp em_inflate.cpp ../../src/ymfm_opl.cpp ../../src/ymfm_opm.cpp ../../src/ymfm_opn.cpp ../../src/ymfm_adpcm.cpp ../../src/ymfm_pcm.cpp ../../src/ymfm_ssg.cpp -o vgmrender.exe // // or: // -// cl -I..\..\src vgmrender.cpp em_inflate.cpp ..\..\src\ymfm_opl.cpp ..\..\src\ymfm_opm.cpp ..\..\src\ymfm_opn.cpp ..\..\src\ymfm_adpcm.cpp ..\..\src\ymfm_ssg.cpp /Od /Zi /std:c++17 /EHsc +// cl -I..\..\src vgmrender.cpp em_inflate.cpp ..\..\src\ymfm_opl.cpp ..\..\src\ymfm_opm.cpp ..\..\src\ymfm_opn.cpp ..\..\src\ymfm_adpcm.cpp ..\..\src\ymfm_pcm.cpp ..\..\src\ymfm_ssg.cpp /Od /Zi /std:c++14 /EHsc // #define _CRT_SECURE_NO_WARNINGS @@ -23,12 +23,15 @@ #include #include #include +#include #include "em_inflate.h" #include "ymfm_opl.h" #include "ymfm_opm.h" #include "ymfm_opn.h" +#define LOG_WRITES (0) + // enable this to run the nuked OPN2 core in parallel; output is not captured, // but logging can be added to observe behaviors #define RUN_NUKED_OPN2 (0) @@ -64,6 +67,7 @@ enum chip_type CHIP_Y8950, CHIP_YM3812, CHIP_YMF262, + CHIP_YMF278B, CHIP_TYPES }; @@ -81,8 +85,9 @@ class vgm_chip_base { public: // construction - vgm_chip_base(uint32_t clock, chip_type type) : - m_type(type) + vgm_chip_base(uint32_t clock, chip_type type, char const *name) : + m_type(type), + m_name(name) { } @@ -95,42 +100,23 @@ public: virtual void generate(emulated_time output_start, emulated_time output_step, int32_t *buffer) = 0; // write data to the ADPCM-A buffer - void write_adpcm_a(uint32_t base, uint32_t length, uint8_t const *src) + void write_data(ymfm::access_class type, uint32_t base, uint32_t length, uint8_t const *src) { uint32_t end = base + length; - if (end > m_adpcm_a_data.size()) - m_adpcm_a_data.resize(end); - memcpy(&m_adpcm_a_data[base], src, length); - } - - // write data to the ADPCM-B buffer - void write_adpcm_b(uint32_t base, uint32_t length, uint8_t const *src) - { - uint32_t end = base + length; - if (end > m_adpcm_b_data.size()) - m_adpcm_b_data.resize(end); - memcpy(&m_adpcm_b_data[base], src, length); - } - - // write data to the PCM buffer - void write_pcm(uint32_t base, uint32_t length, uint8_t const *src) - { - uint32_t end = base + length; - if (end > m_pcm_data.size()) - m_pcm_data.resize(end); - memcpy(&m_pcm_data[base], src, length); + if (end > m_data[type].size()) + m_data[type].resize(end); + memcpy(&m_data[type][base], src, length); } // seek within the PCM stream void seek_pcm(uint32_t pos) { m_pcm_offset = pos; } - uint8_t read_pcm() { return (m_pcm_offset < m_pcm_data.size()) ? m_pcm_data[m_pcm_offset++] : 0; } + uint8_t read_pcm() { auto &pcm = m_data[ymfm::ACCESS_PCM]; return (m_pcm_offset < pcm.size()) ? pcm[m_pcm_offset++] : 0; } protected: // internal state chip_type m_type; - std::vector m_adpcm_a_data; - std::vector m_adpcm_b_data; - std::vector m_pcm_data; + std::string m_name; + std::vector m_data[ymfm::ACCESS_CLASSES]; uint32_t m_pcm_offset; #if (CAPTURE_NATIVE) public: @@ -153,10 +139,11 @@ class vgm_chip : public vgm_chip_base, public ymfm::ymfm_interface { public: // construction - vgm_chip(uint32_t clock, chip_type type) : - vgm_chip_base(clock, type), + vgm_chip(uint32_t clock, chip_type type, char const *name) : + vgm_chip_base(clock, type, name), m_chip(*this), m_clock(clock), + m_clocks(0), m_step(0x100000000ull / m_chip.sample_rate(clock)), m_pos(0) { @@ -192,9 +179,9 @@ public: if (!m_queue.empty()) { auto front = m_queue.front(); - addr1 = 0 + 2 * ((front.first >> 8) & 1); + addr1 = 0 + 2 * ((front.first >> 8) & 3); data1 = front.first & 0xff; - addr2 = (m_type == CHIP_YM2149) ? 2 : (1 + 2 * ((front.first >> 8) & 1)); + addr2 = addr1 + ((m_type == CHIP_YM2149) ? 2 : 1); data2 = front.second; m_queue.erase(m_queue.begin()); } @@ -202,6 +189,8 @@ public: // write to the chip if (addr1 != 0xffff) { + if (LOG_WRITES) + printf("%10.5f: %s %03X=%02X\n", double(m_clocks) / double(m_chip.sample_rate(m_clock)), m_name.c_str(), data1, data2); m_chip.write(addr1, data1); m_chip.write(addr2, data2); } @@ -264,6 +253,11 @@ public: *buffer++ += out0 + out2; *buffer++ += out1 + out2; } + else if (m_type == CHIP_YMF278B) + { + *buffer++ += m_output.data[4]; + *buffer++ += m_output.data[5]; + } else if (ChipType::OUTPUTS == 1) { *buffer++ += m_output.data[0]; @@ -274,24 +268,21 @@ public: *buffer++ += m_output.data[0]; *buffer++ += m_output.data[1 % ChipType::OUTPUTS]; } + m_clocks++; } protected: - // handle a read from the ADPCM-A buffer - virtual uint8_t ymfm_adpcm_a_read(uint32_t offset) override + // handle a read from the buffer + virtual uint8_t ymfm_external_read(ymfm::access_class type, uint32_t offset) override { - return (offset < m_adpcm_a_data.size()) ? m_adpcm_a_data[offset] : 0; - } - - // handle a read from the ADPCM-B buffer - virtual uint8_t ymfm_adpcm_b_read(uint32_t offset) override - { - return (offset < m_adpcm_b_data.size()) ? m_adpcm_b_data[offset] : 0; + auto &data = m_data[type]; + return (offset < data.size()) ? data[offset] : 0; } // internal state ChipType m_chip; uint32_t m_clock; + uint64_t m_clocks; typename ChipType::output_data m_output; emulated_time m_step; emulated_time m_pos; @@ -334,7 +325,11 @@ void add_chips(uint32_t clock, chip_type type, char const *chipname) int numchips = (clock & 0x40000000) ? 2 : 1; printf("Adding %s%s @ %dHz\n", (numchips == 2) ? "2 x " : "", chipname, clockval); for (int index = 0; index < numchips; index++) - active_chips.push_back(new vgm_chip(clockval, type)); + { + char name[100]; + sprintf(name, "%s #%d", chipname, index); + active_chips.push_back(new vgm_chip(clockval, type, chipname)); + } if (type == CHIP_YM2608) { @@ -351,7 +346,7 @@ void add_chips(uint32_t clock, chip_type type, char const *chipname) fclose(rom); for (auto chip : active_chips) if (chip->type() == type) - chip->write_adpcm_a(0, size, &temp[0]); + chip->write_data(ymfm::ACCESS_ADPCM_A, 0, size, &temp[0]); } } } @@ -500,7 +495,7 @@ uint32_t parse_header(std::vector &buffer) return data_start; clock = parse_uint32(buffer, offset); if (version >= 0x151 && clock != 0) - fprintf(stderr, "Warning: clock for YMF278B specified, but not supported\n"); + add_chips(clock, CHIP_YMF278B, "YMF278B"); // +64: YMF271 clock if (offset + 4 > data_start) @@ -752,15 +747,20 @@ void write_chip(chip_type type, uint8_t index, uint32_t reg, uint8_t data) //------------------------------------------------- -// write_chip_hi - handle an upper-address write -// to the given chip and index +// add_rom_data - add data to the given chip +// type in the given access class //------------------------------------------------- -void write_chip_hi(chip_type type, uint8_t index, uint32_t reg, uint8_t data) +void add_rom_data(chip_type type, ymfm::access_class access, std::vector &buffer, uint32_t &localoffset, uint32_t size) { - vgm_chip_base *chip = find_chip(type, index); - if (chip != nullptr) - chip->write(reg | 0x100, data); + uint32_t length = parse_uint32(buffer, localoffset); + uint32_t start = parse_uint32(buffer, localoffset); + for (int index = 0; index < 2; index++) + { + vgm_chip_base *chip = find_chip(type, index); + if (chip != nullptr) + chip->write_data(access, start, size, &buffer[localoffset]); + } } @@ -799,7 +799,7 @@ void generate_all(std::vector &buffer, uint32_t data_start, uint32_t ou // YM2612 port 1, write value dd to register aa case 0x53: case 0xa3: - write_chip_hi(CHIP_YM2612, cmd >> 7, buffer[offset], buffer[offset + 1]); + write_chip(CHIP_YM2612, cmd >> 7, buffer[offset] | 0x100, buffer[offset + 1]); offset += 2; break; @@ -827,7 +827,7 @@ void generate_all(std::vector &buffer, uint32_t data_start, uint32_t ou // YM2608 port 1, write value dd to register aa case 0x57: case 0xa7: - write_chip_hi(CHIP_YM2608, cmd >> 7, buffer[offset], buffer[offset + 1]); + write_chip(CHIP_YM2608, cmd >> 7, buffer[offset] | 0x100, buffer[offset + 1]); offset += 2; break; @@ -841,7 +841,7 @@ void generate_all(std::vector &buffer, uint32_t data_start, uint32_t ou // YM2610 port 1, write value dd to register aa case 0x59: case 0xa9: - write_chip_hi(CHIP_YM2610, cmd >> 7, buffer[offset], buffer[offset + 1]); + write_chip(CHIP_YM2610, cmd >> 7, buffer[offset] | 0x100, buffer[offset + 1]); offset += 2; break; @@ -876,7 +876,7 @@ void generate_all(std::vector &buffer, uint32_t data_start, uint32_t ou // YMF262 port 1, write value dd to register aa case 0x5f: case 0xaf: - write_chip_hi(CHIP_YMF262, cmd >> 7, buffer[offset], buffer[offset + 1]); + write_chip(CHIP_YMF262, cmd >> 7, buffer[offset] | 0x100, buffer[offset + 1]); offset += 2; break; @@ -927,59 +927,34 @@ void generate_all(std::vector &buffer, uint32_t data_start, uint32_t ou { vgm_chip_base *chip = find_chip(CHIP_YM2612, 0); if (chip != nullptr) - chip->write_pcm(0, size - 8, &buffer[localoffset]); + chip->write_data(ymfm::ACCESS_PCM, 0, size - 8, &buffer[localoffset]); break; } case 0x82: // YM2610 ADPCM ROM data - length = parse_uint32(buffer, localoffset); - start = parse_uint32(buffer, localoffset); - for (int index = 0; index < 2; index++) - { - vgm_chip_base *chip = find_chip(CHIP_YM2610, index); - if (chip != nullptr) - chip->write_adpcm_a(start, size - 8, &buffer[localoffset]); - } + add_rom_data(CHIP_YM2610, ymfm::ACCESS_ADPCM_A, buffer, localoffset, size - 8); break; case 0x81: // YM2608 DELTA-T ROM data - length = parse_uint32(buffer, localoffset); - start = parse_uint32(buffer, localoffset); - for (int index = 0; index < 2; index++) - { - vgm_chip_base *chip = find_chip(CHIP_YM2608, index); - if (chip != nullptr) - chip->write_adpcm_b(start, size - 8, &buffer[localoffset]); - } + add_rom_data(CHIP_YM2608, ymfm::ACCESS_ADPCM_B, buffer, localoffset, size - 8); break; case 0x83: // YM2610 DELTA-T ROM data - length = parse_uint32(buffer, localoffset); - start = parse_uint32(buffer, localoffset); - for (int index = 0; index < 2; index++) - { - vgm_chip_base *chip = find_chip(CHIP_YM2610, index); - if (chip != nullptr) - chip->write_adpcm_b(start, size - 8, &buffer[localoffset]); - } + add_rom_data(CHIP_YM2610, ymfm::ACCESS_ADPCM_B, buffer, localoffset, size - 8); + break; + + case 0x84: // YMF278B ROM data + case 0x87: // YMF278B RAM data + add_rom_data(CHIP_YMF278B, ymfm::ACCESS_PCM, buffer, localoffset, size - 8); break; case 0x88: // Y8950 DELTA-T ROM data - length = parse_uint32(buffer, localoffset); - start = parse_uint32(buffer, localoffset); - for (int index = 0; index < 2; index++) - { - vgm_chip_base *chip = find_chip(CHIP_Y8950, index); - if (chip != nullptr) - chip->write_adpcm_b(start, size - 8, &buffer[localoffset]); - } + add_rom_data(CHIP_Y8950, ymfm::ACCESS_ADPCM_B, buffer, localoffset, size - 8); break; case 0x80: // Sega PCM ROM data - case 0x84: // YMF278B ROM data case 0x85: // YMF271 ROM data case 0x86: // YMZ280B ROM data - case 0x87: // YMF278B RAM data case 0x89: // MultiPCM ROM data case 0x8A: // uPD7759 ROM data case 0x8B: // OKIM6295 ROM data @@ -1022,6 +997,12 @@ void generate_all(std::vector &buffer, uint32_t data_start, uint32_t ou offset += 2; break; + // pp aa dd: YMF278B, port pp, write value dd to register aa + case 0xd0: + write_chip(CHIP_YMF278B, buffer[offset] >> 7, ((buffer[offset] & 0x7f) << 8) | buffer[offset + 1], buffer[offset + 2]); + offset += 3; + break; + case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f: delay = (cmd & 15) + 1; @@ -1080,7 +1061,6 @@ void generate_all(std::vector &buffer, uint32_t data_start, uint32_t ou case 0xc6: // mmll dd: WonderSwan, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB) case 0xc7: // mmll dd: VSU, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB) case 0xc8: // mmll dd: X1-010, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB) - case 0xd0: // pp aa dd: YMF278B, port pp, write value dd to register aa case 0xd1: // pp aa dd: YMF271, port pp, write value dd to register aa case 0xd2: // pp aa dd: SCC1, port pp, write value dd to register aa case 0xd3: // pp aa dd: K054539, write value dd to register ppaa diff --git a/3rdparty/ymfm/src/ymfm.h b/3rdparty/ymfm/src/ymfm.h index e4b3ff1d52e..93c8dca22f4 100644 --- a/3rdparty/ymfm/src/ymfm.h +++ b/3rdparty/ymfm/src/ymfm.h @@ -55,6 +55,7 @@ public: static constexpr uint32_t GLOBAL_FM_CHANNEL_MASK = 0xffffffff; static constexpr uint32_t GLOBAL_ADPCM_A_CHANNEL_MASK = 0xffffffff; static constexpr uint32_t GLOBAL_ADPCM_B_CHANNEL_MASK = 0xffffffff; + static constexpr uint32_t GLOBAL_PCM_CHANNEL_MASK = 0xffffffff; // types of logging static constexpr bool LOG_FM_WRITES = false; @@ -108,7 +109,7 @@ inline int32_t clamp(int32_t value, int32_t minval, int32_t maxval) //------------------------------------------------- template -constexpr int32_t array_size(ArrayType (&array)[ArraySize]) +constexpr uint32_t array_size(ArrayType (&array)[ArraySize]) { return ArraySize; } @@ -257,9 +258,33 @@ inline int16_t roundtrip_fp(int32_t value) // HELPER CLASSES //********************************************************* -// forward declarations -enum envelope_state : uint32_t; +// various envelope states +enum envelope_state : uint32_t +{ + EG_DEPRESS = 0, // OPLL only; set EG_HAS_DEPRESS to enable + EG_ATTACK = 1, + EG_DECAY = 2, + EG_SUSTAIN = 3, + EG_RELEASE = 4, + EG_REVERB = 5, // OPZ only; set EG_HAS_REVERB to enable + EG_STATES = 6 +}; +// external I/O access classes +enum access_class : uint32_t +{ + ACCESS_IO = 0, + ACCESS_ADPCM_A, + ACCESS_ADPCM_B, + ACCESS_PCM, + ACCESS_CLASSES +}; + + + +//********************************************************* +// HELPER CLASSES +//********************************************************* // ======================> ymfm_output @@ -270,7 +295,7 @@ struct ymfm_output // clear all outputs to 0 ymfm_output &clear() { - for (int index = 0; index < NumOutputs; index++) + for (uint32_t index = 0; index < NumOutputs; index++) data[index] = 0; return *this; } @@ -278,7 +303,7 @@ struct ymfm_output // clamp all outputs to a 16-bit signed value ymfm_output &clamp16() { - for (int index = 0; index < NumOutputs; index++) + for (uint32_t index = 0; index < NumOutputs; index++) data[index] = clamp(data[index], -32768, 32767); return *this; } @@ -286,7 +311,7 @@ struct ymfm_output // run each output value through the floating-point processor ymfm_output &roundtrip_fp() { - for (int index = 0; index < NumOutputs; index++) + for (uint32_t index = 0; index < NumOutputs; index++) data[index] = ymfm::roundtrip_fp(data[index]); return *this; } @@ -336,7 +361,7 @@ public: void save(uint32_t &data) { write(data).write(data >> 8).write(data >> 16).write(data >> 24); } void save(envelope_state &data) { write(uint8_t(data)); } template - void save(DataType (&data)[Count]) { for (int index = 0; index < Count; index++) save(data[index]); } + void save(DataType (&data)[Count]) { for (uint32_t index = 0; index < Count; index++) save(data[index]); } // restore data from the buffer void restore(bool &data) { data = read() ? true : false; } @@ -348,11 +373,11 @@ public: void restore(uint32_t &data) { data = read(); data |= read() << 8; data |= read() << 16; data |= read() << 24; } void restore(envelope_state &data) { data = envelope_state(read()); } template - void restore(DataType (&data)[Count]) { for (int index = 0; index < Count; index++) restore(data[index]); } + void restore(DataType (&data)[Count]) { for (uint32_t index = 0; index < Count; index++) restore(data[index]); } // internal helper ymfm_saved_state &write(uint8_t data) { m_buffer.push_back(data); return *this; } - uint8_t read() { return (m_offset < m_buffer.size()) ? m_buffer[m_offset++] : 0; } + uint8_t read() { return (m_offset < int32_t(m_buffer.size())) ? m_buffer[m_offset++] : 0; } // internal state std::vector &m_buffer; @@ -442,31 +467,13 @@ public: // needed to the change in IRQ state, signaling any consumers virtual void ymfm_update_irq(bool asserted) { } - // the chip implementation calls this whenever a new value is written to - // one of the chip's output ports (only applies to some chip types); our - // responsibility is to pass the written data on to any consumers - virtual void ymfm_io_write(uint8_t port, uint8_t data) { } + // the chip implementation calls this whenever data is read from outside + // of the chip; our responsibility is to provide the data requested + virtual uint8_t ymfm_external_read(access_class type, uint32_t address) { return 0; } - // the chip implementation calls this whenever an on-chip register is read - // which returns data from one of the chip's input ports; our responsibility - // is to produce the current input value so that it can be reflected by the - // read operation - virtual uint8_t ymfm_io_read(uint8_t port) { return 0; } - - // the chip implementation calls this whenever the ADPCM-A engine needs to - // fetch data for sound generation; our responsibility is to read the data - // from the appropriate ROM/RAM at the given offset and return it - virtual uint8_t ymfm_adpcm_a_read(uint32_t offset) { return 0; } - - // the chip implementation calls this whenever the ADPCM-B engine needs to - // fetch data for sound generation; our responsibility is to read the data - // from the appropriate ROM/RAM at the given offset and return it - virtual uint8_t ymfm_adpcm_b_read(uint32_t offset) { return 0; } - - // the chip implementation calls this whenever the ADPCM-B engine requests - // a write to the sound data; our responsibility is to write the data to - // the appropriate RAM at the given offset - virtual void ymfm_adpcm_b_write(uint32_t offset, uint8_t data) { } + // the chip implementation calls this whenever data is written outside + // of the chip; our responsibility is to pass the written data on to any consumers + virtual void ymfm_external_write(access_class type, uint32_t address, uint8_t data) { } protected: // pointer to engine callbacks -- this is set directly by the engine at diff --git a/3rdparty/ymfm/src/ymfm_adpcm.cpp b/3rdparty/ymfm/src/ymfm_adpcm.cpp index bbe5bc0e200..882dcf3de7c 100644 --- a/3rdparty/ymfm/src/ymfm_adpcm.cpp +++ b/3rdparty/ymfm/src/ymfm_adpcm.cpp @@ -171,7 +171,7 @@ bool adpcm_a_channel::clock() uint8_t data; if (m_curnibble == 0) { - m_curbyte = m_owner.intf().ymfm_adpcm_a_read(m_curaddress++); + m_curbyte = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_A, m_curaddress++); data = m_curbyte >> 4; m_curnibble = 1; } @@ -482,7 +482,7 @@ void adpcm_b_channel::clock() // if we're about to process nibble 0, fetch and increment if (m_curnibble == 0) { - m_curbyte = m_owner.intf().ymfm_adpcm_b_read(m_curaddress++); + m_curbyte = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress++); m_curaddress &= 0xffffff; } } @@ -569,7 +569,7 @@ uint8_t adpcm_b_channel::read(uint32_t regnum) // otherwise, write the data and signal ready else { - result = m_owner.intf().ymfm_adpcm_b_read(m_curaddress++); + result = m_owner.intf().ymfm_external_read(ACCESS_ADPCM_B, m_curaddress++); m_status = STATUS_BRDY; } } @@ -645,7 +645,7 @@ void adpcm_b_channel::write(uint32_t regnum, uint8_t value) // otherwise, write the data and signal ready else { - m_owner.intf().ymfm_adpcm_b_write(m_curaddress++, value); + m_owner.intf().ymfm_external_write(ACCESS_ADPCM_B, m_curaddress++, value); m_status = STATUS_BRDY; } } diff --git a/3rdparty/ymfm/src/ymfm_fm.h b/3rdparty/ymfm/src/ymfm_fm.h index 3fd0931076c..79f4c5a7305 100644 --- a/3rdparty/ymfm/src/ymfm_fm.h +++ b/3rdparty/ymfm/src/ymfm_fm.h @@ -40,18 +40,6 @@ namespace ymfm // GLOBAL ENUMERATORS //********************************************************* -enum envelope_state : uint32_t -{ - EG_DEPRESS = 0, // OPLL only; set EG_HAS_DEPRESS to enable - EG_ATTACK = 1, - EG_DECAY = 2, - EG_SUSTAIN = 3, - EG_RELEASE = 4, - EG_REVERB = 5, // OPZ only; set EG_HAS_REVERB to enable - EG_STATES = 6 -}; - - // three different keyon sources; actual keyon is an OR over all of these enum keyon_type : uint32_t { @@ -213,7 +201,7 @@ public: void keyonoff(uint32_t on, keyon_type type); // return a reference to our registers - RegisterType ®s() { return m_regs; } + RegisterType ®s() const { return m_regs; } // simple getters for debugging envelope_state debug_eg_state() const { return m_env_state; } @@ -274,7 +262,7 @@ public: uint32_t choffs() const { return m_choffs; } // assign operators - void assign(int index, fm_operator *op) + void assign(uint32_t index, fm_operator *op) { assert(index < array_size(m_op)); m_op[index] = op; @@ -309,10 +297,10 @@ public: } // return a reference to our registers - RegisterType ®s() { return m_regs; } + RegisterType ®s() const { return m_regs; } // simple getters for debugging - fm_operator *debug_operator(int index) const { return m_op[index]; } + fm_operator *debug_operator(uint32_t index) const { return m_op[index]; } private: // helper to add values to the outputs based on channel enables @@ -420,8 +408,8 @@ public: void invalidate_caches() { m_modified_channels = RegisterType::ALL_CHANNELS; } // simple getters for debugging - fm_channel *debug_channel(int index) const { return m_channel[index].get(); } - fm_operator *debug_operator(int index) const { return m_operator[index].get(); } + fm_channel *debug_channel(uint32_t index) const { return m_channel[index].get(); } + fm_operator *debug_operator(uint32_t index) const { return m_operator[index].get(); } public: // timer callback; called by the interface when a timer fires diff --git a/3rdparty/ymfm/src/ymfm_fm.ipp b/3rdparty/ymfm/src/ymfm_fm.ipp index 9db4ebda3ae..3f85a241283 100644 --- a/3rdparty/ymfm/src/ymfm_fm.ipp +++ b/3rdparty/ymfm/src/ymfm_fm.ipp @@ -540,8 +540,11 @@ void fm_operator::start_attack(bool is_restart) if (RegisterType::EG_HAS_SSG && !is_restart) m_ssg_inverted = m_regs.op_ssg_eg_enable(m_opoffs) & bitfield(m_regs.op_ssg_eg_mode(m_opoffs), 2); - // reset the phase when we start an attack - m_phase = 0; + // reset the phase when we start an attack due to a key on + // (but not when due to an SSG-EG restart except in certain cases + // managed directly by the SSG-EG code) + if (!is_restart) + m_phase = 0; // if the attack rate >= 62 then immediately go to max attenuation if (m_cache.eg_rate[EG_ATTACK] >= 62) @@ -634,10 +637,10 @@ void fm_operator::clock_ssg_eg_state() // set the inverted flag to the end state (0 for modes 1/7, 1 for modes 3/5) m_ssg_inverted = bitfield(mode, 2) ^ bitfield(mode, 1); - // if holding low (modes 1/5), force the attenuation to maximum - // once we're past the attack phase - if (m_env_state != EG_ATTACK && bitfield(mode, 1) == 0) - m_env_attenuation = 0x3ff; + // if holding, force the attenuation to the expected value once we're + // past the attack phase + if (m_env_state != EG_ATTACK) + m_env_attenuation = m_ssg_inverted ? 0x200 : 0x3ff; } // continuous modes (0/2/4/6) @@ -650,7 +653,7 @@ void fm_operator::clock_ssg_eg_state() if (m_env_state == EG_DECAY || m_env_state == EG_SUSTAIN) start_attack(true); - // phase is reset to 0 regardless in modes 0/4 + // phase is reset to 0 in modes 0/4 if (bitfield(mode, 1) == 0) m_phase = 0; } @@ -837,12 +840,12 @@ void fm_channel::save_restore(ymfm_saved_state &state) template void fm_channel::keyonoff(uint32_t states, keyon_type type, uint32_t chnum) { - for (int opnum = 0; opnum < array_size(m_op); opnum++) + for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) if (m_op[opnum] != nullptr) m_op[opnum]->keyonoff(bitfield(states, opnum), type); if (debug::LOG_KEYON_EVENTS && ((debug::GLOBAL_FM_CHANNEL_MASK >> chnum) & 1) != 0) - for (int opnum = 0; opnum < array_size(m_op); opnum++) + for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) if (m_op[opnum] != nullptr) debug::log_keyon("%c%s\n", bitfield(states, opnum) ? '+' : '-', m_regs.log_keyon(m_choffs, m_op[opnum]->opoffs()).c_str()); } @@ -858,7 +861,7 @@ bool fm_channel::prepare() uint32_t active_mask = 0; // prepare all operators and determine if they are active - for (int opnum = 0; opnum < array_size(m_op); opnum++) + for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) if (m_op[opnum] != nullptr) if (m_op[opnum]->prepare()) active_mask |= 1 << opnum; @@ -878,7 +881,7 @@ void fm_channel::clock(uint32_t env_counter, int32_t lfo_raw_pm) m_feedback[0] = m_feedback[1]; m_feedback[1] = m_feedback_in; - for (int opnum = 0; opnum < array_size(m_op); opnum++) + for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) if (m_op[opnum] != nullptr) m_op[opnum]->clock(env_counter, lfo_raw_pm); } @@ -1173,11 +1176,11 @@ fm_engine_base::fm_engine_base(ymfm_interface &intf) : m_intf.m_engine = this; // create the channels - for (int chnum = 0; chnum < CHANNELS; chnum++) + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) m_channel[chnum] = std::make_unique>(*this, RegisterType::channel_offset(chnum)); // create the operators - for (int opnum = 0; opnum < OPERATORS; opnum++) + for (uint32_t opnum = 0; opnum < OPERATORS; opnum++) m_operator[opnum] = std::make_unique>(*this, RegisterType::operator_offset(opnum)); // do the initial operator assignment @@ -1232,11 +1235,11 @@ void fm_engine_base::save_restore(ymfm_saved_state &state) m_regs.save_restore(state); // save channel data - for (int chnum = 0; chnum < CHANNELS; chnum++) + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) m_channel[chnum]->save_restore(state); // save operator data - for (int opnum = 0; opnum < OPERATORS; opnum++) + for (uint32_t opnum = 0; opnum < OPERATORS; opnum++) m_operator[opnum]->save_restore(state); // invalidate any caches @@ -1262,7 +1265,7 @@ uint32_t fm_engine_base::clock(uint32_t chanmask) // call each channel to prepare m_active_channels = 0; - for (int chnum = 0; chnum < CHANNELS; chnum++) + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) if (bitfield(chanmask, chnum)) if (m_channel[chnum]->prepare()) m_active_channels |= 1 << chnum; @@ -1282,7 +1285,7 @@ uint32_t fm_engine_base::clock(uint32_t chanmask) int32_t lfo_raw_pm = m_regs.clock_noise_and_lfo(); // now update the state of all the channels and operators - for (int chnum = 0; chnum < CHANNELS; chnum++) + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) if (bitfield(chanmask, chnum)) m_channel[chnum]->clock(m_env_counter, lfo_raw_pm); @@ -1318,7 +1321,7 @@ void fm_engine_base::output(output_data &output, uint32_t rshift, uint32_t phase_select = (bitfield(op13phase, 2) ^ bitfield(op13phase, 7)) | bitfield(op13phase, 3) | (bitfield(op17phase, 5) ^ bitfield(op17phase, 3)); // sum over all the desired channels - for (int chnum = 0; chnum < CHANNELS; chnum++) + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) if (bitfield(chanmask, chnum)) { if (chnum == 6) @@ -1336,7 +1339,7 @@ void fm_engine_base::output(output_data &output, uint32_t rshift, else { // sum over all the desired channels - for (int chnum = 0; chnum < CHANNELS; chnum++) + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) if (bitfield(chanmask, chnum)) { if (m_channel[chnum]->is4op()) @@ -1413,8 +1416,8 @@ void fm_engine_base::assign_operators() typename RegisterType::operator_mapping map; m_regs.operator_map(map); - for (int chnum = 0; chnum < CHANNELS; chnum++) - for (int index = 0; index < 4; index++) + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) + for (uint32_t index = 0; index < 4; index++) { uint32_t opnum = bitfield(map.chan[chnum], 8 * index, 8); m_channel[chnum]->assign(index, (opnum == 0xff) ? nullptr : m_operator[opnum].get()); @@ -1466,7 +1469,7 @@ void fm_engine_base::engine_timer_expired(uint32_t tnum) // if timer A fired in CSM mode, trigger CSM on all relevant channels if (tnum == 0 && m_regs.csm()) - for (int chnum = 0; chnum < CHANNELS; chnum++) + for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) if (bitfield(RegisterType::CSM_TRIGGER_MASK, chnum)) m_channel[chnum]->keyonoff(1, KEYON_CSM, chnum); diff --git a/3rdparty/ymfm/src/ymfm_opl.cpp b/3rdparty/ymfm/src/ymfm_opl.cpp index 55e85f398a0..108262f4b63 100644 --- a/3rdparty/ymfm/src/ymfm_opl.cpp +++ b/3rdparty/ymfm/src/ymfm_opl.cpp @@ -80,13 +80,13 @@ opl_registers_base::opl_registers_base() : uint16_t *wf7 = &m_waveform[7 % WAVEFORMS][0]; // create the waveforms - for (int index = 0; index < WAVEFORM_LENGTH; index++) + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) wf0[index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15); if (WAVEFORMS >= 4) { uint16_t zeroval = wf0[0]; - for (int index = 0; index < WAVEFORM_LENGTH; index++) + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) { wf1[index] = bitfield(index, 9) ? zeroval : wf0[index]; wf2[index] = wf0[index] & 0x7fff; @@ -440,17 +440,17 @@ opll_registers::opll_registers() : m_lfo_am(0) { // create the waveforms - for (int index = 0; index < WAVEFORM_LENGTH; index++) + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15); uint16_t zeroval = m_waveform[0][0]; - for (int index = 0; index < WAVEFORM_LENGTH; index++) + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) m_waveform[1][index] = bitfield(index, 9) ? zeroval : m_waveform[0][index]; // initialize the instruments to something sane - for (int choffs = 0; choffs < CHANNELS; choffs++) + for (uint32_t choffs = 0; choffs < CHANNELS; choffs++) m_chinst[choffs] = &m_regdata[0]; - for (int opoffs = 0; opoffs < OPERATORS; opoffs++) + for (uint32_t opoffs = 0; opoffs < OPERATORS; opoffs++) m_opinst[opoffs] = &m_regdata[bitfield(opoffs, 0)]; } @@ -794,7 +794,7 @@ void ym3526::write_address(uint8_t data) { // YM3526 doesn't expose a busy signal, and the datasheets don't indicate // delays, but all other OPL chips need 12 cycles for address writes - m_fm.intf().ymfm_set_busy_end(12); + m_fm.intf().ymfm_set_busy_end(12 * m_fm.clock_prescale()); // just set the address m_address = data; @@ -810,7 +810,7 @@ void ym3526::write_data(uint8_t data) { // YM3526 doesn't expose a busy signal, and the datasheets don't indicate // delays, but all other OPL chips need 84 cycles for data writes - m_fm.intf().ymfm_set_busy_end(84); + m_fm.intf().ymfm_set_busy_end(84 * m_fm.clock_prescale()); // write to FM m_fm.write(m_address, data); @@ -933,7 +933,7 @@ uint8_t y8950::read_data() switch (m_address) { case 0x05: // keyboard in - result = m_fm.intf().ymfm_io_read(1); + result = m_fm.intf().ymfm_external_read(ACCESS_IO, 1); break; case 0x09: // ADPCM data @@ -942,7 +942,7 @@ uint8_t y8950::read_data() break; case 0x19: // I/O data - result = m_fm.intf().ymfm_io_read(0); + result = m_fm.intf().ymfm_external_read(ACCESS_IO, 0); break; default: @@ -983,7 +983,7 @@ void y8950::write_address(uint8_t data) { // Y8950 doesn't expose a busy signal, but it does indicate that // address writes should be no faster than every 12 clocks - m_fm.intf().ymfm_set_busy_end(12); + m_fm.intf().ymfm_set_busy_end(12 * m_fm.clock_prescale()); // just set the address m_address = data; @@ -1000,7 +1000,7 @@ void y8950::write_data(uint8_t data) // Y8950 doesn't expose a busy signal, but it does indicate that // data writes should be no faster than every 12 clocks for // registers 00-1A, or every 84 clocks for other registers - m_fm.intf().ymfm_set_busy_end((m_address <= 0x1a) ? 12 : 84); + m_fm.intf().ymfm_set_busy_end(((m_address <= 0x1a) ? 12 : 84) * m_fm.clock_prescale()); // handle special addresses switch (m_address) @@ -1011,7 +1011,7 @@ void y8950::write_data(uint8_t data) break; case 0x06: // keyboard out - m_fm.intf().ymfm_io_write(1, data); + m_fm.intf().ymfm_external_write(ACCESS_IO, 1, data); break; case 0x08: // split FM/ADPCM-B @@ -1041,7 +1041,7 @@ void y8950::write_data(uint8_t data) break; case 0x19: // I/O data - m_fm.intf().ymfm_io_write(0, data & m_io_ddr); + m_fm.intf().ymfm_external_write(ACCESS_IO, 0, data & m_io_ddr); break; default: // everything else to FM @@ -1174,7 +1174,7 @@ void ym3812::write_address(uint8_t data) { // YM3812 doesn't expose a busy signal, but it does indicate that // address writes should be no faster than every 12 clocks - m_fm.intf().ymfm_set_busy_end(12); + m_fm.intf().ymfm_set_busy_end(12 * m_fm.clock_prescale()); // just set the address m_address = data; @@ -1190,7 +1190,7 @@ void ym3812::write_data(uint8_t data) { // YM3812 doesn't expose a busy signal, but it does indicate that // data writes should be no faster than every 84 clocks - m_fm.intf().ymfm_set_busy_end(84); + m_fm.intf().ymfm_set_busy_end(84 * m_fm.clock_prescale()); // write to FM m_fm.write(m_address, data); @@ -1318,7 +1318,7 @@ void ymf262::write_address(uint8_t data) { // YMF262 doesn't expose a busy signal, but it does indicate that // address writes should be no faster than every 32 clocks - m_fm.intf().ymfm_set_busy_end(32); + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); // just set the address m_address = data; @@ -1334,7 +1334,7 @@ void ymf262::write_data(uint8_t data) { // YMF262 doesn't expose a busy signal, but it does indicate that // data writes should be no faster than every 32 clocks - m_fm.intf().ymfm_set_busy_end(32); + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); // write to FM m_fm.write(m_address, data); @@ -1350,7 +1350,7 @@ void ymf262::write_address_hi(uint8_t data) { // YMF262 doesn't expose a busy signal, but it does indicate that // address writes should be no faster than every 32 clocks - m_fm.intf().ymfm_set_busy_end(32); + m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale()); // just set the address m_address = data | 0x100; @@ -1411,6 +1411,213 @@ void ymf262::generate(output_data *output, uint32_t numsamples) +//********************************************************* +// YMF289B +//********************************************************* + +// YMF289B is a YMF262 with the following changes: +// * "Power down" mode added +// * Bulk register clear added +// * Busy flag added to the status register +// * Shorter busy times +// * All registers can be read +// * Only 2 outputs exposed + +//------------------------------------------------- +// ymf289b - constructor +//------------------------------------------------- + +ymf289b::ymf289b(ymfm_interface &intf) : + m_address(0), + m_fm(intf) +{ +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ymf289b::reset() +{ + // reset the engines + m_fm.reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ymf289b::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + m_fm.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ymf289b::read_status() +{ + uint8_t result = m_fm.status(); + + // YMF289B adds a busy flag + if (ymf289b_mode() && m_fm.intf().ymfm_is_busy()) + result |= STATUS_BUSY_FLAGS; + return result; +} + + +//------------------------------------------------- +// read_data - read the data register +//------------------------------------------------- + +uint8_t ymf289b::read_data() +{ + uint8_t result = 0xff; + + // YMF289B can read register data back + if (ymf289b_mode()) + result = m_fm.regs().read(m_address); + return result; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ymf289b::read(uint32_t offset) +{ + uint8_t result = 0xff; + switch (offset & 3) + { + case 0: // status port + result = read_status(); + break; + + case 1: // data port + result = read_data(); + break; + + case 2: + case 3: + debug::log_unexpected_read_write("Unexpected read from YMF289B offset %d\n", offset & 3); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ymf289b::write_address(uint8_t data) +{ + m_address = data; + + // count busy time + m_fm.intf().ymfm_set_busy_end(56); +} + + +//------------------------------------------------- +// write_data - handle a write to the data +// register +//------------------------------------------------- + +void ymf289b::write_data(uint8_t data) +{ + // write to FM + m_fm.write(m_address, data); + + // writes to 0x108 with the CLR flag set clear the registers + if (m_address == 0x108 && bitfield(data, 2) != 0) + m_fm.regs().reset(); + + // count busy time + m_fm.intf().ymfm_set_busy_end(56); +} + + +//------------------------------------------------- +// write_address_hi - handle a write to the upper +// address register +//------------------------------------------------- + +void ymf289b::write_address_hi(uint8_t data) +{ + // just set the address + m_address = data | 0x100; + + // tests reveal that in compatibility mode, upper bit is masked + // except for register 0x105 + if (m_fm.regs().newflag() == 0 && m_address != 0x105) + m_address &= 0xff; + + // count busy time + m_fm.intf().ymfm_set_busy_end(56); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ymf289b::write(uint32_t offset, uint8_t data) +{ + switch (offset & 3) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + + case 2: // address port + write_address_hi(data); + break; + + case 3: // data port + write_data(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate samples of sound +//------------------------------------------------- + +void ymf289b::generate(output_data *output, uint32_t numsamples) +{ + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + // clock the system + m_fm.clock(fm_engine::ALL_CHANNELS); + + // update the FM content; mixing details for YMF262 need verification + fm_engine::output_data full; + m_fm.output(full.clear(), 0, 32767, fm_engine::ALL_CHANNELS); + + // YMF278B output is 16-bit offset serial via YAC512 DAC, but + // only 2 of the 4 outputs are exposed + output->data[0] = full.data[0]; + output->data[1] = full.data[1]; + output->clamp16(); + } +} + + + //********************************************************* // YMF278B //********************************************************* @@ -1421,7 +1628,11 @@ void ymf262::generate(output_data *output, uint32_t numsamples) ymf278b::ymf278b(ymfm_interface &intf) : m_address(0), - m_fm(intf) + m_fm_pos(0), + m_load_remaining(0), + m_next_status_id(false), + m_fm(intf), + m_pcm(intf) { } @@ -1434,6 +1645,10 @@ void ymf278b::reset() { // reset the engines m_fm.reset(); + m_pcm.reset(); + + // next status read will return ID + m_next_status_id = true; } @@ -1444,7 +1659,11 @@ void ymf278b::reset() void ymf278b::save_restore(ymfm_saved_state &state) { state.save_restore(m_address); + state.save_restore(m_fm_pos); + state.save_restore(m_load_remaining); + state.save_restore(m_next_status_id); m_fm.save_restore(state); + m_pcm.save_restore(state); } @@ -1454,7 +1673,47 @@ void ymf278b::save_restore(ymfm_saved_state &state) uint8_t ymf278b::read_status() { - return m_fm.status(); + uint8_t result; + + // first status read after initialization returns a chip ID, which + // varies based on the "new" flags, indicating the mode + if (m_next_status_id) + { + if (m_fm.regs().new2flag()) + result = 0x02; + else if (m_fm.regs().newflag()) + result = 0x00; + else + result = 0x06; + m_next_status_id = false; + } + else + { + result = m_fm.status(); + if (m_fm.intf().ymfm_is_busy()) + result |= STATUS_BUSY; + if (m_load_remaining != 0) + result |= STATUS_LD; + + // if new2 flag is not set, we're in OPL2 or OPL3 mode + if (!m_fm.regs().new2flag()) + result &= ~(STATUS_BUSY | STATUS_LD); + } + return result; +} + + +//------------------------------------------------- +// write_data_pcm - handle a write to the PCM data +// register +//------------------------------------------------- + +uint8_t ymf278b::read_data_pcm() +{ + // write to FM + if (bitfield(m_address, 9) != 0) + return m_pcm.read(m_address & 0xff); + return 0; } @@ -1471,8 +1730,8 @@ uint8_t ymf278b::read(uint32_t offset) result = read_status(); break; - case 5: // PCM data port (not supported for now) - //result = read_data_pcm(); + case 5: // PCM data port + result = read_data_pcm(); break; default: @@ -1503,7 +1762,19 @@ void ymf278b::write_address(uint8_t data) void ymf278b::write_data(uint8_t data) { // write to FM - m_fm.write(m_address, data); + if (bitfield(m_address, 9) == 0) + { + uint8_t old = m_fm.regs().new2flag(); + m_fm.write(m_address, data); + + // changing NEW2 from 0->1 causes the next status read to + // return the chip ID + if (old == 0 && m_fm.regs().new2flag() != 0) + m_next_status_id = true; + } + + // BUSY goes for 56 clocks on FM writes + m_fm.intf().ymfm_set_busy_end(56); } @@ -1524,6 +1795,44 @@ void ymf278b::write_address_hi(uint8_t data) } +//------------------------------------------------- +// write_address_pcm - handle a write to the upper +// address register +//------------------------------------------------- + +void ymf278b::write_address_pcm(uint8_t data) +{ + // just set the address + m_address = data | 0x200; + + // YMF262, in compatibility mode, treats the upper bit as masked + // except for register 0x105; assuming YMF278B works the same way? + if (m_fm.regs().newflag() == 0 && m_address != 0x105) + m_address &= 0xff; +} + + +//------------------------------------------------- +// write_data_pcm - handle a write to the PCM data +// register +//------------------------------------------------- + +void ymf278b::write_data_pcm(uint8_t data) +{ + // write to FM + if (bitfield(m_address, 9) != 0) + m_pcm.write(m_address & 0xff, data); + + // writes to the waveform number cause loads to happen for "about 300usec" + // which is ~13 samples at the nominal output frequency of 44.1kHz + if (m_address >= 0x08 && m_address <= 0x1f) + m_load_remaining = 13; + + // BUSY goes for 88 clocks on PCM writes + m_fm.intf().ymfm_set_busy_end(88); +} + + //------------------------------------------------- // write - handle a write to the register // interface @@ -1549,12 +1858,12 @@ void ymf278b::write(uint32_t offset, uint8_t data) write_data(data); break; - case 4: // PCM address port (not supported for now) - //write_address_pcm(data); + case 4: // PCM address port + write_address_pcm(data); break; - case 5: // PCM address port (not supported for now) - //write_data_pcm(data); + case 5: // PCM address port + write_data_pcm(data); break; default: @@ -1570,17 +1879,50 @@ void ymf278b::write(uint32_t offset, uint8_t data) void ymf278b::generate(output_data *output, uint32_t numsamples) { - for (uint32_t samp = 0; samp < numsamples; samp++) + static const int16_t s_mix_scale[8] = { 0x7fa, 0x5a4, 0x3fd, 0x2d2, 0x1fe, 0x169, 0xff, 0 }; + int32_t const pcm_l = s_mix_scale[m_pcm.regs().mix_pcm_l()]; + int32_t const pcm_r = s_mix_scale[m_pcm.regs().mix_pcm_r()]; + int32_t const fm_l = s_mix_scale[m_pcm.regs().mix_fm_l()]; + int32_t const fm_r = s_mix_scale[m_pcm.regs().mix_fm_r()]; + for (uint32_t samp = 0; samp < numsamples; samp++, output++) { // clock the system + m_fm_pos += FM_EXTRA_SAMPLE_STEP; + if (m_fm_pos >= FM_EXTRA_SAMPLE_THRESH) + { + m_fm.clock(fm_engine::ALL_CHANNELS); + m_fm_pos -= FM_EXTRA_SAMPLE_THRESH; + } m_fm.clock(fm_engine::ALL_CHANNELS); + m_pcm.clock(pcm_engine::ALL_CHANNELS); // update the FM content; mixing details for YMF278B need verification - m_fm.output(output->clear(), 0, 32767, fm_engine::ALL_CHANNELS); + fm_engine::output_data fmout; + m_fm.output(fmout.clear(), 0, 32767, fm_engine::ALL_CHANNELS); + + // update the PCM content + pcm_engine::output_data pcmout; + m_pcm.output(pcmout.clear(), pcm_engine::ALL_CHANNELS); + + // DO0 output: FM channels 2+3 only + output->data[0] = fmout.data[2]; + output->data[1] = fmout.data[3]; + + // DO1 output: wavetable channels 2+3 only + output->data[2] = pcmout.data[2]; + output->data[3] = pcmout.data[3]; + + // DO2 output: mixed FM channels 0+1 and wavetable channels 0+1 + output->data[4] = (fmout.data[0] * fm_l + pcmout.data[0] * pcm_l) >> 11; + output->data[5] = (fmout.data[1] * fm_r + pcmout.data[1] * pcm_r) >> 11; // YMF278B output is 16-bit 2s complement serial output->clamp16(); } + + // decrement the load waiting count + if (m_load_remaining > 0) + m_load_remaining -= std::min(m_load_remaining, numsamples); } diff --git a/3rdparty/ymfm/src/ymfm_opl.h b/3rdparty/ymfm/src/ymfm_opl.h index 812393904ef..843e5b274d2 100644 --- a/3rdparty/ymfm/src/ymfm_opl.h +++ b/3rdparty/ymfm/src/ymfm_opl.h @@ -36,6 +36,7 @@ #include "ymfm.h" #include "ymfm_adpcm.h" #include "ymfm_fm.h" +#include "ymfm_pcm.h" namespace ymfm { @@ -170,7 +171,7 @@ public: void operator_map(operator_mapping &dest) const; // OPL4 apparently can read back FM registers? - uint8_t read(uint16_t index) { return m_regdata[index]; } + uint8_t read(uint16_t index) const { return m_regdata[index]; } // handle writes to the register array bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask); @@ -375,6 +376,9 @@ public: struct operator_mapping { uint32_t chan[CHANNELS]; }; void operator_map(operator_mapping &dest) const; + // read a register value + uint8_t read(uint16_t index) const { return m_regdata[index]; } + // handle writes to the register array bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask); @@ -673,22 +677,19 @@ protected: }; +// ======================> ymf289b -//********************************************************* -// OPL4 IMPLEMENTATION CLASSES -//********************************************************* - -// ======================> ymf278b - -class ymf278b +class ymf289b { + static constexpr uint8_t STATUS_BUSY_FLAGS = 0x05; + public: - using fm_engine = fm_engine_base; + using fm_engine = fm_engine_base; using output_data = fm_engine::output_data; - static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS; + static constexpr uint32_t OUTPUTS = 2; // constructor - ymf278b(ymfm_interface &intf); + ymf289b(ymfm_interface &intf); // reset void reset(); @@ -702,6 +703,7 @@ public: // read access uint8_t read_status(); + uint8_t read_data(); uint8_t read(uint32_t offset); // write access @@ -714,6 +716,9 @@ public: void generate(output_data *output, uint32_t numsamples = 1); protected: + // internal helpers + bool ymf289b_mode() { return ((m_fm.regs().read(0x105) & 0x04) != 0); } + // internal state uint16_t m_address; // address register fm_engine m_fm; // core FM engine @@ -721,6 +726,74 @@ protected: +//********************************************************* +// OPL4 IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> ymf278b + +class ymf278b +{ + // Using the nominal datasheet frequency of 33.868MHz, the output of the + // chip will be clock/768 = 44.1kHz. However, the FM engine is clocked + // internally at clock/(19*36), or 49.515kHz, so the FM output needs to + // be downsampled. We treat this as needing to clock the FM engine an + // extra tick every few samples. The exact ratio is 768/(19*36) or + // 768/684 = 192/171. So if we always clock the FM once, we'll have + // 192/171 - 1 = 21/171 left. Thus we count 21 for each sample and when + // it gets above 171, we tick an extra time. + static constexpr uint32_t FM_EXTRA_SAMPLE_THRESH = 171; + static constexpr uint32_t FM_EXTRA_SAMPLE_STEP = 192 - FM_EXTRA_SAMPLE_THRESH; + +public: + using fm_engine = fm_engine_base; + static constexpr uint32_t OUTPUTS = 6; + using output_data = ymfm_output; + + static constexpr uint8_t STATUS_BUSY = 0x01; + static constexpr uint8_t STATUS_LD = 0x02; + + // constructor + ymf278b(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return input_clock / 768; } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read_data_pcm(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write_address_hi(uint8_t data); + void write_address_pcm(uint8_t data); + void write_data_pcm(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate samples of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal state + uint16_t m_address; // address register + uint32_t m_fm_pos; // FM resampling position + uint32_t m_load_remaining; // how many more samples until LD flag clears + bool m_next_status_id; // flag to track which status ID to return + fm_engine m_fm; // core FM engine + pcm_engine m_pcm; // core PCM engine +}; + + + //********************************************************* // OPLL IMPLEMENTATION CLASSES //********************************************************* diff --git a/3rdparty/ymfm/src/ymfm_opm.cpp b/3rdparty/ymfm/src/ymfm_opm.cpp index fe682059f63..6a1e96613c3 100644 --- a/3rdparty/ymfm/src/ymfm_opm.cpp +++ b/3rdparty/ymfm/src/ymfm_opm.cpp @@ -51,12 +51,12 @@ opm_registers::opm_registers() : m_lfo_am(0) { // create the waveforms - for (int index = 0; index < WAVEFORM_LENGTH; index++) + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15); // create the LFO waveforms; AM in the low 8 bits, PM in the upper 8 // waveforms are adjusted to match the pictures in the application manual - for (int index = 0; index < LFO_WAVEFORM_LENGTH; index++) + for (uint32_t index = 0; index < LFO_WAVEFORM_LENGTH; index++) { // waveform 0 is a sawtooth uint8_t am = index ^ 0xff; @@ -484,7 +484,7 @@ void ym2151::write_data(uint8_t data) if (m_address == 0x1b) { // writes to register 0x1B send the upper 2 bits to the output lines - m_fm.intf().ymfm_io_write(0, data >> 6); + m_fm.intf().ymfm_external_write(ACCESS_IO, 0, data >> 6); } // mark busy for a bit diff --git a/3rdparty/ymfm/src/ymfm_opn.cpp b/3rdparty/ymfm/src/ymfm_opn.cpp index 78694651274..e84f63002e2 100644 --- a/3rdparty/ymfm/src/ymfm_opn.cpp +++ b/3rdparty/ymfm/src/ymfm_opn.cpp @@ -48,7 +48,7 @@ opn_registers_base::opn_registers_base() : m_lfo_am(0) { // create the waveforms - for (int index = 0; index < WAVEFORM_LENGTH; index++) + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15); } @@ -1548,6 +1548,423 @@ void ym2608::clock_fm_and_adpcm() } +//********************************************************* +// YMF288 +//********************************************************* + +// YMF288 is a YM2608 with the following changes: +// * ADPCM-B part removed +// * prescaler removed (fixed at 6) +// * CSM removed +// * Low power mode added +// * SSG tone frequency is altered in some way? (explicitly DC for Tp 0-7, also double volume in some cases) +// * I/O ports removed +// * Shorter busy times +// * All registers can be read + +//------------------------------------------------- +// ymf288 - constructor +//------------------------------------------------- + +ymf288::ymf288(ymfm_interface &intf) : + m_fidelity(OPN_FIDELITY_MAX), + m_address(0), + m_irq_enable(0x03), + m_flag_control(0x03), + m_fm(intf), + m_ssg(intf), + m_ssg_resampler(m_ssg), + m_adpcm_a(intf, 0) +{ + m_last_fm.clear(); + update_prescale(); +} + + +//------------------------------------------------- +// reset - reset the system +//------------------------------------------------- + +void ymf288::reset() +{ + // reset the engines + m_fm.reset(); + m_ssg.reset(); + m_adpcm_a.reset(); + + // configure ADPCM percussion sounds; these are present in an embedded ROM + m_adpcm_a.set_start_end(0, 0x0000, 0x01bf); // bass drum + m_adpcm_a.set_start_end(1, 0x01c0, 0x043f); // snare drum + m_adpcm_a.set_start_end(2, 0x0440, 0x1b7f); // top cymbal + m_adpcm_a.set_start_end(3, 0x1b80, 0x1cff); // high hat + m_adpcm_a.set_start_end(4, 0x1d00, 0x1f7f); // tom tom + m_adpcm_a.set_start_end(5, 0x1f80, 0x1fff); // rim shot + + // initialize our special interrupt states, then read the upper status + // register, which updates the IRQs + m_irq_enable = 0x03; + m_flag_control = 0x00; + read_status_hi(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void ymf288::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_address); + state.save_restore(m_irq_enable); + state.save_restore(m_flag_control); + state.save_restore(m_last_fm.data); + + m_fm.save_restore(state); + m_ssg.save_restore(state); + m_ssg_resampler.save_restore(state); + m_adpcm_a.save_restore(state); +} + + +//------------------------------------------------- +// read_status - read the status register +//------------------------------------------------- + +uint8_t ymf288::read_status() +{ + uint8_t result = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB); + if (m_fm.intf().ymfm_is_busy()) + result |= fm_engine::STATUS_BUSY; + return result; +} + + +//------------------------------------------------- +// read_data - read the data register +//------------------------------------------------- + +uint8_t ymf288::read_data() +{ + uint8_t result = 0; + if (m_address < 0x0e) + { + // 00-0D: Read from SSG + result = m_ssg.read(m_address & 0x0f); + } + else if (m_address < 0x10) + { + // 0E-0F: I/O ports not supported + result = 0xff; + } + else if (m_address == 0xff) + { + // FF: ID code + result = 2; + } + else if (ymf288_mode()) + { + // registers are readable in YMF288 mode + result = m_fm.regs().read(m_address); + } + return result; +} + + +//------------------------------------------------- +// read_status_hi - read the extended status +// register +//------------------------------------------------- + +uint8_t ymf288::read_status_hi() +{ + // fetch regular status + uint8_t status = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB); + + // turn off any bits that have been requested to be masked + status &= ~(m_flag_control & 0x03); + + // update the status so that IRQs are propagated + m_fm.set_reset_status(status, ~status); + + // merge in the busy flag + if (m_fm.intf().ymfm_is_busy()) + status |= fm_engine::STATUS_BUSY; + return status; +} + + +//------------------------------------------------- +// read - handle a read from the device +//------------------------------------------------- + +uint8_t ymf288::read(uint32_t offset) +{ + uint8_t result = 0; + switch (offset & 3) + { + case 0: // status port, YM2203 compatible + result = read_status(); + break; + + case 1: // data port + result = read_data(); + break; + + case 2: // status port, extended + result = read_status_hi(); + break; + + case 3: // unmapped + debug::log_unexpected_read_write("Unexpected read from YMF288 offset %d\n", offset & 3); + break; + } + return result; +} + + +//------------------------------------------------- +// write_address - handle a write to the address +// register +//------------------------------------------------- + +void ymf288::write_address(uint8_t data) +{ + // just set the address + m_address = data; + + // in YMF288 mode, busy is signaled after address writes too + if (ymf288_mode()) + m_fm.intf().ymfm_set_busy_end(16); +} + + +//------------------------------------------------- +// write - handle a write to the data register +//------------------------------------------------- + +void ymf288::write_data(uint8_t data) +{ + // ignore if paired with upper address + if (bitfield(m_address, 8)) + return; + + // wait times are shorter in YMF288 mode + int busy_cycles = ymf288_mode() ? 16 : 32 * m_fm.clock_prescale(); + if (m_address < 0x0e) + { + // 00-0D: write to SSG + m_ssg.write(m_address & 0x0f, data); + } + else if (m_address < 0x10) + { + // 0E-0F: I/O ports not supported + } + else if (m_address < 0x20) + { + // 10-1F: write to ADPCM-A + m_adpcm_a.write(m_address & 0x0f, data); + busy_cycles = 32 * m_fm.clock_prescale(); + } + else if (m_address == 0x27) + { + // 27: mode register; CSM isn't supported so disable it + data &= 0x7f; + m_fm.write(m_address, data); + } + else if (m_address == 0x29) + { + // 29: special IRQ mask register + m_irq_enable = data; + m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x03); + } + else + { + // 20-27, 2A-FF: write to FM + m_fm.write(m_address, data); + } + + // mark busy for a bit + m_fm.intf().ymfm_set_busy_end(busy_cycles); +} + + +//------------------------------------------------- +// write_address_hi - handle a write to the upper +// address register +//------------------------------------------------- + +void ymf288::write_address_hi(uint8_t data) +{ + // just set the address + m_address = 0x100 | data; + + // in YMF288 mode, busy is signaled after address writes too + if (ymf288_mode()) + m_fm.intf().ymfm_set_busy_end(16); +} + + +//------------------------------------------------- +// write_data_hi - handle a write to the upper +// data register +//------------------------------------------------- + +void ymf288::write_data_hi(uint8_t data) +{ + // ignore if paired with upper address + if (!bitfield(m_address, 8)) + return; + + // wait times are shorter in YMF288 mode + int busy_cycles = ymf288_mode() ? 16 : 32 * m_fm.clock_prescale(); + if (m_address == 0x110) + { + // 110: IRQ flag control + if (bitfield(data, 7)) + m_fm.set_reset_status(0, 0xff); + else + { + m_flag_control = data; + m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x03); + } + } + else + { + // 100-10F,111-1FF: write to FM + m_fm.write(m_address, data); + } + + // mark busy for a bit + m_fm.intf().ymfm_set_busy_end(busy_cycles); +} + + +//------------------------------------------------- +// write - handle a write to the register +// interface +//------------------------------------------------- + +void ymf288::write(uint32_t offset, uint8_t data) +{ + switch (offset & 3) + { + case 0: // address port + write_address(data); + break; + + case 1: // data port + write_data(data); + break; + + case 2: // upper address port + write_address_hi(data); + break; + + case 3: // upper data port + write_data_hi(data); + break; + } +} + + +//------------------------------------------------- +// generate - generate one sample of sound +//------------------------------------------------- + +void ymf288::generate(output_data *output, uint32_t numsamples) +{ + // FM output is just repeated the prescale number of times; note that + // 0 is a special 1.5 case + if (m_fm_samples_per_output != 0) + { + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + if ((m_ssg_resampler.sampindex() + samp) % m_fm_samples_per_output == 0) + clock_fm_and_adpcm(); + output->data[0] = m_last_fm.data[0]; + output->data[1] = m_last_fm.data[1]; + } + } + else + { + for (uint32_t samp = 0; samp < numsamples; samp++, output++) + { + uint32_t step = (m_ssg_resampler.sampindex() + samp) % 3; + if (step == 0) + clock_fm_and_adpcm(); + output->data[0] = m_last_fm.data[0]; + output->data[1] = m_last_fm.data[1]; + if (step == 1) + { + clock_fm_and_adpcm(); + output->data[0] = (output->data[0] + m_last_fm.data[0]) / 2; + output->data[1] = (output->data[1] + m_last_fm.data[1]) / 2; + } + } + } + + // resample the SSG as configured + m_ssg_resampler.resample(output - numsamples, numsamples); +} + + +//------------------------------------------------- +// update_prescale - update the prescale value, +// recomputing derived values +//------------------------------------------------- + +void ymf288::update_prescale() +{ + // Fidelity: ---- minimum ---- ---- medium ----- ---- maximum----- + // rate = clock/144 rate = clock/144 rate = clock/16 + // Prescale FM rate SSG rate FM rate SSG rate FM rate SSG rate + // 6 1:1 2:9 1:1 2:9 9:1 2:1 + + // compute the number of FM samples per output sample, and select the + // resampler function + if (m_fidelity == OPN_FIDELITY_MIN || m_fidelity == OPN_FIDELITY_MED) + { + m_fm_samples_per_output = 1; + m_ssg_resampler.configure(2, 9); + } + else + { + m_fm_samples_per_output = 9; + m_ssg_resampler.configure(2, 1); + } + + // if overriding the SSG, override the configuration with the nop + // resampler to at least keep the sample index moving forward + if (m_ssg.overridden()) + m_ssg_resampler.configure(0, 0); +} + + +//------------------------------------------------- +// clock_fm_and_adpcm - clock FM and ADPCM state +//------------------------------------------------- + +void ymf288::clock_fm_and_adpcm() +{ + // top bit of the IRQ enable flags controls 3-channel vs 6-channel mode + uint32_t fmmask = bitfield(m_irq_enable, 7) ? 0x3f : 0x07; + + // clock the system + uint32_t env_counter = m_fm.clock(fm_engine::ALL_CHANNELS); + + // clock the ADPCM-A engine on every envelope cycle + // (channels 4 and 5 clock every 2 envelope clocks) + if (bitfield(env_counter, 0, 2) == 0) + m_adpcm_a.clock(bitfield(env_counter, 2) ? 0x0f : 0x3f); + + // update the FM content; OPNA is 13-bit with no intermediate clipping + m_fm.output(m_last_fm.clear(), 1, 32767, fmmask); + + // mix in the ADPCM + m_adpcm_a.output(m_last_fm, 0x3f); +} + + //********************************************************* // YM2610 @@ -1629,11 +2046,16 @@ uint8_t ym2610::read_status() uint8_t ym2610::read_data() { uint8_t result = 0; - if (m_address < 0x10) + if (m_address < 0x0e) { - // 00-0F: Read from SSG + // 00-0D: Read from SSG result = m_ssg.read(m_address & 0x0f); } + else if (m_address < 0x10) + { + // 0E-0F: I/O ports not supported + result = 0xff; + } else if (m_address == 0xff) { // FF: ID code @@ -1716,11 +2138,15 @@ void ym2610::write_data(uint8_t data) if (bitfield(m_address, 8)) return; - if (m_address < 0x10) + if (m_address < 0x0e) { - // 00-0F: write to SSG + // 00-0D: write to SSG m_ssg.write(m_address & 0x0f, data); } + else if (m_address < 0x10) + { + // 0E-0F: I/O ports not supported + } else if (m_address < 0x1c) { // 10-1B: write to ADPCM-B diff --git a/3rdparty/ymfm/src/ymfm_opn.h b/3rdparty/ymfm/src/ymfm_opn.h index 3bd5f2ead1a..a9bb01d9763 100644 --- a/3rdparty/ymfm/src/ymfm_opn.h +++ b/3rdparty/ymfm/src/ymfm_opn.h @@ -164,6 +164,9 @@ public: struct operator_mapping { uint32_t chan[CHANNELS]; }; void operator_map(operator_mapping &dest) const; + // read a register value + uint8_t read(uint16_t index) const { return m_regdata[index]; } + // handle writes to the register array bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask); @@ -618,6 +621,80 @@ protected: }; +// ======================> ymf288 + +class ymf288 +{ +public: + using fm_engine = fm_engine_base; + static constexpr uint32_t FM_OUTPUTS = fm_engine::OUTPUTS; + static constexpr uint32_t SSG_OUTPUTS = 1; + static constexpr uint32_t OUTPUTS = FM_OUTPUTS + SSG_OUTPUTS; + using output_data = ymfm_output; + + // constructor + ymf288(ymfm_interface &intf); + + // configuration + void ssg_override(ssg_override &intf) { m_ssg.override(intf); } + void set_fidelity(opn_fidelity fidelity) { m_fidelity = fidelity; update_prescale(); } + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const + { + switch (m_fidelity) + { + case OPN_FIDELITY_MIN: return input_clock / 144; + case OPN_FIDELITY_MED: return input_clock / 144; + default: + case OPN_FIDELITY_MAX: return input_clock / 16; + } + } + uint32_t ssg_effective_clock(uint32_t input_clock) const { return input_clock / 4; } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read_data(); + uint8_t read_status_hi(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write_address_hi(uint8_t data); + void write_data_hi(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate one sample of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal helpers + bool ymf288_mode() { return ((m_fm.regs().read(0x20) & 0x02) != 0); } + void update_prescale(); + void clock_fm_and_adpcm(); + + // internal state + opn_fidelity m_fidelity; // configured fidelity + uint16_t m_address; // address register + uint8_t m_fm_samples_per_output; // how many samples to repeat + uint8_t m_irq_enable; // IRQ enable register + uint8_t m_flag_control; // flag control register + fm_engine::output_data m_last_fm; // last FM output + fm_engine m_fm; // core FM engine + ssg_engine m_ssg; // SSG engine + ssg_resampler m_ssg_resampler; // SSG resampler helper + adpcm_a_engine m_adpcm_a; // ADPCM-A engine +}; + + // ======================> ym2610/ym2610b class ym2610 diff --git a/3rdparty/ymfm/src/ymfm_opq.cpp b/3rdparty/ymfm/src/ymfm_opq.cpp index 3fa67ddbc64..e886722876b 100644 --- a/3rdparty/ymfm/src/ymfm_opq.cpp +++ b/3rdparty/ymfm/src/ymfm_opq.cpp @@ -58,11 +58,11 @@ opq_registers::opq_registers() : m_lfo_am(0) { // create the waveforms - for (int index = 0; index < WAVEFORM_LENGTH; index++) + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15); uint16_t zeroval = m_waveform[0][0]; - for (int index = 0; index < WAVEFORM_LENGTH; index++) + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) m_waveform[1][index] = bitfield(index, 9) ? zeroval : m_waveform[0][index]; } diff --git a/3rdparty/ymfm/src/ymfm_opz.cpp b/3rdparty/ymfm/src/ymfm_opz.cpp index b3acdafc281..ae814e45b13 100644 --- a/3rdparty/ymfm/src/ymfm_opz.cpp +++ b/3rdparty/ymfm/src/ymfm_opz.cpp @@ -99,14 +99,14 @@ opz_registers::opz_registers() : m_lfo_am{ 0, 0 } { // create the waveforms - for (int index = 0; index < WAVEFORM_LENGTH; index++) + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15); uint16_t zeroval = m_waveform[0][0]; - for (int index = 0; index < WAVEFORM_LENGTH; index++) + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) m_waveform[1][index] = (zeroval - m_waveform[0][(index & 0x1ff) ^ 0x100]) | (bitfield(index, 9) << 15); - for (int index = 0; index < WAVEFORM_LENGTH; index++) + for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++) { m_waveform[2][index] = bitfield(index, 9) ? zeroval : m_waveform[0][index]; m_waveform[3][index] = bitfield(index, 9) ? zeroval : m_waveform[1][index]; @@ -118,7 +118,7 @@ opz_registers::opz_registers() : // create the LFO waveforms; AM in the low 8 bits, PM in the upper 8 // waveforms are adjusted to match the pictures in the application manual - for (int index = 0; index < LFO_WAVEFORM_LENGTH; index++) + for (uint32_t index = 0; index < LFO_WAVEFORM_LENGTH; index++) { // waveform 0 is a sawtooth uint8_t am = index ^ 0xff; @@ -701,7 +701,7 @@ void ym2414::write_data(uint8_t data) if (m_address == 0x1b) { // writes to register 0x1B send the upper 2 bits to the output lines - m_fm.intf().ymfm_io_write(0, data >> 6); + m_fm.intf().ymfm_external_write(ACCESS_IO, 0, data >> 6); } // mark busy for a bit diff --git a/3rdparty/ymfm/src/ymfm_pcm.cpp b/3rdparty/ymfm/src/ymfm_pcm.cpp new file mode 100644 index 00000000000..c8c3e2b5218 --- /dev/null +++ b/3rdparty/ymfm/src/ymfm_pcm.cpp @@ -0,0 +1,712 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "ymfm_pcm.h" +#include "ymfm_fm.h" +#include "ymfm_fm.ipp" + +namespace ymfm +{ + +//********************************************************* +// PCM REGISTERS +//********************************************************* + +//------------------------------------------------- +// reset - reset the register state +//------------------------------------------------- + +void pcm_registers::reset() +{ + std::fill_n(&m_regdata[0], REGISTERS, 0); + m_regdata[0x02] = 0x20; + m_regdata[0xf8] = 0x1b; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void pcm_registers::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_regdata); +} + + +//------------------------------------------------- +// cache_channel_data - update the cache with +// data from the registers +//------------------------------------------------- + +void pcm_registers::cache_channel_data(uint32_t choffs, pcm_cache &cache) +{ + // compute step from octave and fnumber; the math here implies + // a .18 fraction but .16 should be perfectly fine + int32_t octave = int8_t(ch_octave(choffs) << 4) >> 4; + uint32_t fnum = ch_fnumber(choffs); + cache.step = ((0x400 | fnum) << (octave + 7)) >> 2; + + // total level is computed as a .10 value for interpolation + cache.total_level = ch_total_level(choffs) << 10; + + // compute panning values in terms of envelope attenuation + int32_t panpot = int8_t(ch_panpot(choffs) << 4) >> 4; + if (panpot >= 0) + { + cache.pan_left = (panpot == 7) ? 96 : 3 * panpot; + cache.pan_right = 0; + } + else if (panpot >= -7) + { + cache.pan_left = 0; + cache.pan_right = (panpot == -7) ? 96 : -3 * panpot; + } + else + cache.pan_left = cache.pan_right = 96; + + // determine the LFO stepping value; this how much to add to a running + // x.18 value for the LFO; steps were derived from frequencies in the + // manual and come out very close with these values + static const uint8_t s_lfo_steps[8] = { 1, 12, 19, 25, 31, 35, 37, 42 }; + cache.lfo_step = s_lfo_steps[ch_lfo_speed(choffs)]; + + // AM LFO depth values, derived from the manual; note each has at most + // 2 bits to make the "multiply" easy in hardware + static const uint8_t s_am_depth[8] = { 0, 0x14, 0x20, 0x28, 0x30, 0x40, 0x50, 0x80 }; + cache.am_depth = s_am_depth[ch_am_depth(choffs)]; + + // PM LFO depth values; these are converted from the manual's cents values + // into f-numbers; the computations come out quite cleanly so pretty sure + // these are correct + static const uint8_t s_pm_depth[8] = { 0, 2, 3, 4, 6, 12, 24, 48 }; + cache.pm_depth = s_pm_depth[ch_vibrato(choffs)]; + + // 4-bit sustain level, but 15 means 31 so effectively 5 bits + cache.eg_sustain = ch_sustain_level(choffs); + cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10; + cache.eg_sustain <<= 5; + + // compute the key scaling correction factor; 15 means don't do any correction + int32_t correction = ch_rate_correction(choffs); + if (correction == 15) + correction = 0; + else + correction = (octave + correction) * 2 + bitfield(fnum, 9); + + // compute the envelope generator rates + cache.eg_rate[EG_ATTACK] = effective_rate(ch_attack_rate(choffs), correction); + cache.eg_rate[EG_DECAY] = effective_rate(ch_decay_rate(choffs), correction); + cache.eg_rate[EG_SUSTAIN] = effective_rate(ch_sustain_rate(choffs), correction); + cache.eg_rate[EG_RELEASE] = effective_rate(ch_release_rate(choffs), correction); + cache.eg_rate[EG_REVERB] = 5; + + // if damping is on, override some things; essentially decay at a hardcoded + // rate of 48 until -12db (0x80), then at maximum rate for the rest + if (ch_damp(choffs) != 0) + { + cache.eg_rate[EG_DECAY] = 48; + cache.eg_rate[EG_SUSTAIN] = 63; + cache.eg_rate[EG_RELEASE] = 63; + cache.eg_sustain = 0x80; + } +} + + +//------------------------------------------------- +// effective_rate - return the effective rate, +// clamping and applying corrections as needed +//------------------------------------------------- + +uint32_t pcm_registers::effective_rate(uint32_t raw, uint32_t correction) +{ + // raw rates of 0 and 15 just pin to min/max + if (raw == 0) + return 0; + if (raw == 15) + return 63; + + // otherwise add the correction and clamp to range + return clamp(raw * 4 + correction, 0, 63); +} + + + +//********************************************************* +// PCM CHANNEL +//********************************************************* + +//------------------------------------------------- +// pcm_channel - constructor +//------------------------------------------------- + +pcm_channel::pcm_channel(pcm_engine &owner, uint32_t choffs) : + m_choffs(choffs), + m_baseaddr(0), + m_endpos(0), + m_looppos(0), + m_curpos(0), + m_nextpos(0), + m_lfo_counter(0), + m_eg_state(EG_RELEASE), + m_env_attenuation(0x3ff), + m_total_level(0x7f << 10), + m_format(0), + m_key_state(0), + m_regs(owner.regs()), + m_owner(owner) +{ +} + + +//------------------------------------------------- +// reset - reset the channel state +//------------------------------------------------- + +void pcm_channel::reset() +{ + m_baseaddr = 0; + m_endpos = 0; + m_looppos = 0; + m_curpos = 0; + m_nextpos = 0; + m_lfo_counter = 0; + m_eg_state = EG_RELEASE; + m_env_attenuation = 0x3ff; + m_total_level = 0x7f << 10; + m_format = 0; + m_key_state = 0; +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void pcm_channel::save_restore(ymfm_saved_state &state) +{ + state.save_restore(m_baseaddr); + state.save_restore(m_endpos); + state.save_restore(m_looppos); + state.save_restore(m_curpos); + state.save_restore(m_nextpos); + state.save_restore(m_lfo_counter); + state.save_restore(m_eg_state); + state.save_restore(m_env_attenuation); + state.save_restore(m_total_level); + state.save_restore(m_format); + state.save_restore(m_key_state); +} + + +//------------------------------------------------- +// prepare - prepare for clocking +//------------------------------------------------- + +bool pcm_channel::prepare() +{ + // cache the data + m_regs.cache_channel_data(m_choffs, m_cache); + + // clock the key state + if ((m_key_state & KEY_PENDING) != 0) + { + uint8_t oldstate = m_key_state; + m_key_state = (m_key_state >> 1) & KEY_ON; + if (((oldstate ^ m_key_state) & KEY_ON) != 0) + { + if ((m_key_state & KEY_ON) != 0) + start_attack(); + else + start_release(); + } + } + + // set the total level directly if not interpolating + if (m_regs.ch_level_direct(m_choffs)) + m_total_level = m_cache.total_level; + + // we're active until we're quiet after the release + return (m_eg_state < EG_RELEASE || m_env_attenuation < EG_QUIET); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +void pcm_channel::clock(uint32_t env_counter) +{ + // clock the LFO, which is an x.18 value incremented based on the + // LFO speed value + m_lfo_counter += m_cache.lfo_step; + + // clock the envelope + clock_envelope(env_counter); + + // determine the step after applying vibrato + uint32_t step = m_cache.step; + if (m_cache.pm_depth != 0) + { + // shift the LFO by 1/4 cycle for PM so that it starts at 0 + uint32_t lfo_shifted = m_lfo_counter + (1 << 16); + int32_t lfo_value = bitfield(lfo_shifted, 10, 7); + if (bitfield(lfo_shifted, 17) != 0) + lfo_value ^= 0x7f; + lfo_value -= 0x40; + step += (lfo_value * int32_t(m_cache.pm_depth)) >> 7; + } + + // advance the sample step and loop as needed + m_curpos = m_nextpos; + m_nextpos = m_curpos + step; + if (m_nextpos >= m_endpos) + m_nextpos += m_looppos - m_endpos; + + // interpolate total level if needed + if (m_total_level != m_cache.total_level) + { + // max->min volume takes 156.4ms, or pretty close to 19/1024 per 44.1kHz sample + // min->max volume is half that, so advance by 38/1024 per sample + if (m_total_level < m_cache.total_level) + m_total_level = std::min(m_total_level + 19, m_cache.total_level); + else + m_total_level = std::max(m_total_level - 38, m_cache.total_level); + } +} + + +//------------------------------------------------- +// output - return the computed output value, with +// panning applied +//------------------------------------------------- + +void pcm_channel::output(output_data &output) const +{ + // early out if the envelope is effectively off + uint32_t envelope = m_env_attenuation; + if (envelope > EG_QUIET) + return; + + // add in LFO AM modulation + if (m_cache.am_depth != 0) + { + uint32_t lfo_value = bitfield(m_lfo_counter, 10, 7); + if (bitfield(m_lfo_counter, 17) != 0) + lfo_value ^= 0x7f; + envelope += (lfo_value * m_cache.am_depth) >> 7; + } + + // add in the current interpolated total level value, which is a .10 + // value shifted left by 2 + envelope += m_total_level >> 8; + + // add in panning effect and clamp + uint32_t lenv = std::min(envelope + m_cache.pan_left, 0x3ff); + uint32_t renv = std::min(envelope + m_cache.pan_right, 0x3ff); + + // convert to volume as a .11 fraction + int32_t lvol = attenuation_to_volume(lenv << 2); + int32_t rvol = attenuation_to_volume(renv << 2); + + // fetch current sample and add + int16_t sample = fetch_sample(); + uint32_t outnum = m_regs.ch_output_channel(m_choffs) * 2; + output.data[outnum + 0] += (lvol * sample) >> 15; + output.data[outnum + 1] += (rvol * sample) >> 15; +} + + +//------------------------------------------------- +// keyonoff - signal key on/off +//------------------------------------------------- + +void pcm_channel::keyonoff(bool on) +{ + // mark the key state as pending + m_key_state |= KEY_PENDING | (on ? KEY_PENDING_ON : 0); + + // don't log masked channels + if ((m_key_state & (KEY_PENDING_ON | KEY_ON)) == KEY_PENDING_ON && ((debug::GLOBAL_PCM_CHANNEL_MASK >> m_choffs) & 1) != 0) + { + debug::log_keyon("KeyOn PCM-%02d: num=%3d oct=%2d fnum=%03X level=%02X%c ADSR=%X/%X/%X/%X SL=%X", + m_choffs, + m_regs.ch_wave_table_num(m_choffs), + int8_t(m_regs.ch_octave(m_choffs) << 4) >> 4, + m_regs.ch_fnumber(m_choffs), + m_regs.ch_total_level(m_choffs), + m_regs.ch_level_direct(m_choffs) ? '!' : '/', + m_regs.ch_attack_rate(m_choffs), + m_regs.ch_decay_rate(m_choffs), + m_regs.ch_sustain_rate(m_choffs), + m_regs.ch_release_rate(m_choffs), + m_regs.ch_sustain_level(m_choffs)); + + if (m_regs.ch_rate_correction(m_choffs) != 15) + debug::log_keyon(" RC=%X", m_regs.ch_rate_correction(m_choffs)); + + if (m_regs.ch_pseudo_reverb(m_choffs) != 0) + debug::log_keyon(" %s", "REV"); + if (m_regs.ch_damp(m_choffs) != 0) + debug::log_keyon(" %s", "DAMP"); + + if (m_regs.ch_vibrato(m_choffs) != 0 || m_regs.ch_am_depth(m_choffs) != 0) + { + if (m_regs.ch_vibrato(m_choffs) != 0) + debug::log_keyon(" VIB=%d", m_regs.ch_vibrato(m_choffs)); + if (m_regs.ch_am_depth(m_choffs) != 0) + debug::log_keyon(" AM=%d", m_regs.ch_am_depth(m_choffs)); + debug::log_keyon(" LFO=%d", m_regs.ch_lfo_speed(m_choffs)); + } + debug::log_keyon("%s", "\n"); + } +} + + +//------------------------------------------------- +// load_wavetable - load a wavetable by fetching +// its data from external memory +//------------------------------------------------- + +void pcm_channel::load_wavetable() +{ + // determine the address of the wave table header + uint32_t wavnum = m_regs.ch_wave_table_num(m_choffs); + uint32_t wavheader = 12 * wavnum; + + // above 384 it may be in a different bank + if (wavnum >= 384) + { + uint32_t bank = m_regs.wave_table_header(); + if (bank != 0) + wavheader = 512*1024 * bank + (wavnum - 384) * 12; + } + + // fetch the 22-bit base address and 2-bit format + uint8_t byte = read_pcm(wavheader + 0); + m_format = bitfield(byte, 6, 2); + m_baseaddr = bitfield(byte, 0, 6) << 16; + m_baseaddr |= read_pcm(wavheader + 1) << 8; + m_baseaddr |= read_pcm(wavheader + 2) << 0; + + // fetch the 16-bit loop position + m_looppos = read_pcm(wavheader + 3) << 8; + m_looppos |= read_pcm(wavheader + 4); + m_looppos <<= 16; + + // fetch the 16-bit end position, which is stored as a negative value + // for some reason that is unclear + m_endpos = read_pcm(wavheader + 5) << 8; + m_endpos |= read_pcm(wavheader + 6); + m_endpos = -m_endpos << 16; + + // remaining data values set registers + m_owner.write(0x80 + m_choffs, read_pcm(wavheader + 7)); + m_owner.write(0x98 + m_choffs, read_pcm(wavheader + 8)); + m_owner.write(0xb0 + m_choffs, read_pcm(wavheader + 9)); + m_owner.write(0xc8 + m_choffs, read_pcm(wavheader + 10)); + m_owner.write(0xe0 + m_choffs, read_pcm(wavheader + 11)); +} + + +//------------------------------------------------- +// read_pcm - read a byte from the external PCM +// memory interface +//------------------------------------------------- + +uint8_t pcm_channel::read_pcm(uint32_t address) const +{ + return m_owner.intf().ymfm_external_read(ACCESS_PCM, address); +} + + +//------------------------------------------------- +// start_attack - start the attack phase +//------------------------------------------------- + +void pcm_channel::start_attack() +{ + // don't change anything if already in attack state + if (m_eg_state == EG_ATTACK) + return; + m_eg_state = EG_ATTACK; + + // reset the LFO if requested + if (m_regs.ch_lfo_reset(m_choffs)) + m_lfo_counter = 0; + + // if the attack rate == 63 then immediately go to max attenuation + if (m_cache.eg_rate[EG_ATTACK] == 63) + m_env_attenuation = 0; + + // reset the positions + m_curpos = m_nextpos = 0; +} + + +//------------------------------------------------- +// start_release - start the release phase +//------------------------------------------------- + +void pcm_channel::start_release() +{ + // don't change anything if already in release or reverb state + if (m_eg_state >= EG_RELEASE) + return; + m_eg_state = EG_RELEASE; +} + + +//------------------------------------------------- +// clock_envelope - clock the envelope generator +//------------------------------------------------- + +void pcm_channel::clock_envelope(uint32_t env_counter) +{ + // handle attack->decay transitions + if (m_eg_state == EG_ATTACK && m_env_attenuation == 0) + m_eg_state = EG_DECAY; + + // handle decay->sustain transitions + if (m_eg_state == EG_DECAY && m_env_attenuation >= m_cache.eg_sustain) + m_eg_state = EG_SUSTAIN; + + // fetch the appropriate 6-bit rate value from the cache + uint32_t rate = m_cache.eg_rate[m_eg_state]; + + // compute the rate shift value; this is the shift needed to + // apply to the env_counter such that it becomes a 5.11 fixed + // point number + uint32_t rate_shift = rate >> 2; + env_counter <<= rate_shift; + + // see if the fractional part is 0; if not, it's not time to clock + if (bitfield(env_counter, 0, 11) != 0) + return; + + // determine the increment based on the non-fractional part of env_counter + uint32_t relevant_bits = bitfield(env_counter, (rate_shift <= 11) ? 11 : rate_shift, 3); + uint32_t increment = attenuation_increment(rate, relevant_bits); + + // attack is the only one that increases + if (m_eg_state == EG_ATTACK) + m_env_attenuation += (~m_env_attenuation * increment) >> 4; + + // all other cases are similar + else + { + // apply the increment + m_env_attenuation += increment; + + // clamp the final attenuation + if (m_env_attenuation >= 0x400) + m_env_attenuation = 0x3ff; + + // transition to reverb at -18dB if enabled + if (m_env_attenuation >= 0xc0 && m_eg_state < EG_REVERB && m_regs.ch_pseudo_reverb(m_choffs)) + m_eg_state = EG_REVERB; + } +} + + +//------------------------------------------------- +// fetch_sample - fetch a sample at the current +// position +//------------------------------------------------- + +int16_t pcm_channel::fetch_sample() const +{ + uint32_t addr = m_baseaddr; + uint32_t pos = m_curpos >> 16; + + // 8-bit PCM: shift up by 8 + if (m_format == 0) + return read_pcm(addr + pos) << 8; + + // 16-bit PCM: assemble from 2 halves + if (m_format == 2) + { + addr += pos * 2; + return (read_pcm(addr) << 8) | read_pcm(addr + 1); + } + + // 12-bit PCM: assemble out of half of 3 bytes + addr += (pos / 2) * 3; + if ((pos & 1) == 0) + return (read_pcm(addr + 0) << 8) | ((read_pcm(addr + 1) << 0) & 0xf0); + else + return (read_pcm(addr + 2) << 8) | ((read_pcm(addr + 1) << 4) & 0xf0); +} + + + +//********************************************************* +// PCM ENGINE +//********************************************************* + +//------------------------------------------------- +// pcm_engine - constructor +//------------------------------------------------- + +pcm_engine::pcm_engine(ymfm_interface &intf) : + m_intf(intf), + m_env_counter(0), + m_modified_channels(ALL_CHANNELS), + m_active_channels(ALL_CHANNELS) +{ + // create the channels + for (int chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum] = std::make_unique(*this, chnum); +} + + +//------------------------------------------------- +// reset - reset the engine state +//------------------------------------------------- + +void pcm_engine::reset() +{ + // reset register state + m_regs.reset(); + + // reset each channel + for (auto &chan : m_channel) + chan->reset(); +} + + +//------------------------------------------------- +// save_restore - save or restore the data +//------------------------------------------------- + +void pcm_engine::save_restore(ymfm_saved_state &state) +{ + // save our data + state.save_restore(m_env_counter); + + // save channel state + for (int chnum = 0; chnum < CHANNELS; chnum++) + m_channel[chnum]->save_restore(state); +} + + +//------------------------------------------------- +// clock - master clocking function +//------------------------------------------------- + +void pcm_engine::clock(uint32_t chanmask) +{ + // if something was modified, prepare + // also prepare every 4k samples to catch ending notes + if (m_modified_channels != 0 || m_prepare_count++ >= 4096) + { + // call each channel to prepare + m_active_channels = 0; + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + if (m_channel[chnum]->prepare()) + m_active_channels |= 1 << chnum; + + // reset the modified channels and prepare count + m_modified_channels = m_prepare_count = 0; + } + + // increment the envelope counter; the envelope generator + // only clocks every other sample in order to make the PCM + // envelopes line up with the FM envelopes (after taking into + // account the different FM sampling rate) + m_env_counter++; + + // now update the state of all the channels and operators + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + m_channel[chnum]->clock(m_env_counter >> 1); +} + + +//------------------------------------------------- +// update - master update function +//------------------------------------------------- + +void pcm_engine::output(output_data &output, uint32_t chanmask) +{ + // mask out some channels for debug purposes + chanmask &= debug::GLOBAL_PCM_CHANNEL_MASK; + + // compute the output of each channel + for (int chnum = 0; chnum < CHANNELS; chnum++) + if (bitfield(chanmask, chnum)) + m_channel[chnum]->output(output); +} + + +//------------------------------------------------- +// read - handle reads from the PCM registers +//------------------------------------------------- + +uint8_t pcm_engine::read(uint32_t regnum) +{ + // handle reads from the data register + if (regnum == 0x06 && m_regs.memory_access_mode() != 0) + return m_intf.ymfm_external_read(ACCESS_PCM, m_regs.memory_address_autoinc()); + + return m_regs.read(regnum); +} + + +//------------------------------------------------- +// write - handle writes to the PCM registers +//------------------------------------------------- + +void pcm_engine::write(uint32_t regnum, uint8_t data) +{ + // handle reads to the data register + if (regnum == 0x06 && m_regs.memory_access_mode() != 0) + { + m_intf.ymfm_external_write(ACCESS_PCM, m_regs.memory_address_autoinc(), data); + return; + } + + // for now just mark all channels as modified + m_modified_channels = ALL_CHANNELS; + + // most writes are passive, consumed only when needed + m_regs.write(regnum, data); + + // however, process keyons immediately + if (regnum >= 0x68 && regnum <= 0x7f) + m_channel[regnum - 0x68]->keyonoff(bitfield(data, 7)); + + // and also wavetable writes + else if (regnum >= 0x08 && regnum <= 0x1f) + m_channel[regnum - 0x08]->load_wavetable(); +} + +} diff --git a/3rdparty/ymfm/src/ymfm_pcm.h b/3rdparty/ymfm/src/ymfm_pcm.h new file mode 100644 index 00000000000..b809aa277af --- /dev/null +++ b/3rdparty/ymfm/src/ymfm_pcm.h @@ -0,0 +1,307 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef YMFM_PCM_H +#define YMFM_PCM_H + +#pragma once + +#include "ymfm.h" + +namespace ymfm +{ + +//********************************************************* +// INTERFACE CLASSES +//********************************************************* + +class pcm_engine; + + +// ======================> pcm_cache + +// this class holds data that is computed once at the start of clocking +// and remains static during subsequent sound generation +struct pcm_cache +{ + uint32_t step; // sample position step, as a .16 value + uint32_t total_level; // target total level, as a .10 value + uint32_t pan_left; // left panning attenuation + uint32_t pan_right; // right panning attenuation + uint32_t eg_sustain; // sustain level, shifted up to envelope values + uint8_t eg_rate[EG_STATES]; // envelope rate, including KSR + uint8_t lfo_step; // stepping value for LFO + uint8_t am_depth; // scale value for AM LFO + uint8_t pm_depth; // scale value for PM LFO +}; + + +// ======================> pcm_registers + +// +// PCM register map: +// +// System-wide registers: +// 00-01 xxxxxxxx LSI Test +// 02 -------x Memory access mode (0=sound gen, 1=read/write) +// ------x- Memory type (0=ROM, 1=ROM+SRAM) +// ---xxx-- Wave table header +// xxx----- Device ID (=1 for YMF278B) +// 03 --xxxxxx Memory address high +// 04 xxxxxxxx Memory address mid +// 05 xxxxxxxx Memory address low +// 06 xxxxxxxx Memory data +// F8 --xxx--- Mix control (FM_R) +// -----xxx Mix control (FM_L) +// F9 --xxx--- Mix control (PCM_R) +// -----xxx Mix control (PCM_L) +// +// Channel-specific registers: +// 08-1F xxxxxxxx Wave table number low +// 20-37 -------x Wave table number high +// xxxxxxx- F-number low +// 38-4F -----xxx F-number high +// ----x--- Pseudo-reverb +// xxxx---- Octave +// 50-67 xxxxxxx- Total level +// -------x Level direct +// 68-7F x------- Key on +// -x------ Damp +// --x----- LFO reset +// ---x---- Output channel +// ----xxxx Panpot +// 80-97 --xxx--- LFO speed +// -----xxx Vibrato +// 98-AF xxxx---- Attack rate +// ----xxxx Decay rate +// B0-C7 xxxx---- Sustain level +// ----xxxx Sustain rate +// C8-DF xxxx---- Rate correction +// ----xxxx Release rate +// E0-F7 -----xxx AM depth + +class pcm_registers +{ +public: + // constants + static constexpr uint32_t OUTPUTS = 4; + static constexpr uint32_t CHANNELS = 24; + static constexpr uint32_t REGISTERS = 0x100; + static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1; + + // constructor + pcm_registers() { } + + // save/restore + void save_restore(ymfm_saved_state &state); + + // reset to initial state + void reset(); + + // update cache information + void cache_channel_data(uint32_t choffs, pcm_cache &cache); + + // direct read/write access + uint8_t read(uint32_t index ) { return m_regdata[index]; } + void write(uint32_t index, uint8_t data) { m_regdata[index] = data; } + + // system-wide registers + uint32_t memory_access_mode() const { return bitfield(m_regdata[0x02], 0); } + uint32_t memory_type() const { return bitfield(m_regdata[0x02], 1); } + uint32_t wave_table_header() const { return bitfield(m_regdata[0x02], 2, 3); } + uint32_t device_id() const { return bitfield(m_regdata[0x02], 5, 3); } + uint32_t memory_address() const { return (bitfield(m_regdata[0x03], 0, 6) << 16) | (m_regdata[0x04] << 8) | m_regdata[0x05]; } + uint32_t memory_data() const { return m_regdata[0x06]; } + uint32_t mix_fm_r() const { return bitfield(m_regdata[0xf8], 3, 3); } + uint32_t mix_fm_l() const { return bitfield(m_regdata[0xf8], 0, 3); } + uint32_t mix_pcm_r() const { return bitfield(m_regdata[0xf9], 3, 3); } + uint32_t mix_pcm_l() const { return bitfield(m_regdata[0xf9], 0, 3); } + + // per-channel registers + uint32_t ch_wave_table_num(uint32_t choffs) const { return m_regdata[choffs + 0x08] | (bitfield(m_regdata[choffs + 0x20], 0) << 8); } + uint32_t ch_fnumber(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x20], 1, 7) | (bitfield(m_regdata[choffs + 0x38], 0, 3) << 7); } + uint32_t ch_pseudo_reverb(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x38], 3); } + uint32_t ch_octave(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x38], 4, 4); } + uint32_t ch_total_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x50], 1, 7); } + uint32_t ch_level_direct(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x50], 0); } + uint32_t ch_keyon(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 7); } + uint32_t ch_damp(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 6); } + uint32_t ch_lfo_reset(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 5); } + uint32_t ch_output_channel(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 4); } + uint32_t ch_panpot(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x68], 0, 4); } + uint32_t ch_lfo_speed(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x80], 3, 3); } + uint32_t ch_vibrato(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x80], 0, 3); } + uint32_t ch_attack_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x98], 4, 4); } + uint32_t ch_decay_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0x98], 0, 4); } + uint32_t ch_sustain_level(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xb0], 4, 4); } + uint32_t ch_sustain_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xb0], 0, 4); } + uint32_t ch_rate_correction(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xc8], 4, 4); } + uint32_t ch_release_rate(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xc8], 0, 4); } + uint32_t ch_am_depth(uint32_t choffs) const { return bitfield(m_regdata[choffs + 0xe0], 0, 3); } + + // return the memory address and increment it + uint32_t memory_address_autoinc() + { + uint32_t result = memory_address(); + uint32_t newval = result + 1; + m_regdata[0x05] = newval >> 0; + m_regdata[0x06] = newval >> 8; + m_regdata[0x07] = (newval >> 16) & 0x3f; + return result; + } + +private: + // internal helpers + uint32_t effective_rate(uint32_t raw, uint32_t correction); + + // internal state + uint8_t m_regdata[REGISTERS]; // register data +}; + + +// ======================> pcm_channel + +class pcm_channel +{ + static constexpr uint8_t KEY_ON = 0x01; + static constexpr uint8_t KEY_PENDING_ON = 0x02; + static constexpr uint8_t KEY_PENDING = 0x04; + + // "quiet" value, used to optimize when we can skip doing working + static constexpr uint32_t EG_QUIET = 0x200; + +public: + using output_data = ymfm_output; + + // constructor + pcm_channel(pcm_engine &owner, uint32_t choffs); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // reset the channel state + void reset(); + + // return the channel offset + uint32_t choffs() const { return m_choffs; } + + // prepare prior to clocking + bool prepare(); + + // master clocking function + void clock(uint32_t env_counter); + + // return the computed output value, with panning applied + void output(output_data &output) const; + + // signal key on/off + void keyonoff(bool on); + + // load a new wavetable entry + void load_wavetable(); + +private: + // internal helpers + void start_attack(); + void start_release(); + void clock_envelope(uint32_t env_counter); + int16_t fetch_sample() const; + uint8_t read_pcm(uint32_t address) const; + + // internal state + uint32_t const m_choffs; // channel offset + uint32_t m_baseaddr; // base address + uint32_t m_endpos; // ending position + uint32_t m_looppos; // loop position + uint32_t m_curpos; // current position + uint32_t m_nextpos; // next position + uint32_t m_lfo_counter; // LFO counter + envelope_state m_eg_state; // envelope state + uint16_t m_env_attenuation; // computed envelope attenuation + uint32_t m_total_level; // total level with as 7.10 for interp + uint8_t m_format; // sample format + uint8_t m_key_state; // current key state + pcm_cache m_cache; // cached data + pcm_registers &m_regs; // reference to registers + pcm_engine &m_owner; // reference to our owner +}; + + +// ======================> pcm_engine + +class pcm_engine +{ +public: + static constexpr int OUTPUTS = pcm_registers::OUTPUTS; + static constexpr int CHANNELS = pcm_registers::CHANNELS; + static constexpr uint32_t ALL_CHANNELS = pcm_registers::ALL_CHANNELS; + using output_data = pcm_channel::output_data; + + // constructor + pcm_engine(ymfm_interface &intf); + + // reset our status + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // master clocking function + void clock(uint32_t chanmask); + + // compute sum of channel outputs + void output(output_data &output, uint32_t chanmask); + + // read from the PCM registers + uint8_t read(uint32_t regnum); + + // write to the PCM registers + void write(uint32_t regnum, uint8_t data); + + // return a reference to our interface + ymfm_interface &intf() { return m_intf; } + + // return a reference to our registers + pcm_registers ®s() { return m_regs; } + +private: + // internal state + ymfm_interface &m_intf; // reference to the interface + uint32_t m_env_counter; // envelope counter + uint32_t m_modified_channels; // bitmask of modified channels + uint32_t m_active_channels; // bitmask of active channels + uint32_t m_prepare_count; // counter to do periodic prepare sweeps + std::unique_ptr m_channel[CHANNELS]; // array of channels + pcm_registers m_regs; // registers +}; + +} + +#endif // YMFM_PCM_H diff --git a/3rdparty/ymfm/src/ymfm_ssg.cpp b/3rdparty/ymfm/src/ymfm_ssg.cpp index 2a7c9668ce2..410452b1c6d 100644 --- a/3rdparty/ymfm/src/ymfm_ssg.cpp +++ b/3rdparty/ymfm/src/ymfm_ssg.cpp @@ -144,9 +144,11 @@ void ssg_engine::clock() } // clock noise; noise period units are clock/16 but since we run at clock/8, - // our counter needs a right shift prior to compare + // our counter needs a right shift prior to compare; note that a period of 0 + // should produce an indentical result to a period of 1, so add a special + // check against that case m_noise_count++; - if ((m_noise_count >> 1) >= m_regs.noise_period()) + if ((m_noise_count >> 1) >= m_regs.noise_period() && m_noise_count != 1) { m_noise_state ^= (bitfield(m_noise_state, 0) ^ bitfield(m_noise_state, 3)) << 17; m_noise_state >>= 1; @@ -240,9 +242,9 @@ uint8_t ssg_engine::read(uint32_t regnum) // read from the I/O ports call the handlers if they are configured for input if (regnum == 0x0e && !m_regs.io_a_out()) - return m_intf.ymfm_io_read(0); + return m_intf.ymfm_external_read(ACCESS_IO, 0); else if (regnum == 0x0f && !m_regs.io_b_out()) - return m_intf.ymfm_io_read(1); + return m_intf.ymfm_external_read(ACCESS_IO, 1); // otherwise just return the register value return m_regs.read(regnum); @@ -269,9 +271,9 @@ void ssg_engine::write(uint32_t regnum, uint8_t data) // writes to the I/O ports call the handlers if they are configured for output else if (regnum == 0x0e && m_regs.io_a_out()) - m_intf.ymfm_io_write(0, data); + m_intf.ymfm_external_write(ACCESS_IO, 0, data); else if (regnum == 0x0f && m_regs.io_b_out()) - m_intf.ymfm_io_write(1, data); + m_intf.ymfm_external_write(ACCESS_IO, 1, data); } } diff --git a/scripts/src/3rdparty.lua b/scripts/src/3rdparty.lua index 01404120033..6da826ea0d5 100644 --- a/scripts/src/3rdparty.lua +++ b/scripts/src/3rdparty.lua @@ -2283,6 +2283,8 @@ project "ymfm" MAME_DIR .. "3rdparty/ymfm/src/ymfm_opq.h", MAME_DIR .. "3rdparty/ymfm/src/ymfm_opz.cpp", MAME_DIR .. "3rdparty/ymfm/src/ymfm_opz.h", + MAME_DIR .. "3rdparty/ymfm/src/ymfm_pcm.cpp", + MAME_DIR .. "3rdparty/ymfm/src/ymfm_pcm.h", MAME_DIR .. "3rdparty/ymfm/src/ymfm_ssg.cpp", MAME_DIR .. "3rdparty/ymfm/src/ymfm_ssg.h", } diff --git a/scripts/src/sound.lua b/scripts/src/sound.lua index 57d9ae8afe1..91c5465f113 100644 --- a/scripts/src/sound.lua +++ b/scripts/src/sound.lua @@ -1180,8 +1180,8 @@ end --@src/devices/sound/ymopl.h,SOUNDS["YM3526"] = true --@src/devices/sound/ymopl.h,SOUNDS["YM3812"] = true --@src/devices/sound/ymopl.h,SOUNDS["YMF262"] = true +--@src/devices/sound/ymopl.h,SOUNDS["YMF278B"] = true --@src/devices/sound/ymf271.h,SOUNDS["YMF271"] = true ---@src/devices/sound/ymf278b.h,SOUNDS["YMF278B"] = true --@src/devices/sound/ymopl.h,SOUNDS["Y8950"] = true --------------------------------------------------- @@ -1215,7 +1215,7 @@ if (SOUNDS["YM2203"]~=null or SOUNDS["YM2608"]~=null or SOUNDS["YM2610"]~=null o } end -if (SOUNDS["YM3526"]~=null or SOUNDS["Y8950"]~=null or SOUNDS["YM3812"]~=null or SOUNDS["YMF262"]~=null or SOUNDS["YM2413"]~=null or SOUNDS["YM2423"]~=null or SOUNDS["YMF281"]~=null or SOUNDS["DS1001"]~=null) then +if (SOUNDS["YM3526"]~=null or SOUNDS["Y8950"]~=null or SOUNDS["YM3812"]~=null or SOUNDS["YMF262"]~=null or SOUNDS["YMF278B"]~=null or SOUNDS["YM2413"]~=null or SOUNDS["YM2423"]~=null or SOUNDS["YMF281"]~=null or SOUNDS["DS1001"]~=null) then files { MAME_DIR .. "src/devices/sound/ymopl.cpp", MAME_DIR .. "src/devices/sound/ymopl.h", @@ -1229,13 +1229,6 @@ if (SOUNDS["YMF271"]~=null) then } end -if (SOUNDS["YMF278B"]~=null) then - files { - MAME_DIR .. "src/devices/sound/ymf278b.cpp", - MAME_DIR .. "src/devices/sound/ymf278b.h", - } -end - --------------------------------------------------- diff --git a/src/devices/bus/msx_cart/moonsound.h b/src/devices/bus/msx_cart/moonsound.h index ca30dfb5c35..62f614ae729 100644 --- a/src/devices/bus/msx_cart/moonsound.h +++ b/src/devices/bus/msx_cart/moonsound.h @@ -6,7 +6,7 @@ #pragma once #include "bus/msx_cart/cartridge.h" -#include "sound/ymf278b.h" +#include "sound/ymopl.h" DECLARE_DEVICE_TYPE(MSX_CART_MOONSOUND, msx_cart_moonsound_device) diff --git a/src/devices/sound/ymf278b.cpp b/src/devices/sound/ymf278b.cpp deleted file mode 100644 index 60646fd620e..00000000000 --- a/src/devices/sound/ymf278b.cpp +++ /dev/null @@ -1,983 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:R. Belmont, Olivier Galibert, hap -/* - - YMF278B FM + Wave table Synthesizer (OPL4) - - Timer and PCM YMF278B. The FM will be shared with the ymf262, eventually. - - This chip roughly splits the difference between the Sega 315-5560 MultiPCM - (Multi32, Model 1/2) and YMF 292-F SCSP (later Model 2, STV, Saturn, Model 3). - - Features as listed in LSI-4MF2782 data sheet: - FM Synthesis (same as YMF262) - 1. Sound generation mode - Two-operater mode - Generates eighteen voices or fifteen voices plus five rhythm sounds simultaneously - Four-operator mode - Generates six voices in four-operator mode plus six voices in two-operator mode simultaneously, - or generates six voices in four-operator mode plus three voices in two-operator mode plus five - rhythm sounds simultaneously - 2. Eight selectable waveforms - 3. Stereo output - Wave Table Synthesis - 1. Generates twenty-four voices simultaneously - 2. 44.1kHz sampling rate for output sound data - 3. Selectable from 8-bit, 12-bit and 16-bit word lengths for wave data - 4. Stereo output (16-stage panpot for each voice) - Wave Data - 1. Accepts 32M bit external memory at maximum - 2. Up to 512 wave tables - 3. External ROM or SRAM can be connected. With SRAM connected, the CPU can download wave data - 4. Outputs chip select signals for 1Mbit, 4Mbit, 8Mbit or 16Mbit memory - 5. Can be directly connected to the Yamaha YRW801 (Wave data ROM) - Features of YRW801 as listed in LSI 4RW801A2 - Built-in wave data of tones which comply with GM system Level 1 - Melody tone ....... 128 tones - Percussion tone ... 47 tones - 16Mbit capacity (2,097,152word x 8) - - By R. Belmont and O. Galibert. - - - TODO: - - accurate timing of envelopes - - LFO (vibrato, tremolo) - - integrate YMF262 mixing (used by Fuuki games, not used by Psikyo and Metro games) - - Envelope and LFO function is similar algorithm as multipcm.cpp (except Damp, Pseudo Reverb) - Can it be merged with/ported to this? -*/ - -#include "emu.h" -#include "ymf278b.h" - -#include - -#define VERBOSE 0 -#define LOG(x) do { if (VERBOSE) logerror x; } while (0) - - -// Using the nominal datasheet frequency of 33.868MHz, the output of -// the chip will be clock/768 = 44.1kHz. However, the FM engine is -// clocked internally at clock/(19*36), or 49.515kHz, so the FM output -// needs to be downsampled. The calculations below produce the fractional -// number of extra FM samples we need to consume for each output sample, -// as a 0.24 fixed point fraction. -static constexpr double NOMINAL_CLOCK = 33868800; -static constexpr double NOMINAL_FM_RATE = NOMINAL_CLOCK / double(ymfm::opl4_registers::DEFAULT_PRESCALE * ymfm::opl4_registers::OPERATORS); -static constexpr double NOMINAL_OUTPUT_RATE = NOMINAL_CLOCK / 768.0; -static constexpr uint32_t FM_STEP = uint32_t((NOMINAL_FM_RATE / NOMINAL_OUTPUT_RATE - 1.0) * double(1 << 24)); - - -/**************************************************************************/ - -int ymf278b_device::compute_rate(YMF278BSlot *slot, int val) -{ - int res, oct; - - if(val == 0) - return 0; - if(val == 15) - return 63; - if(slot->RC != 15) - { - oct = slot->octave; - if (oct & 8) - oct |= -8; - - res = (oct+slot->RC)*2 + (slot->F_NUMBER & 0x200 ? 1 : 0) + val*4; - } - else - res = val * 4; - if(res < 0) - res = 0; - else if(res > 63) - res = 63; - - return res; -} - -uint32_t ymf278b_device::compute_decay_env_vol_step(YMF278BSlot *slot, int val) -{ - int rate; - uint32_t res; - - // rate override with damping/pseudo reverb - if (slot->DAMP) - rate = 56; // approximate, datasheet says it's slightly curved though - else if (slot->preverb && slot->env_vol > ((6*8)<<23)) - { - // pseudo reverb starts at -18dB (6 in voltab) - slot->env_preverb = 1; - rate = 5; - } - else - rate = compute_rate(slot, val); - - if (rate < 4) - res = 0; - else - res = (256U<<23) / m_lut_dr[rate]; - - return res; -} - -void ymf278b_device::compute_freq_step(YMF278BSlot *slot) -{ - uint32_t step; - int oct; - - oct = slot->octave; - if(oct & 8) - oct |= -8; - - step = (slot->F_NUMBER | 1024) << (oct + 8); - slot->step = step >> 3; -} - -void ymf278b_device::compute_envelope(YMF278BSlot *slot) -{ - switch (slot->env_step) - { - // Attack - case 0: - { - // Attack - int rate = compute_rate(slot, slot->AR); - slot->env_vol = 256U<<23; - slot->env_vol_lim = (256U<<23) - 1; - - if (rate==63) - { - // immediate - LOG(("YMF278B: Attack skipped - ")); - slot->env_vol = 0; - slot->env_step++; - compute_envelope(slot); - } - else if (rate<4) - { - slot->env_vol_step = 0; - } - else - { - // NOTE: attack rate is linear here, but datasheet shows a smooth curve - LOG(("YMF278B: Attack, val = %d, rate = %d, delay = %g\n", slot->AR, rate, m_lut_ar[rate]*1000.0)); - slot->env_vol_step = ~((256U<<23) / m_lut_ar[rate]); - } - - break; - } - - // Decay 1 - case 1: - if(slot->DL) - { - LOG(("YMF278B: Decay step 1, dl=%d, val = %d rate = %d, delay = %g, PRVB = %d, DAMP = %d\n", slot->DL, slot->D1R, compute_rate(slot, slot->D1R), m_lut_dr[compute_rate(slot, slot->D1R)]*1000.0, slot->preverb, slot->DAMP)); - slot->env_vol_step = compute_decay_env_vol_step(slot, slot->D1R); - slot->env_vol_lim = (slot->DL*8)<<23; - } - else - { - LOG(("YMF278B: Decay 1 skipped - ")); - slot->env_step++; - compute_envelope(slot); - } - - break; - - // Decay 2 - case 2: - LOG(("YMF278B: Decay step 2, val = %d, rate = %d, delay = %g, , PRVB = %d, DAMP = %d, current vol = %d\n", slot->D2R, compute_rate(slot, slot->D2R), m_lut_dr[compute_rate(slot, slot->D2R)]*1000.0, slot->preverb, slot->DAMP, slot->env_vol >> 23)); - slot->env_vol_step = compute_decay_env_vol_step(slot, slot->D2R); - slot->env_vol_lim = 256U<<23; - break; - - // Decay 2 reached -96dB - case 3: - LOG(("YMF278B: Voice cleared because of decay 2\n")); - slot->env_vol = 256U<<23; - slot->env_vol_step = 0; - slot->env_vol_lim = 0; - slot->active = 0; - break; - - // Release - case 4: - LOG(("YMF278B: Release, val = %d, rate = %d, delay = %g, PRVB = %d, DAMP = %d\n", slot->RR, compute_rate(slot, slot->RR), m_lut_dr[compute_rate(slot, slot->RR)]*1000.0, slot->preverb, slot->DAMP)); - slot->env_vol_step = compute_decay_env_vol_step(slot, slot->RR); - slot->env_vol_lim = 256U<<23; - break; - - // Release reached -96dB - case 5: - LOG(("YMF278B: Release ends\n")); - slot->env_vol = 256U<<23; - slot->env_vol_step = 0; - slot->env_vol_lim = 0; - slot->active = 0; - break; - - default: break; - } -} - -//------------------------------------------------- -// sound_stream_update - handle a stream update -//------------------------------------------------- - -void ymf278b_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) -{ - int i, j; - YMF278BSlot *slot; - int16_t sample = 0; - int32_t *mixp; - - std::fill(m_mix_buffer.begin(), m_mix_buffer.end(), 0); - - for (i = 0; i < 24; i++) - { - slot = &m_slots[i]; - - if (slot->active) - { - mixp = &m_mix_buffer[0]; - - for (j = 0; j < outputs[0].samples(); j++) - { - if (slot->stepptr >= slot->endaddr) - { - slot->stepptr = slot->stepptr - slot->endaddr + slot->loopaddr; - - // NOTE: loop overflow is still possible here if (slot->stepptr >= slot->endaddr) - // This glitch may be (ab)used to your advantage to create pseudorandom noise. - } - - switch (slot->bits) - { - // 8 bit - case 0: - sample = read_byte(slot->startaddr + (slot->stepptr>>16))<<8; - break; - - // 12 bit - case 1: - if (slot->stepptr & 0x10000) - sample = read_byte(slot->startaddr + (slot->stepptr>>17)*3+2)<<8 | - (read_byte(slot->startaddr + (slot->stepptr>>17)*3+1) & 0xf0); - else - sample = read_byte(slot->startaddr + (slot->stepptr>>17)*3)<<8 | - ((read_byte(slot->startaddr + (slot->stepptr>>17)*3+1) << 4) & 0xf0); - break; - - // 16 bit - case 2: - sample = read_byte(slot->startaddr + ((slot->stepptr>>16)*2))<<8 | - read_byte(slot->startaddr + ((slot->stepptr>>16)*2)+1); - break; - - // ?? bit, effect is unknown, datasheet says it's prohibited - case 3: - sample = 0; - break; - } - - if (slot->CH) // DO1 out - { - mixp++; - mixp++; - *mixp++ += (sample * m_volume[slot->TL+m_pan_left [slot->pan]+(slot->env_vol>>23)])>>17; - *mixp++ += (sample * m_volume[slot->TL+m_pan_right[slot->pan]+(slot->env_vol>>23)])>>17; - } - else // DO2 out - { - *mixp++ += (sample * m_volume[slot->TL+m_pan_left [slot->pan]+(slot->env_vol>>23)])>>17; - *mixp++ += (sample * m_volume[slot->TL+m_pan_right[slot->pan]+(slot->env_vol>>23)])>>17; - mixp++; - mixp++; - } - - // update frequency - slot->stepptr += slot->step; - - // update envelope - slot->env_vol += slot->env_vol_step; - if (((int32_t)(slot->env_vol - slot->env_vol_lim)) >= 0) - { - slot->env_step++; - compute_envelope(slot); - } - else if (slot->preverb && !slot->env_preverb && slot->env_step && slot->env_vol > ((6*8)<<23)) - compute_envelope(slot); - } - } - } - - mixp = &m_mix_buffer[0]; - stream_buffer::sample_t wtl = stream_buffer::sample_t(m_mix_level[m_pcm_l]) / (65536.0f * 32768.0f); - stream_buffer::sample_t wtr = stream_buffer::sample_t(m_mix_level[m_pcm_r]) / (65536.0f * 32768.0f); - stream_buffer::sample_t fml = stream_buffer::sample_t(m_mix_level[m_fm_l]) / (65536.0f * 32768.0f); - stream_buffer::sample_t fmr = stream_buffer::sample_t(m_mix_level[m_fm_r]) / (65536.0f * 32768.0f); - for (i = 0; i < outputs[0].samples(); i++) - { - // the FM_STEP value is the fractional number of extra samples consumed per - // output sample; when this overflows, we need to clock the FM engine an - // extra time; since the PCM side of the chip doesn't do interpolation, I'm - // assuming this resampling stage doesn't either - m_fm_pos += FM_STEP; - if (BIT(m_fm_pos, 24)) - { - m_fm.clock(fm_engine::ALL_CHANNELS); - m_fm_pos &= 0xffffff; - } - - // clock the system - m_fm.clock(fm_engine::ALL_CHANNELS); - - // update the FM content; clipping is unknown - fm_engine::output_data sums; - m_fm.output(sums.clear(), 1, 32767, fm_engine::ALL_CHANNELS); - - // DO2 output: mixed FM channels 0+1 and wavetable channels 0+1 - outputs[0].put(i, stream_buffer::sample_t(*mixp++) * wtl + stream_buffer::sample_t(sums.data[0]) * fml); - outputs[1].put(i, stream_buffer::sample_t(*mixp++) * wtr + stream_buffer::sample_t(sums.data[1]) * fmr); - - // DO0 output: FM channels 2+3 only - outputs[2].put_int(i, sums.data[2], 32768); - outputs[3].put_int(i, sums.data[3], 32768); - - // DO1 output: wavetable channels 2+3 only - outputs[4].put_int(i, *mixp++, 32768); - outputs[5].put_int(i, *mixp++, 32768); - } -} - -enum -{ - TIMER_BUSY_CLEAR, - TIMER_LD_CLEAR -}; - -void ymf278b_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) -{ - switch(id) - { - case TIMER_BUSY_CLEAR: - m_fm.set_reset_status(0, STATUS_BUSY); - break; - - case TIMER_LD_CLEAR: - m_fm.set_reset_status(0, STATUS_LD); - break; - } -} - - -/**************************************************************************/ - -void ymf278b_device::retrigger_sample(YMF278BSlot *slot) -{ - // activate channel - if (slot->octave != 8) - slot->active = 1; - - // reset sample pos and go to attack stage - slot->stepptr = 0; - slot->env_step = 0; - slot->env_preverb = 0; - - compute_freq_step(slot); - compute_envelope(slot); -} - -void ymf278b_device::C_w(uint8_t reg, uint8_t data) -{ - // Handle slot registers specifically - if (reg >= 0x08 && reg <= 0xf7) - { - YMF278BSlot *slot; - int snum; - snum = (reg-8) % 24; - slot = &m_slots[snum]; - switch((reg-8) / 24) - { - case 0: - { - attotime period; - uint32_t offset; - uint8_t p[12]; - int i; - - slot->wave &= 0x100; - slot->wave |= data; - - // load wavetable header - if(slot->wave < 384 || !m_wavetblhdr) - offset = slot->wave * 12; - else - offset = m_wavetblhdr*0x80000 + (slot->wave - 384) * 12; - for (i = 0; i < 12; i++) - p[i] = read_byte(offset+i); - - slot->bits = (p[0]&0xc0)>>6; - slot->startaddr = (p[2] | (p[1]<<8) | ((p[0]&0x3f)<<16)); - slot->loopaddr = (p[4]<<16) | (p[3]<<24); - slot->endaddr = (p[6]<<16) | (p[5]<<24); - slot->endaddr -= 0x00010000U; - slot->endaddr ^= 0xffff0000U; - - // copy internal registers data - for (i = 7; i < 12; i++) - C_w(8 + snum + (i-2) * 24, p[i]); - - // status register LD bit is on for approx 300us - m_fm.set_reset_status(STATUS_LD, 0); - period = clocks_to_attotime(10); - m_timer_ld->adjust(period); - - // retrigger if key is on - if (slot->KEY_ON) - retrigger_sample(slot); - else if (slot->active) - { - // deactivate channel - slot->env_step = 5; - compute_envelope(slot); - } - - break; - } - - case 1: - slot->wave &= 0xff; - slot->wave |= ((data&0x1)<<8); - slot->F_NUMBER &= 0x380; - slot->F_NUMBER |= (data>>1); - if (slot->active && (data ^ m_pcmregs[reg]) & 0xfe) - { - compute_freq_step(slot); - compute_envelope(slot); - } - break; - - case 2: - slot->F_NUMBER &= 0x07f; - slot->F_NUMBER |= ((data&0x07)<<7); - slot->preverb = (data&0x8)>>3; - slot->octave = (data&0xf0)>>4; - if (data != m_pcmregs[reg]) - { - // channel goes off if octave is set to -8 (datasheet says it's prohibited) - // (it is ok if this activates the channel while it was off: compute_envelope will reset it again if needed) - slot->active = (slot->octave != 8); - - if (slot->active) - { - slot->env_preverb = 0; - compute_freq_step(slot); - compute_envelope(slot); - } - } - break; - - case 3: - slot->TL = data>>1; - slot->LD = data&0x1; - break; - - case 4: - slot->CH = (data&0x10)>>4; - // CH bit note: output to DO1 pin (1) or DO2 pin (0), this may - // silence the channel depending on how it's wired up on the PCB. - // For now, it's always enabled. - // (bit 5 (LFO reset) is also not hooked up yet) - - slot->pan = data&0xf; - slot->DAMP = (data&0x40)>>6; - if (data & 0x80) - { - // don't retrigger if key was already on - if (slot->KEY_ON) - { - if ((data ^ m_pcmregs[reg]) & 0x40) - compute_envelope(slot); - - break; - } - - retrigger_sample(slot); - } - else if (slot->active) - { - // release - slot->env_step = 4; - compute_envelope(slot); - } - slot->KEY_ON = (data&0x80)>>7; - break; - - case 5: - // LFO and vibrato level, not hooked up yet - slot->LFO = (data>>3)&0x7; - slot->VIB = data&0x7; - break; - - case 6: - slot->AR = data>>4; - slot->D1R = data&0xf; - if (slot->active && data != m_pcmregs[reg]) - compute_envelope(slot); - break; - - case 7: - slot->DL = data>>4; - slot->D2R = data&0xf; - if (slot->active && data != m_pcmregs[reg]) - compute_envelope(slot); - break; - - case 8: - slot->RC = data>>4; - slot->RR = data&0xf; - if (slot->active && data != m_pcmregs[reg]) - compute_envelope(slot); - break; - - case 9: - // tremolo level, not hooked up yet - slot->AM = data & 0x7; - break; - } - } - else - { - // All non-slot registers - switch (reg) - { - // LSI TEST - case 0x00: - case 0x01: - break; - - case 0x02: - m_wavetblhdr = (data>>2)&0x7; - m_memmode = data&3; - break; - - case 0x03: - data &= 0x3f; // ! - break; - case 0x04: - break; - case 0x05: - // set memory address - m_memadr = m_pcmregs[3] << 16 | m_pcmregs[4] << 8 | data; - break; - - case 0x06: - // memory data - space(0).write_byte(m_memadr, data); - m_memadr = (m_memadr + 1) & 0x3fffff; - break; - - case 0x07: - break; // unused - - case 0xf8: - m_fm_l = data & 0x7; - m_fm_r = (data>>3)&0x7; - break; - - case 0xf9: - m_pcm_l = data & 0x7; - m_pcm_r = (data>>3)&0x7; - break; - - default: - logerror("YMF278B: Port C write %02x, %02x\n", reg, data); - break; - } - } - - m_pcmregs[reg] = data; -} - -void ymf278b_device::timer_busy_start(int is_pcm) -{ - // status register BUSY bit is on for 56(FM) or 88(PCM) cycles - m_fm.set_reset_status(STATUS_BUSY, 0); - m_timer_busy->adjust(attotime::from_hz(m_clock / (is_pcm ? 88 : 56))); -} - -void ymf278b_device::write(offs_t offset, u8 data) -{ - uint32_t old; - switch (offset & 7) - { - case 0: - case 2: - timer_busy_start(0); - m_port_AB = data; - m_lastport = BIT(offset, 1); - break; - - case 1: - case 3: - timer_busy_start(0); - old = m_fm.regs().new2flag(); - m_fm.write(m_port_AB | (m_lastport << 8), data); - - // if the new2 flag is turned on, the next status read will set bit 1 - // but only for the first status read after new2 is set - if (old == 0 && m_fm.regs().new2flag() != 0) - m_next_status_id = true; - break; - - case 4: - timer_busy_start(1); - m_port_C = data; - break; - - case 5: - // PCM regs are only accessible if NEW2 is set - if (!m_fm.regs().new2flag()) - break; - - m_stream->update(); - - timer_busy_start(1); - C_w(m_port_C, data); - break; - - default: - logerror("%s: unexpected write at offset %X to ymf278b = %02X\n", machine().describe_context(), offset, data); - break; - } -} - - -u8 ymf278b_device::read(offs_t offset) -{ - uint8_t ret = 0; - - switch (offset & 7) - { - // status register - case 0: - - // first status read after initialization returns a chip ID, which - // varies based on the "new" flags, indicating the mode - if (m_next_status_id) - { - if (m_fm.regs().new2flag()) - ret = 0x02; - else if (m_fm.regs().newflag()) - ret = 0x00; - else - ret = 0x06; - m_next_status_id = false; - } - else - { - ret = m_fm.status(); - - // if new2 flag is not set, we're in OPL2 or OPL3 mode - if (!m_fm.regs().new2flag()) - ret &= ~(STATUS_BUSY | STATUS_LD); - } - break; - - // FM regs can be read too (on contrary to what the datasheet says) - case 1: - case 3: - // but they're not implemented here yet - // This may be incorrect, but it makes the mbwave moonsound detection in msx drivers pass. - ret = m_fm.regs().read(m_port_AB | (m_lastport << 8)); - break; - - // PCM regs - case 5: - // only accessible if NEW2 is set - if (!m_fm.regs().new2flag()) - break; - - switch (m_port_C) - { - // special cases - case 2: - ret = (m_pcmregs[m_port_C] & 0x1f) | 0x20; // device ID in upper bits - break; - case 6: - ret = read_byte(m_memadr); - m_memadr = (m_memadr + 1) & 0x3fffff; - break; - - default: - ret = m_pcmregs[m_port_C]; - break; - } - break; - - default: - logerror("%s: unexpected read at offset %X from ymf278b\n", machine().describe_context(), offset); - break; - } - - return ret; -} - - -/**************************************************************************/ - -//------------------------------------------------- -// device_reset - device-specific reset -//------------------------------------------------- - -void ymf278b_device::device_reset() -{ - int i; - - // clear registers - for (i = 0; i < 8; i++) - C_w(i, 0); - for (i = 0xff; i >= 8; i--) - C_w(i, 0); - C_w(0xf8, 0x1b); - - m_port_AB = m_port_C = 0; - m_lastport = 0; - m_next_status_id = true; - m_memadr = 0; - - // init/silence channels - for (i = 0; i < 24 ; i++) - { - YMF278BSlot *slot = &m_slots[i]; - - slot->LFO = 0; - slot->VIB = 0; - slot->AR = 0; - slot->D1R = 0; - slot->DL = 0; - slot->D2R = 0; - slot->RC = 0; - slot->RR = 0; - slot->AM = 0; - - slot->startaddr = 0; - slot->loopaddr = 0; - slot->endaddr = 0; - - slot->env_step = 5; - compute_envelope(slot); - } - - m_timer_busy->reset(); - m_timer_ld->reset(); - - m_fm.reset(); -} - -void ymf278b_device::device_clock_changed() -{ - int old_rate = m_rate; - m_clock = clock(); - m_rate = m_clock/768; - m_fm_pos = 0; - - if (m_rate > old_rate) - { - m_mix_buffer.resize(m_rate*4,0); - } - m_stream->set_sample_rate(m_rate); -} - -void ymf278b_device::rom_bank_updated() -{ - m_stream->update(); -} - -void ymf278b_device::precompute_rate_tables() -{ - int i; - - // decay rate - for (i = 0; i < 64; i++) - { - if (i <= 3) - m_lut_dr[i] = 0; - else if (i >= 60) - m_lut_dr[i] = 15 << 4; - else - m_lut_dr[i] = (15 << (21 - i / 4)) / (4 + i % 4); - } - - // attack rate (manual shows curve instead of linear though, so this is not entirely accurate) - for (i = 0; i < 64; i++) - { - if (i <= 3 || i == 63) - m_lut_ar[i] = 0; - else if (i >= 60) - m_lut_ar[i] = 17; - else - m_lut_ar[i] = (67 << (15 - i / 4)) / (4 + i % 4); - } -} - -void ymf278b_device::register_save_state() -{ - int i; - - save_item(NAME(m_pcmregs)); - save_item(NAME(m_wavetblhdr)); - save_item(NAME(m_memmode)); - save_item(NAME(m_memadr)); - save_item(NAME(m_fm_l)); - save_item(NAME(m_fm_r)); - save_item(NAME(m_fm_pos)); - save_item(NAME(m_pcm_l)); - save_item(NAME(m_pcm_r)); - save_item(NAME(m_port_AB)); - save_item(NAME(m_port_C)); - save_item(NAME(m_lastport)); - save_item(NAME(m_next_status_id)); - - for (i = 0; i < 24; ++i) - { - save_item(NAME(m_slots[i].wave), i); - save_item(NAME(m_slots[i].F_NUMBER), i); - save_item(NAME(m_slots[i].octave), i); - save_item(NAME(m_slots[i].preverb), i); - save_item(NAME(m_slots[i].DAMP), i); - save_item(NAME(m_slots[i].CH), i); - save_item(NAME(m_slots[i].LD), i); - save_item(NAME(m_slots[i].TL), i); - save_item(NAME(m_slots[i].pan), i); - save_item(NAME(m_slots[i].LFO), i); - save_item(NAME(m_slots[i].VIB), i); - save_item(NAME(m_slots[i].AM), i); - - save_item(NAME(m_slots[i].AR), i); - save_item(NAME(m_slots[i].D1R), i); - save_item(NAME(m_slots[i].DL), i); - save_item(NAME(m_slots[i].D2R), i); - save_item(NAME(m_slots[i].RC), i); - save_item(NAME(m_slots[i].RR), i); - - save_item(NAME(m_slots[i].step), i); - save_item(NAME(m_slots[i].stepptr), i); - - save_item(NAME(m_slots[i].active), i); - save_item(NAME(m_slots[i].KEY_ON), i); - save_item(NAME(m_slots[i].bits), i); - save_item(NAME(m_slots[i].startaddr), i); - save_item(NAME(m_slots[i].loopaddr), i); - save_item(NAME(m_slots[i].endaddr), i); - - save_item(NAME(m_slots[i].env_step), i); - save_item(NAME(m_slots[i].env_vol), i); - save_item(NAME(m_slots[i].env_vol_step), i); - save_item(NAME(m_slots[i].env_vol_lim), i); - save_item(NAME(m_slots[i].env_preverb), i); - } -} - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void ymf278b_device::device_start() -{ - int i; - - m_clock = clock(); - m_rate = m_clock / 768; - m_fm_pos = 0; - - m_timer_busy = timer_alloc(TIMER_BUSY_CLEAR); - m_timer_ld = timer_alloc(TIMER_LD_CLEAR); - - for (i = 0; i < 24; i++) - { - m_slots[i].num = i; - } - - m_stream = stream_alloc(0, 6, m_rate); - m_mix_buffer.resize(m_rate*4,0); - - // rate tables - precompute_rate_tables(); - - // Volume table, 1 = -0.375dB, 8 = -3dB, 256 = -96dB - for(i = 0; i < 256; i++) - m_volume[i] = 65536*pow(2.0, (-0.375/6)*i); - for(i = 256; i < 256*4; i++) - m_volume[i] = 0; - - // Pan values, units are -3dB, i.e. 8. - for(i = 0; i < 16; i++) - { - m_pan_left[i] = i < 7 ? i*8 : i < 9 ? 256 : 0; - m_pan_right[i] = i < 8 ? 0 : i < 10 ? 256 : (16-i)*8; - } - - // Mixing levels, units are -3dB, and add some margin to avoid clipping - for(i=0; i<7; i++) - m_mix_level[i] = m_volume[8*i+13]; - m_mix_level[7] = 0; - - // Register state for saving - register_save_state(); - - // YMF262 related -- cribbed from ymfm_device_base_common - { - // allocate our timers - for (int tnum = 0; tnum < 2; tnum++) - m_timer[tnum] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ymf278b_device::fm_timer_handler), this)); - - // resolve the handlers - m_update_irq.resolve(); - - // compute the size of the save buffer by doing an initial save - ymfm::ymfm_saved_state state(m_save_blob, true); - m_fm.save_restore(state); - - // now register the blob for save, on the assumption the size won't change - save_item(NAME(m_save_blob)); - } -} - - -DEFINE_DEVICE_TYPE(YMF278B, ymf278b_device, "ymf278b", "Yamaha YMF278B OPL4") - -ymf278b_device::ymf278b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, YMF278B, tag, owner, clock) - , device_sound_interface(mconfig, *this) - , device_rom_interface(mconfig, *this) - , m_fm(*this) - , m_update_irq(*this) -{ -} - -// handle pre-saving by filling the blob -void ymf278b_device::device_pre_save() -{ - // remember the original blob size - auto orig_size = m_save_blob.size(); - - // save the state - ymfm::ymfm_saved_state state(m_save_blob, true); - m_fm.save_restore(state); - - // ensure that the size didn't change since we first allocated - if (m_save_blob.size() != orig_size) - throw emu_fatalerror("State size changed for ymfm chip"); -} - -// handle post-loading by restoring from the blob -void ymf278b_device::device_post_load() -{ - // populate the state from the blob - ymfm::ymfm_saved_state state(m_save_blob, false); - m_fm.save_restore(state); -} diff --git a/src/devices/sound/ymf278b.h b/src/devices/sound/ymf278b.h deleted file mode 100644 index 07812279283..00000000000 --- a/src/devices/sound/ymf278b.h +++ /dev/null @@ -1,192 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:R. Belmont, Olivier Galibert, hap -#ifndef MAME_SOUND_YMF278B_H -#define MAME_SOUND_YMF278B_H - -#pragma once - -#include "ymfm/src/ymfm_opl.h" -#include "dirom.h" - -class ymf278b_device : public device_t, public device_sound_interface, public device_rom_interface<22>, public ymfm::ymfm_interface -{ -public: - static constexpr u8 STATUS_BUSY = 0x01; - static constexpr u8 STATUS_LD = 0x02; - - // YMF278B is OPL4 - using fm_engine = ymfm::fm_engine_base; - - // constructor - ymf278b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - // configuration helpers - auto irq_handler() { return m_update_irq.bind(); } - - // read/write access - u8 read(offs_t offset); - void write(offs_t offset, u8 data); - -protected: - // device-level overrides - virtual void device_start() override; - virtual void device_reset() override; - virtual void device_clock_changed() override; - virtual void device_pre_save() override; - virtual void device_post_load() override; - - virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; - - // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; - - // device_rom_interface overrides - virtual void rom_bank_updated() override; - -private: - // timer callbacks - void fm_timer_handler(void *ptr, int param) { m_engine->engine_timer_expired(param); } - void fm_mode_write(void *ptr, int param) { m_engine->engine_mode_write(param); } - void fm_check_interrupts(void *ptr, int param) { m_engine->engine_check_interrupts(); } - - struct YMF278BSlot - { - int16_t wave; /* wavetable number */ - int16_t F_NUMBER; /* frequency */ - int8_t octave; /* octave */ - int8_t preverb; /* pseudo-reverb */ - int8_t DAMP; /* damping */ - int8_t CH; /* output channel */ - int8_t LD; /* level direct */ - int8_t TL; /* total level */ - int8_t pan; /* panpot */ - int8_t LFO; /* LFO */ - int8_t VIB; /* vibrato */ - int8_t AM; /* tremolo */ - - int8_t AR; /* attack rate */ - int8_t D1R; /* decay 1 rate */ - int8_t DL; /* decay level */ - int8_t D2R; /* decay 2 rate */ - int8_t RC; /* rate correction */ - int8_t RR; /* release rate */ - - uint32_t step; /* fixed-point frequency step */ - uint64_t stepptr; /* fixed-point pointer into the sample */ - - int8_t active; /* channel is playing */ - int8_t KEY_ON; /* slot keyed on */ - int8_t bits; /* width of the samples */ - uint32_t startaddr; - uint32_t loopaddr; - uint32_t endaddr; - - int env_step; - uint32_t env_vol; - uint32_t env_vol_step; - uint32_t env_vol_lim; - int8_t env_preverb; - - int num; /* slot number (for debug only) */ - }; - - int compute_rate(YMF278BSlot *slot, int val); - uint32_t compute_decay_env_vol_step(YMF278BSlot *slot, int val); - void compute_freq_step(YMF278BSlot *slot); - void compute_envelope(YMF278BSlot *slot); - void irq_check(); - void retrigger_sample(YMF278BSlot *slot); - void C_w(uint8_t reg, uint8_t data); - void timer_busy_start(int is_pcm); - void precompute_rate_tables(); - void register_save_state(); - - // internal state - uint8_t m_pcmregs[256]; - YMF278BSlot m_slots[24]; - int8_t m_wavetblhdr; - int8_t m_memmode; - int32_t m_memadr; - - emu_timer *m_timer_busy; - emu_timer *m_timer_ld; - - int32_t m_fm_l, m_fm_r; - int32_t m_pcm_l, m_pcm_r; - - uint32_t m_fm_pos; - - uint8_t m_port_C, m_port_AB, m_lastport; - bool m_next_status_id; - - // precomputed tables - uint32_t m_lut_ar[64]; // attack rate - uint32_t m_lut_dr[64]; // decay rate - int32_t m_volume[256*4]; // precalculated attenuation values with some margin for envelope and pan levels - int m_pan_left[16],m_pan_right[16]; // pan volume offsets - int32_t m_mix_level[8]; - - int m_clock; - int m_rate; - - sound_stream * m_stream; - std::vector m_mix_buffer; - - // ymfm OPL4 -- cribbed from ymfm_device_base_common until we figure out how to - // make a proper chip out of this hybrid - fm_engine m_fm; - attotime m_busy_end; // busy end time - emu_timer *m_timer[2]; // two timers - devcb_write_line m_update_irq; // IRQ update callback - std::vector m_save_blob;// save state blob for FM - - // perform a synchronized write - virtual void ymfm_sync_mode_write(uint8_t data) override - { - machine().scheduler().synchronize(timer_expired_delegate(FUNC(ymf278b_device::fm_mode_write), this), data); - } - - // perform a synchronized interrupt check - virtual void ymfm_sync_check_interrupts() override - { - // if we're currently executing a CPU, schedule the interrupt check; - // otherwise, do it directly - auto &scheduler = machine().scheduler(); - if (scheduler.currently_executing()) - scheduler.synchronize(timer_expired_delegate(FUNC(ymf278b_device::fm_check_interrupts), this)); - else - m_engine->engine_check_interrupts(); - } - - // set a timer - virtual void ymfm_set_timer(uint32_t tnum, int32_t duration_in_clocks) override - { - if (duration_in_clocks >= 0) - m_timer[tnum]->adjust(attotime::from_ticks(duration_in_clocks, device_t::clock()), tnum); - else - m_timer[tnum]->enable(false); - } - - // set the time when busy will be clear - virtual void ymfm_set_busy_end(uint32_t clocks) override - { - m_busy_end = machine().time() + attotime::from_ticks(clocks, device_t::clock()); - } - - // are we past the busy clear time? - virtual bool ymfm_is_busy() override - { - return (machine().time() < m_busy_end); - } - - // handle IRQ signaling - virtual void ymfm_update_irq(bool asserted) override - { - if (!m_update_irq.isnull()) - m_update_irq(asserted ? ASSERT_LINE : CLEAR_LINE); - } -}; - -DECLARE_DEVICE_TYPE(YMF278B, ymf278b_device) - -#endif // MAME_SOUND_YMF278B_H diff --git a/src/devices/sound/ymfm_mame.h b/src/devices/sound/ymfm_mame.h index 342e571e747..74acf718182 100644 --- a/src/devices/sound/ymfm_mame.h +++ b/src/devices/sound/ymfm_mame.h @@ -123,22 +123,19 @@ protected: return (machine().time() < m_busy_end); } - // the chip implementation calls this whenever a new value is written to - // one of the chip's output ports (only applies to some chip types); our - // responsibility is to pass the written data on to any consumers - virtual void ymfm_io_write(uint8_t port, uint8_t data) override + // the chip implementation calls this whenever data is read from outside + // of the chip; our responsibility is to provide the data requested + virtual uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address) override { - if (!m_io_write[port & 1].isnull()) - m_io_write[port & 1](data); + return (type != ymfm::ACCESS_IO || m_io_read[address & 1].isnull()) ? 0 : m_io_read[address & 1](); } - // the chip implementation calls this whenever an on-chip register is read - // which returns data from one of the chip's input ports; our responsibility - // is to produce the current input value so that it can be reflected by the - // read operation - virtual uint8_t ymfm_io_read(uint8_t port) override + // the chip implementation calls this whenever data is written outside + // of the chip; our responsibility is to pass the written data on to any consumers + virtual void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) override { - return m_io_read[port & 1].isnull() ? 0 : m_io_read[port & 1](); + if (type == ymfm::ACCESS_IO && !m_io_write[address & 1].isnull()) + m_io_write[address & 1](data); } // handle device start diff --git a/src/devices/sound/ymopl.cpp b/src/devices/sound/ymopl.cpp index 9953a5a3551..352906f40b8 100644 --- a/src/devices/sound/ymopl.cpp +++ b/src/devices/sound/ymopl.cpp @@ -51,26 +51,30 @@ void y8950_device::rom_bank_updated() //------------------------------------------------- -// ymfm_adpcm_b_read - callback to read data for +// ymfm_external_read - callback to read data for // the ADPCM-B engine; in this case, from our // default address space //------------------------------------------------- -uint8_t y8950_device::ymfm_adpcm_b_read(uint32_t offset) +uint8_t y8950_device::ymfm_external_read(ymfm::access_class type, uint32_t offset) { - return read_byte(offset); + if (type == ymfm::ACCESS_ADPCM_B) + return read_byte(offset); + return parent::ymfm_external_read(type, offset); } //------------------------------------------------- -// ymfm_adpcm_b_write - callback to write data to +// ymfm_external_write - callback to write data to // the ADPCM-B engine; in this case, to our // default address space //------------------------------------------------- -void y8950_device::ymfm_adpcm_b_write(uint32_t offset, uint8_t data) +void y8950_device::ymfm_external_write(ymfm::access_class type, uint32_t offset, uint8_t data) { - space().write_byte(offset, data); + if (type == ymfm::ACCESS_ADPCM_B) + return space().write_byte(offset, data); + parent::ymfm_external_write(type, offset, data); } @@ -109,6 +113,75 @@ ymf262_device::ymf262_device(const machine_config &mconfig, const char *tag, dev +//********************************************************* +// YMF278B DEVICE +//********************************************************* + +DEFINE_DEVICE_TYPE(YMF278B, ymf278b_device, "ymf278b", "YMF278B OPL4") + +//------------------------------------------------- +// ymf278b_device - constructor +//------------------------------------------------- + +ymf278b_device::ymf278b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + ymfm_device_base(mconfig, tag, owner, clock, YMF278B), + device_rom_interface(mconfig, *this) +{ +} + + +//------------------------------------------------- +// rom_bank_updated - refresh the stream if the +// ROM banking changes +//------------------------------------------------- + +void ymf278b_device::rom_bank_updated() +{ + m_stream->update(); +} + + +//------------------------------------------------- +// ymfm_external_read - callback to read data for +// the ADPCM-B engine; in this case, from our +// default address space +//------------------------------------------------- + +uint8_t ymf278b_device::ymfm_external_read(ymfm::access_class type, uint32_t offset) +{ + if (type == ymfm::ACCESS_PCM) + return read_byte(offset); + return 0; +} + + +//------------------------------------------------- +// ymfm_external_write - callback to write data to +// the ADPCM-B engine; in this case, to our +// default address space +//------------------------------------------------- + +void ymf278b_device::ymfm_external_write(ymfm::access_class type, uint32_t offset, uint8_t data) +{ + if (type == ymfm::ACCESS_PCM) + return space().write_byte(offset, data); +} + + +//------------------------------------------------- +// ymfm_external_write - callback to write data to +// the ADPCM-B engine; in this case, to our +// default address space +//------------------------------------------------- + +void ymf278b_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +{ + // rotate the outputs so that the DO2 outputs are first + parent::update_internal(outputs, 2); +} + + + //********************************************************* // YM2413 DEVICE //********************************************************* diff --git a/src/devices/sound/ymopl.h b/src/devices/sound/ymopl.h index 4d221529b4b..4ec8f605023 100644 --- a/src/devices/sound/ymopl.h +++ b/src/devices/sound/ymopl.h @@ -29,6 +29,8 @@ DECLARE_DEVICE_TYPE(Y8950, y8950_device); class y8950_device : public ymfm_device_base, public device_rom_interface<21> { + using parent = ymfm_device_base; + public: // constructor y8950_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); @@ -48,8 +50,8 @@ protected: private: // ADPCM read/write callbacks - uint8_t ymfm_adpcm_b_read(offs_t address) override; - void ymfm_adpcm_b_write(offs_t address, uint8_t data) override; + uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address) override; + void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) override; }; @@ -81,6 +83,41 @@ public: }; +// ======================> ymf278b_device + +DECLARE_DEVICE_TYPE(YMF278B, ymf278b_device); + +class ymf278b_device : public ymfm_device_base, public device_rom_interface<22> +{ + using parent = ymfm_device_base; + +public: + // constructor + ymf278b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // additional register reads + uint8_t data_pcm_r() { return update_streams().read_data_pcm(); } + + // additional register writes + void address_hi_w(u8 data) { update_streams().write_address_hi(data); } + void data_hi_w(u8 data) { update_streams().write_data(data); } + void address_pcm_w(u8 data) { update_streams().write_address_pcm(data); } + void data_pcm_w(u8 data) { update_streams().write_data_pcm(data); } + +protected: + // device_rom_interface overrides + virtual void rom_bank_updated() override; + + // sound overrides + virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; + +private: + // ADPCM read/write callbacks + uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address) override; + void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) override; +}; + + // ======================> ym2413_device DECLARE_DEVICE_TYPE(YM2413, ym2413_device); diff --git a/src/devices/sound/ymopn.cpp b/src/devices/sound/ymopn.cpp index 8733a5115cf..2d1751ce8b0 100644 --- a/src/devices/sound/ymopn.cpp +++ b/src/devices/sound/ymopn.cpp @@ -109,38 +109,31 @@ void ym2608_device::rom_bank_updated() //------------------------------------------------- -// ymfm_adpcm_a_read - callback to read data for -// the ADPCM-A engine; in this case, from the -// internal ROM containing drum samples +// ymfm_external_read - callback to read data for +// the ADPCM-A/B engines //------------------------------------------------- -uint8_t ym2608_device::ymfm_adpcm_a_read(uint32_t offset) +uint8_t ym2608_device::ymfm_external_read(ymfm::access_class type, uint32_t offset) { - return m_internal->as_u8(offset % m_internal->bytes()); + if (type == ymfm::ACCESS_ADPCM_A) + return m_internal->as_u8(offset % m_internal->bytes()); + else if (type == ymfm::ACCESS_ADPCM_B) + return space(0).read_byte(offset); + return parent::ymfm_external_read(type, offset); } //------------------------------------------------- -// ymfm_adpcm_b_read - callback to read data for -// the ADPCM-B engine; in this case, from our -// default address space -//------------------------------------------------- - -uint8_t ym2608_device::ymfm_adpcm_b_read(uint32_t offset) -{ - return space(0).read_byte(offset); -} - - -//------------------------------------------------- -// ymfm_adpcm_b_write - callback to write data to +// ymfm_external_write - callback to write data to // the ADPCM-B engine; in this case, to our // default address space //------------------------------------------------- -void ym2608_device::ymfm_adpcm_b_write(uint32_t offset, uint8_t data) +void ym2608_device::ymfm_external_write(ymfm::access_class type, uint32_t offset, uint8_t data) { - space(0).write_byte(offset, data); + if (type == ymfm::ACCESS_ADPCM_B) + return space(0).write_byte(offset, data); + parent::ymfm_external_write(type, offset, data); } @@ -208,28 +201,18 @@ void ym2610_device_base::device_start() //------------------------------------------------- -// ymfm_adpcm_a_read - callback to read data for -// the ADPCM-A engine; in this case, from address -// space 0 +// ymfm_external_read - callback to read data for +// the ADPCM-A/B engines //------------------------------------------------- template -uint8_t ym2610_device_base::ymfm_adpcm_a_read(uint32_t offset) +uint8_t ym2610_device_base::ymfm_external_read(ymfm::access_class type, uint32_t offset) { - return space(0).read_byte(offset); -} - - -//------------------------------------------------- -// ymfm_adpcm_b_read - callback to read data for -// the ADPCM-B engine; in this case, from address -// space 1 -//------------------------------------------------- - -template -uint8_t ym2610_device_base::ymfm_adpcm_b_read(uint32_t offset) -{ - return space(1).read_byte(offset); + if (type == ymfm::ACCESS_ADPCM_A) + return space(0).read_byte(offset); + else if (type == ymfm::ACCESS_ADPCM_B) + return space(1).read_byte(offset); + return 0; } diff --git a/src/devices/sound/ymopn.h b/src/devices/sound/ymopn.h index 7bf72216aff..c65738dee07 100644 --- a/src/devices/sound/ymopn.h +++ b/src/devices/sound/ymopn.h @@ -75,9 +75,8 @@ protected: private: // ADPCM read/write callbacks - virtual uint8_t ymfm_adpcm_a_read(uint32_t address) override; - virtual uint8_t ymfm_adpcm_b_read(uint32_t address) override; - virtual void ymfm_adpcm_b_write(uint32_t address, u8 data) override; + virtual uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address) override; + virtual void ymfm_external_write(ymfm::access_class type, uint32_t address, u8 data) override; // internal state required_memory_region m_internal; // internal memory region @@ -115,8 +114,7 @@ protected: private: // ADPCM read/write callbacks - virtual uint8_t ymfm_adpcm_a_read(offs_t address) override; - virtual uint8_t ymfm_adpcm_b_read(offs_t address) override; + virtual uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address) override; // internal state address_space_config const m_adpcm_a_config; // address space 0 config (ADPCM-A) diff --git a/src/mame/drivers/fuukifg3.cpp b/src/mame/drivers/fuukifg3.cpp index fb8a1aa7dc9..0fe01b02a00 100644 --- a/src/mame/drivers/fuukifg3.cpp +++ b/src/mame/drivers/fuukifg3.cpp @@ -161,7 +161,7 @@ FG-3J ROM-J 507KA0301P04 Rev:1.3 #include "cpu/z80/z80.h" #include "cpu/m68000/m68000.h" -#include "sound/ymf278b.h" +#include "sound/ymopl.h" #include "speaker.h" diff --git a/src/mame/drivers/lordgun.cpp b/src/mame/drivers/lordgun.cpp index 6331a0cdde8..cb0577f7670 100644 --- a/src/mame/drivers/lordgun.cpp +++ b/src/mame/drivers/lordgun.cpp @@ -46,7 +46,6 @@ Notes: #include "cpu/z80/z80.h" #include "machine/i8255.h" #include "sound/ymopl.h" -#include "sound/ymf278b.h" #include "speaker.h" diff --git a/src/mame/drivers/metro.cpp b/src/mame/drivers/metro.cpp index 55deaeba126..ecb593d6970 100644 --- a/src/mame/drivers/metro.cpp +++ b/src/mame/drivers/metro.cpp @@ -102,7 +102,6 @@ driver modified by Hau #include "sound/msm5205.h" #include "sound/ymopl.h" #include "sound/ymopn.h" -#include "sound/ymf278b.h" #include "speaker.h" #include diff --git a/src/mame/drivers/psikyo.cpp b/src/mame/drivers/psikyo.cpp index d63121ab07c..4f6ddeb786f 100644 --- a/src/mame/drivers/psikyo.cpp +++ b/src/mame/drivers/psikyo.cpp @@ -85,8 +85,8 @@ This was pointed out by Bart Puype #include "cpu/m68000/m68000.h" #include "cpu/pic16c5x/pic16c5x.h" #include "sound/okim6295.h" +#include "sound/ymopl.h" #include "sound/ymopn.h" -#include "sound/ymf278b.h" #include "speaker.h" diff --git a/src/mame/drivers/psikyosh.cpp b/src/mame/drivers/psikyosh.cpp index 3f7283040a7..27a104059d9 100644 --- a/src/mame/drivers/psikyosh.cpp +++ b/src/mame/drivers/psikyosh.cpp @@ -279,7 +279,7 @@ Notes: #include "cpu/sh/sh2.h" #include "machine/eepromser.h" #include "machine/watchdog.h" -#include "sound/ymf278b.h" +#include "sound/ymopl.h" #include "speaker.h" diff --git a/src/mame/drivers/vgmplay.cpp b/src/mame/drivers/vgmplay.cpp index 7197135ba7c..3a80f633a10 100644 --- a/src/mame/drivers/vgmplay.cpp +++ b/src/mame/drivers/vgmplay.cpp @@ -39,7 +39,6 @@ #include "sound/vgm_visualizer.h" #include "sound/x1_010.h" #include "sound/ymf271.h" -#include "sound/ymf278b.h" #include "sound/ymopl.h" #include "sound/ymopm.h" #include "sound/ymopn.h" diff --git a/src/mame/includes/fuukifg3.h b/src/mame/includes/fuukifg3.h index 2c14c512e2b..190013533a2 100644 --- a/src/mame/includes/fuukifg3.h +++ b/src/mame/includes/fuukifg3.h @@ -15,8 +15,6 @@ #define CPU_CLOCK (XTAL(40'000'000) / 2) /* clock for 68020 */ #define SOUND_CPU_CLOCK (XTAL(12'000'000) / 2) /* clock for Z80 sound CPU */ -/* NOTE: YMF278B_STD_CLOCK is defined in /src/emu/sound/ymf278b.h */ - class fuuki32_state : public driver_device { diff --git a/src/mame/includes/psikyo4.h b/src/mame/includes/psikyo4.h index 844535f953a..0705aab74a6 100644 --- a/src/mame/includes/psikyo4.h +++ b/src/mame/includes/psikyo4.h @@ -7,7 +7,7 @@ *************************************************************************/ #include "cpu/sh/sh2.h" -#include "sound/ymf278b.h" +#include "sound/ymopl.h" #include "machine/eepromser.h" #include "emupal.h" #include "screen.h"