summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/sound/okim6376.c
diff options
context:
space:
mode:
author Angelo Salese <angelosa@users.noreply.github.com>2011-06-05 21:53:19 +0000
committer Angelo Salese <angelosa@users.noreply.github.com>2011-06-05 21:53:19 +0000
commit69ece3433ccd0a9a30b5c3c3da021f2467c4344f (patch)
tree26f94c4155a700d559ebdb2499eae0adbd820fb2 /src/emu/sound/okim6376.c
parent7506e9cf1348c82f1015c256e17fb7f4b529383d (diff)
Added echo emulation to the OkiM6376 sound chip [J. Wallace]
Diffstat (limited to 'src/emu/sound/okim6376.c')
-rw-r--r--src/emu/sound/okim6376.c267
1 files changed, 183 insertions, 84 deletions
diff --git a/src/emu/sound/okim6376.c b/src/emu/sound/okim6376.c
index c1ac5f5eb92..d974df20812 100644
--- a/src/emu/sound/okim6376.c
+++ b/src/emu/sound/okim6376.c
@@ -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,101 +462,106 @@ 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];
- okim6376_data_w -- write to the data port of an OKIM6376-compatible chip
+ /* 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;
+}
-WRITE8_DEVICE_HANDLER( okim6376_w )
+WRITE_LINE_DEVICE_HANDLER( okim6376_ch2_w )
{
okim6376_state *info = get_safe_token(device);
- /* if a command is pending, process the second half */
- if (info->command != -1)
+ if (info->ch2 != state)
{
- int temp = data >> 4, i, start;
- unsigned char *base/*, *base_end*/;
+ 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]);
+ }
+}
- /* 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();
+WRITE_LINE_DEVICE_HANDLER( okim6376_st_w )
+{
+ //As in STart, presumably, this triggers everything
- /* 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];
+ okim6376_state *info = get_safe_token(device);
+
+ if (info->st != state)
+ {
- /* 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;
+ info->st = state;
+ info->st_update = 1;
- if (start == 0)
+ 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)
{
- voice->playing = 0;
+ info->st_pulses = 3; //undocumented behaviour beyond 3 pulses
}
- 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);
- }
- }
+ voice->volume = volume_table[info->st_pulses];
}
}
+ if (info->st && info->st_update)
+ {
+ info->command[info->channel] = info->latch;
- /* reset the command */
- info->command = -1;
+ if (info->channel ==0)
+ {
+ oki_process(device, 0, info->command[0]);
+ }
+ }
}
+}
- /* 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;
+ okim6376_data_w -- write to the data port of an OKIM6376-compatible chip
- /* 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];
+WRITE8_DEVICE_HANDLER( okim6376_w )
+{
+ // The data port is purely used to set the latch, everything else is started by an ST pulse
- voice->playing = 0;
- }
- }
- }
+ okim6376_state *info = get_safe_token(device);
+ 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;
}
}