Added echo emulation to the OkiM6376 sound chip [J. Wallace]

This commit is contained in:
Angelo Salese 2011-06-05 21:53:19 +00:00
parent 7506e9cf13
commit 69ece3433c
4 changed files with 262 additions and 118 deletions

View File

@ -6,7 +6,10 @@
* TODO:
* add BEEP tone generator
* add 2ch handling (for echoing and continuous speech)
*
* add echo
* Something is definitely not right with the rates and divisions here - the maximum
* oscillation frequency should only be 256KHz, yet this only sounds decent at 3MHz
* add proper NAR handling - this varies with clock rate
**********************************************************************************************/
@ -36,10 +39,19 @@ struct _okim6376_state
{
#define OKIM6376_VOICES 2
struct ADPCMVoice voice[OKIM6376_VOICES];
INT32 command;
INT32 command[OKIM6376_VOICES];
INT32 latch; /* Command data is held before transferring to either channel */
UINT8 *region_base; /* pointer to the base of the region */
sound_stream *stream; /* which stream are we playing on? */
UINT32 master_clock; /* master clock frequency */
UINT8 channel;
UINT8 nar; /* Next Address Ready */
UINT8 busy;
UINT8 ch2; /* 2CH pin - enables Channel 2 operation */
UINT8 st; /* STart */
UINT8 st_pulses; /* Keep track of attenuation */
UINT8 ch2_update; /* Pulse shape */
UINT8 st_update;
};
/* step size index shift table */
@ -49,13 +61,13 @@ static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
static int diff_lookup[49*16];
/* volume lookup table. Upon configuration, the number of ST pulses determine how much
attenuation to apply to the sound signal. */
static const int volume_table[4] =
attenuation to apply to the sound signal. However, this only applies to the second
channel*/
static const int volume_table[3] =
{
0x20, // 0 dB
0x10, // -6.0 dB
0x08, // -12.0 dB
0x04, // -24.0 dB
};
/* tables computed? */
@ -279,13 +291,82 @@ static void adpcm_state_save_register(struct ADPCMVoice *voice, device_t *device
static void okim6376_state_save_register(okim6376_state *info, device_t *device)
{
int j;
device->save_item(NAME(info->command));
for (j = 0; j < OKIM6376_VOICES; j++)
{
adpcm_state_save_register(&info->voice[j], device, j);
}
device->save_item(NAME(info->command[0]));
device->save_item(NAME(info->command[1]));
}
static void oki_process(device_t *device, int channel, int command)
{
okim6376_state *info = get_safe_token(device);
/* if a command is pending, process the second half */
if ((command != -1) && (command != 0)) //process silence separately
{
int start;
unsigned char *base/*, *base_end*/;
info->nar = 0;//processing
/* update the stream */
info->stream->update();
/* determine which voice(s) (voice is set by the state of 2CH) */
{
struct ADPCMVoice *voice = &info->voice[channel];
/* determine the start position, max address space is 16Mbit */
base = &info->region_base[info->command[channel] * 4];
//base_end = &info->region_base[(MAX_WORDS+1) * 4];
start = ((base[0] << 16) + (base[1] << 8) + base[2]) & 0x1fffff;
if (start == 0)
{
voice->playing = 0;
}
else
{
/* set up the voice to play this sample */
if (!voice->playing)
{
voice->playing = 1;
voice->base_offset = start;
voice->sample = 0;
voice->count = 0;
/* also reset the ADPCM parameters */
reset_adpcm(voice);
/* FIX: no attenuation for now, handle for channel 2 separately */
voice->volume = volume_table[0];
}
else
{
logerror("OKIM6376:'%s' requested to play sample %02x on non-stopped voice\n",device->tag(),info->command[channel]);
}
}
info->nar = 1;
}
}
/* otherwise, see if this is a silence command */
else
{
info->nar = 0;
/* update the stream, then turn it off */
info->stream->update();
if (command ==0)
{
int i;
for (i = 0; i < OKIM6376_VOICES; i++)
{
struct ADPCMVoice *voice = &info->voice[i];
voice->playing = 0;
}
info->nar = 1;
}
}
}
/**********************************************************************************************
@ -301,10 +382,17 @@ static DEVICE_START( okim6376 )
compute_tables();
info->command = -1;
info->command[0] = -1;
info->command[1] = -1;
info->latch = 0;
info->region_base = *device->region();
info->master_clock = device->clock();
info->nar = 1;
info->busy = 1;
info->st = 1;
info->st_update = 0;
info->ch2_update = 0;
info->st_pulses = 0;
/* generate the name and create the stream */
info->stream = device->machine().sound().stream_alloc(*device, 0, 1, device->clock()/divisor, info, okim6376_update);
@ -319,7 +407,13 @@ static DEVICE_START( okim6376 )
okim6376_state_save_register(info, device);
}
void okim6376_set_frequency(device_t *device, int frequency)
{
okim6376_state *info = get_safe_token(device);
info->master_clock = frequency;
info->stream->set_sample_rate(info->master_clock / 165);
}
/**********************************************************************************************
@ -368,6 +462,91 @@ READ8_DEVICE_HANDLER( okim6376_r )
}
READ_LINE_DEVICE_HANDLER( okim6376_busy_r )
{
okim6376_state *info = get_safe_token(device);
int i,result=1;
for (i = 0; i < OKIM6376_VOICES; i++)
{
struct ADPCMVoice *voice = &info->voice[i];
/* set the bit low if it's playing */
if (voice->playing)
result = 0;
}
return result;
}
READ_LINE_DEVICE_HANDLER( okim6376_nar_r )
{
okim6376_state *info = get_safe_token(device);
return info->nar;
}
WRITE_LINE_DEVICE_HANDLER( okim6376_ch2_w )
{
okim6376_state *info = get_safe_token(device);
if (info->ch2 != state)
{
info->ch2 = state;
info->ch2_update = 1;
}
if((!info->ch2)&&(info->ch2_update))
{
info->channel = 1;
if (info->command[1] != info->latch)
{
info->command[1] = info->latch;
}
}
if((info->ch2)&&(info->ch2_update))
{
info->channel = 0;
oki_process(device, 0, info->command[1]);
}
}
WRITE_LINE_DEVICE_HANDLER( okim6376_st_w )
{
//As in STart, presumably, this triggers everything
okim6376_state *info = get_safe_token(device);
if (info->st != state)
{
info->st = state;
info->st_update = 1;
if ((info->channel == 1) & !info->st)//ST acts as attenuation for Channel 2 when low, and stays at that level until the channel is reset
{
struct ADPCMVoice *voice = &info->voice[info->channel];
{
info->st_pulses ++;
if (info->st_pulses > 3)
{
info->st_pulses = 3; //undocumented behaviour beyond 3 pulses
}
voice->volume = volume_table[info->st_pulses];
}
}
if (info->st && info->st_update)
{
info->command[info->channel] = info->latch;
if (info->channel ==0)
{
oki_process(device, 0, info->command[0]);
}
}
}
}
/**********************************************************************************************
@ -377,92 +556,12 @@ READ8_DEVICE_HANDLER( okim6376_r )
WRITE8_DEVICE_HANDLER( okim6376_w )
{
// The data port is purely used to set the latch, everything else is started by an ST pulse
okim6376_state *info = get_safe_token(device);
/* if a command is pending, process the second half */
if (info->command != -1)
{
int temp = data >> 4, i, start;
unsigned char *base/*, *base_end*/;
/* FIX: Check if it's possible to start multiple voices at the same time */
if (temp != 0 && temp != 1 && temp != 2)
popmessage("OKI6376 start %x contact MAMEDEV", temp);
/* update the stream */
info->stream->update();
/* determine which voice(s) (voice is set by a 1 bit in the upper 4 bits of the second byte) */
for (i = 0; i < OKIM6376_VOICES; i++, temp >>= 1)
{
if (temp & 1)
{
struct ADPCMVoice *voice = &info->voice[i];
/* determine the start position, max address space is 16Mbit */
base = &info->region_base[info->command * 4];
//base_end = &info->region_base[(MAX_WORDS+1) * 4];
start = ((base[0] << 16) + (base[1] << 8) + base[2]) & 0x1fffff;
if (start == 0)
{
voice->playing = 0;
}
else
{
/* set up the voice to play this sample */
if (!voice->playing)
{
voice->playing = 1;
voice->base_offset = start;
voice->sample = 0;
voice->count = 0;
/* also reset the ADPCM parameters */
reset_adpcm(voice);
/* FIX: no attenuation for now */
voice->volume = volume_table[0];
}
else
{
logerror("OKIM6376:'%s' requested to play sample %02x on non-stopped voice\n",device->tag(),info->command);
}
}
}
}
/* reset the command */
info->command = -1;
}
/* if this is the start of a command, remember the sample number for next time */
else if (data & 0x80)
{
// FIX: maximum adpcm words are 111, there are other 8 commands to generate BEEP tone (0x70 to 0x77),
// and others for internal testing, that manual explicitly says not to use (0x78 to 0x7f)
info->command = data & 0x7f;
}
/* otherwise, see if this is a silence command */
else
{
int temp = data >> 3, i;
/* update the stream, then turn it off */
info->stream->update();
/* determine which voice(s) (voice is set by a 1 bit in bits 3-6 of the command */
for (i = 0; i < OKIM6376_VOICES; i++, temp >>= 1)
{
if (temp & 1)
{
struct ADPCMVoice *voice = &info->voice[i];
voice->playing = 0;
}
}
}
info->latch = data & 0x7f;
// FIX: maximum adpcm words are 111, there are other 8 commands to generate BEEP tone (0x70 to 0x77),
// and others for internal testing, that manual explicitly says not to use (0x78 to 0x7f)
}
@ -476,18 +575,18 @@ DEVICE_GET_INFO( okim6376 )
switch (state)
{
/* --- the following bits of info are returned as 64-bit signed integers --- */
case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(okim6376_state); break;
case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(okim6376_state); break;
/* --- the following bits of info are returned as pointers to data or functions --- */
case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( okim6376 ); break;
case DEVINFO_FCT_STOP: /* nothing */ break;
case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( okim6376 ); break;
case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( okim6376 ); break;
case DEVINFO_FCT_STOP: /* nothing */ break;
case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( okim6376 ); break;
/* --- the following bits of info are returned as NULL-terminated strings --- */
case DEVINFO_STR_NAME: strcpy(info->s, "OKI6376"); break;
case DEVINFO_STR_NAME: strcpy(info->s, "OKI6376"); break;
case DEVINFO_STR_FAMILY: strcpy(info->s, "OKI ADPCM"); break;
case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
}
}

