summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/dave.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/dave.cpp')
-rw-r--r--src/devices/sound/dave.cpp622
1 files changed, 622 insertions, 0 deletions
diff --git a/src/devices/sound/dave.cpp b/src/devices/sound/dave.cpp
new file mode 100644
index 00000000000..69a7f2aec6f
--- /dev/null
+++ b/src/devices/sound/dave.cpp
@@ -0,0 +1,622 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ Intelligent Designs DAVE emulation
+
+**********************************************************************/
+
+#include "dave.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+#define STEP 0x08000
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type DAVE = &device_creator<dave_device>;
+
+
+DEVICE_ADDRESS_MAP_START( z80_program_map, 8, dave_device )
+ AM_RANGE(0x0000, 0xffff) AM_READWRITE(program_r, program_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START( z80_io_map, 8, dave_device )
+ AM_RANGE(0x0000, 0xffff) AM_READWRITE(io_r, io_w)
+ADDRESS_MAP_END
+
+
+static ADDRESS_MAP_START( program_map, AS_PROGRAM, 8, dave_device )
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( io_map, AS_IO, 8, dave_device )
+ADDRESS_MAP_END
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// dave_device - constructor
+//-------------------------------------------------
+
+dave_device::dave_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, DAVE, "DAVE", tag, owner, clock, "dave", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_sound_interface(mconfig, *this),
+ m_program_space_config("program", ENDIANNESS_LITTLE, 8, 22, 0, *ADDRESS_MAP_NAME(program_map)),
+ m_io_space_config("i/o", ENDIANNESS_LITTLE, 8, 16, 0, *ADDRESS_MAP_NAME(io_map)),
+ m_write_irq(*this),
+ m_write_lh(*this),
+ m_write_rh(*this),
+ m_irq_status(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void dave_device::device_start()
+{
+ // resolve callbacks
+ m_write_irq.resolve_safe();
+ m_write_lh.resolve_safe();
+ m_write_rh.resolve_safe();
+
+ // allocate timers
+ m_timer_1hz = timer_alloc(TIMER_1HZ);
+ m_timer_1hz->adjust(attotime::from_hz(2), 0, attotime::from_hz(2));
+
+ m_timer_50hz = timer_alloc(TIMER_50HZ);
+ m_timer_50hz->adjust(attotime::from_hz(2000), 0, attotime::from_hz(2000));
+
+ // state saving
+ save_item(NAME(m_segment));
+ save_item(NAME(m_irq_status));
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_period));
+ save_item(NAME(m_count));
+ save_item(NAME(m_level));
+ save_item(NAME(m_level_or));
+ save_item(NAME(m_level_and));
+ save_item(NAME(m_mame_volumes));
+
+ for (auto & elem : m_period)
+ elem = (STEP * machine().sample_rate()) / 125000;
+
+ for (auto & elem : m_count)
+ elem = (STEP * machine().sample_rate()) / 125000;
+
+ for (auto & elem : m_level)
+ elem = 0;
+
+ for (auto & elem : m_level_or)
+ elem = 0;
+
+ for (auto & elem : m_level_and)
+ elem = 0;
+
+ for (auto & elem : m_mame_volumes)
+ elem = 0;
+
+ /* dave has 3 tone channels and 1 noise channel.
+ the volumes are mixed internally and output as left and right volume */
+
+ /* 3 tone channels + 1 noise channel */
+ m_sound_stream_var = machine().sound().stream_alloc(*this, 0, 2, machine().sample_rate());
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void dave_device::device_reset()
+{
+ m_write_irq(CLEAR_LINE);
+
+ for (auto & elem : m_segment)
+ elem = 0;
+
+ m_irq_status = 0;
+ m_irq_enable = 0;
+
+ for (auto & elem : m_regs)
+ elem = 0;
+}
+
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+
+void dave_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_1HZ:
+ m_irq_status ^= IRQ_1HZ_DIVIDER;
+
+ if (m_irq_status & IRQ_1HZ_DIVIDER)
+ m_irq_status |= IRQ_1HZ_LATCH;
+ break;
+
+ case TIMER_50HZ:
+ m_irq_status ^= IRQ_50HZ_DIVIDER;
+
+ if (m_irq_status & IRQ_50HZ_DIVIDER)
+ m_irq_status |= IRQ_50HZ_LATCH;
+ break;
+ }
+
+ update_interrupt();
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *dave_device::memory_space_config(address_spacenum spacenum) const
+{
+ switch (spacenum)
+ {
+ case AS_PROGRAM: return &m_program_space_config;
+ case AS_IO: return &m_io_space_config;
+ default: return nullptr;
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void dave_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *buffer1, *buffer2;
+ /* 0 = channel 0 left volume, 1 = channel 0 right volume,
+ 2 = channel 1 left volume, 3 = channel 1 right volume,
+ 4 = channel 2 left volume, 5 = channel 2 right volume
+ 6 = noise channel left volume, 7 = noise channel right volume */
+ int output_volumes[8];
+ int left_volume;
+ int right_volume;
+
+ //logerror("sound update!\n");
+
+ buffer1 = outputs[0];
+ buffer2 = outputs[1];
+
+ while (samples)
+ {
+ int vol[4];
+
+ /* vol[] keeps track of how long each square wave stays */
+ /* in the 1 position during the sample period. */
+ vol[0] = vol[1] = vol[2] = vol[3] = 0;
+
+ for (int i = 0; i < 3; i++)
+ {
+ if ((m_regs[7] & (1 << i))==0)
+ {
+ if (m_level[i]) vol[i] += m_count[i];
+ m_count[i] -= STEP;
+ /* Period[i] is the half period of the square wave. Here, in each */
+ /* loop I add Period[i] twice, so that at the end of the loop the */
+ /* square wave is in the same status (0 or 1) it was at the start. */
+ /* vol[i] is also incremented by Period[i], since the wave has been 1 */
+ /* exactly half of the time, regardless of the initial position. */
+ /* If we exit the loop in the middle, Output[i] has to be inverted */
+ /* and vol[i] incremented only if the exit status of the square */
+ /* wave is 1. */
+ while (m_count[i] <= 0)
+ {
+ m_count[i] += m_period[i];
+ if (m_count[i] > 0)
+ {
+ m_level[i] ^= 0x0ffffffff;
+ if (m_level[i]) vol[i] += m_period[i];
+ break;
+ }
+ m_count[i] += m_period[i];
+ vol[i] += m_period[i];
+ }
+ if (m_level[i])
+ vol[i] -= m_count[i];
+ }
+ }
+
+ /* update volume outputs */
+
+ /* setup output volumes for each channel */
+ /* channel 0 */
+ output_volumes[0] = ((m_level[0] & m_level_and[0]) | m_level_or[0]) & m_mame_volumes[0];
+ output_volumes[1] = ((m_level[0] & m_level_and[1]) | m_level_or[1]) & m_mame_volumes[4];
+ /* channel 1 */
+ output_volumes[2] = ((m_level[1] & m_level_and[2]) | m_level_or[2]) & m_mame_volumes[1];
+ output_volumes[3] = ((m_level[1] & m_level_and[3]) | m_level_or[3]) & m_mame_volumes[5];
+ /* channel 2 */
+ output_volumes[4] = ((m_level[2] & m_level_and[4]) | m_level_or[4]) & m_mame_volumes[2];
+ output_volumes[5] = ((m_level[2] & m_level_and[5]) | m_level_or[5]) & m_mame_volumes[6];
+ /* channel 3 */
+ output_volumes[6] = ((m_level[3] & m_level_and[6]) | m_level_or[6]) & m_mame_volumes[3];
+ output_volumes[7] = ((m_level[3] & m_level_and[7]) | m_level_or[7]) & m_mame_volumes[7];
+
+ left_volume = (output_volumes[0] + output_volumes[2] + output_volumes[4] + output_volumes[6])>>2;
+ right_volume = (output_volumes[1] + output_volumes[3] + output_volumes[5] + output_volumes[7])>>2;
+
+ *(buffer1++) = left_volume;
+ *(buffer2++) = right_volume;
+
+ samples--;
+ }
+}
+
+
+//-------------------------------------------------
+// int1_w - interrupt 1 write
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( dave_device::int1_w )
+{
+ if (!(m_irq_status & IRQ_INT1) && state)
+ m_irq_status |= IRQ_INT1_LATCH;
+
+ if (state)
+ m_irq_status |= IRQ_INT1;
+ else
+ m_irq_status &= ~IRQ_INT1;
+
+ update_interrupt();
+}
+
+
+//-------------------------------------------------
+// int2_w - interrupt 2 write
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( dave_device::int2_w )
+{
+ if (!(m_irq_status & IRQ_INT2) && state)
+ m_irq_status |= IRQ_INT2_LATCH;
+
+ if (state)
+ m_irq_status |= IRQ_INT2;
+ else
+ m_irq_status &= ~IRQ_INT2;
+
+ update_interrupt();
+}
+
+
+//-------------------------------------------------
+// program_r - program space read
+//-------------------------------------------------
+
+READ8_MEMBER( dave_device::program_r )
+{
+ uint8_t segment = m_segment[offset >> 14];
+ offset = (segment << 14) | (offset & 0x3fff);
+
+ return this->space(AS_PROGRAM).read_byte(offset);
+}
+
+
+//-------------------------------------------------
+// program_w - program space write
+//-------------------------------------------------
+
+WRITE8_MEMBER( dave_device::program_w )
+{
+ uint8_t segment = m_segment[offset >> 14];
+ offset = (segment << 14) | (offset & 0x3fff);
+
+ this->space(AS_PROGRAM).write_byte(offset, data);
+}
+
+
+//-------------------------------------------------
+// io_r - I/O space read
+//-------------------------------------------------
+
+READ8_MEMBER( dave_device::io_r )
+{
+ uint8_t data = 0;
+
+ switch (offset & 0xff)
+ {
+ case 0xa0:
+ case 0xa1:
+ case 0xa2:
+ case 0xa3:
+ case 0xa4:
+ case 0xa5:
+ case 0xa6:
+ case 0xa7:
+ case 0xa8:
+ case 0xa9:
+ case 0xaa:
+ case 0xab:
+ case 0xac:
+ case 0xad:
+ case 0xae:
+ case 0xaf:
+ case 0xb8:
+ case 0xb9:
+ case 0xba:
+ case 0xbb:
+ case 0xbc:
+ case 0xbd:
+ case 0xbe:
+ case 0xbf:
+ data = 0xff;
+ break;
+
+ case 0xb0: case 0xb1: case 0xb2: case 0xb3:
+ data = m_segment[offset & 0x03];
+ break;
+
+ case 0xb4:
+ data = m_irq_status;
+ break;
+
+ default:
+ data = this->space(AS_IO).read_byte(offset);
+ }
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// io_w - I/O space write
+//-------------------------------------------------
+
+WRITE8_MEMBER( dave_device::io_w )
+{
+ switch (offset & 0xff)
+ {
+ /* channel 0 down-counter */
+ case 0xa0:
+ case 0xa1:
+ /* channel 1 down-counter */
+ case 0xa2:
+ case 0xa3:
+ /* channel 2 down-counter */
+ case 0xa4:
+ case 0xa5:
+ {
+ int count = 0;
+ int channel_index = (offset>>1)&3;
+
+ /* Fout = 125,000 / (n+1) Hz */
+
+ /* sample rate/clock */
+
+
+ /* get down-count */
+ switch (offset & 0x01)
+ {
+ case 0:
+ {
+ count = (data & 0x0ff) | ((m_regs[(offset & 0x1f) + 1] & 0x0f)<<8);
+ }
+ break;
+
+ case 1:
+ {
+ count = (m_regs[(offset & 0x1f) - 1] & 0x0ff) | ((data & 0x0f)<<8);
+
+ }
+ break;
+ }
+
+ count++;
+
+
+ m_period[channel_index] = ((STEP * machine().sample_rate())/125000) * count;
+
+ m_regs[offset & 0x1f] = data;
+ }
+ break;
+
+ /* channel 0 left volume */
+ case 0xa8:
+ /* channel 1 left volume */
+ case 0xa9:
+ /* channel 2 left volume */
+ case 0xaa:
+ /* noise channel left volume */
+ case 0xab:
+ /* channel 0 right volume */
+ case 0xac:
+ /* channel 1 right volume */
+ case 0xad:
+ /* channel 2 right volume */
+ case 0xae:
+ /* noise channel right volume */
+ case 0xaf:
+ {
+ /* update mame version of volume from data written */
+ /* 0x03f->0x07e00. Max is 0x07fff */
+ /* I believe the volume is linear - to be checked! */
+ m_mame_volumes[(offset & 0x1f) - 8] = (data & 0x03f) << 9;
+
+ m_regs[offset & 0x1f] = data;
+ }
+ break;
+
+ case 0xa6:
+ break;
+
+ case 0xa7:
+ {
+ /* force => the value of this register is forced regardless of the wave
+ state,
+ remove => this value is force to zero so that it has no influence over
+ the final volume calculation, regardless of wave state
+ use => the volume value is dependant on the wave state and is included
+ in the final volume calculation */
+
+ //logerror("selectable int ");
+ switch ((data>>5) & 0x03)
+ {
+ case 0:
+ {
+ //logerror("1kHz\n");
+ m_timer_50hz->adjust(attotime::from_hz(2000), 0, attotime::from_hz(2000));
+ }
+ break;
+
+ case 1:
+ {
+ //logerror("50Hz\n");
+ m_timer_50hz->adjust(attotime::from_hz(100), 0, attotime::from_hz(100));
+ }
+ break;
+
+ case 2:
+ {
+ //logerror("tone channel 0\n");
+ }
+ break;
+
+ case 3:
+ {
+ //logerror("tone channel 1\n");
+ }
+ break;
+ }
+
+
+
+ /* turn L.H audio output into D/A, outputting value in R8 */
+ if (data & (1<<3))
+ {
+ /* force r8 value */
+ m_level_or[0] = 0x0ffff;
+ m_level_and[0] = 0x00;
+
+ /* remove r9 value */
+ m_level_or[2] = 0x000;
+ m_level_and[2] = 0x00;
+
+ /* remove r10 value */
+ m_level_or[4] = 0x000;
+ m_level_and[4] = 0x00;
+
+ /* remove r11 value */
+ m_level_or[6] = 0x000;
+ m_level_and[6] = 0x00;
+ }
+ else
+ {
+ /* use r8 value */
+ m_level_or[0] = 0x000;
+ m_level_and[0] = 0xffff;
+
+ /* use r9 value */
+ m_level_or[2] = 0x000;
+ m_level_and[2] = 0xffff;
+
+ /* use r10 value */
+ m_level_or[4] = 0x000;
+ m_level_and[4] = 0xffff;
+
+ /* use r11 value */
+ m_level_or[6] = 0x000;
+ m_level_and[6] = 0xffff;
+ }
+
+ /* turn L.H audio output into D/A, outputting value in R12 */
+ if (data & (1<<4))
+ {
+ /* force r12 value */
+ m_level_or[1] = 0x0ffff;
+ m_level_and[1] = 0x00;
+
+ /* remove r13 value */
+ m_level_or[3] = 0x000;
+ m_level_and[3] = 0x00;
+
+ /* remove r14 value */
+ m_level_or[5] = 0x000;
+ m_level_and[5] = 0x00;
+
+ /* remove r15 value */
+ m_level_or[7] = 0x000;
+ m_level_and[7] = 0x00;
+ }
+ else
+ {
+ /* use r12 value */
+ m_level_or[1] = 0x000;
+ m_level_and[1] = 0xffff;
+
+ /* use r13 value */
+ m_level_or[3] = 0x000;
+ m_level_and[3] = 0xffff;
+
+ /* use r14 value */
+ m_level_or[5] = 0x000;
+ m_level_and[5] = 0xffff;
+
+ /* use r15 value */
+ m_level_or[7] = 0x000;
+ m_level_and[7] = 0xffff;
+ }
+
+ m_regs[offset & 0x1f] = data;
+ }
+ break;
+
+ case 0xb0: case 0xb1: case 0xb2: case 0xb3:
+ m_segment[offset & 0x03] = data;
+
+ m_regs[offset & 0x1f] = data;
+ break;
+
+ case 0xb4:
+ m_irq_enable = data;
+ m_irq_status &= ~(m_irq_enable & IRQ_LATCH);
+ update_interrupt();
+
+ m_regs[offset & 0x1f] = data;
+ break;
+
+ case 0xbf:
+ m_regs[offset & 0x1f] = data;
+ break;
+
+ default:
+ this->space(AS_IO).write_byte(offset, data);
+ }
+}
+
+
+//-------------------------------------------------
+// update_interrupt -
+//-------------------------------------------------
+
+void dave_device::update_interrupt()
+{
+ int state = ((m_irq_status & (m_irq_enable << 1)) & IRQ_LATCH) ? ASSERT_LINE : CLEAR_LINE;
+
+ m_write_irq(state);
+}