diff options
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 |
commit | 69ece3433ccd0a9a30b5c3c3da021f2467c4344f (patch) | |
tree | 26f94c4155a700d559ebdb2499eae0adbd820fb2 /src/emu/sound/okim6376.c | |
parent | 7506e9cf1348c82f1015c256e17fb7f4b529383d (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.c | 267 |
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; } } |