diff options
author | kcgen <kcgen@users.noreply.github.com> | 2022-09-17 21:29:44 +0300 |
---|---|---|
committer | kcgen <kcgen@users.noreply.github.com> | 2022-09-17 22:54:56 +0300 |
commit | 128e500cb23ab6e9610be5904561d997b13f255e (patch) | |
tree | 73ab700f96efd29282c11df2a722b72a6732589b | |
parent | d1af01bb65e466e2e2ece0b2c4c002a3b53a325d (diff) |
Make CMS SAA-1099 chips standalone deviceskc/separate-cms-chips-1
-rw-r--r-- | src/hardware/gameblaster.cpp | 213 | ||||
-rw-r--r-- | src/hardware/gameblaster.h | 88 |
2 files changed, 181 insertions, 120 deletions
diff --git a/src/hardware/gameblaster.cpp b/src/hardware/gameblaster.cpp index 42e44c43d..48032a2a2 100644 --- a/src/hardware/gameblaster.cpp +++ b/src/hardware/gameblaster.cpp @@ -24,63 +24,41 @@ #include "setup.h" #include "pic.h" -void GameBlaster::Open(const int port_choice, const std::string &card_choice, - const std::string &filter_choice) +#include <bitset> + +void Saa1099::Open(const io_port_t port, const std::string &filter_choice) { Close(); assert(!is_open); - is_standalone_gameblaster = (card_choice == "gb"); - - // Ports are filtered and corrected by the conf system, so we simply - // assert here - const std::vector<io_port_t> valid_gb_ports = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260}; - const std::vector<io_port_t> valid_cms_ports = {0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0x300}; - const auto valid_ports = is_standalone_gameblaster ? valid_gb_ports - : valid_cms_ports; - base_port = check_cast<io_port_t>(port_choice); - assert(contains(valid_ports, base_port)); - - // Create the SAA1099 devices - for (auto &d : devices) { - d = std::make_unique<saa1099_device>(machine_config(), "", nullptr, chip_clock, render_divisor); - d->device_start(); - } + // Create the SAA1099 device + device = std::make_unique<saa1099_device>(machine_config(), + "", + nullptr, + saa1099_chip_clock, + saa1099_rate_divisor); + device->device_start(); // Creative included CMS chips on several Sound Blaster cards, which // games could use (in addition to the SB features), so we always setup // those handlers - even if the card type isn't a GameBlaster. using namespace std::placeholders; - const auto data_to_left = std::bind(&GameBlaster::WriteDataToLeftDevice, this, _1, _2, _3); - const auto control_to_left = std::bind(&GameBlaster::WriteControlToLeftDevice, this, _1, _2, _3); - const auto data_to_right = std::bind(&GameBlaster::WriteDataToRightDevice, this, _1, _2, _3); - const auto control_to_right = std::bind(&GameBlaster::WriteControlToRightDevice, this, _1, _2, _3); + const auto write_data = std::bind(&Saa1099::WriteData, this, _1, _2, _3); + const auto write_control = std::bind(&Saa1099::WriteControl, this, _1, _2, _3); - write_handlers[0].Install(base_port, data_to_left, io_width_t::byte); - write_handlers[1].Install(base_port + 1, control_to_left, io_width_t::byte); - write_handlers[2].Install(base_port + 2, data_to_right, io_width_t::byte); - write_handlers[3].Install(base_port + 3, control_to_right, io_width_t::byte); + write_handlers[0].Install(port, write_data, io_width_t::byte); + write_handlers[1].Install(port + 1, write_control, io_width_t::byte); - // However, standalone GameBlaster cards came with a dedicated chip on - // it that could be used for detection. So we setup those handlers for - // this chip only if the card-type is a GameBlaster: - if (is_standalone_gameblaster) { - const auto read_from_detection_port = std::bind(&GameBlaster::ReadFromDetectionPort, this, _1, _2); - const auto write_to_detection_port = std::bind(&GameBlaster::WriteToDetectionPort, this, _1, _2, _3); - - read_handler_for_detection.Install(base_port, read_from_detection_port, io_width_t::byte, 16); - write_handler_for_detection.Install(base_port + 4, - write_to_detection_port, - io_width_t::byte, - 12); - } + const auto read_control = std::bind(&Saa1099::ReadControl, this, _1, _2); + read_handler.Install(port + 1, read_control, io_width_t::byte); // Setup the mixer and level controls - const auto audio_callback = std::bind(&GameBlaster::AudioCallback, this, _1); + const auto audio_callback = std::bind(&Saa1099::AudioCallback, this, _1); + assert(chip_name); channel = MIXER_AddChannel(audio_callback, use_mixer_rate, - CardName(), + chip_name, {ChannelFeature::Sleep, ChannelFeature::Stereo, ChannelFeature::ReverbSend, @@ -99,7 +77,7 @@ void GameBlaster::Open(const int port_choice, const std::string &card_choice, } else if (!channel->TryParseAndSetCustomFilter(filter_choice)) { if (filter_choice != "off") LOG_WARNING("%s: Invalid 'cms_filter' value: '%s', using 'off'", - CardName(), + chip_name, filter_choice.c_str()); channel->SetLowPassFilter(FilterState::Off); @@ -111,44 +89,28 @@ void GameBlaster::Open(const int port_choice, const std::string &card_choice, // Setup the resampler to convert from the render rate to the mixer's frame rate const auto max_freq = std::max(frame_rate_hz * 0.9 / 2, 8000.0); for (auto &r : resamplers) - r.reset(reSIDfp::TwoPassSincResampler::create(render_rate_hz, frame_rate_hz, max_freq)); - - LOG_MSG("%s: Running on port %xh with two %0.3f MHz Phillips SAA-1099 chips", - CardName(), - base_port, - chip_clock / 1e6); + r.reset(reSIDfp::TwoPassSincResampler::create(saa1099_chip_clock / saa1099_rate_divisor, + frame_rate_hz, + max_freq)); assert(channel); - assert(devices[0]); - assert(devices[1]); + assert(device); assert(resamplers[0]); assert(resamplers[1]); - - is_open = true; } -bool GameBlaster::MaybeRenderFrame(AudioFrame &frame) +bool Saa1099::MaybeRenderFrame(AudioFrame &frame) { // Static containers setup once and reused - static std::array<int16_t, 2> buf = {}; // left and right - static int16_t *p_buf[] = {&buf[0], &buf[1]}; static device_sound_interface::sound_stream stream; - // Accumulate the samples from both SAA-1099 devices - devices[0]->sound_stream_update(stream, 0, p_buf, 1); - int left_accum = buf[0]; - int right_accum = buf[1]; - - devices[1]->sound_stream_update(stream, 0, p_buf, 1); - left_accum += buf[0]; - right_accum += buf[1]; - - // Increment our time datum up to which the device has rendered + // Update the stream and increment our time datum + device->sound_stream_update(stream, 0, p_buf, 1); last_rendered_ms += ms_per_render; // Resample the limited frame - const auto l_ready = resamplers[0]->input(left_accum); - const auto r_ready = resamplers[1]->input(right_accum); + const auto l_ready = resamplers[0]->input(buf[0]); + const auto r_ready = resamplers[1]->input(buf[1]); assert(l_ready == r_ready); const auto frame_is_ready = l_ready && r_ready; @@ -160,7 +122,7 @@ bool GameBlaster::MaybeRenderFrame(AudioFrame &frame) return frame_is_ready; } -void GameBlaster::RenderUpToNow() +void Saa1099::RenderUpToNow() { const auto now = PIC_FullIndex(); @@ -178,31 +140,31 @@ void GameBlaster::RenderUpToNow() } } -void GameBlaster::WriteDataToLeftDevice(io_port_t, io_val_t value, io_width_t) +void Saa1099::WriteData(io_port_t, io_val_t value, io_width_t) { RenderUpToNow(); - devices[0]->data_w(0, 0, check_cast<uint8_t>(value)); + LOG_MSG("%s write data = %u", chip_name, value); + device->data_w(0, 0, check_cast<uint8_t>(value)); } -void GameBlaster::WriteControlToLeftDevice(io_port_t, io_val_t value, io_width_t) +void Saa1099::WriteControl(io_port_t, io_val_t value, io_width_t) { RenderUpToNow(); - devices[0]->control_w(0, 0, check_cast<uint8_t>(value)); -} + const auto control_bits = std::bitset<8>(static_cast<uint8_t>(value)); + LOG_MSG("%s write ctrl = %s", chip_name, control_bits.to_string().c_str()); + device->control_w(0, 0, check_cast<uint8_t>(value)); -void GameBlaster::WriteDataToRightDevice(io_port_t, io_val_t value, io_width_t) -{ - RenderUpToNow(); - devices[1]->data_w(0, 0, check_cast<uint8_t>(value)); + constexpr uint8_t cmd_range = 0b00011111; + control_cmd = value & cmd_range; } -void GameBlaster::WriteControlToRightDevice(io_port_t, io_val_t value, io_width_t) +uint8_t Saa1099::ReadControl(const io_port_t, const io_width_t) { - RenderUpToNow(); - devices[1]->control_w(0, 0, check_cast<uint8_t>(value)); + LOG_MSG("%s read control = %u", chip_name, control_cmd); + return control_cmd; } -void GameBlaster::AudioCallback(const uint16_t requested_frames) +void Saa1099::AudioCallback(const uint16_t requested_frames) { assert(channel); @@ -227,6 +189,79 @@ void GameBlaster::AudioCallback(const uint16_t requested_frames) last_rendered_ms = PIC_FullIndex(); } +void Saa1099::Close() +{ + if (!is_open) + return; + + // Drop access to the IO ports + for (auto &w : write_handlers) + w.Uninstall(); + read_handler.Uninstall(); + + // Stop playback + if (channel) + channel->Enable(false); + + // Remove the mixer channel, SAA-1099 device, and resamplers + channel.reset(); + device.reset(); + resamplers[0].reset(); + resamplers[1].reset(); + + is_open = false; +} + +void GameBlaster::Open(const int port_choice, const std::string &card_choice, + const std::string &filter_choice) +{ + Close(); + assert(!is_open); + + is_standalone_gameblaster = (card_choice == "gb"); + + // Ports are filtered and corrected by the conf system, so we simply + // assert here + const std::vector<io_port_t> valid_gb_ports = { + 0x210, 0x220, 0x230, 0x240, 0x250, 0x260}; + const std::vector<io_port_t> valid_cms_ports = { + 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0x300}; + const auto valid_ports = is_standalone_gameblaster ? valid_gb_ports + : valid_cms_ports; + base_port = check_cast<io_port_t>(port_choice); + assert(contains(valid_ports, base_port)); + + saa1099[0].Open(base_port, filter_choice); + saa1099[1].Open(base_port + 2, filter_choice); + + // Standalone GameBlaster cards came with a dedicated chip on + // it that could be used for detection. So we setup those handlers for + // this chip only if the card-type is a GameBlaster: + using namespace std::placeholders; + if (is_standalone_gameblaster) { + const auto read_from_detection_port = std::bind( + &GameBlaster::ReadFromDetectionPort, this, _1, _2); + const auto write_to_detection_port = std::bind( + &GameBlaster::WriteToDetectionPort, this, _1, _2, _3); + + read_handler_for_detection.Install(base_port + 4, + read_from_detection_port, + io_width_t::byte, + 16); + write_handler_for_detection.Install(base_port + 4, + write_to_detection_port, + io_width_t::byte, + 12); + } + + LOG_MSG("%s: Running on port %xh with two %0.3f MHz Phillips SAA-1099 chips", + CardName(), + base_port, + saa1099_chip_clock / 1e6); + + is_open = true; +} + void GameBlaster::WriteToDetectionPort(io_port_t port, io_val_t value, io_width_t) { switch (port - base_port) { @@ -262,22 +297,12 @@ void GameBlaster::Close() LOG_INFO("%s: Shutting down the card on port %xh", CardName(), base_port); - // Drop access to the IO ports - for (auto &w : write_handlers) - w.Uninstall(); write_handler_for_detection.Uninstall(); read_handler_for_detection.Uninstall(); - // Stop playback - if (channel) - channel->Enable(false); - - // Remove the mixer channel, SAA-1099 devices, soft-limiter, and resamplers - channel.reset(); - devices[0].reset(); - devices[1].reset(); - resamplers[0].reset(); - resamplers[1].reset(); + // Stop the devices + saa1099[0].Close(); + saa1099[1].Close(); is_open = false; } diff --git a/src/hardware/gameblaster.h b/src/hardware/gameblaster.h index 2b81d5a9c..52011fa7e 100644 --- a/src/hardware/gameblaster.h +++ b/src/hardware/gameblaster.h @@ -38,29 +38,79 @@ #include "mame/saa1099.h" #include "../libs/residfp/resample/TwoPassSincResampler.h" -class GameBlaster { +constexpr auto saa1099_chip_clock = 14318180 / 2; +constexpr auto saa1099_rate_divisor = 10; + +class Saa1099 { public: - void Open(const int port_choice, const std::string &card_choice, - const std::string &filter_choice); + Saa1099(const char *name) : chip_name(name) + { + assert(chip_name); + } + Saa1099() = delete; + Saa1099(const Saa1099 &) = delete; + Saa1099 &operator=(const Saa1099 &) = delete; + + void Open(const io_port_t port, const std::string &filter_choice); void Close(); - ~GameBlaster() { Close(); } + ~Saa1099() + { + Close(); + } -private: // Audio rendering bool MaybeRenderFrame(AudioFrame &frame); std::vector<int16_t> GetFrame(); void AudioCallback(const uint16_t requested_frames); void RenderUpToNow(); - // IO callbacks to the left SAA1099 device - void WriteDataToLeftDevice(io_port_t port, io_val_t value, io_width_t width); - void WriteControlToLeftDevice(io_port_t port, io_val_t value, io_width_t width); + // IO callback + void WriteData(io_port_t port, io_val_t value, io_width_t width); + void WriteControl(io_port_t port, io_val_t value, io_width_t width); + + uint8_t ReadControl(const io_port_t, const io_width_t); + +private: + // Managed objects + mixer_channel_t channel = nullptr; + + IO_WriteHandleObject write_handlers[2] = {}; + IO_ReadHandleObject read_handler = {}; + + std::unique_ptr<saa1099_device> device = {}; + std::unique_ptr<reSIDfp::TwoPassSincResampler> resamplers[2] = {}; + + std::queue<AudioFrame> fifo = {}; + + // Static rate-related configuration + static constexpr auto ms_per_render = millis_in_second / + (saa1099_chip_clock / + saa1099_rate_divisor); + + // Runtime state + std::array<int16_t, 2> buf = {}; // left and right + + int16_t *p_buf[2] = {&buf[0], &buf[1]}; + + double last_rendered_ms = 0; + uint8_t control_cmd = 0; + const char *chip_name = nullptr; + bool is_open = false; +}; + +class GameBlaster { +public: + void Open(const int port_choice, const std::string &card_choice, + const std::string &filter_choice); - // IO callbacks to the right SAA1099 device - void WriteDataToRightDevice(io_port_t port, io_val_t value, io_width_t width); - void WriteControlToRightDevice(io_port_t port, io_val_t value, io_width_t width); + void Close(); + ~GameBlaster() + { + Close(); + } +private: // IO callbacks to the GameBlaster detection chip void WriteToDetectionPort(io_port_t port, io_val_t value, io_width_t width); uint8_t ReadFromDetectionPort(io_port_t port, io_width_t width) const; @@ -68,26 +118,12 @@ private: const char *CardName() const; // Managed objects - mixer_channel_t channel = nullptr; + Saa1099 saa1099[2] = {"SAA1099_0", "SAA1099_1"}; - IO_WriteHandleObject write_handlers[4] = {}; IO_WriteHandleObject write_handler_for_detection = {}; IO_ReadHandleObject read_handler_for_detection = {}; - std::unique_ptr<saa1099_device> devices[2] = {}; - std::unique_ptr<reSIDfp::TwoPassSincResampler> resamplers[2] = {}; - - std::queue<AudioFrame> fifo = {}; - - // Static rate-related configuration - static constexpr auto chip_clock = 14318180 / 2; - static constexpr auto render_divisor = 32; - static constexpr auto render_rate_hz = ceil_sdivide(chip_clock, - render_divisor); - static constexpr auto ms_per_render = millis_in_second / render_rate_hz; - // Runtime states - double last_rendered_ms = 0; io_port_t base_port = 0; bool is_standalone_gameblaster = false; bool is_open = false; |