diff options
Diffstat (limited to 'src/mame/audio/acan.cpp')
-rw-r--r-- | src/mame/audio/acan.cpp | 234 |
1 files changed, 140 insertions, 94 deletions
diff --git a/src/mame/audio/acan.cpp b/src/mame/audio/acan.cpp index 5f999368625..b881f2e65ac 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) { } @@ -35,11 +37,13 @@ void acan_sound_device::device_start() m_mix = std::make_unique<int32_t[]>((clock() / 16 / 5) * 2); 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)); @@ -47,6 +51,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)); @@ -58,19 +63,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]; @@ -82,7 +87,7 @@ void acan_sound_device::sound_stream_update(sound_stream &stream, std::vector<re { std::fill_n(&m_mix[0], outputs[0].samples() * 2, 0); - for (int i = 0; i < 15 && m_active_channels != 0; i++) + for (int i = 0; i < 16 && m_active_channels != 0; i++) { if (BIT(m_active_channels, i)) { @@ -103,7 +108,12 @@ void acan_sound_device::sound_stream_update(sound_stream &stream, std::vector<re if (channel.curr_addr >= 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); } @@ -128,13 +138,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; @@ -144,107 +169,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; } } |