1
0
Fork 0

Clean mame versions from 10 August 2017

Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/branches/mamesound@4031
This commit is contained in:
Sjoerd van der Berg 2017-08-12 09:11:40 +00:00
parent a6cae1a8f5
commit 6c796f4162
10 changed files with 7427 additions and 0 deletions

2572
src/hardware/mame/fmopl.cpp Normal file

File diff suppressed because it is too large Load diff

109
src/hardware/mame/fmopl.h Normal file
View file

@ -0,0 +1,109 @@
// license:GPL-2.0+
// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh
#ifndef MAME_SOUND_FMOPL_H
#define MAME_SOUND_FMOPL_H
#pragma once
#include <stdint.h>
/* --- select emulation chips --- */
#define BUILD_YM3812 (1)
#define BUILD_YM3526 (1)
#define BUILD_Y8950 (1)
/* select output bits size of output : 8 or 16 */
#define OPL_SAMPLE_BITS 16
typedef stream_sample_t OPLSAMPLE;
/*
#if (OPL_SAMPLE_BITS==16)
typedef int16_t OPLSAMPLE;
#endif
#if (OPL_SAMPLE_BITS==8)
typedef int8_t OPLSAMPLE;
#endif
*/
typedef void (*OPL_TIMERHANDLER)(device_t *device,int timer,const attotime &period);
typedef void (*OPL_IRQHANDLER)(device_t *device,int irq);
typedef void (*OPL_UPDATEHANDLER)(device_t *device,int min_interval_us);
typedef void (*OPL_PORTHANDLER_W)(device_t *device,unsigned char data);
typedef unsigned char (*OPL_PORTHANDLER_R)(device_t *device);
#if BUILD_YM3812
void *ym3812_init(device_t *device, uint32_t clock, uint32_t rate);
void ym3812_clock_changed(void *chip, uint32_t clock, uint32_t rate);
void ym3812_shutdown(void *chip);
void ym3812_reset_chip(void *chip);
int ym3812_write(void *chip, int a, int v);
unsigned char ym3812_read(void *chip, int a);
int ym3812_timer_over(void *chip, int c);
void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length);
void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device);
void ym3812_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device);
void ym3812_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device);
#endif /* BUILD_YM3812 */
#if BUILD_YM3526
/*
** Initialize YM3526 emulator(s).
**
** 'num' is the number of virtual YM3526's to allocate
** 'clock' is the chip clock in Hz
** 'rate' is sampling rate
*/
void *ym3526_init(device_t *device, uint32_t clock, uint32_t rate);
void ym3526_clock_changed(void *chip, uint32_t clock, uint32_t rate);
/* shutdown the YM3526 emulators*/
void ym3526_shutdown(void *chip);
void ym3526_reset_chip(void *chip);
int ym3526_write(void *chip, int a, int v);
unsigned char ym3526_read(void *chip, int a);
int ym3526_timer_over(void *chip, int c);
/*
** Generate samples for one of the YM3526's
**
** 'which' is the virtual YM3526 number
** '*buffer' is the output buffer pointer
** 'length' is the number of samples that should be generated
*/
void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length);
void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device);
void ym3526_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device);
void ym3526_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device);
#endif /* BUILD_YM3526 */
#if BUILD_Y8950
/* Y8950 port handlers */
void y8950_set_port_handler(void *chip, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, device_t *device);
void y8950_set_keyboard_handler(void *chip, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, device_t *device);
void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size );
void * y8950_init(device_t *device, uint32_t clock, uint32_t rate);
void y8950_shutdown(void *chip);
void y8950_reset_chip(void *chip);
int y8950_write(void *chip, int a, int v);
unsigned char y8950_read (void *chip, int a);
int y8950_timer_over(void *chip, int c);
void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length);
void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, device_t *device);
void y8950_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, device_t *device);
void y8950_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, device_t *device);
#endif /* BUILD_Y8950 */
#endif // MAME_SOUND_FMOPL_H

View file

@ -0,0 +1,458 @@
// license:BSD-3-Clause
// copyright-holders:Juergen Buchmueller, Manuel Abadia
/***************************************************************************
Philips SAA1099 Sound driver
By Juergen Buchmueller and Manuel Abadia
SAA1099 register layout:
========================
offs | 7654 3210 | description
-----+-----------+---------------------------
0x00 | ---- xxxx | Amplitude channel 0 (left)
0x00 | xxxx ---- | Amplitude channel 0 (right)
0x01 | ---- xxxx | Amplitude channel 1 (left)
0x01 | xxxx ---- | Amplitude channel 1 (right)
0x02 | ---- xxxx | Amplitude channel 2 (left)
0x02 | xxxx ---- | Amplitude channel 2 (right)
0x03 | ---- xxxx | Amplitude channel 3 (left)
0x03 | xxxx ---- | Amplitude channel 3 (right)
0x04 | ---- xxxx | Amplitude channel 4 (left)
0x04 | xxxx ---- | Amplitude channel 4 (right)
0x05 | ---- xxxx | Amplitude channel 5 (left)
0x05 | xxxx ---- | Amplitude channel 5 (right)
| |
0x08 | xxxx xxxx | Frequency channel 0
0x09 | xxxx xxxx | Frequency channel 1
0x0a | xxxx xxxx | Frequency channel 2
0x0b | xxxx xxxx | Frequency channel 3
0x0c | xxxx xxxx | Frequency channel 4
0x0d | xxxx xxxx | Frequency channel 5
| |
0x10 | ---- -xxx | Channel 0 octave select
0x10 | -xxx ---- | Channel 1 octave select
0x11 | ---- -xxx | Channel 2 octave select
0x11 | -xxx ---- | Channel 3 octave select
0x12 | ---- -xxx | Channel 4 octave select
0x12 | -xxx ---- | Channel 5 octave select
| |
0x14 | ---- ---x | Channel 0 frequency enable (0 = off, 1 = on)
0x14 | ---- --x- | Channel 1 frequency enable (0 = off, 1 = on)
0x14 | ---- -x-- | Channel 2 frequency enable (0 = off, 1 = on)
0x14 | ---- x--- | Channel 3 frequency enable (0 = off, 1 = on)
0x14 | ---x ---- | Channel 4 frequency enable (0 = off, 1 = on)
0x14 | --x- ---- | Channel 5 frequency enable (0 = off, 1 = on)
| |
0x15 | ---- ---x | Channel 0 noise enable (0 = off, 1 = on)
0x15 | ---- --x- | Channel 1 noise enable (0 = off, 1 = on)
0x15 | ---- -x-- | Channel 2 noise enable (0 = off, 1 = on)
0x15 | ---- x--- | Channel 3 noise enable (0 = off, 1 = on)
0x15 | ---x ---- | Channel 4 noise enable (0 = off, 1 = on)
0x15 | --x- ---- | Channel 5 noise enable (0 = off, 1 = on)
| |
0x16 | ---- --xx | Noise generator parameters 0
0x16 | --xx ---- | Noise generator parameters 1
| |
0x18 | --xx xxxx | Envelope generator 0 parameters
0x18 | x--- ---- | Envelope generator 0 control enable (0 = off, 1 = on)
0x19 | --xx xxxx | Envelope generator 1 parameters
0x19 | x--- ---- | Envelope generator 1 control enable (0 = off, 1 = on)
| |
0x1c | ---- ---x | All channels enable (0 = off, 1 = on)
0x1c | ---- --x- | Synch & Reset generators
Unspecified bits should be written as zero.
***************************************************************************/
#include "emu.h"
#include "saa1099.h"
#define LEFT 0x00
#define RIGHT 0x01
static constexpr int amplitude_lookup[16] = {
0*32767/16, 1*32767/16, 2*32767/16, 3*32767/16,
4*32767/16, 5*32767/16, 6*32767/16, 7*32767/16,
8*32767/16, 9*32767/16, 10*32767/16, 11*32767/16,
12*32767/16, 13*32767/16, 14*32767/16, 15*32767/16
};
static constexpr uint8_t envelope[8][64] = {
/* zero amplitude */
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
/* maximum amplitude */
{15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, },
/* single decay */
{15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
/* repetitive decay */
{15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 },
/* single triangular */
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
/* repetitive triangular */
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 },
/* single attack */
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
/* repetitive attack */
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 }
};
// device type definition
DEFINE_DEVICE_TYPE(SAA1099, saa1099_device, "saa1099", "Philips SAA1099")
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// saa1099_device - constructor
//-------------------------------------------------
saa1099_device::saa1099_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, SAA1099, tag, owner, clock)
, device_sound_interface(mconfig, *this)
, m_stream(nullptr)
, m_noise_params{ 0, 0 }
, m_env_enable{ 0, 0 }
, m_env_reverse_right{ 0, 0 }
, m_env_mode{ 0, 0 }
, m_env_bits{ 0, 0 }
, m_env_clock{ 0, 0 }
, m_env_step{ 0, 0 }
, m_all_ch_enable(0)
, m_sync_state(0)
, m_selected_reg(0)
, m_sample_rate(0.0)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void saa1099_device::device_start()
{
/* copy global parameters */
m_master_clock = clock();
m_sample_rate = clock() / 256;
/* for each chip allocate one stream */
m_stream = stream_alloc(0, 2, m_sample_rate);
save_item(NAME(m_noise_params));
save_item(NAME(m_env_enable));
save_item(NAME(m_env_reverse_right));
save_item(NAME(m_env_mode));
save_item(NAME(m_env_bits));
save_item(NAME(m_env_clock));
save_item(NAME(m_env_step));
save_item(NAME(m_all_ch_enable));
save_item(NAME(m_sync_state));
save_item(NAME(m_selected_reg));
for (int i = 0; i < 6; i++)
{
save_item(NAME(m_channels[i].frequency), i);
save_item(NAME(m_channels[i].freq_enable), i);
save_item(NAME(m_channels[i].noise_enable), i);
save_item(NAME(m_channels[i].octave), i);
save_item(NAME(m_channels[i].amplitude), i);
save_item(NAME(m_channels[i].envelope), i);
save_item(NAME(m_channels[i].counter), i);
save_item(NAME(m_channels[i].freq), i);
save_item(NAME(m_channels[i].level), i);
}
for (int i = 0; i < 2; i++)
{
save_item(NAME(m_noise[i].counter), i);
save_item(NAME(m_noise[i].freq), i);
save_item(NAME(m_noise[i].level), i);
}
}
//-------------------------------------------------
// sound_stream_update - handle a stream update
//-------------------------------------------------
void saa1099_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
int j, ch;
/* if the channels are disabled we're done */
if (!m_all_ch_enable)
{
/* init output data */
memset(outputs[LEFT],0,samples*sizeof(*outputs[LEFT]));
memset(outputs[RIGHT],0,samples*sizeof(*outputs[RIGHT]));
return;
}
for (ch = 0; ch < 2; ch++)
{
switch (m_noise_params[ch])
{
case 0: m_noise[ch].freq = m_master_clock/256.0 * 2; break;
case 1: m_noise[ch].freq = m_master_clock/512.0 * 2; break;
case 2: m_noise[ch].freq = m_master_clock/1024.0 * 2; break;
case 3: m_noise[ch].freq = m_channels[ch * 3].freq; break; // todo: this case will be m_master_clock/[ch*3's octave divisor, 0 is = 256*2, higher numbers are higher] * 2 if the tone generator phase reset bit (0x1c bit 1) is set.
}
}
/* fill all data needed */
for( j = 0; j < samples; j++ )
{
int output_l = 0, output_r = 0;
/* for each channel */
for (ch = 0; ch < 6; ch++)
{
if (m_channels[ch].freq == 0.0)
m_channels[ch].freq = (double)((2 * m_master_clock / 512) << m_channels[ch].octave) /
(511.0 - (double)m_channels[ch].frequency);
/* check the actual position in the square wave */
m_channels[ch].counter -= m_channels[ch].freq;
while (m_channels[ch].counter < 0)
{
/* calculate new frequency now after the half wave is updated */
m_channels[ch].freq = (double)((2 * m_master_clock / 512) << m_channels[ch].octave) /
(511.0 - (double)m_channels[ch].frequency);
m_channels[ch].counter += m_sample_rate;
m_channels[ch].level ^= 1;
/* eventually clock the envelope counters */
if (ch == 1 && m_env_clock[0] == 0)
envelope_w(0);
if (ch == 4 && m_env_clock[1] == 0)
envelope_w(1);
}
// if the noise is enabled
if (m_channels[ch].noise_enable)
{
// if the noise level is high (noise 0: chan 0-2, noise 1: chan 3-5)
if (m_noise[ch/3].level & 1)
{
// subtract to avoid overflows, also use only half amplitude
output_l -= m_channels[ch].amplitude[ LEFT] * m_channels[ch].envelope[ LEFT] / 16 / 2;
output_r -= m_channels[ch].amplitude[RIGHT] * m_channels[ch].envelope[RIGHT] / 16 / 2;
}
}
// if the square wave is enabled
if (m_channels[ch].freq_enable)
{
// if the channel level is high
if (m_channels[ch].level & 1)
{
output_l += m_channels[ch].amplitude[ LEFT] * m_channels[ch].envelope[ LEFT] / 16;
output_r += m_channels[ch].amplitude[RIGHT] * m_channels[ch].envelope[RIGHT] / 16;
}
}
}
for (ch = 0; ch < 2; ch++)
{
/* update the state of the noise generator
* polynomial is x^18 + x^11 + x (i.e. 0x20400) and is a plain XOR, initial state is probably all 1s
* see http://www.vogons.org/viewtopic.php?f=9&t=51695 */
m_noise[ch].counter -= m_noise[ch].freq;
while (m_noise[ch].counter < 0)
{
m_noise[ch].counter += m_sample_rate;
if( ((m_noise[ch].level & 0x20000) == 0) != ((m_noise[ch].level & 0x0400) == 0) )
m_noise[ch].level = (m_noise[ch].level << 1) | 1;
else
m_noise[ch].level <<= 1;
}
}
/* write sound data to the buffer */
outputs[LEFT][j] = output_l / 6;
outputs[RIGHT][j] = output_r / 6;
}
}
void saa1099_device::envelope_w(int ch)
{
if (m_env_enable[ch])
{
int step, mode, mask;
mode = m_env_mode[ch];
/* step from 0..63 and then loop in steps 32..63 */
step = m_env_step[ch] =
((m_env_step[ch] + 1) & 0x3f) | (m_env_step[ch] & 0x20);
mask = 15;
if (m_env_bits[ch])
mask &= ~1; /* 3 bit resolution, mask LSB */
m_channels[ch*3+0].envelope[ LEFT] =
m_channels[ch*3+1].envelope[ LEFT] =
m_channels[ch*3+2].envelope[ LEFT] = envelope[mode][step] & mask;
if (m_env_reverse_right[ch] & 0x01)
{
m_channels[ch*3+0].envelope[RIGHT] =
m_channels[ch*3+1].envelope[RIGHT] =
m_channels[ch*3+2].envelope[RIGHT] = (15 - envelope[mode][step]) & mask;
}
else
{
m_channels[ch*3+0].envelope[RIGHT] =
m_channels[ch*3+1].envelope[RIGHT] =
m_channels[ch*3+2].envelope[RIGHT] = envelope[mode][step] & mask;
}
}
else
{
/* envelope mode off, set all envelope factors to 16 */
m_channels[ch*3+0].envelope[ LEFT] =
m_channels[ch*3+1].envelope[ LEFT] =
m_channels[ch*3+2].envelope[ LEFT] =
m_channels[ch*3+0].envelope[RIGHT] =
m_channels[ch*3+1].envelope[RIGHT] =
m_channels[ch*3+2].envelope[RIGHT] = 16;
}
}
WRITE8_MEMBER( saa1099_device::control_w )
{
if ((data & 0xff) > 0x1c)
{
/* Error! */
logerror("%s: (SAA1099 '%s') Unknown register selected\n", machine().describe_context(), tag());
}
m_selected_reg = data & 0x1f;
if (m_selected_reg == 0x18 || m_selected_reg == 0x19)
{
/* clock the envelope channels */
if (m_env_clock[0])
envelope_w(0);
if (m_env_clock[1])
envelope_w(1);
}
}
WRITE8_MEMBER( saa1099_device::data_w )
{
int reg = m_selected_reg;
int ch;
/* first update the stream to this point in time */
m_stream->update();
switch (reg)
{
/* channel i amplitude */
case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
ch = reg & 7;
m_channels[ch].amplitude[LEFT] = amplitude_lookup[data & 0x0f];
m_channels[ch].amplitude[RIGHT] = amplitude_lookup[(data >> 4) & 0x0f];
break;
/* channel i frequency */
case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d:
ch = reg & 7;
m_channels[ch].frequency = data & 0xff;
break;
/* channel i octave */
case 0x10: case 0x11: case 0x12:
ch = (reg - 0x10) << 1;
m_channels[ch + 0].octave = data & 0x07;
m_channels[ch + 1].octave = (data >> 4) & 0x07;
break;
/* channel i frequency enable */
case 0x14:
m_channels[0].freq_enable = data & 0x01;
m_channels[1].freq_enable = data & 0x02;
m_channels[2].freq_enable = data & 0x04;
m_channels[3].freq_enable = data & 0x08;
m_channels[4].freq_enable = data & 0x10;
m_channels[5].freq_enable = data & 0x20;
break;
/* channel i noise enable */
case 0x15:
m_channels[0].noise_enable = data & 0x01;
m_channels[1].noise_enable = data & 0x02;
m_channels[2].noise_enable = data & 0x04;
m_channels[3].noise_enable = data & 0x08;
m_channels[4].noise_enable = data & 0x10;
m_channels[5].noise_enable = data & 0x20;
break;
/* noise generators parameters */
case 0x16:
m_noise_params[0] = data & 0x03;
m_noise_params[1] = (data >> 4) & 0x03;
break;
/* envelope generators parameters */
case 0x18: case 0x19:
ch = reg - 0x18;
m_env_reverse_right[ch] = data & 0x01;
m_env_mode[ch] = (data >> 1) & 0x07;
m_env_bits[ch] = data & 0x10;
m_env_clock[ch] = data & 0x20;
m_env_enable[ch] = data & 0x80;
/* reset the envelope */
m_env_step[ch] = 0;
break;
/* channels enable & reset generators */
case 0x1c:
m_all_ch_enable = data & 0x01;
m_sync_state = data & 0x02;
if (data & 0x02)
{
int i;
/* Synch & Reset generators */
logerror("%s: (SAA1099 '%s') -reg 0x1c- Chip reset\n", machine().describe_context(), tag());
for (i = 0; i < 6; i++)
{
m_channels[i].level = 0;
m_channels[i].counter = 0.0;
}
}
break;
default: /* Error! */
if (data != 0)
logerror("%s: (SAA1099 '%s') Unknown operation (reg:%02x, data:%02x)\n", machine().describe_context(), tag(), reg, data);
}
}
WRITE8_MEMBER(saa1099_device::write)
{
if (offset & 1)
control_w(space, 0, data);
else
data_w(space, 0, data);
}

View file

@ -0,0 +1,96 @@
// license:BSD-3-Clause
// copyright-holders:Juergen Buchmueller, Manuel Abadia
/**********************************************
Philips SAA1099 Sound driver
**********************************************/
#ifndef MAME_SOUND_SAA1099_H
#define MAME_SOUND_SAA1099_H
#pragma once
//**************************************************************************
// INTERFACE CONFIGURATION MACROS
//**************************************************************************
#define MCFG_SAA1099_ADD(_tag, _clock) \
MCFG_DEVICE_ADD(_tag, SAA1099, _clock)
#define MCFG_SAA1099_REPLACE(_tag, _clock) \
MCFG_DEVICE_REPLACE(_tag, SAA1099, _clock)
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> saa1099_device
class saa1099_device : public device_t,
public device_sound_interface
{
public:
saa1099_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_WRITE8_MEMBER( control_w );
DECLARE_WRITE8_MEMBER( data_w );
DECLARE_WRITE8_MEMBER( write );
protected:
// device-level overrides
virtual void device_start() override;
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;
private:
struct saa1099_channel
{
saa1099_channel() : amplitude{ 0, 0 }, envelope{ 0, 0 } { }
int frequency = 0; /* frequency (0x00..0xff) */
int freq_enable = 0; /* frequency enable */
int noise_enable = 0; /* noise enable */
int octave = 0; /* octave (0x00..0x07) */
int amplitude[2]; /* amplitude (0x00..0x0f) */
int envelope[2]; /* envelope (0x00..0x0f or 0x10 == off) */
/* vars to simulate the square wave */
double counter = 0.0;
double freq = 0.0;
int level = 0;
};
struct saa1099_noise
{
saa1099_noise() { }
/* vars to simulate the noise generator output */
double counter = 0.0;
double freq = 0.0;
uint32_t level = 0xFFFFFFFF; /* noise polynomial shifter */
};
void envelope_w(int ch);
sound_stream *m_stream; /* our stream */
int m_noise_params[2]; /* noise generators parameters */
int m_env_enable[2]; /* envelope generators enable */
int m_env_reverse_right[2]; /* envelope reversed for right channel */
int m_env_mode[2]; /* envelope generators mode */
int m_env_bits[2]; /* non zero = 3 bits resolution */
int m_env_clock[2]; /* envelope clock mode (non-zero external) */
int m_env_step[2]; /* current envelope step */
int m_all_ch_enable; /* all channels enable */
int m_sync_state; /* sync all channels */
int m_selected_reg; /* selected register */
saa1099_channel m_channels[6]; /* channels */
saa1099_noise m_noise[2]; /* noise generators */
double m_sample_rate;
int m_master_clock;
};
DECLARE_DEVICE_TYPE(SAA1099, saa1099_device)
#endif // MAME_SOUND_SAA1099_H

View file

