/* Yamaha YMZ280B driver by Aaron Giles YMZ280B 8-Channel PCMD8 PCM/ADPCM Decoder Features as listed in LSI-4MZ280B3 data sheet: Voice data stored in external memory can be played back simultaneously for up to eight voices Voice data format can be selected from 4-bit ADPCM, 8-bit PCM and 16-bit PCM Control of voice data external memory Up to 16M bytes of ROM or SRAM (x 8 bits, access time 150ms max) can be connected Continuous access is possible Loop playback between selective addresses is possible Voice data playback frequency control 4-bit ADPCM ................ 0.172 to 44.1kHz in 256 steps 8-bit PCM, 16-bit PCM ...... 0.172 to 88.2kHz in 512 steps 256 steps total level and 16 steps panpot can be set Voice signal is output in stereo 16-bit 2's complement MSB-first format */ #define _USE_MATH_DEFINES #include #include "mamedef.h" //#include "sndintrf.h" //#include "streams.h" #ifdef _DEBUG #include #endif #include #include #include "ymz280b.h" static void update_irq_state_timer_common(void *param, int voicenum); //unsigned char DISABLE_YMZ_FIX = 0x00; #define MAX_SAMPLE_CHUNK 0x10000 #define MAKE_WAVS 0 #define MAKE_WAVS_CH 0 #define FRAC_BITS 14 #define FRAC_ONE (1 << FRAC_BITS) #define FRAC_MASK (FRAC_ONE - 1) #define INTERNAL_BUFFER_SIZE (1 << 15) //#define INTERNAL_SAMPLE_RATE (chip->master_clock * 2.0) #define INTERNAL_SAMPLE_RATE chip->rate #if MAKE_WAVS #include "wavwrite.h" #endif #if MAKE_WAVS_CH #include FILE* hWavFile[8]; signed short int* wavmem[8]; #endif /* struct describing a single playing ADPCM voice */ struct YMZ280BVoice { UINT8 playing; /* 1 if we are actively playing */ UINT8 keyon; /* 1 if the key is on */ UINT8 looping; /* 1 if looping is enabled */ UINT8 mode; /* current playback mode */ UINT16 fnum; /* frequency */ UINT8 level; /* output level */ UINT8 pan; /* panning */ UINT32 start; /* start address, in nibbles */ UINT32 stop; /* stop address, in nibbles */ UINT32 loop_start; /* loop start address, in nibbles */ UINT32 loop_end; /* loop end address, in nibbles */ UINT32 position; /* current position, in nibbles */ INT32 signal; /* current ADPCM signal */ INT32 step; /* current ADPCM step */ INT32 loop_signal; /* signal at loop start */ INT32 loop_step; /* step at loop start */ UINT32 loop_count; /* number of loops so far */ INT32 output_left; /* output volume (left) */ INT32 output_right; /* output volume (right) */ INT32 output_step; /* step value for frequency conversion */ INT32 output_pos; /* current fractional position */ INT16 last_sample; /* last sample output */ INT16 curr_sample; /* current sample target */ UINT8 irq_schedule; /* 1 if the IRQ state is updated by timer */ UINT8 Muted; /* used for muting */ }; typedef struct _ymz280b_state ymz280b_state; struct _ymz280b_state { //sound_stream * stream; /* which stream are we using */ UINT8 *region_base; /* pointer to the base of the region */ UINT32 region_size; UINT8 current_register; /* currently accessible register */ UINT8 status_register; /* current status register */ UINT8 irq_state; /* current IRQ state */ UINT8 irq_mask; /* current IRQ mask */ UINT8 irq_enable; /* current IRQ enable */ UINT8 keyon_enable; /* key on enable */ double master_clock; /* master clock frequency */ double rate; //void (*irq_callback)(const device_config *, int); /* IRQ callback */ void (*irq_callback)(int); /* IRQ callback */ struct YMZ280BVoice voice[8]; /* the 8 voices */ UINT32 rom_readback_addr; /* where the CPU can read the ROM */ //devcb_resolved_read8 ext_ram_read; /* external RAM read handler */ //devcb_resolved_write8 ext_ram_write; /* external RAM write handler */ #if MAKE_WAVS void * wavresample; /* resampled waveform */ #endif INT16 *scratch; //const device_config *device; }; static void write_to_register(ymz280b_state *chip, int data); /* step size index shift table */ static const int index_scale[8] = { 0x0e6, 0x0e6, 0x0e6, 0x0e6, 0x133, 0x199, 0x200, 0x266 }; /* lookup table for the precomputed difference */ static int diff_lookup[16]; static unsigned char lookup_init = 0x00; /* lookup-table is initialized */ /* timer callback */ /*static TIMER_CALLBACK( update_irq_state_timer_0 ); static TIMER_CALLBACK( update_irq_state_timer_1 ); static TIMER_CALLBACK( update_irq_state_timer_2 ); static TIMER_CALLBACK( update_irq_state_timer_3 ); static TIMER_CALLBACK( update_irq_state_timer_4 ); static TIMER_CALLBACK( update_irq_state_timer_5 ); static TIMER_CALLBACK( update_irq_state_timer_6 ); static TIMER_CALLBACK( update_irq_state_timer_7 ); static const timer_fired_func update_irq_state_cb[] = { update_irq_state_timer_0, update_irq_state_timer_1, update_irq_state_timer_2, update_irq_state_timer_3, update_irq_state_timer_4, update_irq_state_timer_5, update_irq_state_timer_6, update_irq_state_timer_7 };*/ /*INLINE ymz280b_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_YMZ280B); return (ymz280b_state *)device->token; }*/ INLINE void update_irq_state(ymz280b_state *chip) { int irq_bits = chip->status_register & chip->irq_mask; /* always off if the enable is off */ if (!chip->irq_enable) irq_bits = 0; /* update the state if changed */ if (irq_bits && !chip->irq_state) { chip->irq_state = 1; if (chip->irq_callback) //(*chip->irq_callback)(chip->device, 1); (*chip->irq_callback)(1); //else logerror("YMZ280B: IRQ generated, but no callback specified!"); } else if (!irq_bits && chip->irq_state) { chip->irq_state = 0; if (chip->irq_callback) //(*chip->irq_callback)(chip->device, 0); (*chip->irq_callback)(0); //else logerror("YMZ280B: IRQ generated, but no callback specified!"); } } INLINE void update_step(ymz280b_state *chip, struct YMZ280BVoice *voice) { double frequency; /* compute the frequency */ if (voice->mode == 1) frequency = chip->master_clock * (double)((voice->fnum & 0x0ff) + 1) * (1.0 / 256.0); else frequency = chip->master_clock * (double)((voice->fnum & 0x1ff) + 1) * (1.0 / 256.0); voice->output_step = (UINT32)(frequency * (double)FRAC_ONE / INTERNAL_SAMPLE_RATE); } INLINE void update_volumes(struct YMZ280BVoice *voice) { if (voice->pan == 8) { voice->output_left = voice->level; voice->output_right = voice->level; } else if (voice->pan < 8) { voice->output_left = voice->level; /* pan 1 is hard-left, what's pan 0? for now assume same as pan 1 */ voice->output_right = (voice->pan == 0) ? 0 : voice->level * (voice->pan - 1) / 7; } else { voice->output_left = voice->level * (15 - voice->pan) / 7; voice->output_right = voice->level; } } INLINE UINT8 ymz280b_read_memory(UINT8 *base, UINT32 size, UINT32 offset) { offset &= 0xFFFFFF; if (offset < size) return base[offset]; /* 16MB chip limit (shouldn't happen) */ //else if (offset > 0xffffff) // return base[offset & 0xffffff]; else return 0; } static void update_irq_state_timer_common(void *param, int voicenum) { ymz280b_state *chip = (ymz280b_state *)param; struct YMZ280BVoice *voice = &chip->voice[voicenum]; if(!voice->irq_schedule) return; voice->playing = 0; chip->status_register |= 1 << voicenum; update_irq_state(chip); voice->irq_schedule = 0; } /*static TIMER_CALLBACK( update_irq_state_timer_0 ) { update_irq_state_timer_common(ptr, 0); } static TIMER_CALLBACK( update_irq_state_timer_1 ) { update_irq_state_timer_common(ptr, 1); } static TIMER_CALLBACK( update_irq_state_timer_2 ) { update_irq_state_timer_common(ptr, 2); } static TIMER_CALLBACK( update_irq_state_timer_3 ) { update_irq_state_timer_common(ptr, 3); } static TIMER_CALLBACK( update_irq_state_timer_4 ) { update_irq_state_timer_common(ptr, 4); } static TIMER_CALLBACK( update_irq_state_timer_5 ) { update_irq_state_timer_common(ptr, 5); } static TIMER_CALLBACK( update_irq_state_timer_6 ) { update_irq_state_timer_common(ptr, 6); } static TIMER_CALLBACK( update_irq_state_timer_7 ) { update_irq_state_timer_common(ptr, 7); }*/ /********************************************************************************************** compute_tables -- compute the difference tables ***********************************************************************************************/ static void compute_tables(void) { int nib; if (lookup_init) return; /* loop over all nibbles and compute the difference */ for (nib = 0; nib < 16; nib++) { int value = (nib & 0x07) * 2 + 1; diff_lookup[nib] = (nib & 0x08) ? -value : value; } lookup_init = 0x01; } /********************************************************************************************** generate_adpcm -- general ADPCM decoding routine ***********************************************************************************************/ static int generate_adpcm(struct YMZ280BVoice *voice, UINT8 *base, UINT32 size, INT16 *buffer, int samples) { UINT32 position = voice->position; int signal = voice->signal; int step = voice->step; int val; /*if (! DISABLE_YMZ_FIX) { if (position >= voice->stop) { voice->playing = 0; return samples; } }*/ /* two cases: first cases is non-looping */ if (!voice->looping) { /* loop while we still have samples to generate */ while (samples) { /* compute the new amplitude and update the current step */ //val = base[position / 2] >> ((~position & 1) << 2); val = ymz280b_read_memory(base, size, position / 2) >> ((~position & 1) << 2); signal += (step * diff_lookup[val & 15]) / 8; /* clamp to the maximum */ if (signal > 32767) signal = 32767; else if (signal < -32768) signal = -32768; /* adjust the step size and clamp */ step = (step * index_scale[val & 7]) >> 8; if (step > 0x6000) step = 0x6000; else if (step < 0x7f) step = 0x7f; /* output to the buffer, scaling by the volume */ *buffer++ = signal; samples--; /* next! */ position++; if (position >= voice->stop) { if (!samples) samples |= 0x10000; break; } } } /* second case: looping */ else { /* loop while we still have samples to generate */ while (samples) { /* compute the new amplitude and update the current step */ //val = base[position / 2] >> ((~position & 1) << 2); val = ymz280b_read_memory(base, size, position / 2) >> ((~position & 1) << 2); signal += (step * diff_lookup[val & 15]) / 8; /* clamp to the maximum */ if (signal > 32767) signal = 32767; else if (signal < -32768) signal = -32768; /* adjust the step size and clamp */ step = (step * index_scale[val & 7]) >> 8; if (step > 0x6000) step = 0x6000; else if (step < 0x7f) step = 0x7f; /* output to the buffer, scaling by the volume */ *buffer++ = signal; samples--; /* next! */ position++; if (position == voice->loop_start && voice->loop_count == 0) { voice->loop_signal = signal; voice->loop_step = step; } if (position >= voice->loop_end) { if (voice->keyon) { position = voice->loop_start; signal = voice->loop_signal; step = voice->loop_step; voice->loop_count++; } } if (position >= voice->stop) { if (!samples) samples |= 0x10000; break; } } } /* update the parameters */ voice->position = position; voice->signal = signal; voice->step = step; return samples; } /********************************************************************************************** generate_pcm8 -- general 8-bit PCM decoding routine ***********************************************************************************************/ static int generate_pcm8(struct YMZ280BVoice *voice, UINT8 *base, UINT32 size, INT16 *buffer, int samples) { int val; UINT32 position = voice->position; /*if (! DISABLE_YMZ_FIX) { if (position >= voice->stop) { voice->playing = 0; return samples; } }*/ /* two cases: first cases is non-looping */ if (!voice->looping) { /* loop while we still have samples to generate */ while (samples) { /* fetch the current value */ //val = base[position / 2]; val = ymz280b_read_memory(base, size, position / 2); /* output to the buffer, scaling by the volume */ *buffer++ = (INT8)val * 256; samples--; /* next! */ position += 2; if (position >= voice->stop) { if (!samples) samples |= 0x10000; break; } } } /* second case: looping */ else { /* loop while we still have samples to generate */ while (samples) { /* fetch the current value */ //val = base[position / 2]; val = ymz280b_read_memory(base, size, position / 2); /* output to the buffer, scaling by the volume */ *buffer++ = (INT8)val * 256; samples--; /* next! */ position += 2; if (position >= voice->loop_end) { if (voice->keyon) position = voice->loop_start; } if (position >= voice->stop) { if (!samples) samples |= 0x10000; break; } } } /* update the parameters */ voice->position = position; return samples; } /********************************************************************************************** generate_pcm16 -- general 16-bit PCM decoding routine ***********************************************************************************************/ static int generate_pcm16(struct YMZ280BVoice *voice, UINT8 *base, UINT32 size, INT16 *buffer, int samples) { int val; UINT32 position = voice->position; /*if (! DISABLE_YMZ_FIX) { if (position >= voice->stop) { voice->playing = 0; return samples; } }*/ /* is it even used in any MAME game? */ //popmessage("YMZ280B 16-bit PCM contact MAMEDEV"); /* two cases: first cases is non-looping */ if (!voice->looping) { /* loop while we still have samples to generate */ while (samples) { /* fetch the current value */ //val = (INT16)((base[position / 2 + 1] << 8) + base[position / 2 + 0]); val = (INT16)((ymz280b_read_memory(base, size, position / 2 + 0) << 8) + ymz280b_read_memory(base, size, position / 2 + 1)); /* output to the buffer, scaling by the volume */ *buffer++ = val; samples--; /* next! */ position += 4; if (position >= voice->stop) { if (!samples) samples |= 0x10000; break; } } } /* second case: looping */ else { /* loop while we still have samples to generate */ while (samples) { /* fetch the current value */ //val = (INT16)((base[position / 2 + 1] << 8) + base[position / 2 + 0]); val = (INT16)((ymz280b_read_memory(base, size, position / 2 + 0) << 8) + ymz280b_read_memory(base, size, position / 2 + 1)); /* output to the buffer, scaling by the volume */ *buffer++ = val; samples--; /* next! */ position += 4; if (position >= voice->loop_end) { if (voice->keyon) position = voice->loop_start; } if (position >= voice->stop) { if (!samples) samples |= 0x10000; break; } } } /* update the parameters */ voice->position = position; return samples; } /********************************************************************************************** ymz280b_update -- update the sound chip so that it is in sync with CPU execution ***********************************************************************************************/ //static STREAM_UPDATE( ymz280b_update ) void ymz280b_update(void *_chip, stream_sample_t **outputs, int samples) { //ymz280b_state *chip = (ymz280b_state *)param; ymz280b_state *chip = (ymz280b_state *) _chip; stream_sample_t *lacc = outputs[0]; stream_sample_t *racc = outputs[1]; int v; /* clear out the accumulator */ memset(lacc, 0, samples * sizeof(lacc[0])); memset(racc, 0, samples * sizeof(racc[0])); /* loop over voices */ for (v = 0; v < 8; v++) { struct YMZ280BVoice *voice = &chip->voice[v]; INT16 prev = voice->last_sample; INT16 curr = voice->curr_sample; INT16 *curr_data = chip->scratch; INT32 *ldest = lacc; INT32 *rdest = racc; UINT32 new_samples, samples_left; UINT32 final_pos; int remaining = samples; int lvol = voice->output_left; int rvol = voice->output_right; #if MAKE_WAVS_CH signed short int* wavlog; memset(wavmem[v], 0x00, samples * 0x02); wavlog = wavmem[v]; #endif /* skip if muted */ if (voice->Muted) continue; /* quick out if we're not playing and we're at 0 */ if (!voice->playing && curr == 0 && prev == 0) { /* make sure next sound plays immediately */ voice->output_pos = FRAC_ONE; continue; } /* finish off the current sample */ /* interpolate */ while (remaining > 0 && voice->output_pos < FRAC_ONE) { int interp_sample = (((INT32)prev * (FRAC_ONE - voice->output_pos)) + ((INT32)curr * voice->output_pos)) >> FRAC_BITS; *ldest++ += interp_sample * lvol; *rdest++ += interp_sample * rvol; #if MAKE_WAVS_CH *(wavlog ++) = (signed short int)interp_sample; #endif voice->output_pos += voice->output_step; remaining--; } /* if we're over, continue; otherwise, we're done */ if (voice->output_pos >= FRAC_ONE) voice->output_pos -= FRAC_ONE; else continue; /* compute how many new samples we need */ final_pos = voice->output_pos + remaining * voice->output_step; new_samples = (final_pos + FRAC_ONE) >> FRAC_BITS; if (new_samples > MAX_SAMPLE_CHUNK) new_samples = MAX_SAMPLE_CHUNK; samples_left = new_samples; /* generate them into our buffer */ switch (voice->playing << 7 | voice->mode) { case 0x81: samples_left = generate_adpcm(voice, chip->region_base, chip->region_size, chip->scratch, new_samples); break; case 0x82: samples_left = generate_pcm8(voice, chip->region_base, chip->region_size, chip->scratch, new_samples); break; case 0x83: samples_left = generate_pcm16(voice, chip->region_base, chip->region_size, chip->scratch, new_samples); break; default: samples_left = 0; memset(chip->scratch, 0, new_samples * sizeof(chip->scratch[0])); break; } /* if there are leftovers, ramp back to 0 */ if (samples_left) { /* note: samples_left bit 16 is set if the voice was finished at the same time the function ended */ int base; UINT32 i; int t; samples_left &= 0xffff; base = new_samples - samples_left; t = (base == 0) ? curr : chip->scratch[base - 1]; for (i = 0; i < samples_left; i++) { if (t < 0) t = -((-t * 15) >> 4); else if (t > 0) t = (t * 15) >> 4; chip->scratch[base + i] = t; } /* if we hit the end and IRQs are enabled, signal it */ if (base != 0) { voice->playing = 0; /* set update_irq_state_timer. IRQ is signaled on next CPU execution. */ //timer_set(chip->device->machine, attotime_zero, chip, 0, update_irq_state_cb[v]); voice->irq_schedule = 1; } } /* advance forward one sample */ prev = curr; curr = *curr_data++; /* then sample-rate convert with linear interpolation */ while (remaining > 0) { /* interpolate */ while (remaining > 0 && voice->output_pos < FRAC_ONE) { int interp_sample = (((INT32)prev * (FRAC_ONE - voice->output_pos)) + ((INT32)curr * voice->output_pos)) >> FRAC_BITS; *ldest++ += interp_sample * lvol; *rdest++ += interp_sample * rvol; #if MAKE_WAVS_CH *(wavlog ++) = (signed short int)interp_sample; #endif voice->output_pos += voice->output_step; remaining--; } /* if we're over, grab the next samples */ if (voice->output_pos >= FRAC_ONE) { voice->output_pos -= FRAC_ONE; prev = curr; curr = *curr_data++; } } /* remember the last samples */ voice->last_sample = prev; voice->curr_sample = curr; } for (v = 0; v < samples; v++) { outputs[0][v] /= 256; outputs[1][v] /= 256; } for (v = 0; v < 8; v++) update_irq_state_timer_common(chip, v); #if MAKE_WAVS_CH for (v = 0; v < 8; v++) { fwrite(wavmem[v], 0x02, samples, hWavFile[v]); } #endif } /********************************************************************************************** DEVICE_START( ymz280b ) -- start emulation of the YMZ280B ***********************************************************************************************/ //static DEVICE_START( ymz280b ) void * device_start_ymz280b(int clock) { static const ymz280b_interface defintrf = { 0 }; //const ymz280b_interface *intf = (device->static_config != NULL) ? (const ymz280b_interface *)device->static_config : &defintrf; const ymz280b_interface *intf = &defintrf; //ymz280b_state *chip = get_safe_token(device); ymz280b_state *chip; int chn; chip = (ymz280b_state *) calloc(1, sizeof(ymz280b_state)); //chip->device = device; //devcb_resolve_read8(&chip->ext_ram_read, &intf->ext_read, device); //devcb_resolve_write8(&chip->ext_ram_write, &intf->ext_write, device); /* compute ADPCM tables */ compute_tables(); /* initialize the rest of the structure */ chip->master_clock = (double)clock / 384.0; chip->rate = chip->master_clock * 2.0; // disabled until the frequency calculation gets fixed /*if ((CHIP_SAMPLING_MODE == 0x01 && chip->rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) chip->rate = (double)CHIP_SAMPLE_RATE;*/ //chip->region_base = device->region; chip->region_size = 0x00; chip->region_base = NULL; chip->irq_callback = intf->irq_callback; /* create the stream */ //chip->stream = stream_create(device, 0, 2, INTERNAL_SAMPLE_RATE, chip, ymz280b_update); /* allocate memory */ //chip->scratch = auto_alloc_array(device->machine, INT16, MAX_SAMPLE_CHUNK); chip->scratch = malloc(MAX_SAMPLE_CHUNK * sizeof(INT16)); memset(chip->scratch, 0x00, MAX_SAMPLE_CHUNK * sizeof(INT16)); /* state save */ /*{ int j; state_save_register_device_item(device, 0, chip->current_register); state_save_register_device_item(device, 0, chip->status_register); state_save_register_device_item(device, 0, chip->irq_state); state_save_register_device_item(device, 0, chip->irq_mask); state_save_register_device_item(device, 0, chip->irq_enable); state_save_register_device_item(device, 0, chip->keyon_enable); state_save_register_device_item(device, 0, chip->rom_readback_addr); for (j = 0; j < 8; j++) { state_save_register_device_item(device, j, chip->voice[j].playing); state_save_register_device_item(device, j, chip->voice[j].keyon); state_save_register_device_item(device, j, chip->voice[j].looping); state_save_register_device_item(device, j, chip->voice[j].mode); state_save_register_device_item(device, j, chip->voice[j].fnum); state_save_register_device_item(device, j, chip->voice[j].level); state_save_register_device_item(device, j, chip->voice[j].pan); state_save_register_device_item(device, j, chip->voice[j].start); state_save_register_device_item(device, j, chip->voice[j].stop); state_save_register_device_item(device, j, chip->voice[j].loop_start); state_save_register_device_item(device, j, chip->voice[j].loop_end); state_save_register_device_item(device, j, chip->voice[j].position); state_save_register_device_item(device, j, chip->voice[j].signal); state_save_register_device_item(device, j, chip->voice[j].step); state_save_register_device_item(device, j, chip->voice[j].loop_signal); state_save_register_device_item(device, j, chip->voice[j].loop_step); state_save_register_device_item(device, j, chip->voice[j].loop_count); state_save_register_device_item(device, j, chip->voice[j].output_left); state_save_register_device_item(device, j, chip->voice[j].output_right); state_save_register_device_item(device, j, chip->voice[j].output_pos); state_save_register_device_item(device, j, chip->voice[j].last_sample); state_save_register_device_item(device, j, chip->voice[j].curr_sample); state_save_register_device_item(device, j, chip->voice[j].irq_schedule); } }*/ for (chn = 0; chn < 8; chn ++) chip->voice[chn].Muted = 0x00; #if MAKE_WAVS chip->wavresample = wav_open("resamp.wav", INTERNAL_SAMPLE_RATE, 2); #endif #if MAKE_WAVS_CH hWavFile[0] = fopen("logwav0.raw", "wb"); hWavFile[1] = fopen("logwav1.raw", "wb"); hWavFile[2] = fopen("logwav2.raw", "wb"); hWavFile[3] = fopen("logwav3.raw", "wb"); hWavFile[4] = fopen("logwav4.raw", "wb"); hWavFile[5] = fopen("logwav5.raw", "wb"); hWavFile[6] = fopen("logwav6.raw", "wb"); hWavFile[7] = fopen("logwav7.raw", "wb"); { char v; for (v = 0; v < 8; v++) { wavmem[v] = (signed short int*)malloc(0x10 * 0x02); } } #endif return chip; //return (int)INTERNAL_SAMPLE_RATE; } //static DEVICE_STOP( ymz280b ) void device_stop_ymz280b(void *_chip) { //ymz280b_state *chip = get_safe_token(device); ymz280b_state *chip = (ymz280b_state *) _chip; free(chip->region_base); chip->region_base = NULL; free(chip->scratch); #if MAKE_WAVS_CH { char v; for (v = 0; v < 8; v++) { free(wavmem[v]); fclose(hWavFile[v]); } } #endif free(chip); } //static DEVICE_RESET( ymz280b ) void device_reset_ymz280b(void *_chip) { ymz280b_state *chip = (ymz280b_state *) _chip; /*struct YMZ280BVoice *voice; unsigned char curvoc; chip->current_register = 0x00; chip->status_register = 0x00; chip->irq_state = 0x00; chip->irq_mask = 0x00; chip->irq_enable = 0x00; chip->keyon_enable = 0x00; chip->rom_readback_addr = 0x000000; for (curvoc = 0; curvoc < 8; curvoc ++) { voice = &chip->voice[curvoc]; voice->playing = 0; voice->keyon = 0; voice->looping = 0; voice->mode = 0; voice->fnum = 0; voice->level = 0; voice->pan = 8; voice->start = 0x000000; voice->stop = 0x000000; voice->loop_start = 0x000000; voice->loop_end = 0x000000; voice->position = 0x000000; }*/ // new code from MAME 0.143u4 int i; /* initial clear registers */ for (i = 0xff; i >= 0; i--) { if (i == 0x83 || (i >= 88 && i <= 0xFD)) continue; // avoid too many debug messages chip->current_register = i; write_to_register(chip, 0); } chip->current_register = 0; chip->status_register = 0; /* clear other voice parameters */ for (i = 0; i < 8; i++) { struct YMZ280BVoice *voice = &chip->voice[i]; voice->curr_sample = 0; voice->last_sample = 0; voice->output_pos = FRAC_ONE; voice->playing = 0; } } /********************************************************************************************** write_to_register -- handle a write to the current register ***********************************************************************************************/ static void write_to_register(ymz280b_state *chip, int data) { struct YMZ280BVoice *voice; int i; //UINT8 mode_new; /* force an update */ //stream_update(chip->stream); /* lower registers follow a pattern */ if (chip->current_register < 0x80) { voice = &chip->voice[(chip->current_register >> 2) & 7]; switch (chip->current_register & 0xe3) { case 0x00: /* pitch low 8 bits */ voice->fnum = (voice->fnum & 0x100) | (data & 0xff); update_step(chip, voice); break; case 0x01: /* pitch upper 1 bit, loop, key on, mode */ voice->fnum = (voice->fnum & 0xff) | ((data & 0x01) << 8); voice->looping = (data & 0x10) >> 4; /*mode_new = (data & 0x60) >> 5; if (! DISABLE_YMZ_FIX) { // that fixes the scratch-bug if (voice->mode != mode_new) { // On-the-fly Mode-Change won't make sense, // so I'm doing: KeyOff + Mode Change -> Instant Stop. // (Deroon DeroDero uses this quite often) // This is done by setting KeyOn to 0. // Instant Stop/Restarting is done below. voice->keyon = 0; voice->irq_schedule = 0; } if (! mode_new) data &= 0x7F; } voice->mode = mode_new;*/ if ((data & 0x60) == 0) data &= 0x7f; /* ignore mode setting and set to same state as KON=0 */ else voice->mode = (data & 0x60) >> 5; if (!voice->keyon && (data & 0x80) && chip->keyon_enable) { voice->playing = 1; voice->position = voice->start; voice->signal = voice->loop_signal = 0; voice->step = voice->loop_step = 0x7f; voice->loop_count = 0; /* if update_irq_state_timer is set, cancel it. */ voice->irq_schedule = 0; } /*else if (voice->keyon && !(data & 0x80) && !voice->looping) { voice->playing = 0; // if update_irq_state_timer is set, cancel it. voice->irq_schedule = 0; } else if (! DISABLE_YMZ_FIX && ! voice->keyon && !(data & 0x80) && voice->playing) { // 2x KeyOff -> Instant Stop, too (see Deroon DeroDero: Round Start-Tune) voice->playing = 0; voice->irq_schedule = 0; }*/ // new code from MAME 0.143u4 else if (voice->keyon && !(data & 0x80)) { voice->playing = 0; // if update_irq_state_timer is set, cancel it. voice->irq_schedule = 0; } voice->keyon = (data & 0x80) >> 7; update_step(chip, voice); break; case 0x02: /* total level */ voice->level = data; update_volumes(voice); break; case 0x03: /* pan */ voice->pan = data & 0x0f; update_volumes(voice); break; case 0x20: /* start address high */ voice->start = (voice->start & (0x00ffff << 1)) | (data << 17); break; case 0x21: /* loop start address high */ voice->loop_start = (voice->loop_start & (0x00ffff << 1)) | (data << 17); break; case 0x22: /* loop end address high */ voice->loop_end = (voice->loop_end & (0x00ffff << 1)) | (data << 17); break; case 0x23: /* stop address high */ voice->stop = (voice->stop & (0x00ffff << 1)) | (data << 17); break; case 0x40: /* start address middle */ voice->start = (voice->start & (0xff00ff << 1)) | (data << 9); break; case 0x41: /* loop start address middle */ voice->loop_start = (voice->loop_start & (0xff00ff << 1)) | (data << 9); break; case 0x42: /* loop end address middle */ voice->loop_end = (voice->loop_end & (0xff00ff << 1)) | (data << 9); break; case 0x43: /* stop address middle */ voice->stop = (voice->stop & (0xff00ff << 1)) | (data << 9); break; case 0x60: /* start address low */ voice->start = (voice->start & (0xffff00 << 1)) | (data << 1); break; case 0x61: /* loop start address low */ voice->loop_start = (voice->loop_start & (0xffff00 << 1)) | (data << 1); break; case 0x62: /* loop end address low */ voice->loop_end = (voice->loop_end & (0xffff00 << 1)) | (data << 1); break; case 0x63: /* stop address low */ voice->stop = (voice->stop & (0xffff00 << 1)) | (data << 1); break; default: #ifdef _DEBUG logerror("YMZ280B: unknown register write %02X = %02X\n", chip->current_register, data); #endif break; } } /* upper registers are special */ else { switch (chip->current_register) { /* DSP related (not implemented yet) */ case 0x80: // d0-2: DSP Rch, d3: enable Rch (0: yes, 1: no), d4-6: DSP Lch, d7: enable Lch (0: yes, 1: no) case 0x81: // d0: enable control of $82 (0: yes, 1: no) case 0x82: // DSP data /*logerror("YMZ280B: DSP register write %02X = %02X\n", chip->current_register, data);*/ break; case 0x84: /* ROM readback / RAM write (high) */ chip->rom_readback_addr &= 0xffff; chip->rom_readback_addr |= (data<<16); break; case 0x85: /* ROM readback / RAM write (med) */ chip->rom_readback_addr &= 0xff00ff; chip->rom_readback_addr |= (data<<8); break; case 0x86: /* ROM readback / RAM write (low) */ chip->rom_readback_addr &= 0xffff00; chip->rom_readback_addr |= data; break; case 0x87: /* RAM write */ /*if (!chip->ext_ram_write.isnull()) chip->ext_ram_write(chip->rom_readback_addr, data); else logerror("YMZ280B attempted RAM write to %X\n", chip->rom_readback_addr);*/ chip->rom_readback_addr = (chip->rom_readback_addr + 1) & 0xffffff; break; case 0xfe: /* IRQ mask */ chip->irq_mask = data; update_irq_state(chip); break; case 0xff: /* IRQ enable, test, etc */ chip->irq_enable = (data & 0x10) >> 4; update_irq_state(chip); if (chip->keyon_enable && !(data & 0x80)) { for (i = 0; i < 8; i++) { chip->voice[i].playing = 0; /* if update_irq_state_timer is set, cancel it. */ chip->voice[i].irq_schedule = 0; } } else if (!chip->keyon_enable && (data & 0x80)) { for (i = 0; i < 8; i++) { if (chip->voice[i].keyon && chip->voice[i].looping) chip->voice[i].playing = 1; } } chip->keyon_enable = (data & 0x80) >> 7; break; default: #ifdef _DEBUG logerror("YMZ280B: unknown register write %02X = %02X\n", chip->current_register, data); #endif break; } } } /********************************************************************************************** compute_status -- determine the status bits ***********************************************************************************************/ static int compute_status(ymz280b_state *chip) { UINT8 result; /* ROM/RAM readback? */ if (chip->current_register == 0x86) { //result = chip->region_base[chip->rom_readback_addr]; result = ymz280b_read_memory(chip->region_base, chip->region_size, chip->rom_readback_addr); chip->rom_readback_addr = (chip->rom_readback_addr + 1) & 0xffffff; return result; } /* force an update */ //stream_update(chip->stream); result = chip->status_register; /* clear the IRQ state */ chip->status_register = 0; update_irq_state(chip); return result; } /********************************************************************************************** ymz280b_r/ymz280b_w -- handle external accesses ***********************************************************************************************/ //READ8_DEVICE_HANDLER( ymz280b_r ) UINT8 ymz280b_r(void *_chip, offs_t offset) { //ymz280b_state *chip = get_safe_token(device); ymz280b_state *chip = (ymz280b_state *) _chip; if ((offset & 1) == 0) { /* read from external memory */ UINT8 result; /*if (chip->ext_ram_read.isnull()) result = chip->ext_ram_read(chip->rom_readback_addr); else result = ymz280b_read_memory(chip->region_base, chip->region_size, chip->rom_readback_addr);*/ result = 0x00; chip->rom_readback_addr = (chip->rom_readback_addr + 1) & 0xffffff; return result; } else { return compute_status(chip); } } //WRITE8_DEVICE_HANDLER( ymz280b_w ) void ymz280b_w(void *_chip, offs_t offset, UINT8 data) { //ymz280b_state *chip = get_safe_token(device); ymz280b_state *chip = (ymz280b_state *) _chip; if ((offset & 1) == 0) chip->current_register = data; else { /* force an update */ //chip->stream->update(); write_to_register(chip, data); } } void ymz280b_write_rom(void *_chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { ymz280b_state *chip = (ymz280b_state *) _chip; if (chip->region_size != ROMSize) { chip->region_base = (UINT8*)realloc(chip->region_base, ROMSize); chip->region_size = ROMSize; memset(chip->region_base, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(chip->region_base + DataStart, ROMData, DataLength); return; } void ymz280b_set_mute_mask(void *_chip, UINT32 MuteMask) { ymz280b_state *chip = (ymz280b_state *) _chip; UINT8 CurChn; for (CurChn = 0; CurChn < 8; CurChn ++) chip->voice[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; }