1
0
Fork 0

Merge the mamesound branch into trunk

Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@4075
This commit is contained in:
Sjoerd van der Berg 2018-02-07 12:17:02 +00:00
commit 55603e3aa3
19 changed files with 7812 additions and 650 deletions

View file

@ -554,6 +554,7 @@ src/dos/Makefile
src/fpu/Makefile
src/gui/Makefile
src/hardware/Makefile
src/hardware/mame/Makefile
src/hardware/serialport/Makefile
src/ints/Makefile
src/libs/Makefile

View file

@ -13,7 +13,8 @@ endif
dosbox_SOURCES = dosbox.cpp $(ico_stuff)
dosbox_LDADD = cpu/libcpu.a debug/libdebug.a dos/libdos.a fpu/libfpu.a hardware/libhardware.a gui/libgui.a \
ints/libints.a misc/libmisc.a shell/libshell.a hardware/serialport/libserial.a libs/gui_tk/libgui_tk.a
ints/libints.a misc/libmisc.a shell/libshell.a hardware/mame/libmame.a \
hardware/serialport/libserial.a libs/gui_tk/libgui_tk.a
EXTRA_DIST = winres.rc dosbox.ico

View file

@ -602,7 +602,7 @@ void DOSBOX_Init(void) {
Pstring->Set_values(oplmodes);
Pstring->Set_help("Type of OPL emulation. On 'auto' the mode is determined by sblaster type. All OPL modes are Adlib-compatible, except for 'cms'.");
const char* oplemus[]={ "default", "compat", "fast", 0};
const char* oplemus[]={ "default", "compat", "fast", "mame", 0};
Pstring = secprop->Add_string("oplemu",Property::Changeable::WhenIdle,"default");
Pstring->Set_values(oplemus);
Pstring->Set_help("Provider for the OPL emulation. compat might provide better quality (see oplrate as well).");

View file

@ -1,6 +1,6 @@
AM_CPPFLAGS = -I$(top_srcdir)/include
SUBDIRS = serialport
SUBDIRS = serialport mame
EXTRA_DIST = opl.cpp opl.h adlib.h dbopl.h pci_devices.h

View file

@ -28,6 +28,13 @@
#include "mem.h"
#include "dbopl.h"
#include "mame/emu.h"
#include "mame/fmopl.h"
#include "mame/ymf262.h"
#define OPL2_INTERNAL_FREQ 3600000 // The OPL2 operates at 3.6MHz
#define OPL3_INTERNAL_FREQ 14400000 // The OPL3 operates at 14.4MHz
namespace OPL2 {
#include "opl.cpp"
@ -85,6 +92,80 @@ namespace OPL3 {
};
}
namespace MAMEOPL2 {
struct Handler : public Adlib::Handler {
void* chip;
virtual void WriteReg(Bit32u reg, Bit8u val) {
ym3812_write(chip, 0, reg);
ym3812_write(chip, 1, val);
}
virtual Bit32u WriteAddr(Bit32u port, Bit8u val) {
return val;
}
virtual void Generate(MixerChannel* chan, Bitu samples) {
Bit16s buf[1024 * 2];
while (samples > 0) {
Bitu todo = samples > 1024 ? 1024 : samples;
samples -= todo;
ym3812_update_one(chip, buf, todo);
chan->AddSamples_m16(todo, buf);
}
}
virtual void Init(Bitu rate) {
chip = ym3812_init(0, OPL2_INTERNAL_FREQ, rate);
}
~Handler() {
ym3812_shutdown(chip);
}
};
}
namespace MAMEOPL3 {
struct Handler : public Adlib::Handler {
void* chip;
virtual void WriteReg(Bit32u reg, Bit8u val) {
ymf262_write(chip, 0, reg);
ymf262_write(chip, 1, val);
}
virtual Bit32u WriteAddr(Bit32u port, Bit8u val) {
return val;
}
virtual void Generate(MixerChannel* chan, Bitu samples) {
//We generate data for 4 channels, but only the first 2 are connected on a pc
Bit16s buf[4][1024];
Bit16s result[1024][2];
Bit16s* buffers[4] = { buf[0], buf[1], buf[2], buf[3] };
while (samples > 0) {
Bitu todo = samples > 1024 ? 1024 : samples;
samples -= todo;
ymf262_update_one(chip, buffers, todo);
//Interleave the samples before mixing
for (Bitu i = 0; i < todo; i++) {
result[i][0] = buf[0][i];
result[i][1] = buf[1][i];
}
chan->AddSamples_s16(todo, result[0]);
}
}
virtual void Init(Bitu rate) {
chip = ymf262_init(0, OPL3_INTERNAL_FREQ, rate);
}
~Handler() {
ymf262_shutdown(chip);
}
};
}
#define RAW_SIZE 1024
@ -717,6 +798,14 @@ Module::Module( Section* configuration ) : Module_base(configuration) {
} else {
handler = new OPL3::Handler();
}
}
else if (oplemu == "mame") {
if (oplmode == OPL_opl2) {
handler = new MAMEOPL2::Handler();
}
else {
handler = new MAMEOPL3::Handler();
}
} else {
handler = new DBOPL::Handler();
}