@ -0,0 +1,474 @@
// license:BSD-3-Clause
// copyright-holders:Nicola Salmoria
/***************************************************************************
sn76496.c
by Nicola Salmoria
with contributions by others
Routines to emulate the:
Texas Instruments SN76489, SN76489A, SN76494/SN76496
( Also known as, or at least compatible with, the TMS9919 and SN94624.)
and the Sega 'PSG' used on the Master System, Game Gear, and Megadrive/Genesis
This chip is known as the Programmable Sound Generator, or PSG, and is a 4
channel sound generator, with three squarewave channels and a noise/arbitrary
duty cycle channel.
Noise emulation for all verified chips should be accurate:
** SN76489 uses a 15-bit shift register with taps on bits D and E, output on E,
XOR function.
It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle.
Its output is inverted.
** SN94624 is the same as SN76489 but lacks the /8 divider on its clock input.
** SN76489A uses a 15-bit shift register with taps on bits D and E, output on F,
XOR function.
It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle.
Its output is not inverted.
** SN76494 is the same as SN76489A but lacks the /8 divider on its clock input.
** SN76496 is identical in operation to the SN76489A, but the audio input on pin 9 is
documented.
All the TI-made PSG chips have an audio input line which is mixed with the 4 channels
of output. (It is undocumented and may not function properly on the sn76489, 76489a
and 76494; the sn76489a input is mentioned in datasheets for the tms5200)
All the TI-made PSG chips act as if the frequency was set to 0x400 if 0 is
written to the frequency register.
** Sega Master System III/MD/Genesis PSG uses a 16-bit shift register with taps
on bits C and F, output on F
It uses a 16-bit ring buffer for periodic noise/arbitrary duty cycle.
(whether it uses an XOR or XNOR needs to be verified, assumed XOR)
(whether output is inverted or not needs to be verified, assumed to be inverted)
** Sega Game Gear PSG is identical to the SMS3/MD/Genesis one except it has an
extra register for mapping which channels go to which speaker.
The register, connected to a z80 port, means:
for bits 7 6 5 4 3 2 1 0
L3 L2 L1 L0 R3 R2 R1 R0
Noise is an XOR function, and audio output is negated before being output.
All the Sega-made PSG chips act as if the frequency was set to 0 if 0 is written
to the frequency register.
** NCR7496 (as used on the Tandy 1000) is similar to the SN76489 but with a
different noise LFSR patttern: taps on bits A and E, output on E
It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle.
(all this chip's info needs to be verified)
28/03/2005 : Sebastien Chevalier
Update th SN76496Write func, according to SN76489 doc found on SMSPower.
- On write with 0x80 set to 0, when LastRegister is other then TONE,
the function is similar than update with 0x80 set to 1
23/04/2007 : Lord Nightmare
Major update, implement all three different noise generation algorithms and a
set_variant call to discern among them.
28/04/2009 : Lord Nightmare
Add READY line readback; cleaned up struct a bit. Cleaned up comments.
Add more TODOs. Fixed some unsaved savestate related stuff.
04/11/2009 : Lord Nightmare
Changed the way that the invert works (it now selects between XOR and XNOR
for the taps), and added R->OldNoise to simulate the extra 0 that is always
output before the noise LFSR contents are after an LFSR reset.
This fixes SN76489/A to match chips. Added SN94624.
14/11/2009 : Lord Nightmare
Removed STEP mess, vastly simplifying the code. Made output bipolar rather
than always above the 0 line, but disabled that code due to pending issues.
16/11/2009 : Lord Nightmare
Fix screeching in regulus: When summing together four equal channels, the
size of the max amplitude per channel should be 1/4 of the max range, not
1/3. Added NCR7496.
18/11/2009 : Lord Nightmare
Modify Init functions to support negating the audio output. The gamegear
psg does this. Change gamegear and sega psgs to use XOR rather than XNOR
based on testing. Got rid of R->OldNoise and fixed taps accordingly.
Added stereo support for game gear.
15/01/2010 : Lord Nightmare
Fix an issue with SN76489 and SN76489A having the wrong periodic noise periods.
Note that properly emulating the noise cycle bit timing accurately may require
extensive rewriting.
24/01/2010: Lord Nightmare
Implement periodic noise as forcing one of the XNOR or XOR taps to 1 or 0 respectively.
Thanks to PlgDavid for providing samples which helped immensely here.
Added true clock divider emulation, so sn94624 and sn76494 run 8x faster than
the others, as in real life.
15/02/2010: Lord Nightmare & Michael Zapf (additional testing by PlgDavid)
Fix noise period when set to mirror channel 3 and channel 3 period is set to 0 (tested on hardware for noise, wave needs tests) - MZ
Fix phase of noise on sn94624 and sn76489; all chips use a standard XOR, the only inversion is the output itself - LN, Plgdavid
Thanks to PlgDavid and Michael Zapf for providing samples which helped immensely here.
23/02/2011: Lord Nightmare & Enik
Made it so the Sega PSG chips have a frequency of 0 if 0 is written to the
frequency register, while the others have 0x400 as before. Should fix a bug
or two on sega games, particularly Vigilante on Sega Master System. Verified
on SMS hardware.
27/06/2012: Michael Zapf
Converted to modern device, legacy devices were gradually removed afterwards.
16/09/2015: Lord Nightmare
Fix PSG chips to have volume reg inited on reset to 0x0 based on tests by
ValleyBell. Made Sega PSG chips start up with register 0x3 selected (volume
for channel 2) based on hardware tests by Nemesis.
TODO: * Implement the TMS9919 - any difference to sn94624?
* Implement the T6W28; has registers in a weird order, needs writes
to be 'sanitized' first. Also is stereo, similar to game gear.
* Test the NCR7496; Smspower says the whitenoise taps are A and E,
but this needs verification on real hardware.
* Factor out common code so that the SAA1099 can share some code.
***************************************************************************/
#include "emu.h"
#include "sn76496.h"
#define MAX_OUTPUT 0x7fff
sn76496_base_device::sn76496_base_device(
const machine_config &mconfig,
device_type type,
const char *tag,
int feedbackmask,
int noisetap1,
int noisetap2,
bool negate,
bool stereo,
int clockdivider,
bool sega,
device_t *owner,
uint32_t clock)
: device_t(mconfig, type, tag, owner, clock)
, device_sound_interface(mconfig, *this)
, m_ready_handler(*this)
, m_feedback_mask(feedbackmask)
, m_whitenoise_tap1(noisetap1)
, m_whitenoise_tap2(noisetap2)
, m_negate(negate)
, m_stereo(stereo)
, m_clock_divider(clockdivider)
, m_sega_style_psg(sega)
{
}
sn76496_device::sn76496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: sn76496_base_device(mconfig, SN76496, tag, 0x10000, 0x04, 0x08, false, false, 8, true, owner, clock)
{
}
u8106_device::u8106_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: sn76496_base_device(mconfig, U8106, tag, 0x4000, 0x01, 0x02, true, false, 8, true, owner, clock)
{
}
y2404_device::y2404_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: sn76496_base_device(mconfig, Y2404, tag, 0x10000, 0x04, 0x08, false, false, 8, true, owner, clock)
{
}
sn76489_device::sn76489_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: sn76496_base_device(mconfig, SN76489, tag, 0x4000, 0x01, 0x02, true, false, 8, true, owner, clock)
{
}
sn76489a_device::sn76489a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: sn76496_base_device(mconfig, SN76489A, tag, 0x10000, 0x04, 0x08, false, false, 8, true, owner, clock)
{
}
sn76494_device::sn76494_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: sn76496_base_device(mconfig, SN76494, tag, 0x10000, 0x04, 0x08, false, false, 1, true, owner, clock)
{
}
sn94624_device::sn94624_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: sn76496_base_device(mconfig, SN94624, tag, 0x4000, 0x01, 0x02, true, false, 1, true, owner, clock)
{
}
ncr7496_device::ncr7496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: sn76496_base_device(mconfig, NCR7496, tag, 0x8000, 0x02, 0x20, false, false, 8, true, owner, clock)
{
}
gamegear_device::gamegear_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: sn76496_base_device(mconfig, GAMEGEAR, tag, 0x8000, 0x01, 0x08, true, true, 8, false, owner, clock)
{
}
segapsg_device::segapsg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: sn76496_base_device(mconfig, SEGAPSG, tag, 0x8000, 0x01, 0x08, true, false, 8, false, owner, clock)
{
}
void sn76496_base_device::device_start()
{
int sample_rate = clock()/2;
int i;
double out;
int gain;
m_ready_handler.resolve_safe();
m_sound = machine().sound().stream_alloc(*this, 0, (m_stereo? 2:1), sample_rate);
for (i = 0; i < 4; i++) m_volume[i] = 0;
m_last_register = m_sega_style_psg?3:0; // Sega VDP PSG defaults to selected period reg for 2nd channel
for (i = 0; i < 8; i+=2)
{
m_register[i] = 0;
m_register[i + 1] = 0x0; // volume = 0x0 (max volume) on reset; this needs testing on chips other than SN76489A and Sega VDP PSG
}
for (i = 0; i < 4; i++)
{
m_output[i] = 0;
m_period[i] = 0;
m_count[i] = 0;
}
m_RNG = m_feedback_mask;
m_output[3] = m_RNG & 1;
m_cycles_to_ready = 1; // assume ready is not active immediately on init. is this correct?
m_stereo_mask = 0xFF; // all channels enabled
m_current_clock = m_clock_divider-1;
// set gain
gain = 0;
gain &= 0xff;
// increase max output basing on gain (0.2 dB per step)
out = MAX_OUTPUT / 4; // four channels, each gets 1/4 of the total range
while (gain-- > 0)
out *= 1.023292992; // = (10 ^ (0.2/20))
// build volume table (2dB per step)
for (i = 0; i < 15; i++)
{
// limit volume to avoid clipping
if (out > MAX_OUTPUT / 4) m_vol_table[i] = MAX_OUTPUT / 4;
else m_vol_table[i] = out;
out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */
}
m_vol_table[15] = 0;
m_ready_state = true;
register_for_save_states();
}
WRITE8_MEMBER( sn76496_base_device::stereo_w )
{
m_sound->update();
if (m_stereo) m_stereo_mask = data;
else fatalerror("sn76496_base_device: Call to stereo write with mono chip!\n");
}
void sn76496_base_device::write(uint8_t data)
{
int n, r, c;
// update the output buffer before changing the registers
m_sound->update();
// set number of cycles until READY is active; this is always one
// 'sample', i.e. it equals the clock divider exactly
m_cycles_to_ready = 1;
if (data & 0x80)
{
r = (data & 0x70) >> 4;
m_last_register = r;
m_register[r] = (m_register[r] & 0x3f0) | (data & 0x0f);
}
else
{
r = m_last_register;
}
c = r >> 1;
switch (r)
{
case 0: // tone 0: frequency
case 2: // tone 1: frequency
case 4: // tone 2: frequency
if ((data & 0x80) == 0) m_register[r] = (m_register[r] & 0x0f) | ((data & 0x3f) << 4);
if ((m_register[r] != 0) || (!m_sega_style_psg)) m_period[c] = m_register[r];
else m_period[c] = 0x400;
if (r == 4)
{
// update noise shift frequency
if ((m_register[6] & 0x03) == 0x03) m_period[3] = m_period[2]<<1;
}
break;
case 1: // tone 0: volume
case 3: // tone 1: volume
case 5: // tone 2: volume
case 7: // noise: volume
m_volume[c] = m_vol_table[data & 0x0f];
if ((data & 0x80) == 0) m_register[r] = (m_register[r] & 0x3f0) | (data & 0x0f);
break;
case 6: // noise: frequency, mode
{
if ((data & 0x80) == 0) logerror("sn76496_base_device: write to reg 6 with bit 7 clear; data was %03x, new write is %02x! report this to LN!\n", m_register[6], data);
if ((data & 0x80) == 0) m_register[r] = (m_register[r] & 0x3f0) | (data & 0x0f);
n = m_register[6];
// N/512,N/1024,N/2048,Tone #3 output
m_period[3] = ((n&3) == 3)? (m_period[2]<<1) : (1 << (5+(n&3)));
m_RNG = m_feedback_mask;
}
break;
}
}
WRITE8_MEMBER( sn76496_base_device::write )
{
write(data);
}
inline bool sn76496_base_device::in_noise_mode()
{
return ((m_register[6] & 4)!=0);
}
void sn76496_base_device::countdown_cycles()
{
if (m_cycles_to_ready > 0)
{
m_cycles_to_ready--;
if (m_ready_state==true) m_ready_handler(CLEAR_LINE);
m_ready_state = false;
}
else
{
if (m_ready_state==false) m_ready_handler(ASSERT_LINE);
m_ready_state = true;
}
}
void sn76496_base_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
int i;
stream_sample_t *lbuffer = outputs[0];
stream_sample_t *rbuffer = (m_stereo)? outputs[1] : nullptr;
int16_t out;
int16_t out2 = 0;
while (samples > 0)
{
// clock chip once
if (m_current_clock > 0) // not ready for new divided clock
{
m_current_clock--;
}
else // ready for new divided clock, make a new sample
{
m_current_clock = m_clock_divider-1;
// decrement Cycles to READY by one
countdown_cycles();
// handle channels 0,1,2
for (i = 0; i < 3; i++)
{
m_count[i]--;
if (m_count[i] <= 0)
{
m_output[i] ^= 1;
m_count[i] = m_period[i];
}
}
// handle channel 3
m_count[3]--;
if (m_count[3] <= 0)
{
// if noisemode is 1, both taps are enabled
// if noisemode is 0, the lower tap, whitenoisetap2, is held at 0
// The != was a bit-XOR (^) before
if (((m_RNG & m_whitenoise_tap1)!=0) != (((m_RNG & m_whitenoise_tap2)!=0) && in_noise_mode()))
{
m_RNG >>= 1;
m_RNG |= m_feedback_mask;
}
else
{
m_RNG >>= 1;
}
m_output[3] = m_RNG & 1;
m_count[3] = m_period[3];
}
}
if (m_stereo)
{
out = ((((m_stereo_mask & 0x10)!=0) && (m_output[0]!=0))? m_volume[0] : 0)
+ ((((m_stereo_mask & 0x20)!=0) && (m_output[1]!=0))? m_volume[1] : 0)
+ ((((m_stereo_mask & 0x40)!=0) && (m_output[2]!=0))? m_volume[2] : 0)
+ ((((m_stereo_mask & 0x80)!=0) && (m_output[3]!=0))? m_volume[3] : 0);
out2= ((((m_stereo_mask & 0x1)!=0) && (m_output[0]!=0))? m_volume[0] : 0)
+ ((((m_stereo_mask & 0x2)!=0) && (m_output[1]!=0))? m_volume[1] : 0)
+ ((((m_stereo_mask & 0x4)!=0) && (m_output[2]!=0))? m_volume[2] : 0)
+ ((((m_stereo_mask & 0x8)!=0) && (m_output[3]!=0))? m_volume[3] : 0);
}
else
{
out= ((m_output[0]!=0)? m_volume[0]:0)
+((m_output[1]!=0)? m_volume[1]:0)
+((m_output[2]!=0)? m_volume[2]:0)
+((m_output[3]!=0)? m_volume[3]:0);
}
if (m_negate) { out = -out; out2 = -out2; }
*(lbuffer++) = out;
if (m_stereo) *(rbuffer++) = out2;
samples--;
}
}
void sn76496_base_device::register_for_save_states()
{
save_item(NAME(m_vol_table));
save_item(NAME(m_register));
save_item(NAME(m_last_register));
save_item(NAME(m_volume));
save_item(NAME(m_RNG));
// save_item(NAME(m_clock_divider));
save_item(NAME(m_current_clock));
// save_item(NAME(m_feedback_mask));
// save_item(NAME(m_whitenoise_tap1));
// save_item(NAME(m_whitenoise_tap2));
// save_item(NAME(m_negate));
// save_item(NAME(m_stereo));
save_item(NAME(m_stereo_mask));
save_item(NAME(m_period));
save_item(NAME(m_count));
save_item(NAME(m_output));
save_item(NAME(m_cycles_to_ready));
// save_item(NAME(m_sega_style_psg));
}
DEFINE_DEVICE_TYPE(SN76496, sn76496_device, "sn76496", "SN76496")
DEFINE_DEVICE_TYPE(U8106, u8106_device, "u8106", "U8106")
DEFINE_DEVICE_TYPE(Y2404, y2404_device, "y2404", "Y2404")
DEFINE_DEVICE_TYPE(SN76489, sn76489_device, "sn76489", "SN76489")
DEFINE_DEVICE_TYPE(SN76489A, sn76489a_device, "sn76489a", "SN76489A")
DEFINE_DEVICE_TYPE(SN76494, sn76494_device, "sn76494", "SN76494")
DEFINE_DEVICE_TYPE(SN94624, sn94624_device, "sn94624", "SN94624")
DEFINE_DEVICE_TYPE(NCR7496, ncr7496_device, "ncr7496", "NCR7496")
DEFINE_DEVICE_TYPE(GAMEGEAR, gamegear_device, "gamegear_psg", "Game Gear PSG")
DEFINE_DEVICE_TYPE(SEGAPSG, segapsg_device, "segapsg", "Sega VDP PSG")