View File

@ -12,4 +12,14 @@ WRITE8_DEVICE_HANDLER( okim6376_w );
DECLARE_LEGACY_SOUND_DEVICE(OKIM6376, okim6376);
WRITE_LINE_DEVICE_HANDLER( okim6376_st_w );
WRITE_LINE_DEVICE_HANDLER( okim6376_ch2_w );
READ_LINE_DEVICE_HANDLER( okim6376_busy_r );
READ_LINE_DEVICE_HANDLER( okim6376_nar_r );
DECLARE_LEGACY_SOUND_DEVICE(OKIM6376, okim6376);
void okim6376_set_frequency(device_t *device, int frequency);
#endif /* __OKIM6376_H__ */

View File

@ -26,7 +26,7 @@ Port layout:
0x0000 W Keyboard Lights control port
0x0002 W \ Hopper or ticket related
0x0004 W /
0x0006 W OKI6395 ADPCM command: need to be latched
0x0006 W OKI6395 (6376?)ADPCM command: need to be latched
0x0010 W Like 0x3c8 in VGA
0x0014 W Like 0x3c9 in VGA
@ -235,16 +235,26 @@ static WRITE16_HANDLER( tv_vcf_bankselect_w )
}
static WRITE16_DEVICE_HANDLER( tv_oki6395_w )
static WRITE16_DEVICE_HANDLER( tv_oki6376_w )
{
static int okidata;
if (ACCESSING_BITS_0_7 && okidata != data) {
if (ACCESSING_BITS_0_7 && okidata != data)
{
okidata = data;
okim6376_w(device, 0, data);
okim6376_w(device, 0, (1 << 4));
okim6376_w(device, 0, data & ~0x80);
okim6376_st_w (device, data & 0x80);
}
}
static READ16_DEVICE_HANDLER( tv_oki6376_r )
{
if (ACCESSING_BITS_0_7)
{
return okim6376_busy_r(device);
}
return 0xff;
}
static WRITE16_HANDLER( write1_w )
{
/*
@ -278,12 +288,12 @@ ADDRESS_MAP_END
static ADDRESS_MAP_START( tv_vcf_io, AS_IO, 16 )
AM_RANGE(0x0000, 0x0001) AM_WRITE( write1_w ) // lamps
AM_RANGE(0x0006, 0x0007) AM_DEVWRITE( "oki", tv_oki6395_w )
AM_RANGE(0x0006, 0x0007) AM_DEVWRITE( "oki", tv_oki6376_w )
AM_RANGE(0x0008, 0x0009) AM_READ( read1_r )
AM_RANGE(0x000a, 0x000b) AM_READ( read2_r )
AM_RANGE(0x000c, 0x000d) AM_READ( read3_r )
AM_RANGE(0x0010, 0x0015) AM_WRITE( tv_vcf_paletteram_w )
AM_RANGE(0x0030, 0x0031) AM_WRITE( tv_vcf_bankselect_w ) AM_DEVREAD8( "oki", okim6376_r, 0x00ff )
AM_RANGE(0x0030, 0x0031) AM_WRITE( tv_vcf_bankselect_w ) AM_DEVREAD( "oki", tv_oki6376_r )
ADDRESS_MAP_END
@ -298,15 +308,23 @@ static READ16_HANDLER( tv_ncf_read2_r )
return (input_port_read(space->machine(), "IN1") & 0xbf) | resetpulse;
}
static WRITE16_DEVICE_HANDLER( tv_ncf_oki6395_w )
static WRITE16_DEVICE_HANDLER( tv_ncf_oki6376_w )
{
static int okidata;
if (ACCESSING_BITS_0_7 && okidata != data) {
okidata = data;
okim6376_w(device, 0, data | 0x80);
okim6376_w(device, 0, (1 << 4));
okim6376_w(device, 0, data );
}
}
static WRITE16_DEVICE_HANDLER( tv_ncf_oki6376_st_w )
{
if (ACCESSING_BITS_0_7)
{
okim6376_st_w(device, (data & 0x80) );
}
}
static ADDRESS_MAP_START( tv_ncf_map, AS_PROGRAM, 16 )
AM_RANGE(0x00000, 0x003ff) AM_RAM /*irq vector area*/
AM_RANGE(0x00400, 0x03fff) AM_RAM AM_SHARE("nvram")
@ -317,7 +335,8 @@ ADDRESS_MAP_END
static ADDRESS_MAP_START( tv_ncf_io, AS_IO, 16 )
AM_RANGE(0x0000, 0x0001) AM_WRITE( write1_w ) // lamps
AM_RANGE(0x0008, 0x0009) AM_DEVWRITE( "oki", tv_ncf_oki6395_w )
AM_RANGE(0x0008, 0x0009) AM_DEVWRITE( "oki", tv_ncf_oki6376_w )
AM_RANGE(0x000a, 0x000b) AM_DEVWRITE( "oki", tv_ncf_oki6376_st_w )
AM_RANGE(0x000c, 0x000d) AM_READ( read1_r )
AM_RANGE(0x0010, 0x0011) AM_READ( tv_ncf_read2_r )
AM_RANGE(0x0012, 0x0013) AM_READ( read3_r )
@ -361,7 +380,7 @@ ADDRESS_MAP_END
static ADDRESS_MAP_START( tv_tcf_io, AS_IO, 16 )
AM_RANGE(0x0000, 0x0001) AM_WRITE( write1_w ) // lamps
AM_RANGE(0x0006, 0x0007) AM_DEVWRITE( "oki", tv_oki6395_w )
AM_RANGE(0x0006, 0x0007) AM_DEVWRITE( "oki", tv_oki6376_w )
AM_RANGE(0x0008, 0x0009) AM_READ( read1_r )
AM_RANGE(0x000a, 0x000b) AM_READ( read2_r )
AM_RANGE(0x0030, 0x0031) AM_READ( read3_r ) AM_WRITE( tv_tcf_bankselect_w )
@ -423,7 +442,7 @@ static ADDRESS_MAP_START( newmcard_io, AS_IO, 16 )
AM_RANGE(0x0000, 0x0001) AM_WRITE( write1_w ) // lamps
AM_RANGE(0x0002, 0x0003) AM_WRITE( write2_w ) // coin counter & coin lockout
AM_RANGE(0x0004, 0x0005) AM_WRITE( newmcard_vblank_w )
AM_RANGE(0x0006, 0x0007) AM_DEVWRITE( "oki", tv_oki6395_w )
AM_RANGE(0x0006, 0x0007) AM_DEVWRITE( "oki", tv_oki6376_w )
AM_RANGE(0x0008, 0x0009) AM_READ( read1_r )
AM_RANGE(0x000a, 0x000b) AM_READ( read2_r )
AM_RANGE(0x000c, 0x000d) AM_READ( newmcard_vblank_r )
@ -491,7 +510,7 @@ static ADDRESS_MAP_START( brasil_io, AS_IO, 16 )
AM_RANGE(0x0030, 0x0031) AM_WRITE( brasil_status_w )
AM_RANGE(0x0000, 0x0001) AM_WRITE( write1_w ) // lamps
AM_RANGE(0x0002, 0x0003) AM_WRITE( write2_w ) // coin counter & coin lockout
AM_RANGE(0x0006, 0x0007) AM_DEVWRITE( "oki", tv_oki6395_w )
AM_RANGE(0x0006, 0x0007) AM_DEVWRITE( "oki", tv_oki6376_w )
AM_RANGE(0x0008, 0x0009) AM_READ( read1_r )
AM_RANGE(0x000a, 0x000b) AM_READ( read2_r )
AM_RANGE(0x000e, 0x000f) AM_READ( read3_r )

View File

@ -4,6 +4,7 @@
This is the core driver, no video specific stuff should go in here.
This driver holds all the mechanical games.
05-2011: Add better OKI emulation - clock rate may be wrong but samples sound good now.
04-2011: More accurate gamball code, fixed ROM banking (Project Amber), added BwB CHR simulator (Amber)
This is still a hard coded system, but significantly different to Barcrest's version.
Started adding support for the Crystal Gaming program card, and the link keys for setting parameters.
@ -217,8 +218,9 @@ TODO: - Distinguish door switches using manual
For now, we're ignoring any extra writes to strobes, as the alternative is to assign a timer to *everything*
- Flo's move in Great Escape gives spin alarms - need a different opto setting for reverse spin reels?
- Fix BwB characteriser, need to be able to calculate stabiliser bytes. Anyone fancy reading 6809 source?
- Fix MSM6376 - We're triggering 'contact MAMEDEV' since we need all features of the chip,
including dynamic sample rate adjustment and BEEP.
- Fix MSM6376 - We need all features of the chip, including dynamic sample rate adjustment and BEEP.
- OKI sound chip rate - need to confirm independently (3MHz sounds good, but that could be because
of the old driver - BwB manual claims 64KHz to 128KHz).
***********************************************************************************************************/
#include "emu.h"
#include "machine/6821pia.h"
@ -374,6 +376,7 @@ public:
int m_pageset;
int m_hopper;
int m_reels;
int m_chrdata;
const mpu4_chr_table* m_current_chr_table;
const bwb_chr_table* m_bwb_chr_table1;
//Video
@ -1670,6 +1673,8 @@ static WRITE8_DEVICE_HANDLER( pia_gb_porta_w )
static WRITE8_DEVICE_HANDLER( pia_gb_portb_w )
{
mpu4_state *state = device->machine().driver_data<mpu4_state>();
device_t *msm6376 = device->machine().device("msm6376");
int changed = state->m_expansion_latch^data;
LOG_SS(("%s: GAMEBOARD: PIA Port A Set to %2x\n", device->machine().describe_context(),data));
@ -1695,18 +1700,29 @@ static WRITE8_DEVICE_HANDLER( pia_gb_portb_w )
}
}
}
okim6376_ch2_w(msm6376,data&0x02);
okim6376_st_w(msm6376,data&0x01);
}
static READ8_DEVICE_HANDLER( pia_gb_portb_r )
{
device_t *msm6376 = device->machine().device("msm6376");
mpu4_state *state = device->machine().driver_data<mpu4_state>();
LOG_SS(("%s: GAMEBOARD: PIA Read of Port B\n",device->machine().describe_context()));
int data=0;
//
// b7, 1 = OKI ready, 0 = OKI busy
// b5, vol clock
// b4, 1 = Vol down, 0 = Vol up
//
return okim6376_r(msm6376,0);
if ( okim6376_busy_r(msm6376) ) data |= 0x80;
else data &= ~0x80;
if ( okim6376_nar_r(msm6376) ) data |= 0x40;
else data &= ~0x40;
return ( data | state->m_expansion_latch );
}
static WRITE_LINE_DEVICE_HANDLER( pia_gb_ca2_w )
@ -2883,7 +2899,7 @@ static MACHINE_CONFIG_DERIVED( mod4oki, mpu4mod2 )
MCFG_CPU_PROGRAM_MAP(mod4_oki_map)
MCFG_DEVICE_REMOVE("ay8913")
MCFG_SOUND_ADD("msm6376", OKIM6376, 64000) //Dynamic, can also be 85430 at 10.5KHz and 128000 at 16KHz
MCFG_SOUND_ADD("msm6376", OKIM6376, 3000000) //Wrong, needs to be 64 KHz, can also be 85430 at 10.5KHz and 128000 at 16KHz
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0)
MACHINE_CONFIG_END