diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 97742e386a1..c9b6d6e1261 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -2489,6 +2489,8 @@ files { createMESSProjects(_target, _subtarget, "funtech") files { MAME_DIR .. "src/mame/drivers/supracan.cpp", + MAME_DIR .. "src/mame/audio/acan.cpp", + MAME_DIR .. "src/mame/audio/acan.h", } createMESSProjects(_target, _subtarget, "galaxy") diff --git a/src/mame/audio/acan.cpp b/src/mame/audio/acan.cpp new file mode 100644 index 00000000000..b157e5f2b46 --- /dev/null +++ b/src/mame/audio/acan.cpp @@ -0,0 +1,251 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz, superctr +/*************************************************************************** + + Super A'Can sound driver + + Currently has a number of unknown registers and functionality. + +****************************************************************************/ + +#include "emu.h" +#include "acan.h" + +#define VERBOSE (1) +#include "logmacro.h" + +// device type definition +DEFINE_DEVICE_TYPE(ACANSND, acan_sound_device, "acansound", "Super A'Can Audio") + +acan_sound_device::acan_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, ACANSND, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , m_stream(nullptr) + , m_timer(nullptr) + , m_irq_handler(*this) + , m_ram_read(*this) + , m_active_channels(0) +{ +} + + +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_ram_read.resolve_safe(0); + + // register for savestates + save_item(NAME(m_active_channels)); + save_item(STRUCT_MEMBER(m_channels, pitch)); + save_item(STRUCT_MEMBER(m_channels, length)); + save_item(STRUCT_MEMBER(m_channels, start_addr)); + save_item(STRUCT_MEMBER(m_channels, curr_addr)); + 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, envelope)); + save_item(STRUCT_MEMBER(m_channels, volume)); + save_item(STRUCT_MEMBER(m_channels, volume_l)); + save_item(STRUCT_MEMBER(m_channels, volume_r)); + save_item(STRUCT_MEMBER(m_channels, one_shot)); + save_item(NAME(m_regs)); +} + +void acan_sound_device::device_reset() +{ + m_active_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); +} + +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); + + // Update frequency + uint16_t period = (m_regs[0x12] << 8) + m_regs[0x11]; + m_timer->adjust(clocks_to_attotime(10 * (0x10000 - period)), 0); + } +} + +void acan_sound_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +{ + int32_t mix[(clock() / 16 / 5) * 2]; + + std::fill_n(&mix[0], outputs[0].samples() * 2, 0); + + for (int i = 0; i < 15 && m_active_channels != 0; i++) + { + if (BIT(m_active_channels, i)) + { + acan_channel &channel = m_channels[i]; + int32_t *mixp = &mix[0]; + + for (int s = 0; s < outputs[0].samples(); s++) + { + uint8_t data = m_ram_read(channel.curr_addr) + 0x80; + int16_t sample = (int16_t)(data << 8); + + channel.frac += channel.addr_increment; + channel.curr_addr += (uint16_t)(channel.frac >> 16); + channel.frac = (uint16_t)channel.frac; + + *mixp++ += (sample * channel.volume_l) >> 8; + *mixp++ += (sample * channel.volume_r) >> 8; + + if (channel.curr_addr >= channel.end_addr) + { + if (channel.one_shot) + { + m_active_channels &= ~(1 << i); + } + else + { + channel.curr_addr -= channel.length; + } + } + } + } + } + + int32_t *mixp = &mix[0]; + for (int i = 0; i < outputs[0].samples(); i++) + { + outputs[0].put_int(i, *mixp++, 32768 << 4); + outputs[1].put_int(i, *mixp++, 32768 << 4); + } +} + +uint8_t acan_sound_device::read(offs_t offset) +{ + if (offset == 0x14) + { + // acknowledge timer (?) + if (!m_irq_handler.isnull()) + m_irq_handler(0); + } + return m_regs[offset]; +} + +void acan_sound_device::write(offs_t offset, uint8_t data) +{ + const uint8_t upper = (offset >> 4) & 0x0f; + const uint8_t lower = offset & 0x0f; + + m_regs[offset] = 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); + } + 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; + } + break; + + case 0x3: // Pitch (high byte) + if (lower < 0xf) + { + acan_channel &channel = m_channels[lower]; + channel.pitch &= 0x00ff; + channel.pitch |= data << 8; + channel.addr_increment = (uint32_t)channel.pitch << 6; + channel.frac = 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); + } + break; + + case 0x6: // Waveform address (divided by 0x40, high byte) + if (lower < 0xf) + { + 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; + } + break; + + case 0x7: // Waveform address (divided by 0x40, low byte) + if (lower < 0xf) + { + 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; + } + break; + + case 0xa: // Envelope Parameters? (not yet known) + case 0xb: + case 0xc: + case 0xd: + if (lower < 0xf) + m_channels[lower].envelope[upper - 0xa] = data; + 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; + + default: + LOG("Unknown audio register: %02x = %02x\n", offset, data); + break; + } +} diff --git a/src/mame/audio/acan.h b/src/mame/audio/acan.h new file mode 100644 index 00000000000..56046aed241 --- /dev/null +++ b/src/mame/audio/acan.h @@ -0,0 +1,62 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz, superctr +/********************************************************************** + + Super A'Can sound driver + +**********************************************************************/ + +#ifndef MAME_AUDIO_ACAN_H +#define MAME_AUDIO_ACAN_H + +#pragma once + +class acan_sound_device : public device_t, public device_sound_interface +{ +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(); } + + uint8_t read(offs_t offset); + void write(offs_t offset, uint8_t data); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() 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; + +private: + struct acan_channel + { + uint16_t pitch; + uint16_t length; + uint16_t start_addr; + uint16_t curr_addr; + uint16_t end_addr; + uint32_t addr_increment; + uint32_t frac; + uint8_t envelope[4]; + uint8_t volume; + uint8_t volume_l; + uint8_t volume_r; + bool one_shot; + }; + + sound_stream *m_stream; + emu_timer *m_timer; + devcb_write_line m_irq_handler; + devcb_read8 m_ram_read; + uint16_t m_active_channels; + acan_channel m_channels[15]; + uint8_t m_regs[256]; +}; + +DECLARE_DEVICE_TYPE(ACANSND, acan_sound_device) + +#endif // MAME_AUDIO_ACAN_H diff --git a/src/mame/drivers/supracan.cpp b/src/mame/drivers/supracan.cpp index 608ef93fabc..9651a7f8181 100644 --- a/src/mame/drivers/supracan.cpp +++ b/src/mame/drivers/supracan.cpp @@ -80,9 +80,11 @@ DEBUG TRICKS: #include "cpu/m6502/m6502.h" #include "bus/generic/slot.h" #include "bus/generic/carts.h" +#include "audio/acan.h" #include "emupal.h" #include "screen.h" #include "softlist.h" +#include "speaker.h" #include "tilemap.h" @@ -113,7 +115,7 @@ namespace { #define LOG_ALL (LOG_UNKNOWNS | LOG_HFUNKNOWNS | LOG_DMA | LOG_VIDEO | LOG_HFVIDEO | LOG_IRQS | LOG_SOUND | LOG_68K_SOUND | LOG_CONTROLS) #define LOG_DEFAULT (LOG_ALL & ~(LOG_HFVIDEO | LOG_HFUNKNOWNS)) -#define VERBOSE (0) +#define VERBOSE (LOG_UNKNOWNS | LOG_SOUND | LOG_DMA) #include "logmacro.h" class supracan_state : public driver_device @@ -126,6 +128,7 @@ public: , m_cart(*this, "cartslot") , m_vram(*this, "vram") , m_soundram(*this, "soundram") + , m_sound(*this, "acansnd") , m_gfxdecode(*this, "gfxdecode") , m_screen(*this, "screen") , m_pads(*this, "P%u", 1U) @@ -158,6 +161,8 @@ private: void video_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); void vram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + uint8_t sound_ram_read(offs_t offset); + struct dma_regs_t { uint32_t source[2]; @@ -182,6 +187,7 @@ private: required_shared_ptr m_vram; required_shared_ptr m_soundram; + required_device m_sound; required_device m_gfxdecode; required_device m_screen; @@ -199,7 +205,6 @@ private: uint16_t m_latched_controls[2]; uint8_t m_sound_status; uint8_t m_sound_reg_addr; - uint8_t m_sound_regs[0x100]; emu_timer *m_video_timer; emu_timer *m_hbl_timer; @@ -255,8 +260,8 @@ private: TILE_GET_INFO_MEMBER(get_tilemap2_tile_info); TILE_GET_INFO_MEMBER(get_roz_tile_info); void palette_init(palette_device &palette) const; + void sound_timer_irq(int state); uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); - INTERRUPT_GEN_MEMBER(sound_irq); TIMER_CALLBACK_MEMBER(hbl_callback); TIMER_CALLBACK_MEMBER(line_on_callback); TIMER_CALLBACK_MEMBER(line_off_callback); @@ -273,7 +278,7 @@ private: void mark_active_tilemap_all_dirty(int layer); void draw_roz_layer(bitmap_ind16 &bitmap, const rectangle &cliprect, tilemap_t *tmap, uint32_t startx, uint32_t starty, int incxx, int incxy, int incyx, int incyy, int wraparound/*, int columnscroll, uint32_t* scrollram*/, int transmask); - void set_sound_irq(uint8_t mask); + void set_sound_irq(uint8_t bit, uint8_t state); }; @@ -1187,6 +1192,10 @@ uint32_t supracan_state::screen_update(screen_device &screen, bitmap_ind16 &bitm return 0; } +void supracan_state::sound_timer_irq(int state) +{ + set_sound_irq(7, state); +} void supracan_state::dma_w(int offset, uint16_t data, uint16_t mem_mask, int ch) { @@ -1315,10 +1324,18 @@ void supracan_state::supracan_mem(address_map &map) map(0xfc0000, 0xfcffff).mirror(0x30000).ram(); /* System work ram */ } -void supracan_state::set_sound_irq(uint8_t mask) +uint8_t supracan_state::sound_ram_read(offs_t offset) +{ + return m_soundram[offset]; +} + +void supracan_state::set_sound_irq(uint8_t bit, uint8_t state) { const uint8_t old = m_soundcpu_irq_source; - m_soundcpu_irq_source |= mask; + if (state) + m_soundcpu_irq_source |= 1 << bit; + else + m_soundcpu_irq_source &= ~(1 << bit); const uint8_t changed = old ^ m_soundcpu_irq_source; if (changed) { @@ -1357,7 +1374,7 @@ uint8_t supracan_state::_6502_soundmem_r(offs_t offset) m_soundcpu_irq_source = 0; if (!machine().side_effects_disabled()) { - LOGMASKED(LOG_SOUND, "%s: 6502_soundmem_r: Sound IRQ source read + clear: %02x\n", machine().describe_context(), data); + LOGMASKED(LOG_SOUND, "%s: %s: 6502_soundmem_r: Sound IRQ source read + clear: %02x\n", machine().describe_context(), machine().time().to_string(), data); m_soundcpu->set_input_line(0, CLEAR_LINE); } break; @@ -1365,14 +1382,14 @@ uint8_t supracan_state::_6502_soundmem_r(offs_t offset) if (!machine().side_effects_disabled()) { data = m_sound_status; - LOGMASKED(LOG_SOUND, "%s: 6502_soundmem_r: Sound hardware status read: 0420 = %02x\n", machine().describe_context(), m_sound_status); + LOGMASKED(LOG_SOUND, "%s: %s: 6502_soundmem_r: Sound hardware status read: 0420 = %02x\n", machine().describe_context(), machine().time().to_string(), m_sound_status); } break; case 0x422: if (!machine().side_effects_disabled()) { - data = m_sound_regs[m_sound_reg_addr]; - LOGMASKED(LOG_SOUND, "%s: 6502_soundmem_r: Sound hardware reg data read: 0422 = %02x\n", machine().describe_context(), data); + data = m_sound->read(m_sound_reg_addr); + LOGMASKED(LOG_SOUND, "%s: %s: 6502_soundmem_r: Sound hardware reg data read: 0422 = %02x\n", machine().describe_context(), machine().time().to_string(), data); } break; case 0x404: @@ -1382,7 +1399,7 @@ uint8_t supracan_state::_6502_soundmem_r(offs_t offset) case 0x416: // Intentional fall-through default: - if (offset >= 0x300 && offset < 0x500) + if (offset >= 0x400 && offset < 0x500) { if (!machine().side_effects_disabled()) { @@ -1397,6 +1414,9 @@ uint8_t supracan_state::_6502_soundmem_r(offs_t offset) void supracan_state::_6502_soundmem_w(offs_t offset, uint8_t data) { + static attotime s_curr_time = attotime::zero; + attotime now = machine().time(); + switch (offset) { case 0x407: @@ -1429,15 +1449,19 @@ void supracan_state::_6502_soundmem_w(offs_t offset, uint8_t data) LOGMASKED(LOG_SOUND | LOG_IRQS, "%s: 6502_soundmem_w: IRQ enable: %02x\n", machine().describe_context(), data); break; case 0x420: - LOGMASKED(LOG_SOUND, "%s: 6502_soundmem_w: Sound hardware reg addr write: 0420 = %02x\n", machine().describe_context(), data); + LOGMASKED(LOG_SOUND, "%s: %s: 6502_soundmem_w: Sound addr write: 0420 = %02x\n", machine().describe_context(), now.to_string(), data); m_sound_reg_addr = data; break; case 0x422: - LOGMASKED(LOG_SOUND, "%s: 6502_soundmem_w: Sound hardware reg data write: 0422 = %02x\n", machine().describe_context(), data); - m_sound_regs[m_sound_reg_addr] = data; + { + attotime delta = (s_curr_time == attotime::zero ? attotime::zero : (now - s_curr_time)); + s_curr_time = now; + LOGMASKED(LOG_SOUND, "%s: %s: 6502_soundmem_w: Sound data write: 0422 = %02x (delta %0.3f)\n", machine().describe_context(), now.to_string(), data, (float)delta.as_double()); + m_sound->write(m_sound_reg_addr, data); break; + } default: - if (offset >= 0x300 && offset < 0x500) + if (offset >= 0x400 && offset < 0x500) { LOGMASKED(LOG_SOUND | LOG_UNKNOWNS, "%s: 6502_soundmem_w: Unknown register %04x = %02x\n", machine().describe_context(), offset, data); } @@ -1500,7 +1524,7 @@ void supracan_state::_68k_soundram_w(offs_t offset, uint16_t data, uint16_t mem_ m_soundram[offset * 2 + 1] = data & 0xff; m_soundram[offset * 2] = data >> 8; - if (offset * 2 < 0x500 && offset * 2 >= 0x300) + if (offset * 2 < 0x500 && offset * 2 >= 0x400) { if (ACCESSING_BITS_8_15) { @@ -1519,7 +1543,7 @@ uint16_t supracan_state::_68k_soundram_r(offs_t offset, uint16_t mem_mask) uint16_t data = m_soundram[offset * 2] << 8; data |= m_soundram[offset * 2 + 1]; - if (offset * 2 >= 0x300 && offset * 2 < 0x500) + if (offset * 2 >= 0x400 && offset * 2 < 0x500) { data = 0; if (ACCESSING_BITS_8_15) @@ -1543,7 +1567,7 @@ uint16_t supracan_state::sound_r(offs_t offset, uint16_t mem_mask) switch (offset) { default: - LOGMASKED(LOG_SOUND | LOG_UNKNOWNS, "sound_r: Unknown register: (%08x) & %04x\n", 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; } @@ -1556,7 +1580,7 @@ void supracan_state::sound_w(offs_t offset, uint16_t data, uint16_t mem_mask) { case 0x000a/2: /* Sound cpu IRQ request. */ LOGMASKED(LOG_SOUND, "%s: Sound CPU IRQ request: %04x\n", machine().describe_context(), data); - set_sound_irq(0x20); + set_sound_irq(5, 1); //m_soundcpu->set_input_line(0, ASSERT_LINE); break; case 0x001c/2: /* Sound cpu control. Bit 0 tied to sound cpu RESET line */ @@ -1914,7 +1938,6 @@ void supracan_state::machine_start() save_item(NAME(m_latched_controls)); save_item(NAME(m_sound_status)); save_item(NAME(m_sound_reg_addr)); - save_item(NAME(m_sound_regs)); save_item(NAME(m_sprite_count)); save_item(NAME(m_sprite_base_addr)); @@ -1972,7 +1995,6 @@ void supracan_state::machine_reset() std::fill(std::begin(m_latched_controls), std::end(m_latched_controls), 0); m_sound_status = 0; m_sound_reg_addr = 0; - std::fill(std::begin(m_sound_regs), std::end(m_sound_regs), 0); m_soundcpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); @@ -2066,11 +2088,6 @@ static GFXDECODE_START( gfx_supracan ) GFXDECODE_RAM( "vram", 0, supracan_gfx1bpp_alt, 0, 0x80 ) GFXDECODE_END -INTERRUPT_GEN_MEMBER(supracan_state::sound_irq) -{ - set_sound_irq(0x80); -} - void supracan_state::supracan(machine_config &config) { M68000(config, m_maincpu, XTAL(10'738'635)); /* Correct frequency unknown */ @@ -2078,7 +2095,6 @@ void supracan_state::supracan(machine_config &config) M6502(config, m_soundcpu, XTAL(3'579'545)); /* TODO: Verify actual clock */ m_soundcpu->set_addrmap(AS_PROGRAM, &supracan_state::supracan_sound_mem); - m_soundcpu->set_vblank_int("screen", FUNC(supracan_state::sound_irq)); config.set_perfect_quantum(m_soundcpu); @@ -2086,11 +2102,21 @@ void supracan_state::supracan(machine_config &config) m_screen->set_raw(XTAL(10'738'635)/2, 348, 0, 256, 256, 0, 240); /* No idea if this is correct */ m_screen->set_screen_update(FUNC(supracan_state::screen_update)); m_screen->set_palette("palette"); + //m_screen->screen_vblank().set(FUNC(supracan_state::screen_vblank)); PALETTE(config, "palette", FUNC(supracan_state::palette_init)).set_format(palette_device::xBGR_555, 32768); GFXDECODE(config, m_gfxdecode, "palette", gfx_supracan); + SPEAKER(config, "lspeaker").front_left(); + SPEAKER(config, "rspeaker").front_right(); + + 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->add_route(0, "lspeaker", 1.0); + m_sound->add_route(1, "rspeaker", 1.0); + generic_cartslot_device &cartslot(GENERIC_CARTSLOT(config, "cartslot", generic_plain_slot, "supracan_cart")); cartslot.set_width(GENERIC_ROM16_WIDTH); cartslot.set_endian(ENDIANNESS_BIG);