155
src/hardware/mame/sn76496.h Normal file
View file

@ -0,0 +1,155 @@
// license:BSD-3-Clause
// copyright-holders:Nicola Salmoria
#ifndef MAME_SOUND_SN76496_H
#define MAME_SOUND_SN76496_H
#pragma once
DECLARE_DEVICE_TYPE(SN76496, sn76496_device)
DECLARE_DEVICE_TYPE(U8106, u8106_device)
DECLARE_DEVICE_TYPE(Y2404, y2404_device)
DECLARE_DEVICE_TYPE(SN76489, sn76489_device)
DECLARE_DEVICE_TYPE(SN76489A, sn76489a_device)
DECLARE_DEVICE_TYPE(SN76494, sn76494_device)
DECLARE_DEVICE_TYPE(SN94624, sn94624_device)
DECLARE_DEVICE_TYPE(NCR7496, ncr7496_device)
DECLARE_DEVICE_TYPE(GAMEGEAR, gamegear_device)
DECLARE_DEVICE_TYPE(SEGAPSG, segapsg_device)
#define MCFG_SN76496_READY_HANDLER(cb) \
devcb = &sn76496_base_device::set_ready_handler(*device, (DEVCB_##cb));
class sn76496_base_device : public device_t, public device_sound_interface
{
public:
// static configuration helpers
template <class Object> static devcb_base &set_ready_handler(device_t &device, Object &&cb) { return downcast<sn76496_base_device &>(device).m_ready_handler.set_callback(std::forward<Object>(cb)); }
DECLARE_WRITE8_MEMBER( stereo_w );
void write(uint8_t data);
DECLARE_WRITE8_MEMBER( write );
DECLARE_READ_LINE_MEMBER( ready_r ) { return m_ready_state ? 1 : 0; }
protected:
sn76496_base_device(
const machine_config &mconfig,
device_type type,
const char *tag,
int feedbackmask,
int noisetap1,
int noisetap2,
bool negate,
bool stereo,
int clockdivider,
bool sega,
device_t *owner,
uint32_t clock);
virtual void device_start() override;
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;
private:
inline bool in_noise_mode();
void register_for_save_states();
void countdown_cycles();
bool m_ready_state;
devcb_write_line m_ready_handler;
sound_stream* m_sound;
const int32_t m_feedback_mask; // mask for feedback
const int32_t m_whitenoise_tap1; // mask for white noise tap 1 (higher one, usually bit 14)
const int32_t m_whitenoise_tap2; // mask for white noise tap 2 (lower one, usually bit 13)
const bool m_negate; // output negate flag
const bool m_stereo; // whether we're dealing with stereo or not
const int32_t m_clock_divider; // clock divider
const bool m_sega_style_psg; // flag for if frequency zero acts as if it is one more than max (0x3ff+1) or if it acts like 0; AND if the initial register is pointing to 0x3 instead of 0x0 AND if the volume reg is preloaded with 0xF instead of 0x0
int32_t m_vol_table[16]; // volume table (for 4-bit to db conversion)
int32_t m_register[8]; // registers
int32_t m_last_register; // last register written
int32_t m_volume[4]; // db volume of voice 0-2 and noise
uint32_t m_RNG; // noise generator LFSR
int32_t m_current_clock;
int32_t m_stereo_mask; // the stereo output mask
int32_t m_period[4]; // Length of 1/2 of waveform
int32_t m_count[4]; // Position within the waveform
int32_t m_output[4]; // 1-bit output of each channel, pre-volume
int32_t m_cycles_to_ready; // number of cycles until the READY line goes active
};
// SN76496: Whitenoise verified, phase verified, periodic verified (by Michael Zapf)
class sn76496_device : public sn76496_base_device
{
public:
sn76496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
// U8106 not verified yet. todo: verify; (a custom marked sn76489? only used on mr. do and maybe other universal games)
class u8106_device : public sn76496_base_device
{
public:
u8106_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
// Y2404 not verified yet. todo: verify; (don't be fooled by the Y, it's a TI chip, not Yamaha)
class y2404_device : public sn76496_base_device
{
public:
y2404_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
// SN76489 not verified yet. todo: verify;
class sn76489_device : public sn76496_base_device
{
public:
sn76489_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
// SN76489A: whitenoise verified, phase verified, periodic verified (by plgdavid)
class sn76489a_device : public sn76496_base_device
{
public:
sn76489a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
// SN76494 not verified, (according to datasheet: same as sn76489a but without the /8 divider)
class sn76494_device : public sn76496_base_device
{
public:
sn76494_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
// SN94624 whitenoise verified, phase verified, period verified; verified by PlgDavid
class sn94624_device : public sn76496_base_device
{
public:
sn94624_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
// NCR7496 not verified; info from smspower wiki
class ncr7496_device : public sn76496_base_device
{
public:
ncr7496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
// Verified by Justin Kerk
class gamegear_device : public sn76496_base_device
{
public:
gamegear_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
// todo: verify; from smspower wiki, assumed to have same invert as gamegear
class segapsg_device : public sn76496_base_device
{
public:
segapsg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
#endif // MAME_SOUND_SN76496_H

View file

@ -0,0 +1,650 @@
// license:GPL-2.0+
// copyright-holders:Jarek Burczynski
/*
**
** File: ymdeltat.c
**
** YAMAHA DELTA-T adpcm sound emulation subroutine
** used by fmopl.c (Y8950) and fm.c (YM2608 and YM2610/B)
**
** Base program is YM2610 emulator by Hiromitsu Shioya.
** Written by Tatsuyuki Satoh
** Improvements by Jarek Burczynski (bujar at mame dot net)
**
**
** History:
**
** 03-08-2003 Jarek Burczynski:
** - fixed BRDY flag implementation.
**
** 24-07-2003 Jarek Burczynski, Frits Hilderink:
** - fixed delault value for control2 in YM_DELTAT_ADPCM_Reset
**
** 22-07-2003 Jarek Burczynski, Frits Hilderink:
** - fixed external memory support
**
** 15-06-2003 Jarek Burczynski:
** - implemented CPU -> AUDIO ADPCM synthesis (via writes to the ADPCM data reg $08)
** - implemented support for the Limit address register
** - supported two bits from the control register 2 ($01): RAM TYPE (x1 bit/x8 bit), ROM/RAM
** - implemented external memory access (read/write) via the ADPCM data reg reads/writes
** Thanks go to Frits Hilderink for the example code.
**
** 14-06-2003 Jarek Burczynski:
** - various fixes to enable proper support for status register flags: BSRDY, PCM BSY, ZERO
** - modified EOS handling
**
** 05-04-2003 Jarek Burczynski:
** - implemented partial support for external/processor memory on sample replay
**
** 01-12-2002 Jarek Burczynski:
** - fixed first missing sound in gigandes thanks to previous fix (interpolator) by ElSemi
** - renamed/removed some YM_DELTAT struct fields
**
** 28-12-2001 Acho A. Tang
** - added EOS status report on ADPCM playback.
**
** 05-08-2001 Jarek Burczynski:
** - now_step is initialized with 0 at the start of play.
**
** 12-06-2001 Jarek Burczynski:
** - corrected end of sample bug in YM_DELTAT_ADPCM_CALC.
** Checked on real YM2610 chip - address register is 24 bits wide.
** Thanks go to Stefan Jokisch (stefan.jokisch@gmx.de) for tracking down the problem.
**
** TO DO:
** Check size of the address register on the other chips....
**
** Version 0.72
**
** sound chips that have this unit:
** YM2608 OPNA
** YM2610/B OPNB
** Y8950 MSX AUDIO
**
*/
#include "emu.h"
#include "ymdeltat.h"
#define YM_DELTAT_SHIFT (16)
#define YM_DELTAT_DELTA_MAX (24576)
#define YM_DELTAT_DELTA_MIN (127)
#define YM_DELTAT_DELTA_DEF (127)
#define YM_DELTAT_DECODE_RANGE 32768
#define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE))
#define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1)
/* Forecast to next Forecast (rate = *8) */
/* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */
static constexpr int32_t ym_deltat_decode_tableB1[16] = {
1, 3, 5, 7, 9, 11, 13, 15,
-1, -3, -5, -7, -9, -11, -13, -15,
};
/* delta to next delta (rate= *64) */
/* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */
static constexpr int32_t ym_deltat_decode_tableB2[16] = {
57, 57, 57, 57, 77, 102, 128, 153,
57, 57, 57, 57, 77, 102, 128, 153
};
#if 0
void YM_DELTAT::BRDY_callback()
{
logerror("BRDY_callback reached (flag set) !\n");
/* set BRDY bit in status register */
if(status_set_handler)
if(status_change_BRDY_bit)
(status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
}
#endif
uint8_t YM_DELTAT::ADPCM_Read()
{
uint8_t v = 0;
/* external memory read */
if ((portstate & 0xe0) == 0x20)
{
/* two dummy reads */
if (memread)
{
now_addr = start << 1;
memread--;
return 0;
}
if (now_addr != (end << 1))
{
v = memory[now_addr>>1];
/*logerror("YM Delta-T memory read $%08x, v=$%02x\n", now_addr >> 1, v);*/
now_addr += 2; /* two nibbles at a time */
/* reset BRDY bit in status register, which means we are reading the memory now */
if (status_reset_handler && status_change_BRDY_bit)
(status_reset_handler)(status_change_which_chip, status_change_BRDY_bit);
/* setup a timer that will callback us in 10 master clock cycles for Y8950
* in the callback set the BRDY flag to 1 , which means we have another data ready.
* For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work.
*/
/* set BRDY bit in status register */
if (status_set_handler && status_change_BRDY_bit)
(status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
}
else
{
/* set EOS bit in status register */
if (status_set_handler && status_change_EOS_bit)
(status_set_handler)(status_change_which_chip, status_change_EOS_bit);
}
}
return v;
}
/* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */
static constexpr uint8_t dram_rightshift[4]={3,0,0,0};
/* DELTA-T ADPCM write register */
void YM_DELTAT::ADPCM_Write(int r, int v)
{
if (r >= 0x10) return;
reg[r] = v; /* stock data */
switch (r)
{
case 0x00:
/*
START:
Accessing *external* memory is started when START bit (D7) is set to "1", so
you must set all conditions needed for recording/playback before starting.
If you access *CPU-managed* memory, recording/playback starts after
read/write of ADPCM data register $08.
REC:
0 = ADPCM synthesis (playback)
1 = ADPCM analysis (record)
MEMDATA:
0 = processor (*CPU-managed*) memory (means: using register $08)
1 = external memory (using start/end/limit registers to access memory: RAM or ROM)
SPOFF:
controls output pin that should disable the speaker while ADPCM analysis
RESET and REPEAT only work with external memory.
some examples:
value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning:
C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register
E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register
80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register
a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register
60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08
20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08
*/
/* handle emulation mode */
if (emulation_mode == EMULATION_MODE_YM2610)
{
v |= 0x20; /* YM2610 always uses external memory and doesn't even have memory flag bit. */
}
portstate = v & (0x80|0x40|0x20|0x10|0x01); /* start, rec, memory mode, repeat flag copy, reset(bit0) */
if (portstate & 0x80)/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */
{
/* set PCM BUSY bit */
PCM_BSY = 1;
/* start ADPCM */
now_step = 0;
acc = 0;
prev_acc = 0;
adpcml = 0;
adpcmd = YM_DELTAT_DELTA_DEF;
now_data = 0;
}
if (portstate & 0x20) /* do we access external memory? */
{
now_addr = start << 1;
memread = 2; /* two dummy reads needed before accesing external memory via register $08*/
/* if yes, then let's check if ADPCM memory is mapped and big enough */
if (!memory)
{
device->logerror("YM Delta-T ADPCM rom not mapped\n");
portstate = 0x00;
PCM_BSY = 0;
}
else
{
if (end >= memory_size) /* Check End in Range */
{
device->logerror("YM Delta-T ADPCM end out of range: $%08x\n", end);
end = memory_size - 1;
}
if (start >= memory_size) /* Check Start in Range */
{
device->logerror("YM Delta-T ADPCM start out of range: $%08x\n", start);
portstate = 0x00;
PCM_BSY = 0;
}
}
}
else /* we access CPU memory (ADPCM data register $08) so we only reset now_addr here */
{
now_addr = 0;
}
if (portstate & 0x01)
{
portstate = 0x00;
/* clear PCM BUSY bit (in status register) */
PCM_BSY = 0;
/* set BRDY flag */
if (status_set_handler && status_change_BRDY_bit)
(status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
}
break;
case 0x01: /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */
/* handle emulation mode */
if (emulation_mode == EMULATION_MODE_YM2610)
{
v |= 0x01; /* YM2610 always uses ROM as an external memory and doesn't tave ROM/RAM memory flag bit. */
}
pan = &output_pointer[(v >> 6) & 0x03];
if ((control2 & 3) != (v & 3))
{
/*0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */
if (DRAMportshift != dram_rightshift[v & 3])
{
DRAMportshift = dram_rightshift[v & 3];
/* final shift value depends on chip type and memory type selected:
8 for YM2610 (ROM only),
5 for ROM for Y8950 and YM2608,
5 for x8bit DRAMs for Y8950 and YM2608,
2 for x1bit DRAMs for Y8950 and YM2608.
*/
/* refresh addresses */
start = (reg[0x3] * 0x0100 | reg[0x2]) << (portshift - DRAMportshift);
end = (reg[0x5] * 0x0100 | reg[0x4]) << (portshift - DRAMportshift);
end += (1 << (portshift - DRAMportshift)) - 1;
limit = (reg[0xd]*0x0100 | reg[0xc]) << (portshift - DRAMportshift);
}
}
control2 = v;
break;
case 0x02: /* Start Address L */
case 0x03: /* Start Address H */
start = (reg[0x3] * 0x0100 | reg[0x2]) << (portshift - DRAMportshift);
/*logerror("DELTAT start: 02=%2x 03=%2x addr=%8x\n",reg[0x2], reg[0x3],start );*/
break;
case 0x04: /* Stop Address L */
case 0x05: /* Stop Address H */
end = (reg[0x5]*0x0100 | reg[0x4]) << (portshift - DRAMportshift);
end += (1 << (portshift - DRAMportshift)) - 1;
/*logerror("DELTAT end : 04=%2x 05=%2x addr=%8x\n",reg[0x4], reg[0x5],end );*/
break;
case 0x06: /* Prescale L (ADPCM and Record frq) */
case 0x07: /* Prescale H */
break;
case 0x08: /* ADPCM data */
/*
some examples:
value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning:
C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register
E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register
80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register
a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register
60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08
20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08
*/
/* external memory write */
if ((portstate & 0xe0) == 0x60)
{
if (memread)
{
now_addr = start << 1;
memread = 0;
}
/*logerror("YM Delta-T memory write $%08x, v=$%02x\n", now_addr >> 1, v);*/
if (now_addr != (end << 1))
{
memory[now_addr >> 1] = v;
now_addr += 2; /* two nybbles at a time */
/* reset BRDY bit in status register, which means we are processing the write */
if (status_reset_handler && status_change_BRDY_bit)
(status_reset_handler)(status_change_which_chip, status_change_BRDY_bit);
/* setup a timer that will callback us in 10 master clock cycles for Y8950
* in the callback set the BRDY flag to 1 , which means we have written the data.
* For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work.
*/
/* set BRDY bit in status register */
if (status_set_handler && status_change_BRDY_bit)
(status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
}
else
{
/* set EOS bit in status register */
if (status_set_handler && status_change_EOS_bit)
(status_set_handler)(status_change_which_chip, status_change_EOS_bit);
}
return;
}
/* ADPCM synthesis from CPU */
if ((portstate & 0xe0) == 0x80)
{
CPU_data = v;
/* Reset BRDY bit in status register, which means we are full of data */
if (status_reset_handler && status_change_BRDY_bit)
(status_reset_handler)(status_change_which_chip, status_change_BRDY_bit);
return;
}
break;
case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */
case 0x0a: /* DELTA-N H */
delta = (reg[0xa] * 0x0100 | reg[0x9]);
step = uint32_t(double(delta /* *(1<<(YM_DELTAT_SHIFT-16)) */) * freqbase);
/*logerror("DELTAT deltan:09=%2x 0a=%2x\n",reg[0x9], reg[0xa]);*/
break;
case 0x0b: /* Output level control (volume, linear) */
{
const int32_t oldvol = volume;
volume = (v & 0xff) * (output_range / 256) / YM_DELTAT_DECODE_RANGE;
/* v * ((1<<16)>>8) >> 15;
* thus: v * (1<<8) >> 15;
* thus: output_range must be (1 << (15+8)) at least
* v * ((1<<23)>>8) >> 15;
* v * (1<<15) >> 15;
*/
/*logerror("DELTAT vol = %2x\n",v&0xff);*/
if (oldvol != 0)
{
adpcml = int(double(adpcml) / double(oldvol) * double(volume));
}
}
break;
case 0x0c: /* Limit Address L */
case 0x0d: /* Limit Address H */
limit = (reg[0xd] * 0x0100 | reg[0xc]) << (portshift - DRAMportshift);
/*logerror("DELTAT limit: 0c=%2x 0d=%2x addr=%8x\n",reg[0xc], reg[0xd],limit );*/
break;
}
}
void YM_DELTAT::ADPCM_Reset(int panidx, int mode, device_t *dev)
{
device = dev;
now_addr = 0;
now_step = 0;
step = 0;
start = 0;
end = 0;
limit = ~0; /* this way YM2610 and Y8950 (both of which don't have limit address reg) will still work */
volume = 0;
pan = &output_pointer[panidx];
acc = 0;
prev_acc = 0;
adpcmd = 127;
adpcml = 0;
emulation_mode = uint8_t(mode);
portstate = (emulation_mode == EMULATION_MODE_YM2610) ? 0x20 : 0;
control2 = (emulation_mode == EMULATION_MODE_YM2610) ? 0x01 : 0; /* default setting depends on the emulation mode. MSX demo called "facdemo_4" doesn't setup control2 register at all and still works */
DRAMportshift = dram_rightshift[control2 & 3];
/* The flag mask register disables the BRDY after the reset, however
** as soon as the mask is enabled the flag needs to be set. */
/* set BRDY bit in status register */
if (status_set_handler && status_change_BRDY_bit)
(status_set_handler)(status_change_which_chip, status_change_BRDY_bit);
}
void YM_DELTAT::postload(uint8_t *regs)
{
/* to keep adpcml */
volume = 0;
/* update */
for (int r = 1; r < 16; r++)
ADPCM_Write(r, regs[r]);
reg[0] = regs[0];
/* current rom data */
if (memory)
now_data = *(memory + (now_addr >> 1));
}
void YM_DELTAT::savestate(device_t *device)
{
#ifdef MAME_EMU_SAVE_H
YM_DELTAT *const DELTAT = this; // makes the save name sensible
device->save_item(NAME(DELTAT->portstate));
device->save_item(NAME(DELTAT->now_addr));
device->save_item(NAME(DELTAT->now_step));
device->save_item(NAME(DELTAT->acc));
device->save_item(NAME(DELTAT->prev_acc));
device->save_item(NAME(DELTAT->adpcmd));
device->save_item(NAME(DELTAT->adpcml));
#endif
}
#define YM_DELTAT_Limit(val,max,min) \
{ \
if ( val > max ) val = max; \
else if ( val < min ) val = min; \
}
static inline void YM_DELTAT_synthesis_from_external_memory(YM_DELTAT *DELTAT)
{
uint32_t step;
int data;
DELTAT->now_step += DELTAT->step;
if ( DELTAT->now_step >= (1<<YM_DELTAT_SHIFT) )
{
step = DELTAT->now_step >> YM_DELTAT_SHIFT;
DELTAT->now_step &= (1<<YM_DELTAT_SHIFT)-1;
do{
if ( DELTAT->now_addr == (DELTAT->limit<<1) )
DELTAT->now_addr = 0;
if ( DELTAT->now_addr == (DELTAT->end<<1) ) { /* 12-06-2001 JB: corrected comparison. Was > instead of == */
if( DELTAT->portstate&0x10 ){
/* repeat start */
DELTAT->now_addr = DELTAT->start<<1;
DELTAT->acc = 0;
DELTAT->adpcmd = YM_DELTAT_DELTA_DEF;
DELTAT->prev_acc = 0;
}else{
/* set EOS bit in status register */
if(DELTAT->status_set_handler)
if(DELTAT->status_change_EOS_bit)
(DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit);
/* clear PCM BUSY bit (reflected in status register) */
DELTAT->PCM_BSY = 0;
DELTAT->portstate = 0;
DELTAT->adpcml = 0;
DELTAT->prev_acc = 0;
return;
}
}
if( DELTAT->now_addr&1 ) data = DELTAT->now_data & 0x0f;
else
{
DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1));
data = DELTAT->now_data >> 4;
}
DELTAT->now_addr++;
/* 12-06-2001 JB: */
/* YM2610 address register is 24 bits wide.*/
/* The "+1" is there because we use 1 bit more for nibble calculations.*/
/* WARNING: */
/* Side effect: we should take the size of the mapped ROM into account */
DELTAT->now_addr &= ( (1<<(24+1))-1);
/* store accumulator value */
DELTAT->prev_acc = DELTAT->acc;
/* Forecast to next Forecast */
DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8);
YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN);
/* delta to next delta */
DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64;
YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN );
/* ElSemi: Fix interpolator. */
/*DELTAT->prev_acc = prev_acc + ((DELTAT->acc - prev_acc) / 2 );*/
}while(--step);
}
/* ElSemi: Fix interpolator. */
DELTAT->adpcml = DELTAT->prev_acc * (int)((1<<YM_DELTAT_SHIFT)-DELTAT->now_step);
DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step);
DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume;
/* output for work of output channels (outd[OPNxxxx])*/
*(DELTAT->pan) += DELTAT->adpcml;
}
static inline void YM_DELTAT_synthesis_from_CPU_memory(YM_DELTAT *DELTAT)
{
uint32_t step;
int data;
DELTAT->now_step += DELTAT->step;
if ( DELTAT->now_step >= (1<<YM_DELTAT_SHIFT) )
{
step = DELTAT->now_step >> YM_DELTAT_SHIFT;
DELTAT->now_step &= (1<<YM_DELTAT_SHIFT)-1;
do{
if( DELTAT->now_addr&1 )
{
data = DELTAT->now_data & 0x0f;
DELTAT->now_data = DELTAT->CPU_data;
/* after we used CPU_data, we set BRDY bit in status register,
* which means we are ready to accept another byte of data */
if(DELTAT->status_set_handler)
if(DELTAT->status_change_BRDY_bit)
(DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);
}
else
{
data = DELTAT->now_data >> 4;
}
DELTAT->now_addr++;
/* store accumulator value */
DELTAT->prev_acc = DELTAT->acc;
/* Forecast to next Forecast */
DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8);
YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN);
/* delta to next delta */
DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64;
YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN );
}while(--step);
}
/* ElSemi: Fix interpolator. */
DELTAT->adpcml = DELTAT->prev_acc * (int)((1<<YM_DELTAT_SHIFT)-DELTAT->now_step);
DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step);
DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume;
/* output for work of output channels (outd[OPNxxxx])*/
*(DELTAT->pan) += DELTAT->adpcml;
}
/* ADPCM B (Delta-T control type) */
void YM_DELTAT::ADPCM_CALC()
{
/*
some examples:
value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning:
80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register
a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register
C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register
E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register
60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08
20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08
*/
if ( (portstate & 0xe0)==0xa0 )
{
YM_DELTAT_synthesis_from_external_memory(this);
return;
}
if ( (portstate & 0xe0)==0x80 )
{
/* ADPCM synthesis from CPU-managed memory (from reg $08) */
YM_DELTAT_synthesis_from_CPU_memory(this); /* change output based on data in ADPCM data reg ($08) */
return;
}
//todo: ADPCM analysis
// if ( (portstate & 0xe0)==0xc0 )
// if ( (portstate & 0xe0)==0xe0 )
return;
}

View file

@ -0,0 +1,85 @@
// license:GPL-2.0+
// copyright-holders:Jarek Burczynski
#ifndef MAME_SOUND_YMDELTAT_H
#define MAME_SOUND_YMDELTAT_H
#pragma once
typedef void (*STATUS_CHANGE_HANDLER)(void *chip, uint8_t status_bits);
/* DELTA-T (adpcm type B) struct */
struct YM_DELTAT { /* AT: rearranged and tightened structure */
static constexpr int EMULATION_MODE_NORMAL = 0;
static constexpr int EMULATION_MODE_YM2610 = 1;
uint8_t *memory;
int32_t *output_pointer;/* pointer of output pointers */
int32_t *pan; /* pan : &output_pointer[pan] */
double freqbase;
#if 0
double write_time; /* Y8950: 10 cycles of main clock; YM2608: 20 cycles of main clock */
double read_time; /* Y8950: 8 cycles of main clock; YM2608: 18 cycles of main clock */
#endif
uint32_t memory_size;
int output_range;
uint32_t now_addr; /* current address */
uint32_t now_step; /* correct step */
uint32_t step; /* step */
uint32_t start; /* start address */
uint32_t limit; /* limit address */
uint32_t end; /* end address */
uint32_t delta; /* delta scale */
int32_t volume; /* current volume */
int32_t acc; /* shift Measurement value*/
int32_t adpcmd; /* next Forecast */
int32_t adpcml; /* current value */
int32_t prev_acc; /* leveling value */
uint8_t now_data; /* current rom data */
uint8_t CPU_data; /* current data from reg 08 */
uint8_t portstate; /* port status */
uint8_t control2; /* control reg: SAMPLE, DA/AD, RAM TYPE (x8bit / x1bit), ROM/RAM */
uint8_t portshift; /* address bits shift-left:
** 8 for YM2610,
** 5 for Y8950 and YM2608 */
uint8_t DRAMportshift; /* address bits shift-right:
** 0 for ROM and x8bit DRAMs,
** 3 for x1 DRAMs */
uint8_t memread; /* needed for reading/writing external memory */
/* handlers and parameters for the status flags support */
STATUS_CHANGE_HANDLER status_set_handler;
STATUS_CHANGE_HANDLER status_reset_handler;
/* note that different chips have these flags on different
** bits of the status register
*/
void * status_change_which_chip; /* this chip id */
uint8_t status_change_EOS_bit; /* 1 on End Of Sample (record/playback/cycle time of AD/DA converting has passed)*/
uint8_t status_change_BRDY_bit; /* 1 after recording 2 datas (2x4bits) or after reading/writing 1 data */
uint8_t status_change_ZERO_bit; /* 1 if silence lasts for more than 290 milliseconds on ADPCM recording */
/* neither Y8950 nor YM2608 can generate IRQ when PCMBSY bit changes, so instead of above,
** the statusflag gets ORed with PCM_BSY (below) (on each read of statusflag of Y8950 and YM2608)
*/
uint8_t PCM_BSY; /* 1 when ADPCM is playing; Y8950/YM2608 only */
uint8_t reg[16]; /* adpcm registers */
uint8_t emulation_mode; /* which chip we're emulating */
device_t *device;
/*void BRDY_callback();*/
uint8_t ADPCM_Read();
void ADPCM_Write(int r, int v);
void ADPCM_Reset(int panidx, int mode, device_t *dev);
void ADPCM_CALC();
void postload(uint8_t *regs);
void savestate(device_t *device);
};
#endif // MAME_SOUND_YMDELTAT_H

2788
src/hardware/mame/ymf262.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,40 @@
// license:GPL-2.0+
// copyright-holders:Jarek Burczynski
#ifndef MAME_SOUND_YMF262_H
#define MAME_SOUND_YMF262_H
#pragma once
/* select number of output bits: 8 or 16 */
#define OPL3_SAMPLE_BITS 16
typedef stream_sample_t OPL3SAMPLE;
/*
#if (OPL3_SAMPLE_BITS==16)
typedef int16_t OPL3SAMPLE;
#endif
#if (OPL3_SAMPLE_BITS==8)
typedef int8_t OPL3SAMPLE;
#endif
*/
typedef void (*OPL3_TIMERHANDLER)(device_t *device,int timer,const attotime &period);
typedef void (*OPL3_IRQHANDLER)(device_t *device,int irq);
typedef void (*OPL3_UPDATEHANDLER)(device_t *device,int min_interval_us);
void *ymf262_init(device_t *device, int clock, int rate);
void ymf262_post_load(void *chip);
void ymf262_shutdown(void *chip);
void ymf262_reset_chip(void *chip);
int ymf262_write(void *chip, int a, int v);
unsigned char ymf262_read(void *chip, int a);
int ymf262_timer_over(void *chip, int c);
void ymf262_update_one(void *chip, OPL3SAMPLE **buffers, int length);
void ymf262_set_timer_handler(void *chip, OPL3_TIMERHANDLER TimerHandler, device_t *device);
void ymf262_set_irq_handler(void *chip, OPL3_IRQHANDLER IRQHandler, device_t *device);
void ymf262_set_update_handler(void *chip, OPL3_UPDATEHANDLER UpdateHandler, device_t *device);
#endif // MAME_SOUND_YMF262_H