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:
commit
55603e3aa3
19 changed files with 7812 additions and 650 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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).");
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
};
|
||||
|
||||
|
|
9
src/hardware/mame/Makefile.am
Normal file
9
src/hardware/mame/Makefile.am
Normal 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
123
src/hardware/mame/emu.h
Normal 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
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
109
src/hardware/mame/fmopl.h
Normal 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
|
471
src/hardware/mame/saa1099.cpp
Normal file
471
src/hardware/mame/saa1099.cpp
Normal 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
120
src/hardware/mame/saa1099.h
Normal 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
|
491
src/hardware/mame/sn76496.cpp
Normal file
491
src/hardware/mame/sn76496.cpp
Normal 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
167
src/hardware/mame/sn76496.h
Normal 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
|
650
src/hardware/mame/ymdeltat.cpp
Normal file
650
src/hardware/mame/ymdeltat.cpp
Normal 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;
|
||||
}
|
87
src/hardware/mame/ymdeltat.h
Normal file
87
src/hardware/mame/ymdeltat.h
Normal 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
2792
src/hardware/mame/ymf262.cpp
Normal file
File diff suppressed because it is too large
Load diff
40
src/hardware/mame/ymf262.h
Normal file
40
src/hardware/mame/ymf262.h
Normal 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
|
|
@ -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(){ }
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue