diff options
Diffstat (limited to 'trunk/src/mame/audio/exidy.c')
-rw-r--r-- | trunk/src/mame/audio/exidy.c | 1136 |
1 files changed, 1136 insertions, 0 deletions
diff --git a/trunk/src/mame/audio/exidy.c b/trunk/src/mame/audio/exidy.c new file mode 100644 index 00000000000..d40d45d3b8e --- /dev/null +++ b/trunk/src/mame/audio/exidy.c @@ -0,0 +1,1136 @@ +/************************************************************************* + + Exidy 6502 hardware + +*************************************************************************/ + +#include "emu.h" +#include "cpu/z80/z80.h" +#include "machine/rescap.h" +#include "cpu/m6502/m6502.h" +#include "machine/6821pia.h" +#include "machine/6532riot.h" +#include "sound/hc55516.h" +#include "sound/tms5220.h" +#include "audio/exidy.h" + + + +/************************************* + * + * Constants + * + *************************************/ + +#define CRYSTAL_OSC (XTAL_3_579545MHz) +#define SH8253_CLOCK (CRYSTAL_OSC / 2) +#define SH6840_CLOCK (CRYSTAL_OSC / 4) +#define SH6532_CLOCK (CRYSTAL_OSC / 4) +#define CVSD_CLOCK (1.0 / (0.693 * (RES_K(2.4) + 2.0 * RES_K(20)) * CAP_P(2200))) +#define CVSD_Z80_CLOCK (CRYSTAL_OSC / 2) +#define BASE_VOLUME (32767 / 6) + +enum +{ + RIOT_IDLE, + RIOT_COUNT, + RIOT_POST_COUNT +}; + + + +/************************************* + * + * Local variables + * + *************************************/ + +/* 6840 variables */ +struct sh6840_timer_channel +{ + UINT8 cr; + UINT8 state; + UINT8 leftovers; + UINT16 timer; + UINT32 clocks; + union + { +#ifdef LSB_FIRST + struct { UINT8 l, h; } b; +#else + struct { UINT8 h, l; } b; +#endif + UINT16 w; + } counter; +}; + +struct sh8253_timer_channel +{ + UINT8 clstate; + UINT8 enable; + UINT16 count; + UINT32 step; + UINT32 fraction; +}; + +typedef struct _exidy_sound_state exidy_sound_state; +struct _exidy_sound_state +{ + device_t *m_maincpu; + + /* IRQ variable */ + UINT8 m_riot_irq_state; + + /* 6532 variables */ + device_t *m_riot; + + struct sh6840_timer_channel m_sh6840_timer[3]; + INT16 m_sh6840_volume[3]; + UINT8 m_sh6840_MSB_latch; + UINT8 m_sh6840_LSB_latch; + UINT8 m_sh6840_LFSR_oldxor; + UINT32 m_sh6840_LFSR_0; + UINT32 m_sh6840_LFSR_1; + UINT32 m_sh6840_LFSR_2; + UINT32 m_sh6840_LFSR_3; + UINT32 m_sh6840_clocks_per_sample; + UINT32 m_sh6840_clock_count; + + UINT8 m_sfxctrl; + + /* 8253 variables */ + struct sh8253_timer_channel m_sh8253_timer[3]; + int m_has_sh8253; + + /* 5220/CVSD variables */ + device_t *m_cvsd; + device_t *m_tms; + pia6821_device *m_pia1; + + /* sound streaming variables */ + sound_stream *m_stream; + double m_freq_to_step; + + UINT8 m_victory_sound_response_ack_clk; /* 7474 @ F4 */ +}; + + +INLINE exidy_sound_state *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == EXIDY || device->type() == EXIDY_VENTURE || device->type() == EXIDY_VICTORY); + + return (exidy_sound_state *)downcast<legacy_device_base *>(device)->token(); +} + +/************************************* + * + * Interrupt generation helper + * + *************************************/ + +static WRITE_LINE_DEVICE_HANDLER( update_irq_state ) +{ + exidy_sound_state *sndstate = get_safe_token(device); + cputag_set_input_line(device->machine(), "audiocpu", M6502_IRQ_LINE, (sndstate->m_pia1->irq_b_state() | sndstate->m_riot_irq_state) ? ASSERT_LINE : CLEAR_LINE); +} + + + +/************************************* + * + * 6840 clock counting helper + * + *************************************/ + +INLINE void sh6840_apply_clock(struct sh6840_timer_channel *t, int clocks) +{ + /* dual 8-bit case */ + if (t->cr & 0x04) + { + /* handle full decrements */ + while (clocks > t->counter.b.l) + { + clocks -= t->counter.b.l + 1; + t->counter.b.l = t->timer; + + /* decrement MSB */ + if (!t->counter.b.h--) + { + t->state = 0; + t->counter.w = t->timer; + } + + /* state goes high when MSB is 0 */ + else if (!t->counter.b.h) + { + t->state = 1; + t->clocks++; + } + } + + /* subtract off the remainder */ + t->counter.b.l -= clocks; + } + + /* 16-bit case */ + else + { + /* handle full decrements */ + while (clocks > t->counter.w) + { + clocks -= t->counter.w + 1; + t->state ^= 1; + t->clocks += t->state; + t->counter.w = t->timer; + } + + /* subtract off the remainder */ + t->counter.w -= clocks; + } +} + + + +/************************************* + * + * Noise generation helper + * + *************************************/ + +INLINE int sh6840_update_noise(exidy_sound_state *state, int clocks) +{ + UINT32 newxor; + int noise_clocks = 0; + int i; + + /* loop over clocks */ + for (i = 0; i < clocks; i++) + { + /* shift the LFSR. its a LOOOONG LFSR, so we need + * four longs to hold it all! + * first we grab new sample, then shift the high bits, + * then the low ones; finally or in the result and see if we've + * had a 0->1 transition */ + newxor = (state->m_sh6840_LFSR_3 ^ state->m_sh6840_LFSR_2) >> 31; /* high bits of 3 and 2 xored is new xor */ + state->m_sh6840_LFSR_3 <<= 1; + state->m_sh6840_LFSR_3 |= state->m_sh6840_LFSR_2 >> 31; + state->m_sh6840_LFSR_2 <<= 1; + state->m_sh6840_LFSR_2 |= state->m_sh6840_LFSR_1 >> 31; + state->m_sh6840_LFSR_1 <<= 1; + state->m_sh6840_LFSR_1 |= state->m_sh6840_LFSR_0 >> 31; + state->m_sh6840_LFSR_0 <<= 1; + state->m_sh6840_LFSR_0 |= newxor ^ state->m_sh6840_LFSR_oldxor; + state->m_sh6840_LFSR_oldxor = newxor; + /*printf("LFSR: %4x, %4x, %4x, %4x\n", sh6840_LFSR_3, sh6840_LFSR_2, sh6840_LFSR_1, sh6840_LFSR_0);*/ + /* if we clocked 0->1, that will serve as an external clock */ + if ((state->m_sh6840_LFSR_2 & 0x03) == 0x01) /* tap is at 96th bit */ + { + noise_clocks++; + } + } + return noise_clocks; +} + + + +/************************************* + * + * 6840 state saving + * + *************************************/ + +static void sh6840_register_state_globals(device_t *device) +{ + exidy_sound_state *state = get_safe_token(device); + + device->save_item(NAME(state->m_sh6840_volume)); + device->save_item(NAME(state->m_sh6840_MSB_latch)); + device->save_item(NAME(state->m_sh6840_LSB_latch)); + device->save_item(NAME(state->m_sh6840_LFSR_oldxor)); + device->save_item(NAME(state->m_sh6840_LFSR_0)); + device->save_item(NAME(state->m_sh6840_LFSR_1)); + device->save_item(NAME(state->m_sh6840_LFSR_2)); + device->save_item(NAME(state->m_sh6840_LFSR_3)); + device->save_item(NAME(state->m_sh6840_clock_count)); + device->save_item(NAME(state->m_sfxctrl)); + device->save_item(NAME(state->m_sh6840_timer[0].cr)); + device->save_item(NAME(state->m_sh6840_timer[0].state)); + device->save_item(NAME(state->m_sh6840_timer[0].leftovers)); + device->save_item(NAME(state->m_sh6840_timer[0].timer)); + device->save_item(NAME(state->m_sh6840_timer[0].clocks)); + device->save_item(NAME(state->m_sh6840_timer[0].counter.w)); + device->save_item(NAME(state->m_sh6840_timer[1].cr)); + device->save_item(NAME(state->m_sh6840_timer[1].state)); + device->save_item(NAME(state->m_sh6840_timer[1].leftovers)); + device->save_item(NAME(state->m_sh6840_timer[1].timer)); + device->save_item(NAME(state->m_sh6840_timer[1].clocks)); + device->save_item(NAME(state->m_sh6840_timer[1].counter.w)); + device->save_item(NAME(state->m_sh6840_timer[2].cr)); + device->save_item(NAME(state->m_sh6840_timer[2].state)); + device->save_item(NAME(state->m_sh6840_timer[2].leftovers)); + device->save_item(NAME(state->m_sh6840_timer[2].timer)); + device->save_item(NAME(state->m_sh6840_timer[2].clocks)); + device->save_item(NAME(state->m_sh6840_timer[2].counter.w)); +} + + + +/************************************* + * + * Core sound generation + * + *************************************/ + +static STREAM_UPDATE( exidy_stream_update ) +{ + exidy_sound_state *state = get_safe_token(device); + struct sh6840_timer_channel *sh6840_timer = state->m_sh6840_timer; + + /* hack to skip the expensive lfsr noise generation unless at least one of the 3 channels actually depends on it being generated */ + int noisy = ((sh6840_timer[0].cr & sh6840_timer[1].cr & sh6840_timer[2].cr & 0x02) == 0); + stream_sample_t *buffer = outputs[0]; + + /* loop over samples */ + while (samples--) + { + struct sh6840_timer_channel *t; + struct sh8253_timer_channel *c; + int clocks_this_sample; + int clocks; + INT16 sample = 0; + + /* determine how many 6840 clocks this sample */ + state->m_sh6840_clock_count += state->m_sh6840_clocks_per_sample; + clocks_this_sample = state->m_sh6840_clock_count >> 24; + state->m_sh6840_clock_count &= (1 << 24) - 1; + + /* skip if nothing enabled */ + if ((sh6840_timer[0].cr & 0x01) == 0) + { + int noise_clocks_this_sample = 0; + UINT32 chan0_clocks; + + /* generate E-clocked noise if configured to do so */ + if (noisy && !(state->m_sfxctrl & 0x01)) + noise_clocks_this_sample = sh6840_update_noise(state, clocks_this_sample); + + /* handle timer 0 if enabled */ + t = &sh6840_timer[0]; + chan0_clocks = t->clocks; + clocks = (t->cr & 0x02) ? clocks_this_sample : noise_clocks_this_sample; + sh6840_apply_clock(t, clocks); + if (t->state && !(state->m_sfxctrl & 0x02) && (t->cr & 0x80)) + sample += state->m_sh6840_volume[0]; + + /* generate channel 0-clocked noise if configured to do so */ + if (noisy && (state->m_sfxctrl & 0x01)) + noise_clocks_this_sample = sh6840_update_noise(state, t->clocks - chan0_clocks); + + /* handle timer 1 if enabled */ + t = &sh6840_timer[1]; + clocks = (t->cr & 0x02) ? clocks_this_sample : noise_clocks_this_sample; + sh6840_apply_clock(t, clocks); + if (t->state && (t->cr & 0x80)) + sample += state->m_sh6840_volume[1]; + + /* handle timer 2 if enabled */ + t = &sh6840_timer[2]; + clocks = (t->cr & 0x02) ? clocks_this_sample : noise_clocks_this_sample; + /* prescale */ + if (t->cr & 0x01) + { + clocks += t->leftovers; + t->leftovers = clocks % 8; + clocks /= 8; + } + sh6840_apply_clock(t, clocks); + if (t->state && (t->cr & 0x80)) + sample += state->m_sh6840_volume[2]; + } + + /* music (if present) */ + if (state->m_has_sh8253) + { + /* music channel 0 */ + c = &state->m_sh8253_timer[0]; + if (c->enable) + { + c->fraction += c->step; + if (c->fraction & 0x0800000) + sample += BASE_VOLUME; + } + + /* music channel 1 */ + c = &state->m_sh8253_timer[1]; + if (c->enable) + { + c->fraction += c->step; + if (c->fraction & 0x0800000) + sample += BASE_VOLUME; + } + + /* music channel 2 */ + c = &state->m_sh8253_timer[2]; + if (c->enable) + { + c->fraction += c->step; + if (c->fraction & 0x0800000) + sample += BASE_VOLUME; + } + } + + /* stash */ + *buffer++ = sample; + } +} + + + +/************************************* + * + * Audio startup routines + * + *************************************/ + +static DEVICE_START( common_sh_start ) +{ + exidy_sound_state *state = get_safe_token(device); + int sample_rate = SH8253_CLOCK; + + state->m_sh6840_clocks_per_sample = (int)((double)SH6840_CLOCK / (double)sample_rate * (double)(1 << 24)); + + /* allocate the stream */ + state->m_stream = device->machine().sound().stream_alloc(*device, 0, 1, sample_rate, NULL, exidy_stream_update); + state->m_maincpu = device->machine().device("maincpu"); + + sh6840_register_state_globals(device); +} + +static DEVICE_START( exidy_sound ) +{ + exidy_sound_state *state = get_safe_token(device); + + /* indicate no additional hardware */ + state->m_has_sh8253 = FALSE; + state->m_tms = NULL; + state->m_cvsd = NULL; + + DEVICE_START_CALL(common_sh_start); +} + +DEFINE_LEGACY_SOUND_DEVICE(EXIDY, exidy_sound); + +/************************************* + * + * Audio reset routines + * + *************************************/ + +static DEVICE_RESET( common_sh_reset ) +{ + exidy_sound_state *state = get_safe_token(device); + + /* 6840 */ + memset(state->m_sh6840_timer, 0, sizeof(state->m_sh6840_timer)); + state->m_sh6840_MSB_latch = 0; + state->m_sh6840_LSB_latch = 0; + state->m_sh6840_volume[0] = 0; + state->m_sh6840_volume[1] = 0; + state->m_sh6840_volume[2] = 0; + state->m_sh6840_clock_count = 0; + state->m_sfxctrl = 0; + + /* LFSR */ + state->m_sh6840_LFSR_oldxor = 0; + state->m_sh6840_LFSR_0 = 0xffffffff; + state->m_sh6840_LFSR_1 = 0xffffffff; + state->m_sh6840_LFSR_2 = 0xffffffff; + state->m_sh6840_LFSR_3 = 0xffffffff; +} + +static DEVICE_RESET( exidy_sound ) +{ + DEVICE_RESET_CALL(common_sh_reset); +} + + +DEVICE_GET_INFO( exidy_sound ) +{ + switch (state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(exidy_sound_state); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(exidy_sound); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(exidy_sound); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: strcpy(info->s, "Exidy SFX"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + } +} + + +/************************************* + * + * 6532 interface + * + *************************************/ + +static void r6532_irq(device_t *device, int state) +{ + exidy_sound_state *sndstate = get_safe_token(device); + sndstate->m_riot_irq_state = (state == ASSERT_LINE) ? 1 : 0; + update_irq_state(device, 0); +} + + +static WRITE8_DEVICE_HANDLER( r6532_porta_w ) +{ + exidy_sound_state *state = get_safe_token(device); + if (state->m_cvsd != NULL) + cputag_set_input_line(device->machine(), "cvsdcpu", INPUT_LINE_RESET, (data & 0x10) ? CLEAR_LINE : ASSERT_LINE); + + if (state->m_tms != NULL) + { + logerror("(%f)%s:TMS5220 data write = %02X\n", device->machine().time().as_double(), device->machine().describe_context(), riot6532_porta_out_get(state->m_riot)); + tms5220_data_w(state->m_tms, 0, data); + } +} + +static READ8_DEVICE_HANDLER( r6532_porta_r ) +{ + exidy_sound_state *state = get_safe_token(device); + if (state->m_tms != NULL) + { + logerror("(%f)%s:TMS5220 status read = %02X\n", device->machine().time().as_double(), device->machine().describe_context(), tms5220_status_r(state->m_tms, 0)); + return tms5220_status_r(state->m_tms, 0); + } + else + return 0xff; +} + +static WRITE8_DEVICE_HANDLER( r6532_portb_w ) +{ + exidy_sound_state *state = get_safe_token(device); + if (state->m_tms != NULL) + { + tms5220_rsq_w(state->m_tms, data & 0x01); + tms5220_wsq_w(state->m_tms, (data >> 1) & 0x01); + } +} + + +static READ8_DEVICE_HANDLER( r6532_portb_r ) +{ + exidy_sound_state *state = get_safe_token(device); + UINT8 newdata = riot6532_portb_in_get(state->m_riot); + if (state->m_tms != NULL) + { + newdata &= ~0x0c; + if (tms5220_readyq_r(state->m_tms)) newdata |= 0x04; + if (tms5220_intq_r(state->m_tms)) newdata |= 0x08; + } + return newdata; +} + + +static const riot6532_interface r6532_interface = +{ + DEVCB_DEVICE_HANDLER("custom", r6532_porta_r), /* port A read handler */ + DEVCB_DEVICE_HANDLER("custom", r6532_portb_r), /* port B read handler */ + DEVCB_DEVICE_HANDLER("custom", r6532_porta_w), /* port A write handler */ + DEVCB_DEVICE_HANDLER("custom", r6532_portb_w), /* port B write handler */ + DEVCB_DEVICE_LINE("custom", r6532_irq) /* IRQ callback */ +}; + + + +/************************************* + * + * 8253 state saving + * + *************************************/ + + +static void sh8253_register_state_globals(device_t *device) +{ + exidy_sound_state *state = get_safe_token(device); + + device->save_item(NAME(state->m_sh8253_timer[0].clstate)); + device->save_item(NAME(state->m_sh8253_timer[0].enable)); + device->save_item(NAME(state->m_sh8253_timer[0].count)); + device->save_item(NAME(state->m_sh8253_timer[0].step)); + device->save_item(NAME(state->m_sh8253_timer[0].fraction)); + device->save_item(NAME(state->m_sh8253_timer[1].clstate)); + device->save_item(NAME(state->m_sh8253_timer[1].enable)); + device->save_item(NAME(state->m_sh8253_timer[1].count)); + device->save_item(NAME(state->m_sh8253_timer[1].step)); + device->save_item(NAME(state->m_sh8253_timer[1].fraction)); + device->save_item(NAME(state->m_sh8253_timer[2].clstate)); + device->save_item(NAME(state->m_sh8253_timer[2].enable)); + device->save_item(NAME(state->m_sh8253_timer[2].count)); + device->save_item(NAME(state->m_sh8253_timer[2].step)); + device->save_item(NAME(state->m_sh8253_timer[2].fraction)); +} + +/************************************* + * + * 8253 timer handlers + * + *************************************/ + +static WRITE8_DEVICE_HANDLER( exidy_sh8253_w ) +{ + exidy_sound_state *state = get_safe_token(device); + int chan; + + state->m_stream->update(); + + switch (offset) + { + case 0: + case 1: + case 2: + chan = offset; + if (!state->m_sh8253_timer[chan].clstate) + { + state->m_sh8253_timer[chan].clstate = 1; + state->m_sh8253_timer[chan].count = (state->m_sh8253_timer[chan].count & 0xff00) | (data & 0x00ff); + } + else + { + state->m_sh8253_timer[chan].clstate = 0; + state->m_sh8253_timer[chan].count = (state->m_sh8253_timer[chan].count & 0x00ff) | ((data << 8) & 0xff00); + if (state->m_sh8253_timer[chan].count) + state->m_sh8253_timer[chan].step = state->m_freq_to_step * (double)SH8253_CLOCK / (double)state->m_sh8253_timer[chan].count; + else + state->m_sh8253_timer[chan].step = 0; + } + break; + + case 3: + chan = (data & 0xc0) >> 6; + state->m_sh8253_timer[chan].enable = ((data & 0x0e) != 0); + break; + } +} + + +static READ8_DEVICE_HANDLER( exidy_sh8253_r ) +{ + logerror("8253(R): %x\n",offset); + + return 0; +} + + + +/************************************* + * + * 6840 timer handlers + * + *************************************/ + +READ8_DEVICE_HANDLER( exidy_sh6840_r ) +{ + exidy_sound_state *state = get_safe_token(device); + + /* force an update of the stream */ + state->m_stream->update(); + + switch (offset) + { + /* offset 0: Motorola datasheet says it isn't used, Hitachi datasheet says it reads as 0s always*/ + case 0: + return 0; + /* offset 1 reads the status register: bits 2 1 0 correspond to ints on channels 2,1,0, and bit 7 is an 'OR' of bits 2,1,0 */ + case 1: + logerror("%04X:exidy_sh6840_r - unexpected read, status register is TODO!\n", cpu_get_pc(state->m_maincpu)); + return 0; + /* offsets 2,4,6 read channel 0,1,2 MSBs and latch the LSB*/ + case 2: case 4: case 6: + state->m_sh6840_LSB_latch = state->m_sh6840_timer[((offset<<1)-1)].counter.b.l; + return state->m_sh6840_timer[((offset<<1)-1)].counter.b.h; + /* offsets 3,5,7 read the LSB latch*/ + default: /* case 3,5,7 */ + return state->m_sh6840_LSB_latch; + } +} + + +WRITE8_DEVICE_HANDLER( exidy_sh6840_w ) +{ + exidy_sound_state *state = get_safe_token(device); + struct sh6840_timer_channel *sh6840_timer = state->m_sh6840_timer; + + /* force an update of the stream */ + state->m_stream->update(); + + switch (offset) + { + /* offset 0 writes to either channel 0 control or channel 2 control */ + case 0: + if (sh6840_timer[1].cr & 0x01) + sh6840_timer[0].cr = data; + else + sh6840_timer[2].cr = data; + + /* only support mode 0 and 2 */ + if (((data >> 3) & 5) != 0) + fatalerror("exidy_sh6840_w - channel %d configured for mode %d", (sh6840_timer[1].cr & 0x01) ? 0 : 2, (data >> 3) & 7); + break; + + /* offset 1 writes to channel 1 control */ + case 1: + sh6840_timer[1].cr = data; + + /* only support mode 0 and 2 */ + if (((data >> 3) & 5) != 0) + fatalerror("exidy_sh6840_w - channel 1 configured for mode %d", (data >> 3) & 7); + break; + + /* offsets 2/4/6 write to the common MSB latch */ + case 2: + case 4: + case 6: + state->m_sh6840_MSB_latch = data; + break; + + /* offsets 3/5/7 write to the LSB controls */ + case 3: + case 5: + case 7: + { + /* latch the timer value */ + int ch = (offset - 3) / 2; + sh6840_timer[ch].timer = (state->m_sh6840_MSB_latch << 8) | (data & 0xff); + + /* if CR4 is clear, the value is loaded immediately */ + if (!(sh6840_timer[ch].cr & 0x10)) + sh6840_timer[ch].counter.w = sh6840_timer[ch].timer; + break; + } + } +} + + + +/************************************* + * + * External sound effect controls + * + *************************************/ + +WRITE8_DEVICE_HANDLER( exidy_sfxctrl_w ) +{ + exidy_sound_state *state = get_safe_token(device); + + state->m_stream->update(); + + switch (offset) + { + case 0: + state->m_sfxctrl = data; + break; + + case 1: + case 2: + case 3: + state->m_sh6840_volume[offset - 1] = ((data & 7) * BASE_VOLUME) / 7; + break; + } +} + + + +/************************************* + * + * Sound filter control + * + *************************************/ + +static WRITE8_DEVICE_HANDLER( exidy_sound_filter_w ) +{ + logerror("exidy_sound_filter_w = %02X\n", data); +} + + + +/************************************* + * + * Venture, etc. + * + *************************************/ + +static const pia6821_interface venture_pia0_intf = +{ + DEVCB_NULL, /* port A in */ + DEVCB_NULL, /* port B in */ + DEVCB_NULL, /* line CA1 in */ + DEVCB_NULL, /* line CB1 in */ + DEVCB_NULL, /* line CA2 in */ + DEVCB_NULL, /* line CB2 in */ + DEVCB_DEVICE_MEMBER("pia1", pia6821_device, portb_w), /* port A out */ + DEVCB_DEVICE_MEMBER("pia1", pia6821_device, porta_w), /* port B out */ + DEVCB_DEVICE_LINE_MEMBER("pia1", pia6821_device, cb1_w), /* line CA2 out */ + DEVCB_DEVICE_LINE_MEMBER("pia1", pia6821_device, ca1_w), /* port CB2 out */ + DEVCB_NULL, /* IRQA */ + DEVCB_NULL /* IRQB */ +}; + + +static const pia6821_interface venture_pia1_intf = +{ + DEVCB_NULL, /* port A in */ + DEVCB_NULL, /* port B in */ + DEVCB_NULL, /* line CA1 in */ + DEVCB_NULL, /* line CB1 in */ + DEVCB_NULL, /* line CA2 in */ + DEVCB_NULL, /* line CB2 in */ + DEVCB_DEVICE_MEMBER("pia0", pia6821_device, portb_w), /* port A out */ + DEVCB_DEVICE_MEMBER("pia0", pia6821_device, porta_w), /* port B out */ + DEVCB_DEVICE_LINE_MEMBER("pia0", pia6821_device, cb1_w), /* line CA2 out */ + DEVCB_DEVICE_LINE_MEMBER("pia0", pia6821_device, ca1_w), /* port CB2 out */ + DEVCB_NULL, /* IRQA */ + DEVCB_DEVICE_LINE("custom", update_irq_state) /* IRQB */ +}; + + +static DEVICE_START( venture_common_sh_start ) +{ + exidy_sound_state *state = get_safe_token(device); + running_machine &machine = device->machine(); + + DEVICE_START_CALL(common_sh_start); + + state->m_riot = machine.device("riot"); + + state->m_has_sh8253 = TRUE; + state->m_tms = NULL; + state->m_pia1 = device->machine().device<pia6821_device>("pia1"); + + /* determine which sound hardware is installed */ + state->m_cvsd = device->machine().device("cvsd"); + + /* 8253 */ + state->m_freq_to_step = (double)(1 << 24) / (double)SH8253_CLOCK; + + device->save_item(NAME(state->m_riot_irq_state)); + sh8253_register_state_globals(device); +} + + +static DEVICE_START( venture_sound ) +{ + DEVICE_START_CALL(venture_common_sh_start); +} + + +static DEVICE_RESET( venture_sound ) +{ + exidy_sound_state *state = get_safe_token(device); + + DEVICE_RESET_CALL(common_sh_reset); + + /* PIA */ + devtag_reset(device->machine(), "pia0"); + devtag_reset(device->machine(), "pia1"); + + /* 6532 */ + state->m_riot->reset(); + + /* 8253 */ + memset(state->m_sh8253_timer, 0, sizeof(state->m_sh8253_timer)); +} + + +DEVICE_GET_INFO( venture_sound ) +{ + switch (state) + { + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(exidy_sound_state); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(venture_sound); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(venture_sound); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: strcpy(info->s, "Exidy SFX+PSG"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + } +} + +DEFINE_LEGACY_SOUND_DEVICE(EXIDY_VENTURE, venture_sound); + + +static ADDRESS_MAP_START( venture_audio_map, AS_PROGRAM, 8, driver_device ) + ADDRESS_MAP_GLOBAL_MASK(0x7fff) + AM_RANGE(0x0000, 0x007f) AM_MIRROR(0x0780) AM_RAM + AM_RANGE(0x0800, 0x087f) AM_MIRROR(0x0780) AM_DEVREADWRITE_LEGACY("riot", riot6532_r, riot6532_w) + AM_RANGE(0x1000, 0x1003) AM_MIRROR(0x07fc) AM_DEVREADWRITE("pia1", pia6821_device, read, write) + AM_RANGE(0x1800, 0x1803) AM_MIRROR(0x07fc) AM_DEVREADWRITE_LEGACY("custom", exidy_sh8253_r, exidy_sh8253_w) + AM_RANGE(0x2000, 0x27ff) AM_DEVWRITE_LEGACY("custom", exidy_sound_filter_w) + AM_RANGE(0x2800, 0x2807) AM_MIRROR(0x07f8) AM_DEVREADWRITE_LEGACY("custom", exidy_sh6840_r, exidy_sh6840_w) + AM_RANGE(0x3000, 0x3003) AM_MIRROR(0x07fc) AM_DEVWRITE_LEGACY("custom", exidy_sfxctrl_w) + AM_RANGE(0x5800, 0x7fff) AM_ROM +ADDRESS_MAP_END + + +MACHINE_CONFIG_FRAGMENT( venture_audio ) + + MCFG_CPU_ADD("audiocpu", M6502, 3579545/4) + MCFG_CPU_PROGRAM_MAP(venture_audio_map) + + MCFG_RIOT6532_ADD("riot", SH6532_CLOCK, r6532_interface) + + MCFG_PIA6821_ADD("pia0", venture_pia0_intf) + MCFG_PIA6821_ADD("pia1", venture_pia1_intf) + + MCFG_SPEAKER_STANDARD_MONO("mono") + + MCFG_SOUND_ADD("custom", EXIDY_VENTURE, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) +MACHINE_CONFIG_END + + + +/************************************* + * + * CVSD sound for Mouse Trap + * + *************************************/ + +static WRITE8_DEVICE_HANDLER( mtrap_voiceio_w ) +{ + exidy_sound_state *state = get_safe_token(device); + + if (!(offset & 0x10)) + hc55516_digit_w(state->m_cvsd, data & 1); + + if (!(offset & 0x20)) + riot6532_portb_in_set(state->m_riot, data & 1, 0xff); +} + + +static READ8_DEVICE_HANDLER( mtrap_voiceio_r ) +{ + exidy_sound_state *state = get_safe_token(device); + + if (!(offset & 0x80)) + { + UINT8 porta = riot6532_porta_out_get(state->m_riot); + UINT8 data = (porta & 0x06) >> 1; + data |= (porta & 0x01) << 2; + data |= (porta & 0x08); + return data; + } + + if (!(offset & 0x40)) + return hc55516_clock_state_r(state->m_cvsd) << 7; + + return 0; +} + + +static ADDRESS_MAP_START( cvsd_map, AS_PROGRAM, 8, driver_device ) + ADDRESS_MAP_GLOBAL_MASK(0x3fff) + AM_RANGE(0x0000, 0x3fff) AM_ROM +ADDRESS_MAP_END + + +static ADDRESS_MAP_START( cvsd_iomap, AS_IO, 8, driver_device ) + ADDRESS_MAP_GLOBAL_MASK(0xff) + AM_RANGE(0x00, 0xff) AM_DEVREADWRITE_LEGACY("custom", mtrap_voiceio_r, mtrap_voiceio_w) +ADDRESS_MAP_END + + +MACHINE_CONFIG_FRAGMENT( mtrap_cvsd_audio ) + + MCFG_CPU_ADD("cvsdcpu", Z80, CVSD_Z80_CLOCK) + MCFG_CPU_PROGRAM_MAP(cvsd_map) + MCFG_CPU_IO_MAP(cvsd_iomap) + + /* audio hardware */ + MCFG_SOUND_ADD("cvsd", MC3417, CVSD_CLOCK) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.80) +MACHINE_CONFIG_END + + + +/************************************* + * + * Victory + * + *************************************/ + +#define VICTORY_AUDIO_CPU_CLOCK (XTAL_3_579545MHz / 4) +#define VICTORY_LOG_SOUND 0 + + + +READ8_DEVICE_HANDLER( victory_sound_response_r ) +{ + exidy_sound_state *state = get_safe_token(device); + UINT8 ret = state->m_pia1->b_output(); + + if (VICTORY_LOG_SOUND) logerror("%04X:!!!! Sound response read = %02X\n", cpu_get_previouspc(state->m_maincpu), ret); + + state->m_pia1->cb1_w(0); + + return ret; +} + + +READ8_DEVICE_HANDLER( victory_sound_status_r ) +{ + exidy_sound_state *state = get_safe_token(device); + UINT8 ret = (state->m_pia1->ca1_r() << 7) | (state->m_pia1->cb1_r() << 6); + + if (VICTORY_LOG_SOUND) logerror("%04X:!!!! Sound status read = %02X\n", cpu_get_previouspc(state->m_maincpu), ret); + + return ret; +} + + +static TIMER_CALLBACK( delayed_command_w ) +{ + pia6821_device *pia1 = (pia6821_device *)ptr; + pia1->set_a_input(param, 0); + pia1->ca1_w(0); +} + +WRITE8_DEVICE_HANDLER( victory_sound_command_w ) +{ + exidy_sound_state *state = get_safe_token(device); + + if (VICTORY_LOG_SOUND) logerror("%04X:!!!! Sound command = %02X\n", cpu_get_previouspc(state->m_maincpu), data); + + device->machine().scheduler().synchronize(FUNC(delayed_command_w), data, state->m_pia1); +} + + +static WRITE8_DEVICE_HANDLER( victory_sound_irq_clear_w ) +{ + exidy_sound_state *state = get_safe_token(device); + + if (VICTORY_LOG_SOUND) logerror("%s:!!!! Sound IRQ clear = %02X\n", device->machine().describe_context(), data); + + if (!data) state->m_pia1->ca1_w(1); +} + + +static WRITE8_DEVICE_HANDLER( victory_main_ack_w ) +{ + exidy_sound_state *state = get_safe_token(device); + + if (VICTORY_LOG_SOUND) logerror("%s:!!!! Sound Main ACK W = %02X\n", device->machine().describe_context(), data); + + if (state->m_victory_sound_response_ack_clk && !data) + state->m_pia1->cb1_w(1); + + state->m_victory_sound_response_ack_clk = data; +} + + +static const pia6821_interface victory_pia1_intf = +{ + DEVCB_NULL, /* port A in */ + DEVCB_NULL, /* port B in */ + DEVCB_NULL, /* line CA1 in */ + DEVCB_NULL, /* line CB1 in */ + DEVCB_NULL, /* line CA2 in */ + DEVCB_NULL, /* line CB2 in */ + DEVCB_NULL, /* port A out */ + DEVCB_NULL, /* port B out */ + DEVCB_DEVICE_HANDLER("custom", victory_sound_irq_clear_w), /* line CA2 out */ + DEVCB_DEVICE_HANDLER("custom", victory_main_ack_w), /* port CB2 out */ + DEVCB_NULL, /* IRQA */ + DEVCB_DEVICE_LINE("custom", update_irq_state) /* IRQB */ +}; + + + +static DEVICE_START( victory_sound ) +{ + exidy_sound_state *state = get_safe_token(device); + + device->save_item(NAME(state->m_victory_sound_response_ack_clk)); + + DEVICE_START_CALL(venture_common_sh_start); + state->m_tms = device->machine().device("tms"); +} + + +static DEVICE_RESET( victory_sound ) +{ + exidy_sound_state *state = get_safe_token(device); + pia6821_device *pia1 = state->m_pia1; + + DEVICE_RESET_CALL(common_sh_reset); + pia1->reset(); + state->m_riot->reset(); + memset(state->m_sh8253_timer, 0, sizeof(state->m_sh8253_timer)); + + /* the flip-flop @ F4 is reset */ + state->m_victory_sound_response_ack_clk = 0; + pia1->cb1_w(1); + + /* these two lines shouldn't be needed, but it avoids the log entry + as the sound CPU checks port A before the main CPU ever writes to it */ + pia1->set_a_input(0, 0); + pia1->ca1_w(1); +} + + +DEVICE_GET_INFO( victory_sound ) +{ + switch (state) + { + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(exidy_sound_state); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(victory_sound); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(victory_sound); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: strcpy(info->s, "Exidy SFX+PSG+Speech"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(EXIDY_VICTORY, victory_sound); + + +static ADDRESS_MAP_START( victory_audio_map, AS_PROGRAM, 8, driver_device ) + AM_RANGE(0x0000, 0x00ff) AM_MIRROR(0x0f00) AM_RAM + AM_RANGE(0x1000, 0x107f) AM_MIRROR(0x0f80) AM_DEVREADWRITE_LEGACY("riot", riot6532_r, riot6532_w) + AM_RANGE(0x2000, 0x2003) AM_MIRROR(0x0ffc) AM_DEVREADWRITE("pia1", pia6821_device, read, write) + AM_RANGE(0x3000, 0x3003) AM_MIRROR(0x0ffc) AM_DEVREADWRITE_LEGACY("custom", exidy_sh8253_r, exidy_sh8253_w) + AM_RANGE(0x4000, 0x4fff) AM_NOP + AM_RANGE(0x5000, 0x5007) AM_MIRROR(0x0ff8) AM_DEVREADWRITE_LEGACY("custom", exidy_sh6840_r, exidy_sh6840_w) + AM_RANGE(0x6000, 0x6003) AM_MIRROR(0x0ffc) AM_DEVWRITE_LEGACY("custom", exidy_sfxctrl_w) + AM_RANGE(0x7000, 0xafff) AM_NOP + AM_RANGE(0xb000, 0xffff) AM_ROM +ADDRESS_MAP_END + + +MACHINE_CONFIG_FRAGMENT( victory_audio ) + + MCFG_CPU_ADD("audiocpu", M6502, VICTORY_AUDIO_CPU_CLOCK) + MCFG_CPU_PROGRAM_MAP(victory_audio_map) + + MCFG_RIOT6532_ADD("riot", SH6532_CLOCK, r6532_interface) + MCFG_PIA6821_ADD("pia1", victory_pia1_intf) + + MCFG_SPEAKER_STANDARD_MONO("mono") + + MCFG_SOUND_ADD("custom", EXIDY_VICTORY, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0) + + MCFG_SOUND_ADD("tms", TMS5220, 640000) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0) +MACHINE_CONFIG_END |