From 4e5b9cd7ed4fbb9b02b89aba644eee5ed10bc8c1 Mon Sep 17 00:00:00 2001 From: MooglyGuy Date: Thu, 10 Jun 2021 18:10:00 +0200 Subject: Super A'Can: DMA sample support and corrected sample length (#8160) [Ryan Holtz, Ian Karlsson] --- src/mame/audio/acan.cpp | 234 +++++++++++++++++++++++++----------------- src/mame/audio/acan.h | 12 ++- src/mame/drivers/supracan.cpp | 26 ++++- 3 files changed, 171 insertions(+), 101 deletions(-) diff --git a/src/mame/audio/acan.cpp b/src/mame/audio/acan.cpp index b157e5f2b46..2f53ca751b2 100644 --- a/src/mame/audio/acan.cpp +++ b/src/mame/audio/acan.cpp @@ -22,9 +22,11 @@ acan_sound_device::acan_sound_device(const machine_config &mconfig, const char * , device_sound_interface(mconfig, *this) , m_stream(nullptr) , m_timer(nullptr) - , m_irq_handler(*this) + , m_timer_irq_handler(*this) + , m_dma_irq_handler(*this) , m_ram_read(*this) , m_active_channels(0) + , m_dma_channels(0) { } @@ -34,11 +36,13 @@ void acan_sound_device::device_start() m_stream = stream_alloc(0, 2, clock() / 16 / 5); m_timer = timer_alloc(0); - m_irq_handler.resolve(); + m_timer_irq_handler.resolve_safe(); + m_dma_irq_handler.resolve_safe(); m_ram_read.resolve_safe(0); // register for savestates save_item(NAME(m_active_channels)); + save_item(NAME(m_dma_channels)); save_item(STRUCT_MEMBER(m_channels, pitch)); save_item(STRUCT_MEMBER(m_channels, length)); save_item(STRUCT_MEMBER(m_channels, start_addr)); @@ -46,6 +50,7 @@ void acan_sound_device::device_start() save_item(STRUCT_MEMBER(m_channels, end_addr)); save_item(STRUCT_MEMBER(m_channels, addr_increment)); save_item(STRUCT_MEMBER(m_channels, frac)); + save_item(STRUCT_MEMBER(m_channels, register9)); save_item(STRUCT_MEMBER(m_channels, envelope)); save_item(STRUCT_MEMBER(m_channels, volume)); save_item(STRUCT_MEMBER(m_channels, volume_l)); @@ -57,19 +62,19 @@ void acan_sound_device::device_start() void acan_sound_device::device_reset() { m_active_channels = 0; + m_dma_channels = 0; std::fill(std::begin(m_regs), std::end(m_regs), 0); m_timer->reset(); - if (!m_irq_handler.isnull()) - m_irq_handler(0); + m_timer_irq_handler(0); + m_dma_irq_handler(0); } void acan_sound_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { if (m_regs[0x14] & 0x40) { - if (!m_irq_handler.isnull()) - m_irq_handler(1); + m_timer_irq_handler(1); // Update frequency uint16_t period = (m_regs[0x12] << 8) + m_regs[0x11]; @@ -83,7 +88,7 @@ void acan_sound_device::sound_stream_update(sound_stream &stream, std::vector= channel.end_addr) { - if (channel.one_shot) + if (channel.register9) + { + m_dma_irq_handler(1); + keyon_voice(i); + } + else if (channel.one_shot) { m_active_channels &= ~(1 << i); } @@ -129,13 +139,28 @@ uint8_t acan_sound_device::read(offs_t offset) { if (offset == 0x14) { - // acknowledge timer (?) - if (!m_irq_handler.isnull()) - m_irq_handler(0); + // acknowledge timer IRQ? + m_timer_irq_handler(0); + } + else if (offset == 0x16) + { + // acknowledge DMA IRQ? + m_dma_irq_handler(0); } return m_regs[offset]; } +void acan_sound_device::keyon_voice(uint8_t voice) +{ + acan_channel &channel = m_channels[voice]; + channel.curr_addr = channel.start_addr << 6; + channel.end_addr = channel.curr_addr + channel.length; + + m_active_channels |= (1 << voice); + + //printf("Keyon voice %d\n", voice); +} + void acan_sound_device::write(offs_t offset, uint8_t data) { const uint8_t upper = (offset >> 4) & 0x0f; @@ -145,107 +170,128 @@ void acan_sound_device::write(offs_t offset, uint8_t data) switch (upper) { - case 0x1: - if (lower == 0x7) // Keyon/keyoff - { - const uint16_t mask = 1 << (data & 0xf); - if (data & 0xf0) - m_active_channels |= mask; - else - m_active_channels &= ~mask; - } - else if (lower == 0x4) // Timer control - { - if (data & 0x80) - { - // Update frequency - uint16_t period = (m_regs[0x12] << 8) + m_regs[0x11]; - m_timer->adjust(clocks_to_attotime(10 * (0x10000 - period)), 0); - } - // the meaning of the data that is actually written is unknown - LOG("Sound timer control: %02x = %02x\n", offset, data); - } - else if (lower != 0x1 && lower != 0x2) - { - LOG("Unknown audio register: %02x = %02x\n", offset, data); - } + case 0x1: + switch (lower) + { + case 0x1: // Timer frequency (low byte) + LOG("%s: Sound timer frequency (low byte) = %02x\n", machine().describe_context(), data); break; - case 0x2: // Pitch (low byte) - if (lower < 0xf) - { - acan_channel &channel = m_channels[lower]; - channel.pitch &= 0xff00; - channel.pitch |= data; - channel.addr_increment = (uint32_t)channel.pitch << 6; - channel.frac = 0; - } + case 0x2: // Timer frequency (high byte) + LOG("%s: Sound timer frequency (high byte) = %02x\n", machine().describe_context(), data); break; - case 0x3: // Pitch (high byte) - if (lower < 0xf) + case 0x4: // Timer control + // The meaning of the data that is actually written is unknown + LOG("%s: Sound timer control = %02x\n", machine().describe_context(), data); + if (BIT(data, 7)) { - acan_channel &channel = m_channels[lower]; - channel.pitch &= 0x00ff; - channel.pitch |= data << 8; - channel.addr_increment = (uint32_t)channel.pitch << 6; - channel.frac = 0; + // Update frequency + uint16_t period = (m_regs[0x12] << 8) + m_regs[0x11]; + m_timer->adjust(clocks_to_attotime(10 * (0x10000 - period)), 0); } break; - case 0x5: // Waveform length - if (lower < 0xf) - { - acan_channel &channel = m_channels[lower]; - //channel.length = (data & ~0x01) << 6; - channel.length = (data & 0x0e) << 6; // Temporary hack to make jttlaugh audible. Not the proper fix! - channel.end_addr = channel.curr_addr + channel.length; - channel.one_shot = BIT(data, 0); - } + case 0x6: // DMA-driven channel flags? + // The meaning of the data that is actually written is unknown + m_dma_channels = data << 8; + LOG("%s: DMA-driven channel flag(?) = %02x\n", machine().describe_context(), data); break; - case 0x6: // Waveform address (divided by 0x40, high byte) - if (lower < 0xf) + case 0x7: // Keyon/keyoff + { + LOG("%s: Sound key control, voice %02x key%s\n", machine().describe_context(), data & 0xf, (data & 0xf0) ? "on" : "off"); + const uint16_t mask = 1 << (data & 0xf); + if (data & 0xf0) { - acan_channel &channel = m_channels[lower]; - channel.start_addr &= 0x00ff; - channel.start_addr |= data << 8; - channel.curr_addr = channel.start_addr << 6; - channel.end_addr = channel.curr_addr + channel.length; + keyon_voice(data & 0xf); } - break; - - case 0x7: // Waveform address (divided by 0x40, low byte) - if (lower < 0xf) + else { - acan_channel &channel = m_channels[lower]; - channel.start_addr &= 0xff00; - channel.start_addr |= data; - channel.curr_addr = channel.start_addr << 6; - channel.end_addr = channel.curr_addr + channel.length; + m_active_channels &= ~mask; } break; + } - case 0xa: // Envelope Parameters? (not yet known) - case 0xb: - case 0xc: - case 0xd: - if (lower < 0xf) - m_channels[lower].envelope[upper - 0xa] = data; + default: + LOG("Unknown sound register: %02x = %02x\n", offset, data); break; + } + break; - case 0xe: // Volume - if (lower < 0xf) - { - acan_channel &channel = m_channels[lower]; - channel.volume = data; - channel.volume_l = (data & 0xf0) | (data >> 4); - channel.volume_r = (data & 0x0f) | (data << 4); - } - break; + case 0x2: // Pitch (low byte) + { + acan_channel &channel = m_channels[lower]; + channel.pitch &= 0xff00; + channel.pitch |= data; + channel.addr_increment = (uint32_t)channel.pitch << 6; + break; + } - default: - LOG("Unknown audio register: %02x = %02x\n", offset, data); - break; + case 0x3: // Pitch (high byte) + { + acan_channel &channel = m_channels[lower]; + channel.pitch &= 0x00ff; + channel.pitch |= data << 8; + channel.addr_increment = (uint32_t)channel.pitch << 6; + break; + } + + case 0x5: // Waveform length + { + acan_channel &channel = m_channels[lower]; + channel.length = 0x40 << ((data & 0x0e) >> 1); + channel.one_shot = BIT(data, 0); + LOG("%s: Waveform length and attributes (voice %02x): %02x\n", machine().describe_context(), lower, data); + break; + } + + case 0x6: // Waveform address (divided by 0x40, high byte) + { + acan_channel &channel = m_channels[lower]; + channel.start_addr &= 0x00ff; + channel.start_addr |= data << 8; + LOG("%s: Waveform address (high) (voice %02x): %02x, will be %04x\n", machine().describe_context(), lower, data, channel.start_addr << 6); + break; + } + + case 0x7: // Waveform address (divided by 0x40, low byte) + { + acan_channel &channel = m_channels[lower]; + channel.start_addr &= 0xff00; + channel.start_addr |= data; + LOG("%s: Waveform address (low) (voice %02x): %02x, will be %04x\n", machine().describe_context(), lower, data, channel.start_addr << 6); + break; + } + + case 0x9: // Unknown (set to 0xFF for DMA-driven channels) + { + acan_channel &channel = m_channels[lower]; + channel.register9 = data; + LOG("%s: Unknown voice register 9 (voice %02x): %02x\n", machine().describe_context(), lower, data); + break; + } + + case 0xa: // Envelope Parameters? (not yet known) + case 0xb: + case 0xc: + case 0xd: + m_channels[lower].envelope[upper - 0xa] = data; + LOG("%s: Envelope parameter %d (voice %02x) = %02x\n", machine().describe_context(), upper - 0xa, lower, data); + break; + + case 0xe: // Volume + { + acan_channel &channel = m_channels[lower]; + channel.volume = data; + channel.volume_l = (data & 0xf0) | (data >> 4); + channel.volume_r = (data & 0x0f) | (data << 4); + LOG("%s: Volume register (voice %02x) = = %02x\n", machine().describe_context(), lower, data); + break; + } + + default: + LOG("Unknown sound register: %02x = %02x\n", offset, data); + break; } } diff --git a/src/mame/audio/acan.h b/src/mame/audio/acan.h index 56046aed241..72aaa729ad9 100644 --- a/src/mame/audio/acan.h +++ b/src/mame/audio/acan.h @@ -17,7 +17,8 @@ public: acan_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); auto ram_read() { return m_ram_read.bind(); } - auto irq_handler() { return m_irq_handler.bind(); } + auto timer_irq_handler() { return m_timer_irq_handler.bind(); } + auto dma_irq_handler() { return m_dma_irq_handler.bind(); } uint8_t read(offs_t offset); void write(offs_t offset, uint8_t data); @@ -41,6 +42,7 @@ private: uint16_t end_addr; uint32_t addr_increment; uint32_t frac; + uint8_t register9; uint8_t envelope[4]; uint8_t volume; uint8_t volume_l; @@ -48,12 +50,16 @@ private: bool one_shot; }; + void keyon_voice(uint8_t voice); + sound_stream *m_stream; emu_timer *m_timer; - devcb_write_line m_irq_handler; + devcb_write_line m_timer_irq_handler; + devcb_write_line m_dma_irq_handler; devcb_read8 m_ram_read; uint16_t m_active_channels; - acan_channel m_channels[15]; + uint16_t m_dma_channels; + acan_channel m_channels[16]; uint8_t m_regs[256]; }; diff --git a/src/mame/drivers/supracan.cpp b/src/mame/drivers/supracan.cpp index 9651a7f8181..9090c1c59de 100644 --- a/src/mame/drivers/supracan.cpp +++ b/src/mame/drivers/supracan.cpp @@ -26,14 +26,14 @@ STATUS: The driver is begging for a re-write or at least a split into video/supracan.c. It will happen eventually. - Sound chip is completely unknown. + Sound chip is completely custom, and partially implemented. There are 6 interrupt sources on the 6502 side, all of which use the IRQ line. The register at 0x411 is bitmapped to indicate what source(s) are active. In priority order from most to least important, they are: 411 value How acked Notes - 0x40 read reg 0x16 of sound chip likely timer. snd regs 0x16/0x17 are time constant, write 0 to reg 0x9f to start + 0x40 read reg 0x16 of sound chip used for DMA-driven sample playback. Register 0x16 may contain which DMA-driven samples are active. 0x04 read at 0x405 latch 1? 0xcd is magic value 0x08 read at 0x404 latch 2? 0xcd is magic value 0x10 read at 0x409 unknown, dispatched but not used in startup 6502 code @@ -261,6 +261,7 @@ private: TILE_GET_INFO_MEMBER(get_roz_tile_info); void palette_init(palette_device &palette) const; void sound_timer_irq(int state); + void sound_dma_irq(int state); uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); TIMER_CALLBACK_MEMBER(hbl_callback); TIMER_CALLBACK_MEMBER(line_on_callback); @@ -1197,6 +1198,11 @@ void supracan_state::sound_timer_irq(int state) set_sound_irq(7, state); } +void supracan_state::sound_dma_irq(int state) +{ + set_sound_irq(6, state); +} + void supracan_state::dma_w(int offset, uint16_t data, uint16_t mem_mask, int ch) { address_space &mem = m_maincpu->space(AS_PROGRAM); @@ -1566,8 +1572,19 @@ uint16_t supracan_state::sound_r(offs_t offset, uint16_t mem_mask) switch (offset) { + case 0x04/2: + data = (m_soundram[0x40c] << 8) | m_soundram[0x40d]; + LOGMASKED(LOG_SOUND, "%s: sound_r: DMA Request address from 6502, %08x: %04x & %04x\n", machine().describe_context(), 0xe90000 + (offset << 1), data, mem_mask); + break; + + case 0x0c/2: + data = m_soundram[0x40a]; + LOGMASKED(LOG_SOUND, "%s: sound_r: DMA Request flag from 6502, %08x: %04x & %04x\n", machine().describe_context(), 0xe90000 + (offset << 1), data, mem_mask); + machine().debug_break(); + break; + default: - LOGMASKED(LOG_SOUND | LOG_UNKNOWNS, "%s: sound_r: Unknown register: (%08x) & %04x\n", machine().describe_context(), 0xe90000 + (offset << 1), mem_mask); + LOGMASKED(LOG_SOUND | LOG_UNKNOWNS, "%s: sound_r: Unknown register: %08x & %04x\n", machine().describe_context(), 0xe90000 + (offset << 1), mem_mask); break; } @@ -2113,7 +2130,8 @@ void supracan_state::supracan(machine_config &config) ACANSND(config, m_sound, XTAL(3'579'545)); m_sound->ram_read().set(FUNC(supracan_state::sound_ram_read)); - m_sound->irq_handler().set(FUNC(supracan_state::sound_timer_irq)); + m_sound->timer_irq_handler().set(FUNC(supracan_state::sound_timer_irq)); + m_sound->dma_irq_handler().set(FUNC(supracan_state::sound_dma_irq)); m_sound->add_route(0, "lspeaker", 1.0); m_sound->add_route(1, "rspeaker", 1.0); -- cgit v1.2.3