View file

@ -27,408 +27,75 @@
#include <cstring>
#include <math.h>
#include "mame/emu.h"
#include "mame/saa1099.h"
#define LEFT 0x00
#define RIGHT 0x01
#define CMS_BUFFER_SIZE 128
#define CMS_RATE 22050
/*#define MASTER_CLOCK 14318180/2 */
#define MASTER_CLOCK 7159090
typedef Bit8u UINT8;
typedef Bit16s INT16;
/* this structure defines a channel */
struct saa1099_channel
{
int frequency; /* frequency (0x00..0xff) */
int freq_enable; /* frequency enable */
int noise_enable; /* noise enable */
int octave; /* 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;
double freq;
int level;
};
/* this structure defines a noise channel */
struct saa1099_noise
{
/* vars to simulate the noise generator output */
double counter;
double freq;
int level; /* noise polynomal shifter */
};
/* this structure defines a SAA1099 chip */
struct SAA1099
{
int stream; /* our stream */
int noise_params[2]; /* noise generators parameters */
int env_enable[2]; /* envelope generators enable */
int env_reverse_right[2]; /* envelope reversed for right channel */
int env_mode[2]; /* envelope generators mode */
int env_bits[2]; /* non zero = 3 bits resolution */
int env_clock[2]; /* envelope clock mode (non-zero external) */
int env_step[2]; /* current envelope step */
int all_ch_enable; /* all channels enable */
int sync_state; /* sync all channels */
int selected_reg; /* selected register */
struct saa1099_channel channels[6]; /* channels */
struct saa1099_noise noise[2]; /* noise generators */
};
static const UINT8 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 }
};
static const 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
};
/* global parameters */
static double sample_rate;
static SAA1099 saa1099[2];
//My mixer channel
static MixerChannel * cms_chan;
static Bit16s cms_buffer[2][2][CMS_BUFFER_SIZE];
static Bit16s * cms_buf_point[4] = {
cms_buffer[0][0],cms_buffer[0][1],cms_buffer[1][0],cms_buffer[1][1] };
static Bitu last_command;
static Bitu base_port;
static void saa1099_envelope(int chip, int ch)
{
struct SAA1099 *saa = &saa1099[chip];
if (saa->env_enable[ch])
{
int step, mode, mask;
mode = saa->env_mode[ch];
/* step from 0..63 and then loop in steps 32..63 */
step = saa->env_step[ch] =
((saa->env_step[ch] + 1) & 0x3f) | (saa->env_step[ch] & 0x20);
mask = 15;
if (saa->env_bits[ch])
mask &= ~1; /* 3 bit resolution, mask LSB */
saa->channels[ch*3+0].envelope[ LEFT] =
saa->channels[ch*3+1].envelope[ LEFT] =
saa->channels[ch*3+2].envelope[ LEFT] = envelope[mode][step] & mask;
if (saa->env_reverse_right[ch] & 0x01)
{
saa->channels[ch*3+0].envelope[RIGHT] =
saa->channels[ch*3+1].envelope[RIGHT] =
saa->channels[ch*3+2].envelope[RIGHT] = (15 - envelope[mode][step]) & mask;
}
else
{
saa->channels[ch*3+0].envelope[RIGHT] =
saa->channels[ch*3+1].envelope[RIGHT] =
saa->channels[ch*3+2].envelope[RIGHT] = envelope[mode][step] & mask;
}
}
else
{
/* envelope mode off, set all envelope factors to 16 */
saa->channels[ch*3+0].envelope[ LEFT] =
saa->channels[ch*3+1].envelope[ LEFT] =
saa->channels[ch*3+2].envelope[ LEFT] =
saa->channels[ch*3+0].envelope[RIGHT] =
saa->channels[ch*3+1].envelope[RIGHT] =
saa->channels[ch*3+2].envelope[RIGHT] = 16;
}
}
static void saa1099_update(int chip, INT16 **buffer, int length)
{
struct SAA1099 *saa = &saa1099[chip];
int j, ch;
/* if the channels are disabled we're done */
if (!saa->all_ch_enable)
{
/* init output data */
memset(buffer[LEFT],0,length*sizeof(INT16));
memset(buffer[RIGHT],0,length*sizeof(INT16));
return;
}
for (ch = 0; ch < 2; ch++)
{
switch (saa->noise_params[ch])
{
case 0: saa->noise[ch].freq = MASTER_CLOCK/256 * 2; break;
case 1: saa->noise[ch].freq = MASTER_CLOCK/512 * 2; break;
case 2: saa->noise[ch].freq = MASTER_CLOCK/1024 * 2; break;
case 3: saa->noise[ch].freq = saa->channels[ch * 3].freq; break;
}
}
/* fill all data needed */
for( j = 0; j < length; j++ )
{
int output_l = 0, output_r = 0;
/* for each channel */
for (ch = 0; ch < 6; ch++)
{
if (saa->channels[ch].freq == 0.0)
saa->channels[ch].freq = (double)((2 * MASTER_CLOCK/512) << saa->channels[ch].octave) /
(511.0 - (double)saa->channels[ch].frequency);
/* check the actual position in the square wave */
saa->channels[ch].counter -= saa->channels[ch].freq;
while (saa->channels[ch].counter < 0)
{
/* calculate new frequency now after the half wave is updated */
saa->channels[ch].freq = (double)((2 * MASTER_CLOCK/512) << saa->channels[ch].octave) /
(511.0 - (double)saa->channels[ch].frequency);
saa->channels[ch].counter += sample_rate;
saa->channels[ch].level ^= 1;
/* eventually clock the envelope counters */
if (ch == 1 && saa->env_clock[0] == 0)
saa1099_envelope(chip, 0);
if (ch == 4 && saa->env_clock[1] == 0)
saa1099_envelope(chip, 1);
}
/* if the noise is enabled */
if (saa->channels[ch].noise_enable)
{
/* if the noise level is high (noise 0: chan 0-2, noise 1: chan 3-5) */
if (saa->noise[ch/3].level & 1)
{
/* subtract to avoid overflows, also use only half amplitude */
output_l -= saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16 / 2;
output_r -= saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16 / 2;
}
}
/* if the square wave is enabled */
if (saa->channels[ch].freq_enable)
{
/* if the channel level is high */
if (saa->channels[ch].level & 1)
{
output_l += saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16;
output_r += saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16;
}
}
}
for (ch = 0; ch < 2; ch++)
{
/* check the actual position in noise generator */
saa->noise[ch].counter -= saa->noise[ch].freq;
while (saa->noise[ch].counter < 0)
{
saa->noise[ch].counter += sample_rate;
if( ((saa->noise[ch].level & 0x4000) == 0) == ((saa->noise[ch].level & 0x0040) == 0) )
saa->noise[ch].level = (saa->noise[ch].level << 1) | 1;
else
saa->noise[ch].level <<= 1;
}
}
/* write sound data to the buffer */
buffer[LEFT][j] = output_l / 6;
buffer[RIGHT][j] = output_r / 6;
}
}
static void saa1099_write_port_w( int chip, int offset, int data )
{
struct SAA1099 *saa = &saa1099[chip];
if(offset == 1) {
// address port
saa->selected_reg = data & 0x1f;
if (saa->selected_reg == 0x18 || saa->selected_reg == 0x19) {
/* clock the envelope channels */
if (saa->env_clock[0]) saa1099_envelope(chip,0);
if (saa->env_clock[1]) saa1099_envelope(chip,1);
}
return;
}
int reg = saa->selected_reg;
int ch;
switch (reg)
{
/* channel i amplitude */
case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
ch = reg & 7;
saa->channels[ch].amplitude[LEFT] = amplitude_lookup[data & 0x0f];
saa->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;
saa->channels[ch].frequency = data & 0xff;
break;
/* channel i octave */
case 0x10: case 0x11: case 0x12:
ch = (reg - 0x10) << 1;
saa->channels[ch + 0].octave = data & 0x07;
saa->channels[ch + 1].octave = (data >> 4) & 0x07;
break;
/* channel i frequency enable */
case 0x14:
saa->channels[0].freq_enable = data & 0x01;
saa->channels[1].freq_enable = data & 0x02;
saa->channels[2].freq_enable = data & 0x04;
saa->channels[3].freq_enable = data & 0x08;
saa->channels[4].freq_enable = data & 0x10;
saa->channels[5].freq_enable = data & 0x20;
break;
/* channel i noise enable */
case 0x15:
saa->channels[0].noise_enable = data & 0x01;
saa->channels[1].noise_enable = data & 0x02;
saa->channels[2].noise_enable = data & 0x04;
saa->channels[3].noise_enable = data & 0x08;
saa->channels[4].noise_enable = data & 0x10;
saa->channels[5].noise_enable = data & 0x20;
break;
/* noise generators parameters */
case 0x16:
saa->noise_params[0] = data & 0x03;
saa->noise_params[1] = (data >> 4) & 0x03;
break;
/* envelope generators parameters */
case 0x18: case 0x19:
ch = reg - 0x18;
saa->env_reverse_right[ch] = data & 0x01;
saa->env_mode[ch] = (data >> 1) & 0x07;
saa->env_bits[ch] = data & 0x10;
saa->env_clock[ch] = data & 0x20;
saa->env_enable[ch] = data & 0x80;
/* reset the envelope */
saa->env_step[ch] = 0;
break;
/* channels enable & reset generators */
case 0x1c:
saa->all_ch_enable = data & 0x01;
saa->sync_state = data & 0x02;
if (data & 0x02)
{
int i;
// logerror("%04x: (SAA1099 #%d) -reg 0x1c- Chip reset\n",activecpu_get_pc(), chip);
/* Synch & Reset generators */
for (i = 0; i < 6; i++)
{
saa->channels[i].level = 0;
saa->channels[i].counter = 0.0;
}
}
break;
default: /* Error! */
// logerror("%04x: (SAA1099 #%d) Unknown operation (reg:%02x, data:%02x)\n",activecpu_get_pc(), chip, reg, data);
LOG(LOG_MISC,LOG_ERROR)("CMS Unkown write to reg %x with %x",reg, data);
}
}
//Timer to disable the channel after a while
static Bit32u lastWriteTicks;
static Bit32u cmsBase;
static saa1099_device* device[2];
static void write_cms(Bitu port, Bitu val, Bitu /* iolen */) {
if(cms_chan && (!cms_chan->enabled)) cms_chan->Enable(true);
last_command = PIC_Ticks;
switch (port-base_port) {
case 0:
saa1099_write_port_w(0,0,val);
break;
lastWriteTicks = PIC_Ticks;
switch ( port - cmsBase ) {
case 1:
saa1099_write_port_w(0,1,val);
device[0]->control_w(0, 0, val);
break;
case 2:
saa1099_write_port_w(1,0,val);
case 0:
device[0]->data_w(0, 0, val);
break;
case 3:
saa1099_write_port_w(1,1,val);
device[1]->control_w(0, 0, val);
break;
case 2:
device[1]->data_w(0, 0, val);
break;
}
}
static void CMS_CallBack(Bitu len) {
if (len > CMS_BUFFER_SIZE) return;
enum {
BUFFER_SIZE = 2048
};
saa1099_update(0, &cms_buf_point[0], (int)len);
saa1099_update(1, &cms_buf_point[2], (int)len);
if ( len > BUFFER_SIZE )
return;
Bit16s * stream=(Bit16s *) MixTemp;
/* Mix chip outputs */
for (Bitu l=0;l<len;l++) {
register Bits left, right;
left = cms_buffer[0][LEFT][l] + cms_buffer[1][LEFT][l];
right = cms_buffer[0][RIGHT][l] + cms_buffer[1][RIGHT][l];
if ( cms_chan ) {
if (left>MAX_AUDIO) *stream=MAX_AUDIO;
else if (left<MIN_AUDIO) *stream=MIN_AUDIO;
else *stream=(Bit16s)left;
stream++;
if (right>MAX_AUDIO) *stream=MAX_AUDIO;
else if (right<MIN_AUDIO) *stream=MIN_AUDIO;
else *stream=(Bit16s)right;
stream++;
//Have there been 10 seconds of no commands, disable channel
if ( lastWriteTicks + 10000 < PIC_Ticks ) {
cms_chan->Enable( false );
return;
}
Bit32s result[BUFFER_SIZE][2];
Bit16s work[2][BUFFER_SIZE];
Bit16s* buffers[2] = { work[0], work[1] };
device_sound_interface::sound_stream stream;
device[0]->sound_stream_update(stream, 0, buffers, len);
for (Bitu i = 0; i < len; i++) {
result[i][0] = work[0][i];
result[i][1] = work[1][i];
}
device[1]->sound_stream_update(stream, 0, buffers, len);
for (Bitu i = 0; i < len; i++) {
result[i][0] += work[0][i];
result[i][1] += work[1][i];
}
cms_chan->AddSamples_s32( len, result[0] );
}
if(cms_chan) cms_chan->AddSamples_s16(len,(Bit16s *)MixTemp);
if (last_command + 10000 < PIC_Ticks) if(cms_chan) cms_chan->Enable(false);
}
// The Gameblaster detection
static Bit8u cms_detect_register = 0xff;
static void write_cms_detect(Bitu port, Bitu val, Bitu /* iolen */) {
switch(port-base_port) {
switch ( port - cmsBase ) {
case 0x6:
case 0x7:
cms_detect_register = val;
@ -438,7 +105,7 @@ static void write_cms_detect(Bitu port, Bitu val, Bitu /* iolen */) {
static Bitu read_cms_detect(Bitu port, Bitu /* iolen */) {
Bit8u retval = 0xff;
switch(port-base_port) {
switch ( port - cmsBase ) {
case 0x4:
retval = 0x7f;
break;
@ -461,31 +128,37 @@ private:
public:
CMS(Section* configuration):Module_base(configuration) {
Section_prop * section = static_cast<Section_prop *>(configuration);
Bitu sample_rate_temp = section->Get_int("oplrate");
sample_rate = static_cast<double>(sample_rate_temp);
base_port = section->Get_hex("sbbase");
WriteHandler.Install(base_port, write_cms, IO_MB,4);
Bitu sampleRate = section->Get_int( "oplrate" );
cmsBase = section->Get_hex("sbbase");
WriteHandler.Install( cmsBase, write_cms, IO_MB, 4 );
// A standalone Gameblaster has a magic chip on it which is
// sometimes used for detection.
const char * sbtype=section->Get_string("sbtype");
if (!strcasecmp(sbtype,"gb")) {
DetWriteHandler.Install(base_port+4,write_cms_detect,IO_MB,12);
DetReadHandler.Install(base_port,read_cms_detect,IO_MB,16);
DetWriteHandler.Install( cmsBase + 4, write_cms_detect, IO_MB, 12 );
DetReadHandler.Install(cmsBase,read_cms_detect,IO_MB,16);
}
/* Register the Mixer CallBack */
cms_chan = MixerChan.Install(CMS_CallBack,sample_rate_temp,"CMS");
cms_chan = MixerChan.Install(CMS_CallBack,sampleRate,"CMS");
last_command = PIC_Ticks;
for (int s=0;s<2;s++) {
struct SAA1099 *saa = &saa1099[s];
memset(saa, 0, sizeof(struct SAA1099));
}
lastWriteTicks = PIC_Ticks;
Bit32u freq = 7159000; //14318180 isa clock / 2
machine_config config;
device[0] = new saa1099_device(config, "", 0, 7159090);
device[1] = new saa1099_device(config, "", 0, 7159090);
device[0]->device_start();
device[1]->device_start();
}
~CMS() {
cms_chan = 0;
delete device[0];
delete device[1];
}
};

View file

@ -0,0 +1,9 @@
AM_CPPFLAGS = -I$(top_srcdir)/include
noinst_LIBRARIES = libmame.a
libmame_a_SOURCES = emu.h \
fmopl.cpp fmopl.h \
saa1099.cpp saa1099.h \
sn76496.cpp sn76496.cpp \
ymdeltat.cpp ymdeltat.h \
ymf262.cpp ymf262.h

123
src/hardware/mame/emu.h Normal file
View file

@ -0,0 +1,123 @@
#ifndef DOSBOX_EMU_H
#define DOSBOX_EMU_H
#include "dosbox.h"
#include <stdint.h>
#include <math.h>
#include <float.h>
#include <stdlib.h>
#include <memory.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
typedef Bit16s stream_sample_t;
typedef Bit8u u8;
typedef Bit32u u32;
class device_t;
struct machine_config;
#define NAME( _ASDF_ ) 0
#define BIT( _INPUT_, _BIT_ ) ( ( _INPUT_) >> (_BIT_)) & 1
#define ATTR_UNUSED
#define DECLARE_READ8_MEMBER(name) u8 name( int, int)
#define DECLARE_WRITE8_MEMBER(name) void name( int, int, u8 data)
#define READ8_MEMBER(name) u8 name( int, int)
#define WRITE8_MEMBER(name) void name( int offset, int space, u8 data)
#define DECLARE_DEVICE_TYPE(Type, Class) \
extern const device_type Type; \
class Class;
#define DEFINE_DEVICE_TYPE(Type, Class, ShortName, FullName) \
const device_type Type = 0;
class device_sound_interface {
public:
struct sound_stream {
void update() {
}
};
sound_stream temp;
sound_stream* stream_alloc(int whatever, int channels, int size) {
return &temp;
};
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) = 0;
device_sound_interface(const machine_config &mconfig, device_t& _device) {
}
};
struct attotime {
int whatever;
static attotime from_hz(int hz) {
return attotime();
}
};
struct machine_config {
};
typedef int device_type;
class device_t {
u32 clockRate;
public:
struct machine_t {
int describe_context() const {
return 0;
}
};
machine_t machine() const {
return machine_t();
}
//int offset, space;
u32 clock() const {
return clockRate;
}
void logerror(const char* msg, ...) {
}
static int tag() {
return 0;
}
virtual void device_start() {
}
void save_item(int wtf, int blah= 0) {
}
device_t(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 _clock) : clockRate( _clock ) {
}
};
static void auto_free(const device_t::machine_t& machine, void * buffer) {
free(buffer);
}
#define auto_alloc_array_clear(m, t, c) calloc(c, sizeof(t) )
#define auto_alloc_clear(m, t) static_cast<t*>( calloc(1, sizeof(t) ) )
#endif

2577
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 (0)
#define BUILD_Y8950 (0)
/* 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,471 @@
// 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 const 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 const 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")
#define SAA1099 1
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// saa1099_device - constructor
//-------------------------------------------------
#define FILL_ARRAY( _FILL_ ) memset( _FILL_, 0, sizeof( _FILL_ ) )
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(0)
#if 0
, 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 }
#endif
, m_all_ch_enable(0)
, m_sync_state(0)
, m_selected_reg(0)
, m_sample_rate(0.0)
{
FILL_ARRAY( m_noise_params );
FILL_ARRAY( m_env_enable );
FILL_ARRAY( m_env_reverse_right );
FILL_ARRAY( m_env_mode );
FILL_ARRAY( m_env_bits );
FILL_ARRAY( m_env_clock );
FILL_ARRAY( m_env_step );
}
//-------------------------------------------------
// 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);
}

120
src/hardware/mame/saa1099.h Normal file
View file

@ -0,0 +1,120 @@
// 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
//**************************************************************************
//Container class for int that just initalizes to 0
class NullInt {
int value;
public:
operator int& () {
return value;
}
int& operator= ( int set ) {
value = set;
return value;
}
NullInt( int set = 0 ) : value( set ) {
}
};
// ======================> 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();
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
private:
struct saa1099_channel
{
saa1099_channel() {
//Quite hacky, but let's see how it goes
memset( this, 0, sizeof( *this ) );
}
int frequency ; /* frequency (0x00..0xff) */
int freq_enable ; /* frequency enable */
int noise_enable ; /* noise enable */
int octave ; /* 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 ;
double freq ;
int level ;
};
struct saa1099_noise
{
saa1099_noise() {
counter = 0;
freq = 0;
level = 0xFFFFFFFF;
}
/* vars to simulate the noise generator output */
double counter;
double freq;
uint32_t level; /* 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,491 @@
// 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
//When you go over this create sample
#define RATE_MAX ( 1 << 30)
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()
{
sample_rate = clock()/2;
rate_add = RATE_MAX;
rate_counter = 0;
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] : 0;
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];
}
}
//Skip final generation if you don't need an actual sample
rate_counter += rate_add;
if (rate_counter < RATE_MAX)
continue;
rate_counter -= RATE_MAX;
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::convert_samplerate(int32_t target_rate) {
//Simple 10 bit shift for samplerate conversion
rate_add = (int32_t)( RATE_MAX * (target_rate / (double)sample_rate) );
rate_counter = 0;
}
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")

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

@ -0,0 +1,167 @@
// 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)
#if 0
#define MCFG_SN76496_READY_HANDLER(cb) \
devcb = &sn76496_base_device::set_ready_handler(*device, (DEVCB_##cb));
#endif
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; }
void convert_samplerate(int32_t target_rate);
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();
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
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
//Modifications for easier sample conversion
int32_t sample_rate;
//Sample rate conversion
int32_t rate_add;
int32_t rate_counter;
};
// 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 const 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 const 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 const 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,87 @@
// 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 */
enum {
EMULATION_MODE_NORMAL = 0,
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

2792
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

View file

@ -30,47 +30,11 @@
#include "hardware.h"
#include <cstring>
#include <math.h>
#define MAX_OUTPUT 0x7fff
#define STEP 0x10000
/* Formulas for noise generator */
/* bit0 = output */
/* noise feedback for white noise mode (verified on real SN76489 by John Kortink) */
#define FB_WNOISE 0x14002 /* (16bits) bit16 = bit0(out) ^ bit2 ^ bit15 */
/* noise feedback for periodic noise mode */
//#define FB_PNOISE 0x10000 /* 16bit rorate */
#define FB_PNOISE 0x08000 /* JH 981127 - fixes Do Run Run */
/*
0x08000 is definitely wrong. The Master System conversion of Marble Madness
uses periodic noise as a baseline. With a 15-bit rotate, the bassline is
out of tune.
The 16-bit rotate has been confirmed against a real PAL Sega Master System 2.
Hope that helps the System E stuff, more news on the PSG as and when!
*/
/* noise generator start preset (for periodic noise) */
#define NG_PRESET 0x0f35
#include "mame/emu.h"
#include "mame/sn76496.h"
struct SN76496 {
int SampleRate;
unsigned int UpdateStep;
int VolTable[16]; /* volume table */
int Register[8]; /* registers */
int LastRegister; /* last register written */
int Volume[4]; /* volume of voice 0-2 and noise */
unsigned int RNG; /* noise generator */
int NoiseFB; /* noise feedback mask */
int Period[4];
int Count[4];
int Output[4];
};
static struct SN76496 sn;
#define SOUND_CLOCK (14318180 / 4)
#define TDAC_DMA_BUFSIZE 1024
@ -99,221 +63,36 @@ static struct {
} dac;
} tandy;
static sn76496_device device(machine_config(), 0, 0, SOUND_CLOCK );
static void SN76496Write(Bitu /*port*/,Bitu data,Bitu /*iolen*/) {
struct SN76496 *R = &sn;
tandy.last_write=PIC_Ticks;
if (!tandy.enabled) {
tandy.chan->Enable(true);
tandy.enabled=true;
}
/* update the output buffer before changing the registers */
if (data & 0x80)
{
int r = (data & 0x70) >> 4;
int c = r/2;
R->LastRegister = r;
R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f);
switch (r)
{
case 0: /* tone 0 : frequency */
case 2: /* tone 1 : frequency */
case 4: /* tone 2 : frequency */
R->Period[c] = R->UpdateStep * R->Register[r];
if (R->Period[c] == 0) R->Period[c] = 0x3fe;
if (r == 4)
{
/* update noise shift frequency */
if ((R->Register[6] & 0x03) == 0x03)
R->Period[3] = 2 * R->Period[2];
}
break;
case 1: /* tone 0 : volume */
case 3: /* tone 1 : volume */
case 5: /* tone 2 : volume */
case 7: /* noise : volume */
R->Volume[c] = R->VolTable[data & 0x0f];
break;
case 6: /* noise : frequency, mode */
{
int n = R->Register[6];
R->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE;
n &= 3;
/* N/512,N/1024,N/2048,Tone #3 output */
R->Period[3] = (n == 3) ? 2 * R->Period[2] : (R->UpdateStep << (5+n));
/* reset noise shifter */
// R->RNG = NG_PRESET;
// R->Output[3] = R->RNG & 1;
}
break;
}
}
else
{
int r = R->LastRegister;
int c = r/2;
switch (r)
{
case 0: /* tone 0 : frequency */
case 2: /* tone 1 : frequency */
case 4: /* tone 2 : frequency */
R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4);
R->Period[c] = R->UpdateStep * R->Register[r];
if (R->Period[c] == 0) R->Period[c] = 0x3fe;
if (r == 4)
{
/* update noise shift frequency */
if ((R->Register[6] & 0x03) == 0x03)
R->Period[3] = 2 * R->Period[2];
}
break;
}
}
device.write(data);
}
static void SN76496Update(Bitu length) {
//Disable the channel if it's been quiet for a while
if ((tandy.last_write+5000)<PIC_Ticks) {
tandy.enabled=false;
tandy.chan->Enable(false);
return;
}
int i;
struct SN76496 *R = &sn;
Bit16s * buffer=(Bit16s *)MixTemp;
const Bitu MAX_SAMPLES = 2048;
if (length > MAX_SAMPLES)
return;
Bit16s buffer[MAX_SAMPLES];
Bit16s* outputs = buffer;
/* If the volume is 0, increase the counter */
for (i = 0;i < 4;i++)
{
if (R->Volume[i] == 0)
{
/* note that I do count += length, NOT count = length + 1. You might think */
/* it's the same since the volume is 0, but doing the latter could cause */
/* interferencies when the program is rapidly modulating the volume. */
if (R->Count[i] <= (int)length*STEP) R->Count[i] += length*STEP;
}
}
Bitu count=length;
while (count)
{
int vol[4];
unsigned int out;
int left;
/* vol[] keeps track of how long each square wave stays */
/* in the 1 position during the sample period. */
vol[0] = vol[1] = vol[2] = vol[3] = 0;
for (i = 0;i < 3;i++)
{
if (R->Output[i]) vol[i] += R->Count[i];
R->Count[i] -= STEP;
/* Period[i] is the half period of the square wave. Here, in each */
/* loop I add Period[i] twice, so that at the end of the loop the */
/* square wave is in the same status (0 or 1) it was at the start. */
/* vol[i] is also incremented by Period[i], since the wave has been 1 */
/* exactly half of the time, regardless of the initial position. */
/* If we exit the loop in the middle, Output[i] has to be inverted */
/* and vol[i] incremented only if the exit status of the square */
/* wave is 1. */
while (R->Count[i] <= 0)
{
R->Count[i] += R->Period[i];
if (R->Count[i] > 0)
{
R->Output[i] ^= 1;
if (R->Output[i]) vol[i] += R->Period[i];
break;
}
R->Count[i] += R->Period[i];
vol[i] += R->Period[i];
}
if (R->Output[i]) vol[i] -= R->Count[i];
}
left = STEP;
do
{
int nextevent;
if (R->Count[3] < left) nextevent = R->Count[3];
else nextevent = left;
if (R->Output[3]) vol[3] += R->Count[3];
R->Count[3] -= nextevent;
if (R->Count[3] <= 0)
{
if (R->RNG & 1) R->RNG ^= R->NoiseFB;
R->RNG >>= 1;
R->Output[3] = R->RNG & 1;
R->Count[3] += R->Period[3];
if (R->Output[3]) vol[3] += R->Period[3];
}
if (R->Output[3]) vol[3] -= R->Count[3];
left -= nextevent;
} while (left > 0);
out = vol[0] * R->Volume[0] + vol[1] * R->Volume[1] +
vol[2] * R->Volume[2] + vol[3] * R->Volume[3];
if (out > MAX_OUTPUT * STEP) out = MAX_OUTPUT * STEP;
*(buffer++) = (Bit16s)(out / STEP);
count--;
}
tandy.chan->AddSamples_m16(length,(Bit16s *)MixTemp);
device_sound_interface::sound_stream stream;
static_cast<device_sound_interface&>(device).sound_stream_update(stream, 0, &outputs, length);
tandy.chan->AddSamples_m16(length, buffer);
}
static void SN76496_set_clock(int clock) {
struct SN76496 *R = &sn;
/* the base clock for the tone generators is the chip clock divided by 16; */
/* for the noise generator, it is clock / 256. */
/* Here we calculate the number of steps which happen during one sample */
/* at the given sample rate. No. of events = sample rate / (clock/16). */
/* STEP is a multiplier used to turn the fraction into a fixed point */
/* number. */
R->UpdateStep = (unsigned int)(((double)STEP * R->SampleRate * 16) / clock);
}
static void SN76496_set_gain(int gain) {
struct SN76496 *R = &sn;
int i;
double out;
gain &= 0xff;
/* increase max output basing on gain (0.2 dB per step) */
out = MAX_OUTPUT / 3;
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 / 3) R->VolTable[i] = MAX_OUTPUT / 3;
else R->VolTable[i] = (int)out;
out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */
}
R->VolTable[15] = 0;
}
bool TS_Get_Address(Bitu& tsaddr, Bitu& tsirq, Bitu& tsdma) {
tsaddr=0;
tsirq =0;
@ -548,26 +327,9 @@ public:
tandy.enabled=false;
real_writeb(0x40,0xd4,0xff); /* BIOS Tandy DAC initialization value */
Bitu i;
struct SN76496 *R = &sn;
R->SampleRate = sample_rate;
SN76496_set_clock(3579545);
for (i = 0;i < 4;i++) R->Volume[i] = 0;
R->LastRegister = 0;
for (i = 0;i < 8;i+=2)
{
R->Register[i] = 0;
R->Register[i + 1] = 0x0f; /* volume = 0 */
}
for (i = 0;i < 4;i++)
{
R->Output[i] = 0;
R->Period[i] = R->Count[i] = R->UpdateStep;
}
R->RNG = NG_PRESET;
R->Output[3] = R->RNG & 1;
SN76496_set_gain(0x1);
((device_t&)device).device_start();
device.convert_samplerate(sample_rate);
}
~TANDYSOUND(){ }
};