summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2015-09-13 08:41:44 +0200
committer Miodrag Milanovic <mmicko@gmail.com>2015-09-13 08:41:44 +0200
commitf88cefad27a1737c76e09d99c9fb43e173506081 (patch)
tree2d8167d03579c46e226471747eb4407bd00ed6fa /src/devices/sound
parente92ac9e0fa8e99869894bea00589bbb526be30aa (diff)
Move all devices into separate part of src tree (nw)
Diffstat (limited to 'src/devices/sound')
-rw-r--r--src/devices/sound/2151intf.c131
-rw-r--r--src/devices/sound/2151intf.h84
-rw-r--r--src/devices/sound/2203intf.c195
-rw-r--r--src/devices/sound/2203intf.h56
-rw-r--r--src/devices/sound/2413intf.c95
-rw-r--r--src/devices/sound/2413intf.h42
-rw-r--r--src/devices/sound/2608intf.c231
-rw-r--r--src/devices/sound/2608intf.h53
-rw-r--r--src/devices/sound/2610intf.c226
-rw-r--r--src/devices/sound/2610intf.h65
-rw-r--r--src/devices/sound/2612intf.c183
-rw-r--r--src/devices/sound/2612intf.h64
-rw-r--r--src/devices/sound/262intf.c155
-rw-r--r--src/devices/sound/262intf.h51
-rw-r--r--src/devices/sound/315-5641.c70
-rw-r--r--src/devices/sound/315-5641.h30
-rw-r--r--src/devices/sound/3526intf.c173
-rw-r--r--src/devices/sound/3526intf.h57
-rw-r--r--src/devices/sound/3812intf.c171
-rw-r--r--src/devices/sound/3812intf.h56
-rw-r--r--src/devices/sound/8950intf.c226
-rw-r--r--src/devices/sound/8950intf.h81
-rw-r--r--src/devices/sound/aica.c1662
-rw-r--r--src/devices/sound/aica.h213
-rw-r--r--src/devices/sound/aicadsp.c351
-rw-r--r--src/devices/sound/aicadsp.h42
-rw-r--r--src/devices/sound/amiga.c245
-rw-r--r--src/devices/sound/amiga.h65
-rw-r--r--src/devices/sound/asc.c597
-rw-r--r--src/devices/sound/asc.h136
-rw-r--r--src/devices/sound/astrocde.c285
-rw-r--r--src/devices/sound/astrocde.h73
-rw-r--r--src/devices/sound/awacs.c176
-rw-r--r--src/devices/sound/awacs.h77
-rw-r--r--src/devices/sound/ay8910.c1270
-rw-r--r--src/devices/sound/ay8910.h273
-rw-r--r--src/devices/sound/beep.c147
-rw-r--r--src/devices/sound/beep.h44
-rw-r--r--src/devices/sound/bsmt2000.c358
-rw-r--r--src/devices/sound/bsmt2000.h112
-rw-r--r--src/devices/sound/c140.c489
-rw-r--r--src/devices/sound/c140.h124
-rw-r--r--src/devices/sound/c352.c551
-rw-r--r--src/devices/sound/c352.h121
-rw-r--r--src/devices/sound/c6280.c302
-rw-r--r--src/devices/sound/c6280.h61
-rw-r--r--src/devices/sound/cdda.c271
-rw-r--r--src/devices/sound/cdda.h58
-rw-r--r--src/devices/sound/cdp1863.c198
-rw-r--r--src/devices/sound/cdp1863.h89
-rw-r--r--src/devices/sound/cdp1864.c467
-rw-r--r--src/devices/sound/cdp1864.h208
-rw-r--r--src/devices/sound/cdp1869.c1004
-rw-r--r--src/devices/sound/cdp1869.h302
-rw-r--r--src/devices/sound/cem3394.c552
-rw-r--r--src/devices/sound/cem3394.h117
-rw-r--r--src/devices/sound/dac.c92
-rw-r--r--src/devices/sound/dac.h70
-rw-r--r--src/devices/sound/digitalk.c692
-rw-r--r--src/devices/sound/digitalk.h101
-rw-r--r--src/devices/sound/disc_cls.h258
-rw-r--r--src/devices/sound/disc_dev.h129
-rw-r--r--src/devices/sound/disc_dev.inc1776
-rw-r--r--src/devices/sound/disc_flt.h180
-rw-r--r--src/devices/sound/disc_flt.inc1438
-rw-r--r--src/devices/sound/disc_inp.inc325
-rw-r--r--src/devices/sound/disc_mth.h240
-rw-r--r--src/devices/sound/disc_mth.inc2773
-rw-r--r--src/devices/sound/disc_sys.inc123
-rw-r--r--src/devices/sound/disc_wav.h193
-rw-r--r--src/devices/sound/disc_wav.inc1787
-rw-r--r--src/devices/sound/discrete.c1148
-rw-r--r--src/devices/sound/discrete.h4728
-rw-r--r--src/devices/sound/dmadac.c238
-rw-r--r--src/devices/sound/dmadac.h56
-rw-r--r--src/devices/sound/es1373.c499
-rw-r--r--src/devices/sound/es1373.h145
-rw-r--r--src/devices/sound/es5503.c451
-rw-r--r--src/devices/sound/es5503.h108
-rw-r--r--src/devices/sound/es5506.c2274
-rw-r--r--src/devices/sound/es5506.h248
-rw-r--r--src/devices/sound/es8712.c349
-rw-r--r--src/devices/sound/es8712.h78
-rw-r--r--src/devices/sound/esqpump.c170
-rw-r--r--src/devices/sound/esqpump.h124
-rw-r--r--src/devices/sound/filter.c234
-rw-r--r--src/devices/sound/filter.h133
-rw-r--r--src/devices/sound/flt_rc.c139
-rw-r--r--src/devices/sound/flt_rc.h104
-rw-r--r--src/devices/sound/flt_vol.c52
-rw-r--r--src/devices/sound/flt_vol.h50
-rw-r--r--src/devices/sound/fm.c3908
-rw-r--r--src/devices/sound/fm.h201
-rw-r--r--src/devices/sound/fm2612.c2559
-rw-r--r--src/devices/sound/fmopl.c2582
-rw-r--r--src/devices/sound/fmopl.h115
-rw-r--r--src/devices/sound/gaelco.c276
-rw-r--r--src/devices/sound/gaelco.h98
-rw-r--r--src/devices/sound/hc55516.c337
-rw-r--r--src/devices/sound/hc55516.h95
-rw-r--r--src/devices/sound/i5000.c303
-rw-r--r--src/devices/sound/i5000.h89
-rw-r--r--src/devices/sound/ics2115.c890
-rw-r--r--src/devices/sound/ics2115.h158
-rw-r--r--src/devices/sound/iremga20.c268
-rw-r--r--src/devices/sound/iremga20.h78
-rw-r--r--src/devices/sound/k005289.c242
-rw-r--r--src/devices/sound/k005289.h65
-rw-r--r--src/devices/sound/k007232.c424
-rw-r--r--src/devices/sound/k007232.h74
-rw-r--r--src/devices/sound/k051649.c288
-rw-r--r--src/devices/sound/k051649.h95
-rw-r--r--src/devices/sound/k053260.c474
-rw-r--r--src/devices/sound/k053260.h113
-rw-r--r--src/devices/sound/k054539.c534
-rw-r--r--src/devices/sound/k054539.h124
-rw-r--r--src/devices/sound/k056800.c178
-rw-r--r--src/devices/sound/k056800.h61
-rw-r--r--src/devices/sound/l7a1045_l6028_dsp_a.c348
-rw-r--r--src/devices/sound/l7a1045_l6028_dsp_a.h69
-rw-r--r--src/devices/sound/lmc1992.c232
-rw-r--r--src/devices/sound/lmc1992.h113
-rw-r--r--src/devices/sound/mas3507d.c262
-rw-r--r--src/devices/sound/mas3507d.h68
-rw-r--r--src/devices/sound/mos6560.c969
-rw-r--r--src/devices/sound/mos6560.h260
-rw-r--r--src/devices/sound/mos6581.c157
-rw-r--r--src/devices/sound/mos6581.h109
-rw-r--r--src/devices/sound/mos7360.c1206
-rw-r--r--src/devices/sound/mos7360.h205
-rw-r--r--src/devices/sound/mpeg_audio.c759
-rw-r--r--src/devices/sound/mpeg_audio.h134
-rw-r--r--src/devices/sound/msm5205.c340
-rw-r--r--src/devices/sound/msm5205.h109
-rw-r--r--src/devices/sound/msm5232.c802
-rw-r--r--src/devices/sound/msm5232.h111
-rw-r--r--src/devices/sound/multipcm.c658
-rw-r--r--src/devices/sound/multipcm.h110
-rw-r--r--src/devices/sound/n63701x.c153
-rw-r--r--src/devices/sound/n63701x.h69
-rw-r--r--src/devices/sound/namco.c850
-rw-r--r--src/devices/sound/namco.h132
-rw-r--r--src/devices/sound/nes_apu.c774
-rw-r--r--src/devices/sound/nes_apu.h96
-rw-r--r--src/devices/sound/nes_defs.h290
-rw-r--r--src/devices/sound/nile.c228
-rw-r--r--src/devices/sound/nile.h60
-rw-r--r--src/devices/sound/okiadpcm.c192
-rw-r--r--src/devices/sound/okiadpcm.h64
-rw-r--r--src/devices/sound/okim6258.c337
-rw-r--r--src/devices/sound/okim6258.h99
-rw-r--r--src/devices/sound/okim6295.c421
-rw-r--r--src/devices/sound/okim6295.h126
-rw-r--r--src/devices/sound/okim6376.c581
-rw-r--r--src/devices/sound/okim6376.h82
-rw-r--r--src/devices/sound/okim9810.c585
-rw-r--r--src/devices/sound/okim9810.h163
-rw-r--r--src/devices/sound/pci-ac97.c36
-rw-r--r--src/devices/sound/pci-ac97.h28
-rw-r--r--src/devices/sound/pokey.c1453
-rw-r--r--src/devices/sound/pokey.h381
-rw-r--r--src/devices/sound/pokey.txt319
-rw-r--r--src/devices/sound/qs1000.c646
-rw-r--r--src/devices/sound/qs1000.h165
-rw-r--r--src/devices/sound/qsound.c324
-rw-r--r--src/devices/sound/qsound.h81
-rw-r--r--src/devices/sound/rf5c400.c551
-rw-r--r--src/devices/sound/rf5c400.h117
-rw-r--r--src/devices/sound/rf5c68.c233
-rw-r--r--src/devices/sound/rf5c68.h96
-rw-r--r--src/devices/sound/s14001a.c651
-rw-r--r--src/devices/sound/s14001a.h67
-rw-r--r--src/devices/sound/saa1099.c452
-rw-r--r--src/devices/sound/saa1099.h112
-rw-r--r--src/devices/sound/samples.c642
-rw-r--r--src/devices/sound/samples.h187
-rw-r--r--src/devices/sound/sb0400.c24
-rw-r--r--src/devices/sound/sb0400.h27
-rw-r--r--src/devices/sound/scsp.c1574
-rw-r--r--src/devices/sound/scsp.h214
-rw-r--r--src/devices/sound/scspdsp.c355
-rw-r--r--src/devices/sound/scspdsp.h42
-rw-r--r--src/devices/sound/segapcm.c151
-rw-r--r--src/devices/sound/segapcm.h70
-rw-r--r--src/devices/sound/sid.c352
-rw-r--r--src/devices/sound/sid.h66
-rw-r--r--src/devices/sound/side6581.h128
-rw-r--r--src/devices/sound/sidenvel.c588
-rw-r--r--src/devices/sound/sidenvel.h39
-rw-r--r--src/devices/sound/sidvoice.c810
-rw-r--r--src/devices/sound/sidvoice.h120
-rw-r--r--src/devices/sound/sidw6581.h1053
-rw-r--r--src/devices/sound/sidw8580.h1399
-rw-r--r--src/devices/sound/sn76477.c2308
-rw-r--r--src/devices/sound/sn76477.h337
-rw-r--r--src/devices/sound/sn76496.c441
-rw-r--r--src/devices/sound/sn76496.h144
-rw-r--r--src/devices/sound/snkwave.c153
-rw-r--r--src/devices/sound/snkwave.h64
-rw-r--r--src/devices/sound/sp0250.c247
-rw-r--r--src/devices/sound/sp0250.h58
-rw-r--r--src/devices/sound/sp0256.c1367
-rw-r--r--src/devices/sound/sp0256.h136
-rw-r--r--src/devices/sound/speaker.c409
-rw-r--r--src/devices/sound/speaker.h93
-rw-r--r--src/devices/sound/spu.c3081
-rw-r--r--src/devices/sound/spu.h255
-rw-r--r--src/devices/sound/spu_tables.c703
-rw-r--r--src/devices/sound/spureverb.c343
-rw-r--r--src/devices/sound/spureverb.h72
-rw-r--r--src/devices/sound/st0016.c164
-rw-r--r--src/devices/sound/st0016.h54
-rw-r--r--src/devices/sound/t6721a.c112
-rw-r--r--src/devices/sound/t6721a.h123
-rw-r--r--src/devices/sound/t6w28.c356
-rw-r--r--src/devices/sound/t6w28.h47
-rw-r--r--src/devices/sound/tc8830f.c256
-rw-r--r--src/devices/sound/tc8830f.h71
-rw-r--r--src/devices/sound/tiaintf.c64
-rw-r--r--src/devices/sound/tiaintf.h50
-rw-r--r--src/devices/sound/tiasound.c612
-rw-r--r--src/devices/sound/tiasound.h47
-rw-r--r--src/devices/sound/tms3615.c109
-rw-r--r--src/devices/sound/tms3615.h61
-rw-r--r--src/devices/sound/tms36xx.c557
-rw-r--r--src/devices/sound/tms36xx.h137
-rw-r--r--src/devices/sound/tms5110.c1620
-rw-r--r--src/devices/sound/tms5110.h397
-rw-r--r--src/devices/sound/tms5110r.inc705
-rw-r--r--src/devices/sound/tms5220.c1953
-rw-r--r--src/devices/sound/tms5220.h254
-rw-r--r--src/devices/sound/tms5220.txt86
-rw-r--r--src/devices/sound/upd7752.c195
-rw-r--r--src/devices/sound/upd7752.h78
-rw-r--r--src/devices/sound/upd7759.c874
-rw-r--r--src/devices/sound/upd7759.h151
-rw-r--r--src/devices/sound/vlm5030.c655
-rw-r--r--src/devices/sound/vlm5030.h100
-rw-r--r--src/devices/sound/votrax.c1362
-rw-r--r--src/devices/sound/votrax.h164
-rw-r--r--src/devices/sound/vrc6.c320
-rw-r--r--src/devices/sound/vrc6.h61
-rw-r--r--src/devices/sound/vrender0.c250
-rw-r--r--src/devices/sound/vrender0.h63
-rw-r--r--src/devices/sound/wave.c110
-rw-r--r--src/devices/sound/wave.h47
-rw-r--r--src/devices/sound/wavwrite.c189
-rw-r--r--src/devices/sound/wavwrite.h18
-rw-r--r--src/devices/sound/x1_010.c276
-rw-r--r--src/devices/sound/x1_010.h58
-rw-r--r--src/devices/sound/ym2151.c2500
-rw-r--r--src/devices/sound/ym2151.h88
-rw-r--r--src/devices/sound/ym2151.txt162
-rw-r--r--src/devices/sound/ym2413.c2176
-rw-r--r--src/devices/sound/ym2413.h45
-rw-r--r--src/devices/sound/ymdeltat.c650
-rw-r--r--src/devices/sound/ymdeltat.h86
-rw-r--r--src/devices/sound/ymf262.c2715
-rw-r--r--src/devices/sound/ymf262.h50
-rw-r--r--src/devices/sound/ymf271.c1777
-rw-r--r--src/devices/sound/ymf271.h166
-rw-r--r--src/devices/sound/ymf278b.c1051
-rw-r--r--src/devices/sound/ymf278b.h147
-rw-r--r--src/devices/sound/ymz280b.c968
-rw-r--r--src/devices/sound/ymz280b.h133
-rw-r--r--src/devices/sound/ymz770.c345
-rw-r--r--src/devices/sound/ymz770.h99
-rw-r--r--src/devices/sound/zsg2.c464
-rw-r--r--src/devices/sound/zsg2.h92
269 files changed, 112276 insertions, 0 deletions
diff --git a/src/devices/sound/2151intf.c b/src/devices/sound/2151intf.c
new file mode 100644
index 00000000000..e4739c758ee
--- /dev/null
+++ b/src/devices/sound/2151intf.c
@@ -0,0 +1,131 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+/***************************************************************************
+
+ 2151intf.c
+
+ Support interface YM2151(OPM)
+
+***************************************************************************/
+
+#include "emu.h"
+#include "fm.h"
+#include "2151intf.h"
+#include "ym2151.h"
+
+
+
+const device_type YM2151 = &device_creator<ym2151_device>;
+
+
+//-------------------------------------------------
+// ym2151_device - constructor
+//-------------------------------------------------
+
+ym2151_device::ym2151_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, YM2151, "YM2151", tag, owner, clock, "ym2151", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_irqhandler(*this),
+ m_portwritehandler(*this)
+{
+}
+
+
+//-------------------------------------------------
+// read - read from the device
+//-------------------------------------------------
+
+READ8_MEMBER( ym2151_device::read )
+{
+ if (offset & 1)
+ {
+ m_stream->update();
+ return ym2151_read_status(m_chip);
+ }
+ else
+ return 0xff; /* confirmed on a real YM2151 */
+}
+
+
+//-------------------------------------------------
+// write - write from the device
+//-------------------------------------------------
+
+WRITE8_MEMBER( ym2151_device::write )
+{
+ if (offset & 1)
+ {
+ m_stream->update();
+ ym2151_write_reg(m_chip, m_lastreg, data);
+ }
+ else
+ m_lastreg = data;
+}
+
+
+READ8_MEMBER( ym2151_device::status_r ) { return read(space, 1); }
+
+WRITE8_MEMBER( ym2151_device::register_w ) { write(space, 0, data); }
+WRITE8_MEMBER( ym2151_device::data_w ) { write(space, 1, data); }
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ym2151_device::device_start()
+{
+ m_irqhandler.resolve_safe();
+ m_portwritehandler.resolve_safe();
+
+ // stream setup
+ int rate = clock() / 64;
+ m_stream = stream_alloc(0, 2, rate);
+
+ m_chip = ym2151_init(this, clock(), rate);
+ assert_always(m_chip != NULL, "Error creating YM2151 chip");
+
+ ym2151_set_irq_handler(m_chip, irq_frontend);
+ ym2151_set_port_write_handler(m_chip, port_write_frontend);
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ym2151_device::device_reset()
+{
+ ym2151_reset_chip(m_chip);
+}
+
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void ym2151_device::device_stop()
+{
+ ym2151_shutdown(m_chip);
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void ym2151_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ ym2151_update_one(m_chip, outputs, samples);
+}
+
+
+void ym2151_device::irq_frontend(device_t *device, int irq)
+{
+ downcast<ym2151_device *>(device)->m_irqhandler(irq);
+}
+
+void ym2151_device::port_write_frontend(device_t *device, offs_t offset, UINT8 data)
+{
+ downcast<ym2151_device *>(device)->m_portwritehandler(offset, data);
+}
diff --git a/src/devices/sound/2151intf.h b/src/devices/sound/2151intf.h
new file mode 100644
index 00000000000..79952d76228
--- /dev/null
+++ b/src/devices/sound/2151intf.h
@@ -0,0 +1,84 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+/***************************************************************************
+
+ 2151intf.h
+
+ MAME interface to YM2151 emulator.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __2151INTF_H__
+#define __2151INTF_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_YM2151_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, YM2151, _clock)
+
+#define MCFG_YM2151_IRQ_HANDLER(_devcb) \
+ devcb = &ym2151_device::set_irq_handler(*device, DEVCB_##_devcb);
+#define MCFG_YM2151_PORT_WRITE_HANDLER(_devcb) \
+ devcb = &ym2151_device::set_port_write_handler(*device, DEVCB_##_devcb);
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+
+// ======================> ym2151_device
+
+class ym2151_device : public device_t,
+ public device_sound_interface
+{
+public:
+ // construction/destruction
+ ym2151_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<ym2151_device &>(device).m_irqhandler.set_callback(object); }
+ template<class _Object> static devcb_base &set_port_write_handler(device_t &device, _Object object) { return downcast<ym2151_device &>(device).m_portwritehandler.set_callback(object); }
+
+ // read/write
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ DECLARE_READ8_MEMBER( status_r );
+ DECLARE_WRITE8_MEMBER( register_w );
+ DECLARE_WRITE8_MEMBER( data_w );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_stop();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ // internal helpers
+ static void irq_frontend(device_t *device, int irq);
+ static void port_write_frontend(device_t *device, offs_t offset, UINT8 data);
+
+ // internal state
+ sound_stream * m_stream;
+ //emu_timer * m_timer[2];
+ void * m_chip;
+ UINT8 m_lastreg;
+ devcb_write_line m_irqhandler;
+ devcb_write8 m_portwritehandler;
+};
+
+
+// device type definition
+extern const device_type YM2151;
+
+
+#endif /* __2151INTF_H__ */
diff --git a/src/devices/sound/2203intf.c b/src/devices/sound/2203intf.c
new file mode 100644
index 00000000000..279ae52cea4
--- /dev/null
+++ b/src/devices/sound/2203intf.c
@@ -0,0 +1,195 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+#include "2203intf.h"
+#include "fm.h"
+
+static void psg_set_clock(void *param, int clock)
+{
+ ym2203_device *ym2203 = (ym2203_device *) param;
+ ym2203->ay_set_clock(clock);
+}
+
+static void psg_write(void *param, int address, int data)
+{
+ ym2203_device *ym2203 = (ym2203_device *) param;
+ ym2203->ay8910_write_ym(address, data);
+}
+
+static int psg_read(void *param)
+{
+ ym2203_device *ym2203 = (ym2203_device *) param;
+ return ym2203->ay8910_read_ym();
+}
+
+static void psg_reset(void *param)
+{
+ ym2203_device *ym2203 = (ym2203_device *) param;
+ ym2203->ay8910_reset_ym();
+}
+
+static const ssg_callbacks psgintf =
+{
+ psg_set_clock,
+ psg_write,
+ psg_read,
+ psg_reset
+};
+
+/* IRQ Handler */
+static void IRQHandler(void *param,int irq)
+{
+ ym2203_device *ym2203 = (ym2203_device *) param;
+ ym2203->_IRQHandler(irq);
+}
+
+void ym2203_device::_IRQHandler(int irq)
+{
+ if (!m_irq_handler.isnull())
+ m_irq_handler(irq);
+}
+
+/* Timer overflow callback from timer.c */
+void ym2203_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case 0:
+ ym2203_timer_over(m_chip,0);
+ break;
+
+ case 1:
+ ym2203_timer_over(m_chip,1);
+ break;
+ }
+}
+
+static void timer_handler(void *param,int c,int count,int clock)
+{
+ ym2203_device *ym2203 = (ym2203_device *) param;
+ ym2203->_timer_handler(c, count, clock);
+}
+
+void ym2203_device::_timer_handler(int c,int count,int clock)
+{
+ if( count == 0 )
+ { /* Reset FM Timer */
+ m_timer[c]->enable(false);
+ }
+ else
+ { /* Start FM Timer */
+ attotime period = attotime::from_hz(clock) * count;
+
+ if (!m_timer[c]->enable(true))
+ m_timer[c]->adjust(period);
+ }
+}
+
+/* update request from fm.c */
+void ym2203_update_request(void *param)
+{
+ ym2203_device *ym2203 = (ym2203_device *) param;
+ ym2203->_ym2203_update_request();
+}
+
+void ym2203_device::_ym2203_update_request()
+{
+ m_stream->update();
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+
+void ym2203_device::stream_generate(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ ym2203_update_one(m_chip, outputs[0], samples);
+}
+
+
+void ym2203_device::device_post_load()
+{
+ ym2203_postload(m_chip);
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ym2203_device::device_start()
+{
+ ay8910_device::device_start();
+
+ int rate = clock()/72; /* ??? */
+
+ m_irq_handler.resolve();
+
+ /* Timer Handler set */
+ m_timer[0] = timer_alloc(0);
+ m_timer[1] = timer_alloc(1);
+
+ /* stream system initialize */
+ m_stream = machine().sound().stream_alloc(*this,0,1,rate, stream_update_delegate(FUNC(ym2203_device::stream_generate),this));
+
+ /* Initialize FM emurator */
+ m_chip = ym2203_init(this,this,clock(),rate,timer_handler,IRQHandler,&psgintf);
+ assert_always(m_chip != NULL, "Error creating YM2203 chip");
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void ym2203_device::device_stop()
+{
+ ym2203_shutdown(m_chip);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ym2203_device::device_reset()
+{
+ ym2203_reset_chip(m_chip);
+}
+
+
+READ8_MEMBER( ym2203_device::read )
+{
+ return ym2203_read(m_chip, offset & 1);
+}
+
+WRITE8_MEMBER( ym2203_device::write )
+{
+ ym2203_write(m_chip, offset & 1, data);
+}
+
+READ8_MEMBER( ym2203_device::status_port_r )
+{
+ return read(space, 0);
+}
+
+READ8_MEMBER( ym2203_device::read_port_r )
+{
+ return read(space, 1);
+}
+
+WRITE8_MEMBER( ym2203_device::control_port_w )
+{
+ write(space, 0, data);
+}
+
+WRITE8_MEMBER( ym2203_device::write_port_w )
+{
+ write(space, 1, data);
+}
+
+const device_type YM2203 = &device_creator<ym2203_device>;
+
+ym2203_device::ym2203_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ay8910_device(mconfig, YM2203, "YM2203", tag, owner, clock, PSG_TYPE_YM, 3, 2, "ym2203", __FILE__),
+ m_irq_handler(*this)
+{
+}
diff --git a/src/devices/sound/2203intf.h b/src/devices/sound/2203intf.h
new file mode 100644
index 00000000000..8d81c54282a
--- /dev/null
+++ b/src/devices/sound/2203intf.h
@@ -0,0 +1,56 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+#pragma once
+
+#ifndef __2203INTF_H__
+#define __2203INTF_H__
+
+#include "emu.h"
+#include "ay8910.h"
+
+void ym2203_update_request(void *param);
+
+#define MCFG_YM2203_IRQ_HANDLER(_devcb) \
+ devcb = &ym2203_device::set_irq_handler(*device, DEVCB_##_devcb);
+
+class ym2203_device : public ay8910_device
+{
+public:
+ ym2203_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<ym2203_device &>(device).m_irq_handler.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ DECLARE_READ8_MEMBER( status_port_r );
+ DECLARE_READ8_MEMBER( read_port_r );
+ DECLARE_WRITE8_MEMBER( control_port_w );
+ DECLARE_WRITE8_MEMBER( write_port_w );
+
+ void _IRQHandler(int irq);
+ void _timer_handler(int c,int count,int clock);
+ void _ym2203_update_request();
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_post_load();
+ virtual void device_stop();
+ virtual void device_reset();
+
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+ void stream_generate(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+private:
+ // internal state
+ sound_stream * m_stream;
+ emu_timer * m_timer[2];
+ void * m_chip;
+ devcb_write_line m_irq_handler;
+};
+
+extern const device_type YM2203;
+
+
+#endif /* __2203INTF_H__ */
diff --git a/src/devices/sound/2413intf.c b/src/devices/sound/2413intf.c
new file mode 100644
index 00000000000..3ecfd061241
--- /dev/null
+++ b/src/devices/sound/2413intf.c
@@ -0,0 +1,95 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+/****************************************************************
+
+ MAME / MESS functions
+
+****************************************************************/
+
+#include "emu.h"
+#include "ym2413.h"
+#include "2413intf.h"
+
+
+static void ym2413_update_request(void *param, int interval)
+{
+ ym2413_device *ym2413 = (ym2413_device *) param;
+ ym2413->_ym2413_update_request();
+}
+
+void ym2413_device::_ym2413_update_request()
+{
+ m_stream->update();
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void ym2413_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ ym2413_update_one(m_chip, outputs, samples);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ym2413_device::device_start()
+{
+ int rate = clock()/72;
+
+ /* emulator create */
+ m_chip = ym2413_init(this, clock(), rate);
+ assert_always(m_chip != NULL, "Error creating YM2413 chip");
+
+ /* stream system initialize */
+ m_stream = machine().sound().stream_alloc(*this,0,2,rate);
+
+ ym2413_set_update_handler(m_chip, ym2413_update_request, this);
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void ym2413_device::device_stop()
+{
+ ym2413_shutdown(m_chip);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ym2413_device::device_reset()
+{
+ ym2413_reset_chip(m_chip);
+}
+
+
+WRITE8_MEMBER( ym2413_device::write )
+{
+ ym2413_write(m_chip, offset & 1, data);
+}
+
+WRITE8_MEMBER( ym2413_device::register_port_w ) { write(space, 0, data); }
+WRITE8_MEMBER( ym2413_device::data_port_w ) { write(space, 1, data); }
+
+const device_type YM2413 = &device_creator<ym2413_device>;
+
+ym2413_device::ym2413_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, YM2413, "YM2413", tag, owner, clock, "ym2413", __FILE__),
+ device_sound_interface(mconfig, *this)
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void ym2413_device::device_config_complete()
+{
+}
diff --git a/src/devices/sound/2413intf.h b/src/devices/sound/2413intf.h
new file mode 100644
index 00000000000..0edf7d1a696
--- /dev/null
+++ b/src/devices/sound/2413intf.h
@@ -0,0 +1,42 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+#pragma once
+
+#ifndef __2413INTF_H__
+#define __2413INTF_H__
+
+#include "emu.h"
+
+class ym2413_device : public device_t,
+ public device_sound_interface
+{
+public:
+ ym2413_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_WRITE8_MEMBER( write );
+
+ DECLARE_WRITE8_MEMBER( register_port_w );
+ DECLARE_WRITE8_MEMBER( data_port_w );
+
+ void _ym2413_update_request();
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+ virtual void device_stop();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ // internal state
+ sound_stream * m_stream;
+ void * m_chip;
+};
+
+extern const device_type YM2413;
+
+
+#endif /* __2413INTF_H__ */
diff --git a/src/devices/sound/2608intf.c b/src/devices/sound/2608intf.c
new file mode 100644
index 00000000000..4a2c25597bb
--- /dev/null
+++ b/src/devices/sound/2608intf.c
@@ -0,0 +1,231 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+/***************************************************************************
+
+ 2608intf.c
+
+ The YM2608 emulator supports up to 2 chips.
+ Each chip has the following connections:
+ - Status Read / Control Write A
+ - Port Read / Data Write A
+ - Control Write B
+ - Data Write B
+
+***************************************************************************/
+
+#include "2608intf.h"
+#include "fm.h"
+
+static void psg_set_clock(void *param, int clock)
+{
+ ym2608_device *ym2608 = (ym2608_device *) param;
+ ym2608->ay_set_clock(clock);
+}
+
+static void psg_write(void *param, int address, int data)
+{
+ ym2608_device *ym2608 = (ym2608_device *) param;
+ ym2608->ay8910_write_ym(address, data);
+}
+
+static int psg_read(void *param)
+{
+ ym2608_device *ym2608 = (ym2608_device *) param;
+ return ym2608->ay8910_read_ym();
+}
+
+static void psg_reset(void *param)
+{
+ ym2608_device *ym2608 = (ym2608_device *) param;
+ ym2608->ay8910_reset_ym();
+}
+
+static const ssg_callbacks psgintf =
+{
+ psg_set_clock,
+ psg_write,
+ psg_read,
+ psg_reset
+};
+
+/* IRQ Handler */
+static void IRQHandler(void *param,int irq)
+{
+ ym2608_device *ym2608 = (ym2608_device *) param;
+ ym2608->_IRQHandler(irq);
+}
+
+void ym2608_device::_IRQHandler(int irq)
+{
+ if (!m_irq_handler.isnull())
+ m_irq_handler(irq);
+}
+
+/* Timer overflow callback from timer.c */
+void ym2608_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case 0:
+ ym2608_timer_over(m_chip,0);
+ break;
+
+ case 1:
+ ym2608_timer_over(m_chip,1);
+ break;
+ }
+}
+
+static void timer_handler(void *param,int c,int count,int clock)
+{
+ ym2608_device *ym2608 = (ym2608_device *) param;
+ ym2608->_timer_handler(c, count, clock);
+}
+
+void ym2608_device::_timer_handler(int c,int count,int clock)
+{
+ if( count == 0 )
+ { /* Reset FM Timer */
+ m_timer[c]->enable(false);
+ }
+ else
+ { /* Start FM Timer */
+ attotime period = attotime::from_hz(clock) * count;
+
+ if (!m_timer[c]->enable(true))
+ m_timer[c]->adjust(period);
+ }
+}
+
+/* update request from fm.c */
+void ym2608_update_request(void *param)
+{
+ ym2608_device *ym2608 = (ym2608_device *) param;
+ ym2608->_ym2608_update_request();
+}
+
+void ym2608_device::_ym2608_update_request()
+{
+ m_stream->update();
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void ym2608_device::stream_generate(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ ym2608_update_one(m_chip, outputs, samples);
+}
+
+void ym2608_device::device_post_load()
+{
+ ym2608_postload(m_chip);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ym2608_device::device_start()
+{
+ ay8910_device::device_start();
+
+ int rate = clock()/72;
+ void *pcmbufa;
+ int pcmsizea;
+
+ m_irq_handler.resolve();
+
+ /* Timer Handler set */
+ m_timer[0] = timer_alloc(0);
+ m_timer[1] = timer_alloc(1);
+
+ /* stream system initialize */
+ m_stream = machine().sound().stream_alloc(*this,0,2,rate, stream_update_delegate(FUNC(ym2608_device::stream_generate),this));
+ /* setup adpcm buffers */
+ pcmbufa = region()->base();
+ pcmsizea = region()->bytes();
+
+ /* initialize YM2608 */
+ m_chip = ym2608_init(this,this,clock(),rate,
+ pcmbufa,pcmsizea,
+ timer_handler,IRQHandler,&psgintf);
+ assert_always(m_chip != NULL, "Error creating YM2608 chip");
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void ym2608_device::device_stop()
+{
+ ym2608_shutdown(m_chip);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ym2608_device::device_reset()
+{
+ ym2608_reset_chip(m_chip);
+}
+
+
+READ8_MEMBER( ym2608_device::read )
+{
+ return ym2608_read(m_chip, offset & 3);
+}
+
+WRITE8_MEMBER( ym2608_device::write )
+{
+ ym2608_write(m_chip, offset & 3, data);
+}
+
+const device_type YM2608 = &device_creator<ym2608_device>;
+
+ym2608_device::ym2608_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ay8910_device(mconfig, YM2608, "YM2608", tag, owner, clock, PSG_TYPE_YM, 1, 2, "ym2608", __FILE__),
+ m_irq_handler(*this)
+{
+}
+
+ROM_START( ym2608 )
+ ROM_REGION( 0x2000, "ym2608", 0 )
+ /*
+ This data is derived from the chip's output - internal ROM can't be read.
+ It was verified, using real YM2608, that this ADPCM stream produces 100% correct output signal.
+ */
+ // see YM2608_ADPCM_ROM_addr table in fm.c for current sample offsets
+ // original offset comments from Jarek:
+ // offset 0:
+ /* Source: 01BD.ROM */
+ /* Length: 448 / 0x000001C0 */
+ // offset 0x1C0:
+ /* Source: 02SD.ROM */
+ /* Length: 640 / 0x00000280 */
+ // offset 0x440:
+ /* Source: 04TOP.ROM */
+ /* Length: 5952 / 0x00001740 */
+ // offset 0x1B80:
+ /* Source: 08HH.ROM */
+ /* Length: 384 / 0x00000180 */
+ // offset 0x1D00
+ /* Source: 10TOM.ROM */
+ /* Length: 640 / 0x00000280 */
+ // offset 0x1F80
+ /* Source: 20RIM.ROM */
+ /* Length: 128 / 0x00000080 */
+ /* while this rom was dumped by output analysis, not decap, it was tested
+ by playing it back into the chip as an external adpcm sample and produced
+ an identical dac result. a decap would be nice to verify things 100%,
+ but there is currently no reason to think this rom dump is incorrect. */
+ ROM_LOAD16_WORD( "ym2608_adpcm_rom.bin", 0x0000, 0x2000, CRC(23c9e0d8) SHA1(50b6c3e288eaa12ad275d4f323267bb72b0445df) )
+ROM_END
+
+
+const rom_entry *ym2608_device::device_rom_region() const
+{
+ return ROM_NAME( ym2608 );
+}
diff --git a/src/devices/sound/2608intf.h b/src/devices/sound/2608intf.h
new file mode 100644
index 00000000000..ea610036d88
--- /dev/null
+++ b/src/devices/sound/2608intf.h
@@ -0,0 +1,53 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+#pragma once
+
+#ifndef __2608INTF_H__
+#define __2608INTF_H__
+
+#include "emu.h"
+#include "ay8910.h"
+
+void ym2608_update_request(void *param);
+
+#define MCFG_YM2608_IRQ_HANDLER(_devcb) \
+ devcb = &ym2608_device::set_irq_handler(*device, DEVCB_##_devcb);
+
+class ym2608_device : public ay8910_device
+{
+public:
+ ym2608_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<ym2608_device &>(device).m_irq_handler.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ void _IRQHandler(int irq);
+ void _timer_handler(int c,int count,int clock);
+ void _ym2608_update_request();
+
+protected:
+ // device-level overrides
+ virtual const rom_entry *device_rom_region() const;
+ virtual void device_start();
+ virtual void device_post_load();
+ virtual void device_stop();
+ virtual void device_reset();
+
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ void stream_generate(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+private:
+ // internal state
+ sound_stream * m_stream;
+ emu_timer * m_timer[2];
+ void * m_chip;
+ devcb_write_line m_irq_handler;
+};
+
+extern const device_type YM2608;
+
+
+#endif /* __2608INTF_H__ */
diff --git a/src/devices/sound/2610intf.c b/src/devices/sound/2610intf.c
new file mode 100644
index 00000000000..88f05e354cc
--- /dev/null
+++ b/src/devices/sound/2610intf.c
@@ -0,0 +1,226 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+/***************************************************************************
+
+ 2610intf.c
+
+ The YM2610 emulator supports up to 2 chips.
+ Each chip has the following connections:
+ - Status Read / Control Write A
+ - Port Read / Data Write A
+ - Control Write B
+ - Data Write B
+
+***************************************************************************/
+
+#include "2610intf.h"
+#include "fm.h"
+
+static void psg_set_clock(void *param, int clock)
+{
+ ym2610_device *ym2610 = (ym2610_device *) param;
+ ym2610->ay_set_clock(clock);
+}
+
+static void psg_write(void *param, int address, int data)
+{
+ ym2610_device *ym2610 = (ym2610_device *) param;
+ ym2610->ay8910_write_ym(address, data);
+}
+
+static int psg_read(void *param)
+{
+ ym2610_device *ym2610 = (ym2610_device *) param;
+ return ym2610->ay8910_read_ym();
+}
+
+static void psg_reset(void *param)
+{
+ ym2610_device *ym2610 = (ym2610_device *) param;
+ ym2610->ay8910_reset_ym();
+}
+
+static const ssg_callbacks psgintf =
+{
+ psg_set_clock,
+ psg_write,
+ psg_read,
+ psg_reset
+};
+
+/*------------------------- TM2610 -------------------------------*/
+/* IRQ Handler */
+static void IRQHandler(void *param,int irq)
+{
+ ym2610_device *ym2610 = (ym2610_device *) param;
+ ym2610->_IRQHandler(irq);
+}
+
+void ym2610_device::_IRQHandler(int irq)
+{
+ if (!m_irq_handler.isnull())
+ m_irq_handler(irq);
+}
+
+/* Timer overflow callback from timer.c */
+void ym2610_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case 0:
+ ym2610_timer_over(m_chip,0);
+ break;
+
+ case 1:
+ ym2610_timer_over(m_chip,1);
+ break;
+ }
+}
+
+static void timer_handler(void *param,int c,int count,int clock)
+{
+ ym2610_device *ym2610 = (ym2610_device *) param;
+ ym2610->_timer_handler(c, count, clock);
+}
+
+void ym2610_device::_timer_handler(int c,int count,int clock)
+{
+ if( count == 0 )
+ { /* Reset FM Timer */
+ m_timer[c]->enable(false);
+ }
+ else
+ { /* Start FM Timer */
+ attotime period = attotime::from_hz(clock) * count;
+
+ if (!m_timer[c]->enable(true))
+ m_timer[c]->adjust(period);
+ }
+}
+
+/* update request from fm.c */
+void ym2610_update_request(void *param)
+{
+ ym2610_device *ym2610 = (ym2610_device *) param;
+ ym2610->_ym2610_update_request();
+}
+
+void ym2610_device::_ym2610_update_request()
+{
+ m_stream->update();
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+void ym2610_device::stream_generate(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ ym2610_update_one(m_chip, outputs, samples);
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void ym2610b_device::stream_generate(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ ym2610b_update_one(m_chip, outputs, samples);
+}
+
+
+void ym2610_device::device_post_load()
+{
+ ym2610_postload(m_chip);
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ym2610_device::device_start()
+{
+ ay8910_device::device_start();
+
+ int rate = clock()/72;
+ void *pcmbufa,*pcmbufb;
+ int pcmsizea,pcmsizeb;
+ std::string name(tag());
+
+ m_irq_handler.resolve();
+
+ /* Timer Handler set */
+ m_timer[0] = timer_alloc(0);
+ m_timer[1] = timer_alloc(1);
+
+ /* stream system initialize */
+ m_stream = machine().sound().stream_alloc(*this,0,2,rate, stream_update_delegate(FUNC(ym2610_device::stream_generate),this));
+ /* setup adpcm buffers */
+ pcmbufa = region()->base();
+ pcmsizea = region()->bytes();
+ name.append(".deltat");
+ pcmbufb = (void *)(machine().root_device().memregion(name.c_str())->base());
+ pcmsizeb = machine().root_device().memregion(name.c_str())->bytes();
+ if (pcmbufb == NULL || pcmsizeb == 0)
+ {
+ pcmbufb = pcmbufa;
+ pcmsizeb = pcmsizea;
+ }
+
+ /**** initialize YM2610 ****/
+ m_chip = ym2610_init(this,this,clock(),rate,
+ pcmbufa,pcmsizea,pcmbufb,pcmsizeb,
+ timer_handler,IRQHandler,&psgintf);
+ assert_always(m_chip != NULL, "Error creating YM2610 chip");
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void ym2610_device::device_stop()
+{
+ ym2610_shutdown(m_chip);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ym2610_device::device_reset()
+{
+ ym2610_reset_chip(m_chip);
+}
+
+
+READ8_MEMBER( ym2610_device::read )
+{
+ return ym2610_read(m_chip, offset & 3);
+}
+
+WRITE8_MEMBER( ym2610_device::write )
+{
+ ym2610_write(m_chip, offset & 3, data);
+}
+
+
+const device_type YM2610 = &device_creator<ym2610_device>;
+
+ym2610_device::ym2610_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ay8910_device(mconfig, YM2610, "YM2610", tag, owner, clock, PSG_TYPE_YM, 1, 0, "ym2610", __FILE__),
+ m_irq_handler(*this)
+{
+}
+
+ym2610_device::ym2610_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : ay8910_device(mconfig, type, name, tag, owner, clock, PSG_TYPE_YM, 1, 0, shortname, source),
+ m_irq_handler(*this)
+{
+}
+
+const device_type YM2610B = &device_creator<ym2610b_device>;
+
+ym2610b_device::ym2610b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ym2610_device(mconfig, YM2610B, "YM2610B", tag, owner, clock, "ym2610b", __FILE__)
+{
+}
diff --git a/src/devices/sound/2610intf.h b/src/devices/sound/2610intf.h
new file mode 100644
index 00000000000..40842f48411
--- /dev/null
+++ b/src/devices/sound/2610intf.h
@@ -0,0 +1,65 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+#pragma once
+
+#ifndef __2610INTF_H__
+#define __2610INTF_H__
+
+#include "emu.h"
+#include "ay8910.h"
+
+void ym2610_update_request(void *param);
+
+#define MCFG_YM2610_IRQ_HANDLER(_devcb) \
+ devcb = &ym2610_device::set_irq_handler(*device, DEVCB_##_devcb);
+
+class ym2610_device : public ay8910_device
+{
+public:
+ ym2610_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ym2610_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<ym2610_device &>(device).m_irq_handler.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ void _IRQHandler(int irq);
+ void _timer_handler(int c,int count,int clock);
+ void _ym2610_update_request();
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_post_load();
+ virtual void device_stop();
+ virtual void device_reset();
+
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ virtual void stream_generate(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ void * m_chip;
+
+private:
+ // internal state
+ sound_stream * m_stream;
+ emu_timer * m_timer[2];
+ devcb_write_line m_irq_handler;
+};
+
+extern const device_type YM2610;
+
+class ym2610b_device : public ym2610_device
+{
+public:
+ ym2610b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ virtual void stream_generate(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+};
+
+extern const device_type YM2610B;
+
+
+#endif /* __2610INTF_H__ */
diff --git a/src/devices/sound/2612intf.c b/src/devices/sound/2612intf.c
new file mode 100644
index 00000000000..b5464d4e824
--- /dev/null
+++ b/src/devices/sound/2612intf.c
@@ -0,0 +1,183 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+/***************************************************************************
+
+ 2612intf.c
+
+ The YM2612 emulator supports up to 2 chips.
+ Each chip has the following connections:
+ - Status Read / Control Write A
+ - Port Read / Data Write A
+ - Control Write B
+ - Data Write B
+
+***************************************************************************/
+
+#include "2612intf.h"
+#include "fm.h"
+
+/*------------------------- TM2612 -------------------------------*/
+/* IRQ Handler */
+static void IRQHandler(void *param,int irq)
+{
+ ym2612_device *ym2612 = (ym2612_device *) param;
+ ym2612->_IRQHandler(irq);
+}
+
+void ym2612_device::_IRQHandler(int irq)
+{
+ if (!m_irq_handler.isnull())
+ m_irq_handler(irq);
+}
+
+/* Timer overflow callback from timer.c */
+void ym2612_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case 0:
+ ym2612_timer_over(m_chip,0);
+ break;
+
+ case 1:
+ ym2612_timer_over(m_chip,1);
+ break;
+ }
+}
+
+static void timer_handler(void *param,int c,int count,int clock)
+{
+ ym2612_device *ym2612 = (ym2612_device *) param;
+ ym2612->_timer_handler(c, count, clock);
+}
+
+void ym2612_device::_timer_handler(int c,int count,int clock)
+{
+ if( count == 0 )
+ { /* Reset FM Timer */
+ m_timer[c]->enable(false);
+ }
+ else
+ { /* Start FM Timer */
+ attotime period = attotime::from_hz(clock) * count;
+
+ if (!m_timer[c]->enable(true))
+ m_timer[c]->adjust(period);
+ }
+}
+
+/* update request from fm.c */
+void ym2612_update_request(void *param)
+{
+ ym2612_device *ym2612 = (ym2612_device *) param;
+ ym2612->_ym2612_update_request();
+}
+
+void ym2612_device::_ym2612_update_request()
+{
+ m_stream->update();
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void ym2612_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ ym2612_update_one(m_chip, outputs, samples);
+}
+
+
+void ym2612_device::device_post_load()
+{
+ ym2612_postload(m_chip);
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ym2612_device::device_start()
+{
+ int rate = clock()/72;
+
+ m_irq_handler.resolve();
+
+ /* FM init */
+ /* Timer Handler set */
+ m_timer[0] = timer_alloc(0);
+ m_timer[1] = timer_alloc(1);
+
+ /* stream system initialize */
+ m_stream = machine().sound().stream_alloc(*this,0,2,rate);
+
+ /**** initialize YM2612 ****/
+ m_chip = ym2612_init(this,this,clock(),rate,timer_handler,IRQHandler);
+ assert_always(m_chip != NULL, "Error creating YM2612 chip");
+}
+
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void ym2612_device::device_stop()
+{
+ ym2612_shutdown(m_chip);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ym2612_device::device_reset()
+{
+ ym2612_reset_chip(m_chip);
+}
+
+
+READ8_MEMBER( ym2612_device::read )
+{
+ return ym2612_read(m_chip, offset & 3);
+}
+
+WRITE8_MEMBER( ym2612_device::write )
+{
+ ym2612_write(m_chip, offset & 3, data);
+}
+
+
+const device_type YM2612 = &device_creator<ym2612_device>;
+
+ym2612_device::ym2612_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, YM2612, "YM2612", tag, owner, clock, "ym2612", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_irq_handler(*this)
+{
+}
+
+ym2612_device::ym2612_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_sound_interface(mconfig, *this),
+ m_irq_handler(*this)
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void ym2612_device::device_config_complete()
+{
+}
+
+
+const device_type YM3438 = &device_creator<ym3438_device>;
+
+ym3438_device::ym3438_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ym2612_device(mconfig, YM3438, "YM3438", tag, owner, clock, "ym3438", __FILE__)
+{
+}
diff --git a/src/devices/sound/2612intf.h b/src/devices/sound/2612intf.h
new file mode 100644
index 00000000000..8199172044d
--- /dev/null
+++ b/src/devices/sound/2612intf.h
@@ -0,0 +1,64 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+#pragma once
+
+#ifndef __2612INTF_H__
+#define __2612INTF_H__
+
+#include "emu.h"
+
+void ym2612_update_request(void *param);
+
+#define MCFG_YM2612_IRQ_HANDLER(_devcb) \
+ devcb = &ym2612_device::set_irq_handler(*device, DEVCB_##_devcb);
+
+class ym2612_device : public device_t,
+ public device_sound_interface
+{
+public:
+ ym2612_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ym2612_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<ym2612_device &>(device).m_irq_handler.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ void _IRQHandler(int irq);
+ void _timer_handler(int c,int count,int clock);
+ void _ym2612_update_request();
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+ virtual void device_post_load();
+ virtual void device_stop();
+ virtual void device_reset();
+
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ sound_stream * m_stream;
+ emu_timer * m_timer[2];
+ void * m_chip;
+ devcb_write_line m_irq_handler;
+};
+
+extern const device_type YM2612;
+
+
+class ym3438_device : public ym2612_device
+{
+public:
+ ym3438_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+extern const device_type YM3438;
+
+
+#endif /* __2612INTF_H__ */
diff --git a/src/devices/sound/262intf.c b/src/devices/sound/262intf.c
new file mode 100644
index 00000000000..b15b6f4deda
--- /dev/null
+++ b/src/devices/sound/262intf.c
@@ -0,0 +1,155 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+/***************************************************************************
+
+ 262intf.c
+
+ MAME interface for YMF262 (OPL3) emulator
+
+***************************************************************************/
+#include "emu.h"
+#include "262intf.h"
+#include "ymf262.h"
+
+
+/* IRQ Handler */
+static void IRQHandler(void *param,int irq)
+{
+ ymf262_device *ymf262 = (ymf262_device *) param;
+ ymf262->_IRQHandler(irq);
+}
+
+void ymf262_device::_IRQHandler(int irq)
+{
+ if (!m_irq_handler.isnull())
+ m_irq_handler(irq);
+}
+
+/* Timer overflow callback from timer.c */
+void ymf262_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case 0:
+ ymf262_timer_over(m_chip,0);
+ break;
+
+ case 1:
+ ymf262_timer_over(m_chip,1);
+ break;
+ }
+}
+
+
+static void timer_handler(void *param, int c, const attotime &period)
+{
+ ymf262_device *ymf262 = (ymf262_device *) param;
+ ymf262->_timer_handler(c, period);
+}
+
+void ymf262_device::_timer_handler(int c, const attotime &period)
+{
+ if( period == attotime::zero )
+ { /* Reset FM Timer */
+ m_timer[c]->enable(false);
+ }
+ else
+ { /* Start FM Timer */
+ m_timer[c]->adjust(period);
+ }
+}
+
+void ymf262_update_request(void *param, int interval)
+{
+ ymf262_device *ymf262 = (ymf262_device *) param;
+ ymf262->_ymf262_update_request();
+}
+
+void ymf262_device::_ymf262_update_request()
+{
+ m_stream->update();
+}
+
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void ymf262_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ ymf262_update_one(m_chip, outputs, samples);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ymf262_device::device_start()
+{
+ int rate = clock()/288;
+
+ m_irq_handler.resolve();
+
+ /* stream system initialize */
+ m_chip = ymf262_init(this,clock(),rate);
+ assert_always(m_chip != NULL, "Error creating YMF262 chip");
+
+ m_stream = machine().sound().stream_alloc(*this,0,4,rate);
+
+ /* YMF262 setup */
+ ymf262_set_timer_handler (m_chip, timer_handler, this);
+ ymf262_set_irq_handler (m_chip, IRQHandler, this);
+ ymf262_set_update_handler(m_chip, ymf262_update_request, this);
+
+ m_timer[0] = timer_alloc(0);
+ m_timer[1] = timer_alloc(1);
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void ymf262_device::device_stop()
+{
+ ymf262_shutdown(m_chip);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ymf262_device::device_reset()
+{
+ ymf262_reset_chip(m_chip);
+}
+
+
+READ8_MEMBER( ymf262_device::read )
+{
+ return ymf262_read(m_chip, offset & 3);
+}
+
+WRITE8_MEMBER( ymf262_device::write )
+{
+ ymf262_write(m_chip, offset & 3, data);
+}
+
+const device_type YMF262 = &device_creator<ymf262_device>;
+
+ymf262_device::ymf262_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, YMF262, "YMF262", tag, owner, clock, "ymf262", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_irq_handler(*this)
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void ymf262_device::device_config_complete()
+{
+}
diff --git a/src/devices/sound/262intf.h b/src/devices/sound/262intf.h
new file mode 100644
index 00000000000..2ff45f3190c
--- /dev/null
+++ b/src/devices/sound/262intf.h
@@ -0,0 +1,51 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+#pragma once
+
+#ifndef __262INTF_H__
+#define __262INTF_H__
+
+#include "emu.h"
+
+#define MCFG_YMF262_IRQ_HANDLER(_devcb) \
+ devcb = &ymf262_device::set_irq_handler(*device, DEVCB_##_devcb);
+
+class ymf262_device : public device_t,
+ public device_sound_interface
+{
+public:
+ ymf262_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<ymf262_device &>(device).m_irq_handler.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ void _IRQHandler(int irq);
+ void _timer_handler(int c, const attotime &period);
+ void _ymf262_update_request();
+
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+ virtual void device_stop();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+private:
+ // internal state
+ sound_stream * m_stream;
+ emu_timer * m_timer[2];
+ void * m_chip;
+ devcb_write_line m_irq_handler;
+};
+
+extern const device_type YMF262;
+
+
+#endif /* __262INTF_H__ */
diff --git a/src/devices/sound/315-5641.c b/src/devices/sound/315-5641.c
new file mode 100644
index 00000000000..a0de2fce330
--- /dev/null
+++ b/src/devices/sound/315-5641.c
@@ -0,0 +1,70 @@
+/* Sega 315-5641 / D77591 / 9442CA010 */
+
+#include "emu.h"
+#include "315-5641.h"
+
+const device_type SEGA_315_5641_PCM = &device_creator<sega_315_5641_pcm_device>;
+
+sega_315_5641_pcm_device::sega_315_5641_pcm_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : upd7759_device(mconfig, SEGA_315_5641_PCM, "315-5641 PCM", tag, owner, clock, "315-5641_pcm", __FILE__)
+{
+}
+
+void sega_315_5641_pcm_device::device_start()
+{
+ save_item(NAME(m_fifo_data), 0x40);
+ save_item(NAME(m_fifo_read));
+ save_item(NAME(m_fifo_write));
+
+ upd7759_device::device_start();
+}
+
+void sega_315_5641_pcm_device::advance_state()
+{
+ switch (m_state)
+ {
+ case STATE_DROP_DRQ:
+ if (m_rombase == NULL)
+ {
+ // Slave Mode: get data from FIFO buffer
+ UINT8 fiforead = (m_fifo_read + 1) & 0x3F;
+ if (fiforead != m_fifo_write)
+ {
+ m_fifo_in = m_fifo_data[fiforead];
+ m_fifo_read = fiforead;
+ }
+ }
+ break;
+ }
+
+ upd775x_device::advance_state();
+}
+
+
+WRITE8_MEMBER( sega_315_5641_pcm_device::port_w )
+{
+ if (m_rombase != NULL)
+ {
+ /* update the FIFO value */
+ m_fifo_in = data;
+ }
+ else
+ {
+ m_fifo_data[m_fifo_write++] = data;
+ m_fifo_write &= 0x3F;
+ }
+}
+
+
+UINT8 sega_315_5641_pcm_device::get_fifo_space()
+{
+ return (m_fifo_read - m_fifo_write) & 0x3F;
+}
+
+void sega_315_5641_pcm_device::device_reset()
+{
+ m_fifo_read = 0x3F;
+ m_fifo_write = 0x00;
+
+ upd775x_device::device_reset();
+} \ No newline at end of file
diff --git a/src/devices/sound/315-5641.h b/src/devices/sound/315-5641.h
new file mode 100644
index 00000000000..c8fb0f6270f
--- /dev/null
+++ b/src/devices/sound/315-5641.h
@@ -0,0 +1,30 @@
+/* Sega 315-5641 / D77591 / 9442CA010 */
+
+// this is the PICO sound chip, we are not sure if it's the same as a 7759 or not, it requires FIFO logic
+// which the 7759 does _not_ have but it is possible that is handled somewhere else on the PICO hardawre.
+
+#include "upd7759.h"
+
+
+class sega_315_5641_pcm_device : public upd7759_device
+{
+public:
+ sega_315_5641_pcm_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ UINT8 get_fifo_space();
+ void advance_state();
+ DECLARE_WRITE8_MEMBER(port_w);
+
+ UINT8 m_fifo_data[0x40];
+ UINT8 m_fifo_read; // last read offset (will read in m_fifo_read+1)
+ UINT8 m_fifo_write; // write offset
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+
+};
+
+extern const device_type SEGA_315_5641_PCM; \ No newline at end of file
diff --git a/src/devices/sound/3526intf.c b/src/devices/sound/3526intf.c
new file mode 100644
index 00000000000..d90b053a4d3
--- /dev/null
+++ b/src/devices/sound/3526intf.c
@@ -0,0 +1,173 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+/******************************************************************************
+* FILE
+* Yamaha 3812 emulator interface - MAME VERSION
+*
+* CREATED BY
+* Ernesto Corvi
+*
+* UPDATE LOG
+* JB 28-04-2002 Fixed simultaneous usage of all three different chip types.
+* Used real sample rate when resample filter is active.
+* AAT 12-28-2001 Protected Y8950 from accessing unmapped port and keyboard handlers.
+* CHS 1999-01-09 Fixes new ym3812 emulation interface.
+* CHS 1998-10-23 Mame streaming sound chip update
+* EC 1998 Created Interface
+*
+* NOTES
+*
+******************************************************************************/
+#include "3526intf.h"
+#include "fm.h"
+#include "fmopl.h"
+
+
+/* IRQ Handler */
+static void IRQHandler(void *param,int irq)
+{
+ ym3526_device *ym3526 = (ym3526_device *) param;
+ ym3526->_IRQHandler(irq);
+}
+
+void ym3526_device::_IRQHandler(int irq)
+{
+ if (!m_irq_handler.isnull())
+ m_irq_handler(irq);
+}
+
+/* Timer overflow callback from timer.c */
+void ym3526_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case 0:
+ ym3526_timer_over(m_chip,0);
+ break;
+
+ case 1:
+ ym3526_timer_over(m_chip,1);
+ break;
+ }
+}
+
+static void timer_handler(void *param,int c,const attotime &period)
+{
+ ym3526_device *ym3526 = (ym3526_device *) param;
+ ym3526->_timer_handler(c, period);
+}
+
+void ym3526_device::_timer_handler(int c,const attotime &period)
+{
+ if( period == attotime::zero )
+ { /* Reset FM Timer */
+ m_timer[c]->enable(false);
+ }
+ else
+ { /* Start FM Timer */
+ m_timer[c]->adjust(period);
+ }
+}
+
+/* update request from fm.c */
+void ym3526_update_request(void *param, int interval)
+{
+ ym3526_device *ym3526 = (ym3526_device *) param;
+ ym3526->_ym3526_update_request();
+}
+
+void ym3526_device::_ym3526_update_request()
+{
+ m_stream->update();
+}
+
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void ym3526_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ ym3526_update_one(m_chip, outputs[0], samples);
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ym3526_device::device_start()
+{
+ int rate = clock()/72;
+
+ // resolve callbacks
+ m_irq_handler.resolve();
+
+ /* stream system initialize */
+ m_chip = ym3526_init(this,clock(),rate);
+ assert_always(m_chip != NULL, "Error creating YM3526 chip");
+
+ m_stream = machine().sound().stream_alloc(*this,0,1,rate);
+ /* YM3526 setup */
+ ym3526_set_timer_handler (m_chip, timer_handler, this);
+ ym3526_set_irq_handler (m_chip, IRQHandler, this);
+ ym3526_set_update_handler(m_chip, ym3526_update_request, this);
+
+ m_timer[0] = timer_alloc(0);
+ m_timer[1] = timer_alloc(1);
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void ym3526_device::device_stop()
+{
+ ym3526_shutdown(m_chip);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ym3526_device::device_reset()
+{
+ ym3526_reset_chip(m_chip);
+}
+
+
+READ8_MEMBER( ym3526_device::read )
+{
+ return ym3526_read(m_chip, offset & 1);
+}
+
+WRITE8_MEMBER( ym3526_device::write )
+{
+ ym3526_write(m_chip, offset & 1, data);
+}
+
+READ8_MEMBER( ym3526_device::status_port_r ) { return read(space, 0); }
+READ8_MEMBER( ym3526_device::read_port_r ) { return read(space, 1); }
+WRITE8_MEMBER( ym3526_device::control_port_w ) { write(space, 0, data); }
+WRITE8_MEMBER( ym3526_device::write_port_w ) { write(space, 1, data); }
+
+
+const device_type YM3526 = &device_creator<ym3526_device>;
+
+ym3526_device::ym3526_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, YM3526, "YM3526", tag, owner, clock, "ym3526", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_irq_handler(*this)
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void ym3526_device::device_config_complete()
+{
+}
diff --git a/src/devices/sound/3526intf.h b/src/devices/sound/3526intf.h
new file mode 100644
index 00000000000..4f0d59f1c74
--- /dev/null
+++ b/src/devices/sound/3526intf.h
@@ -0,0 +1,57 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+#pragma once
+
+#ifndef __3526INTF_H__
+#define __3526INTF_H__
+
+#include "emu.h"
+
+#define MCFG_YM3526_IRQ_HANDLER(_devcb) \
+ devcb = &ym3526_device::set_irq_handler(*device, DEVCB_##_devcb);
+
+class ym3526_device : public device_t,
+ public device_sound_interface
+{
+public:
+ ym3526_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<ym3526_device &>(device).m_irq_handler.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ DECLARE_READ8_MEMBER( status_port_r );
+ DECLARE_READ8_MEMBER( read_port_r );
+ DECLARE_WRITE8_MEMBER( control_port_w );
+ DECLARE_WRITE8_MEMBER( write_port_w );
+
+ void _IRQHandler(int irq);
+ void _timer_handler(int c,const attotime &period);
+ void _ym3526_update_request();
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+ virtual void device_stop();
+ virtual void device_reset();
+
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ // internal state
+ sound_stream * m_stream;
+ emu_timer * m_timer[2];
+ void * m_chip;
+ devcb_write_line m_irq_handler;
+};
+
+extern const device_type YM3526;
+
+
+#endif /* __3526INTF_H__ */
diff --git a/src/devices/sound/3812intf.c b/src/devices/sound/3812intf.c
new file mode 100644
index 00000000000..ee4a9e3d5af
--- /dev/null
+++ b/src/devices/sound/3812intf.c
@@ -0,0 +1,171 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+/******************************************************************************
+* FILE
+* Yamaha 3812 emulator interface - MAME VERSION
+*
+* CREATED BY
+* Ernesto Corvi
+*
+* UPDATE LOG
+* JB 28-04-2002 Fixed simultaneous usage of all three different chip types.
+* Used real sample rate when resample filter is active.
+* AAT 12-28-2001 Protected Y8950 from accessing unmapped port and keyboard handlers.
+* CHS 1999-01-09 Fixes new ym3812 emulation interface.
+* CHS 1998-10-23 Mame streaming sound chip update
+* EC 1998 Created Interface
+*
+* NOTES
+*
+******************************************************************************/
+#include "emu.h"
+#include "3812intf.h"
+#include "fm.h"
+#include "sound/fmopl.h"
+
+
+static void IRQHandler(void *param,int irq)
+{
+ ym3812_device *ym3812 = (ym3812_device *) param;
+ ym3812->_IRQHandler(irq);
+}
+
+void ym3812_device::_IRQHandler(int irq)
+{
+ if (!m_irq_handler.isnull())
+ m_irq_handler(irq);
+}
+
+/* Timer overflow callback from timer.c */
+void ym3812_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case 0:
+ ym3812_timer_over(m_chip,0);
+ break;
+
+ case 1:
+ ym3812_timer_over(m_chip,1);
+ break;
+ }
+}
+
+static void timer_handler(void *param,int c,const attotime &period)
+{
+ ym3812_device *ym3812 = (ym3812_device *) param;
+ ym3812->_timer_handler(c, period);
+}
+
+void ym3812_device::_timer_handler(int c, const attotime &period)
+{
+ if( period == attotime::zero )
+ { /* Reset FM Timer */
+ m_timer[c]->enable(false);
+ }
+ else
+ { /* Start FM Timer */
+ m_timer[c]->adjust(period);
+ }
+}
+
+
+static void ym3812_update_request(void * param, int interval)
+{
+ ym3812_device *ym3812 = (ym3812_device *) param;
+ ym3812->_ym3812_update_request();
+}
+
+void ym3812_device::_ym3812_update_request()
+{
+ m_stream->update();
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void ym3812_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ ym3812_update_one(m_chip, outputs[0], samples);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ym3812_device::device_start()
+{
+ int rate = clock()/72;
+
+ m_irq_handler.resolve();
+
+ /* stream system initialize */
+ m_chip = ym3812_init(this,clock(),rate);
+ assert_always(m_chip != NULL, "Error creating YM3812 chip");
+
+ m_stream = machine().sound().stream_alloc(*this,0,1,rate);
+
+ /* YM3812 setup */
+ ym3812_set_timer_handler (m_chip, timer_handler, this);
+ ym3812_set_irq_handler (m_chip, IRQHandler, this);
+ ym3812_set_update_handler(m_chip, ym3812_update_request, this);
+
+ m_timer[0] = timer_alloc(0);
+ m_timer[1] = timer_alloc(1);
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void ym3812_device::device_stop()
+{
+ ym3812_shutdown(m_chip);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ym3812_device::device_reset()
+{
+ ym3812_reset_chip(m_chip);
+}
+
+
+READ8_MEMBER( ym3812_device::read )
+{
+ return ym3812_read(m_chip, offset & 1);
+}
+
+WRITE8_MEMBER( ym3812_device::write )
+{
+ ym3812_write(m_chip, offset & 1, data);
+}
+
+READ8_MEMBER( ym3812_device::status_port_r ) { return read(space, 0); }
+READ8_MEMBER( ym3812_device::read_port_r ) { return read(space, 1); }
+WRITE8_MEMBER( ym3812_device::control_port_w ) { write(space, 0, data); }
+WRITE8_MEMBER( ym3812_device::write_port_w ) { write( space, 1, data); }
+
+
+const device_type YM3812 = &device_creator<ym3812_device>;
+
+ym3812_device::ym3812_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, YM3812, "YM3812", tag, owner, clock, "ym3812", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_irq_handler(*this)
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void ym3812_device::device_config_complete()
+{
+}
diff --git a/src/devices/sound/3812intf.h b/src/devices/sound/3812intf.h
new file mode 100644
index 00000000000..ee87e8a43d9
--- /dev/null
+++ b/src/devices/sound/3812intf.h
@@ -0,0 +1,56 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+#pragma once
+
+#ifndef __3812INTF_H__
+#define __3812INTF_H__
+
+#include "emu.h"
+
+#define MCFG_YM3812_IRQ_HANDLER(_devcb) \
+ devcb = &ym3812_device::set_irq_handler(*device, DEVCB_##_devcb);
+
+class ym3812_device : public device_t,
+ public device_sound_interface
+{
+public:
+ ym3812_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<ym3812_device &>(device).m_irq_handler.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ DECLARE_READ8_MEMBER( status_port_r );
+ DECLARE_READ8_MEMBER( read_port_r );
+ DECLARE_WRITE8_MEMBER( control_port_w );
+ DECLARE_WRITE8_MEMBER( write_port_w );
+
+ void _IRQHandler(int irq);
+ void _timer_handler(int c, const attotime &period);
+ void _ym3812_update_request();
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+ virtual void device_stop();
+ virtual void device_reset();
+
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ sound_stream * m_stream;
+ emu_timer * m_timer[2];
+ void * m_chip;
+ devcb_write_line m_irq_handler;
+};
+
+extern const device_type YM3812;
+
+
+#endif /* __3812INTF_H__ */
diff --git a/src/devices/sound/8950intf.c b/src/devices/sound/8950intf.c
new file mode 100644
index 00000000000..172ee03ef25
--- /dev/null
+++ b/src/devices/sound/8950intf.c
@@ -0,0 +1,226 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+/******************************************************************************
+* FILE
+* Yamaha 3812 emulator interface - MAME VERSION
+*
+* CREATED BY
+* Ernesto Corvi
+*
+* UPDATE LOG
+* JB 28-04-2002 Fixed simultaneous usage of all three different chip types.
+* Used real sample rate when resample filter is active.
+* AAT 12-28-2001 Protected Y8950 from accessing unmapped port and keyboard handlers.
+* CHS 1999-01-09 Fixes new ym3812 emulation interface.
+* CHS 1998-10-23 Mame streaming sound chip update
+* EC 1998 Created Interface
+*
+* NOTES
+*
+******************************************************************************/
+#include "8950intf.h"
+#include "fm.h"
+#include "fmopl.h"
+
+
+static void IRQHandler(void *param,int irq)
+{
+ y8950_device *y8950 = (y8950_device *) param;
+ y8950->_IRQHandler(irq);
+}
+
+void y8950_device::_IRQHandler(int irq)
+{
+ m_irq_handler(irq);
+}
+
+void y8950_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case 0:
+ y8950_timer_over(m_chip,0);
+ break;
+
+ case 1:
+ y8950_timer_over(m_chip,1);
+ break;
+ }
+}
+
+static void timer_handler(void *param, int c, const attotime &period)
+{
+ y8950_device *y8950 = (y8950_device *) param;
+ y8950->_timer_handler(c, period);
+}
+
+void y8950_device::_timer_handler(int c, const attotime &period)
+{
+ if( period == attotime::zero )
+ { /* Reset FM Timer */
+ m_timer[c]->enable(false);
+ }
+ else
+ { /* Start FM Timer */
+ m_timer[c]->adjust(period);
+ }
+}
+
+
+static unsigned char Y8950PortHandler_r(void *param)
+{
+ y8950_device *y8950 = (y8950_device *) param;
+ return y8950->_Y8950PortHandler_r();
+}
+
+unsigned char y8950_device::_Y8950PortHandler_r()
+{
+ return m_io_read_handler(0);
+}
+
+static void Y8950PortHandler_w(void *param,unsigned char data)
+{
+ y8950_device *y8950 = (y8950_device *) param;
+ y8950->_Y8950PortHandler_w(data);
+}
+
+void y8950_device::_Y8950PortHandler_w(unsigned char data)
+{
+ m_io_write_handler((offs_t)0,data);
+}
+
+static unsigned char Y8950KeyboardHandler_r(void *param)
+{
+ y8950_device *y8950 = (y8950_device *) param;
+ return y8950->_Y8950KeyboardHandler_r();
+}
+
+unsigned char y8950_device::_Y8950KeyboardHandler_r()
+{
+ return m_keyboard_read_handler(0);
+}
+
+static void Y8950KeyboardHandler_w(void *param,unsigned char data)
+{
+ y8950_device *y8950 = (y8950_device *) param;
+ y8950->_Y8950KeyboardHandler_w(data);
+}
+
+void y8950_device::_Y8950KeyboardHandler_w(unsigned char data)
+{
+ m_keyboard_write_handler((offs_t)0,data);
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void y8950_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ y8950_update_one(m_chip, outputs[0], samples);
+}
+
+static void y8950_update_request(void *param, int interval)
+{
+ y8950_device *y8950 = (y8950_device *) param;
+ y8950->_y8950_update_request();
+}
+
+void y8950_device::_y8950_update_request()
+{
+ m_stream->update();
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void y8950_device::device_start()
+{
+ int rate = clock()/72;
+
+ m_irq_handler.resolve_safe();
+ m_keyboard_read_handler.resolve_safe(0);
+ m_keyboard_write_handler.resolve_safe();
+ m_io_read_handler.resolve_safe(0);
+ m_io_write_handler.resolve_safe();
+
+ /* stream system initialize */
+ m_chip = y8950_init(this,clock(),rate);
+ assert_always(m_chip != NULL, "Error creating Y8950 chip");
+
+ /* ADPCM ROM data */
+ y8950_set_delta_t_memory(m_chip, region()->base(), region()->bytes());
+
+ m_stream = machine().sound().stream_alloc(*this,0,1,rate);
+ /* port and keyboard handler */
+ y8950_set_port_handler(m_chip, Y8950PortHandler_w, Y8950PortHandler_r, this);
+ y8950_set_keyboard_handler(m_chip, Y8950KeyboardHandler_w, Y8950KeyboardHandler_r, this);
+
+ /* Y8950 setup */
+ y8950_set_timer_handler (m_chip, timer_handler, this);
+ y8950_set_irq_handler (m_chip, IRQHandler, this);
+ y8950_set_update_handler(m_chip, y8950_update_request, this);
+
+ m_timer[0] = timer_alloc(0);
+ m_timer[1] = timer_alloc(1);
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void y8950_device::device_stop()
+{
+ y8950_shutdown(m_chip);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void y8950_device::device_reset()
+{
+ y8950_reset_chip(m_chip);
+}
+
+
+READ8_MEMBER( y8950_device::read )
+{
+ return y8950_read(m_chip, offset & 1);
+}
+
+WRITE8_MEMBER( y8950_device::write )
+{
+ y8950_write(m_chip, offset & 1, data);
+}
+
+READ8_MEMBER( y8950_device::status_port_r ) { return read(space, 0); }
+READ8_MEMBER( y8950_device::read_port_r ) { return read(space, 1); }
+WRITE8_MEMBER( y8950_device::control_port_w ) { write(space, 0, data); }
+WRITE8_MEMBER( y8950_device::write_port_w ) { write(space, 1, data); }
+
+
+const device_type Y8950 = &device_creator<y8950_device>;
+
+y8950_device::y8950_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, Y8950, "Y8950", tag, owner, clock, "y8950", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_irq_handler(*this),
+ m_keyboard_read_handler(*this),
+ m_keyboard_write_handler(*this),
+ m_io_read_handler(*this),
+ m_io_write_handler(*this)
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void y8950_device::device_config_complete()
+{
+}
diff --git a/src/devices/sound/8950intf.h b/src/devices/sound/8950intf.h
new file mode 100644
index 00000000000..42db5339696
--- /dev/null
+++ b/src/devices/sound/8950intf.h
@@ -0,0 +1,81 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi
+#pragma once
+
+#ifndef __8950INTF_H__
+#define __8950INTF_H__
+
+#include "emu.h"
+
+#define MCFG_Y8950_IRQ_HANDLER(_devcb) \
+ devcb = &y8950_device::set_irq_handler(*device, DEVCB_##_devcb);
+
+#define MCFG_Y8950_KEYBOARD_READ_HANDLER(_devcb) \
+ devcb = &y8950_device::set_keyboard_read_handler(*device, DEVCB_##_devcb);
+
+#define MCFG_Y8950_KEYBOARD_WRITE_HANDLER(_devcb) \
+ devcb = &y8950_device::set_keyboard_write_handler(*device, DEVCB_##_devcb);
+
+#define MCFG_Y8950_IO_READ_HANDLER(_devcb) \
+ devcb = &y8950_device::set_io_read_handler(*device, DEVCB_##_devcb);
+
+#define MCFG_Y8950_IO_WRITE_HANDLER(_devcb) \
+ devcb = &y8950_device::set_io_write_handler(*device, DEVCB_##_devcb);
+
+class y8950_device : public device_t,
+ public device_sound_interface
+{
+public:
+ y8950_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<y8950_device &>(device).m_irq_handler.set_callback(object); }
+ template<class _Object> static devcb_base &set_keyboard_read_handler(device_t &device, _Object object) { return downcast<y8950_device &>(device).m_keyboard_read_handler.set_callback(object); }
+ template<class _Object> static devcb_base &set_keyboard_write_handler(device_t &device, _Object object) { return downcast<y8950_device &>(device).m_keyboard_write_handler.set_callback(object); }
+ template<class _Object> static devcb_base &set_io_read_handler(device_t &device, _Object object) { return downcast<y8950_device &>(device).m_io_read_handler.set_callback(object); }
+ template<class _Object> static devcb_base &set_io_write_handler(device_t &device, _Object object) { return downcast<y8950_device &>(device).m_io_write_handler.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ DECLARE_READ8_MEMBER( status_port_r );
+ DECLARE_READ8_MEMBER( read_port_r );
+ DECLARE_WRITE8_MEMBER( control_port_w );
+ DECLARE_WRITE8_MEMBER( write_port_w );
+
+ void _IRQHandler(int irq);
+ void _timer_handler(int c, const attotime &period);
+ void _y8950_update_request();
+ unsigned char _Y8950PortHandler_r();
+ void _Y8950PortHandler_w(unsigned char data);
+ unsigned char _Y8950KeyboardHandler_r();
+ void _Y8950KeyboardHandler_w(unsigned char data);
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+ virtual void device_stop();
+ virtual void device_reset();
+
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ // internal state
+ sound_stream * m_stream;
+ emu_timer * m_timer[2];
+ void * m_chip;
+ devcb_write_line m_irq_handler;
+ devcb_read8 m_keyboard_read_handler;
+ devcb_write8 m_keyboard_write_handler;
+ devcb_read8 m_io_read_handler;
+ devcb_write8 m_io_write_handler;
+};
+
+extern const device_type Y8950;
+
+
+#endif /* __8950INTF_H__ */
diff --git a/src/devices/sound/aica.c b/src/devices/sound/aica.c
new file mode 100644
index 00000000000..e788b4fda34
--- /dev/null
+++ b/src/devices/sound/aica.c
@@ -0,0 +1,1662 @@
+// license:???
+// copyright-holders:ElSemi, kingshriek, Deunan Knute, R. Belmont
+/*
+ Sega/Yamaha AICA emulation
+ By ElSemi, kingshriek, Deunan Knute, and R. Belmont
+
+ This is effectively a 64-voice SCSP, with the following differences:
+ - No FM mode
+ - A third sample format (ADPCM) has been added
+ - Some minor other tweeks (no EGHOLD, slighly more capable DSP)
+*/
+
+#include "emu.h"
+#include "aica.h"
+
+#define ICLIP16(x) (x<-32768)?-32768:((x>32767)?32767:x)
+
+#define SHIFT 12
+#define FIX(v) ((UINT32) ((float) (1<<SHIFT)*(v)))
+
+#define EG_SHIFT 16
+#define LFO_SHIFT 8
+
+#define LFIX(v) ((unsigned int) ((float) (1<<LFO_SHIFT)*(v)))
+
+//Convert DB to multiply amplitude
+#define DB(v) LFIX(powf(10.0f,v/20.0f))
+
+//Convert cents to step increment
+#define CENTS(v) LFIX(powf(2.0f,v/1200.0f))
+
+/*
+ AICA features 64 programmable slots
+ that can generate PCM and ADPCM (from ROM/RAM) sound
+*/
+
+//SLOT PARAMETERS
+#define KEYONEX(slot) ((slot->udata.data[0x0]>>0x0)&0x8000)
+#define KEYONB(slot) ((slot->udata.data[0x0]>>0x0)&0x4000)
+#define SSCTL(slot) ((slot->udata.data[0x0]>>0xA)&0x0001)
+#define LPCTL(slot) ((slot->udata.data[0x0]>>0x9)&0x0001)
+#define PCMS(slot) ((slot->udata.data[0x0]>>0x7)&0x0003)
+
+#define SA(slot) (((slot->udata.data[0x0]&0x7F)<<16)|(slot->udata.data[0x4/2]))
+
+#define LSA(slot) (slot->udata.data[0x8/2])
+
+#define LEA(slot) (slot->udata.data[0xc/2])
+
+#define D2R(slot) ((slot->udata.data[0x10/2]>>0xB)&0x001F)
+#define D1R(slot) ((slot->udata.data[0x10/2]>>0x6)&0x001F)
+#define AR(slot) ((slot->udata.data[0x10/2]>>0x0)&0x001F)
+
+#define LPSLNK(slot) ((slot->udata.data[0x14/2]>>0x0)&0x4000)
+#define KRS(slot) ((slot->udata.data[0x14/2]>>0xA)&0x000F)
+#define DL(slot) ((slot->udata.data[0x14/2]>>0x5)&0x001F)
+#define RR(slot) ((slot->udata.data[0x14/2]>>0x0)&0x001F)
+
+#define TL(slot) ((slot->udata.data[0x28/2]>>0x8)&0x00FF)
+
+#define OCT(slot) ((slot->udata.data[0x18/2]>>0xB)&0x000F)
+#define FNS(slot) ((slot->udata.data[0x18/2]>>0x0)&0x03FF)
+
+#define LFORE(slot) ((slot->udata.data[0x1c/2]>>0x0)&0x8000)
+#define LFOF(slot) ((slot->udata.data[0x1c/2]>>0xA)&0x001F)
+#define PLFOWS(slot) ((slot->udata.data[0x1c/2]>>0x8)&0x0003)
+#define PLFOS(slot) ((slot->udata.data[0x1c/2]>>0x5)&0x0007)
+#define ALFOWS(slot) ((slot->udata.data[0x1c/2]>>0x3)&0x0003)
+#define ALFOS(slot) ((slot->udata.data[0x1c/2]>>0x0)&0x0007)
+
+#define ISEL(slot) ((slot->udata.data[0x20/2]>>0x0)&0x000F)
+#define IMXL(slot) ((slot->udata.data[0x20/2]>>0x4)&0x000F)
+
+#define DISDL(slot) ((slot->udata.data[0x24/2]>>0x8)&0x000F)
+#define DIPAN(slot) ((slot->udata.data[0x24/2]>>0x0)&0x001F)
+
+#define EFSDL(slot) ((m_EFSPAN[slot*4]>>8)&0x000f)
+#define EFPAN(slot) ((m_EFSPAN[slot*4]>>0)&0x001f)
+
+//Envelope times in ms
+static const double ARTimes[64]={100000/*infinity*/,100000/*infinity*/,8100.0,6900.0,6000.0,4800.0,4000.0,3400.0,3000.0,2400.0,2000.0,1700.0,1500.0,
+ 1200.0,1000.0,860.0,760.0,600.0,500.0,430.0,380.0,300.0,250.0,220.0,190.0,150.0,130.0,110.0,95.0,
+ 76.0,63.0,55.0,47.0,38.0,31.0,27.0,24.0,19.0,15.0,13.0,12.0,9.4,7.9,6.8,6.0,4.7,3.8,3.4,3.0,2.4,
+ 2.0,1.8,1.6,1.3,1.1,0.93,0.85,0.65,0.53,0.44,0.40,0.35,0.0,0.0};
+static const double DRTimes[64]={100000/*infinity*/,100000/*infinity*/,118200.0,101300.0,88600.0,70900.0,59100.0,50700.0,44300.0,35500.0,29600.0,25300.0,22200.0,17700.0,
+ 14800.0,12700.0,11100.0,8900.0,7400.0,6300.0,5500.0,4400.0,3700.0,3200.0,2800.0,2200.0,1800.0,1600.0,1400.0,1100.0,
+ 920.0,790.0,690.0,550.0,460.0,390.0,340.0,270.0,230.0,200.0,170.0,140.0,110.0,98.0,85.0,68.0,57.0,49.0,43.0,34.0,
+ 28.0,25.0,22.0,18.0,14.0,12.0,11.0,8.5,7.1,6.1,5.4,4.3,3.6,3.1};
+
+#define MEM4B(aica) ((m_udata.data[0]>>0x0)&0x0200)
+#define DAC18B(aica) ((m_udata.data[0]>>0x0)&0x0100)
+#define MVOL(aica) ((m_udata.data[0]>>0x0)&0x000F)
+#define RBL(aica) ((m_udata.data[2]>>0xD)&0x0003)
+#define RBP(aica) ((m_udata.data[2]>>0x0)&0x0fff)
+#define MOFULL(aica) ((m_udata.data[4]>>0x0)&0x1000)
+#define MOEMPTY(aica) ((m_udata.data[4]>>0x0)&0x0800)
+#define MIOVF(aica) ((m_udata.data[4]>>0x0)&0x0400)
+#define MIFULL(aica) ((m_udata.data[4]>>0x0)&0x0200)
+#define MIEMPTY(aica) ((m_udata.data[4]>>0x0)&0x0100)
+
+#define AFSEL(aica) ((m_udata.data[0xc/2]>>0x0)&0x4000)
+#define MSLC(aica) ((m_udata.data[0xc/2]>>0x8)&0x3F)
+
+#define SCILV0(aica) ((m_udata.data[0xa8/2]>>0x0)&0xff)
+#define SCILV1(aica) ((m_udata.data[0xac/2]>>0x0)&0xff)
+#define SCILV2(aica) ((m_udata.data[0xb0/2]>>0x0)&0xff)
+
+#define MCIEB(aica) ((m_udata.data[0xb4/2]>>0x0)&0xff)
+#define MCIPD(aica) ((m_udata.data[0xb8/2]>>0x0)&0xff)
+#define MCIRE(aica) ((m_udata.data[0xbc/2]>>0x0)&0xff)
+
+#define SCIEX0 0
+#define SCIEX1 1
+#define SCIEX2 2
+#define SCIMID 3
+#define SCIDMA 4
+#define SCIIRQ 5
+#define SCITMA 6
+#define SCITMB 7
+
+static const float SDLT[16]={-1000000.0,-42.0,-39.0,-36.0,-33.0,-30.0,-27.0,-24.0,-21.0,-18.0,-15.0,-12.0,-9.0,-6.0,-3.0,0.0};
+
+unsigned char aica_device::DecodeSCI(unsigned char irq)
+{
+ unsigned char SCI=0;
+ unsigned char v;
+ v=(SCILV0((AICA))&(1<<irq))?1:0;
+ SCI|=v;
+ v=(SCILV1((AICA))&(1<<irq))?1:0;
+ SCI|=v<<1;
+ v=(SCILV2((AICA))&(1<<irq))?1:0;
+ SCI|=v<<2;
+ return SCI;
+}
+
+void aica_device::ResetInterrupts()
+{
+#if 0
+ UINT32 reset = m_udata.data[0xa4/2];
+
+ if (reset & 0x40)
+ m_irq_cb(-m_IrqTimA);
+ if (reset & 0x180)
+ m_irq_cb(-m_IrqTimBC);
+#endif
+}
+
+void aica_device::CheckPendingIRQ()
+{
+ UINT32 pend=m_udata.data[0xa0/2];
+ UINT32 en=m_udata.data[0x9c/2];
+
+ if(m_MidiW!=m_MidiR)
+ {
+ m_IRQL = m_IrqMidi;
+ m_irq_cb(1);
+ return;
+ }
+ if(!pend)
+ return;
+ if(pend&0x40)
+ if(en&0x40)
+ {
+ m_IRQL = m_IrqTimA;
+ m_irq_cb(1);
+ return;
+ }
+ if(pend&0x80)
+ if(en&0x80)
+ {
+ m_IRQL = m_IrqTimBC;
+ m_irq_cb(1);
+ return;
+ }
+ if(pend&0x100)
+ if(en&0x100)
+ {
+ m_IRQL = m_IrqTimBC;
+ m_irq_cb(1);
+ return;
+ }
+}
+
+void aica_device::CheckPendingIRQ_SH4()
+{
+ if(m_mcipd & m_mcieb)
+ m_main_irq_cb(1);
+
+ if((m_mcipd & m_mcieb) == 0)
+ m_main_irq_cb(0);
+}
+
+TIMER_CALLBACK_MEMBER( aica_device::timerA_cb )
+{
+ m_TimCnt[0] = 0xFFFF;
+ m_udata.data[0xa0/2]|=0x40;
+ m_mcipd |= 0x40;
+ m_udata.data[0x90/2]&=0xff00;
+ m_udata.data[0x90/2]|=m_TimCnt[0]>>8;
+
+ CheckPendingIRQ();
+ CheckPendingIRQ_SH4();
+
+}
+
+TIMER_CALLBACK_MEMBER( aica_device::timerB_cb )
+{
+ m_TimCnt[1] = 0xFFFF;
+ m_udata.data[0xa0/2]|=0x80;
+ m_mcipd |= 0x80;
+ m_udata.data[0x94/2]&=0xff00;
+ m_udata.data[0x94/2]|=m_TimCnt[1]>>8;
+
+ CheckPendingIRQ();
+ CheckPendingIRQ_SH4();
+}
+
+TIMER_CALLBACK_MEMBER( aica_device::timerC_cb )
+{
+ m_TimCnt[2] = 0xFFFF;
+ m_udata.data[0xa0/2]|=0x100;
+ m_mcipd |= 0x100;
+ m_udata.data[0x98/2]&=0xff00;
+ m_udata.data[0x98/2]|=m_TimCnt[2]>>8;
+
+ CheckPendingIRQ();
+ CheckPendingIRQ_SH4();
+}
+
+int aica_device::Get_AR(int base,int R)
+{
+ int Rate=base+(R<<1);
+ if(Rate>63) Rate=63;
+ if(Rate<0) Rate=0;
+ return m_ARTABLE[Rate];
+}
+
+int aica_device::Get_DR(int base,int R)
+{
+ int Rate=base+(R<<1);
+ if(Rate>63) Rate=63;
+ if(Rate<0) Rate=0;
+ return m_DRTABLE[Rate];
+}
+
+int aica_device::Get_RR(int base,int R)
+{
+ int Rate=base+(R<<1);
+ if(Rate>63) Rate=63;
+ if(Rate<0) Rate=0;
+ return m_DRTABLE[Rate];
+}
+
+void aica_device::Compute_EG(AICA_SLOT *slot)
+{
+ int octave=(OCT(slot)^8)-8;
+ int rate;
+ if(KRS(slot)!=0xf)
+ rate=octave+2*KRS(slot)+((FNS(slot)>>9)&1);
+ else
+ rate=0; //rate=((FNS(slot)>>9)&1);
+
+ slot->EG.volume=0x17f<<EG_SHIFT;
+ slot->EG.AR=Get_AR(rate,AR(slot));
+ slot->EG.D1R=Get_DR(rate,D1R(slot));
+ slot->EG.D2R=Get_DR(rate,D2R(slot));
+ slot->EG.RR=Get_RR(rate,RR(slot));
+ slot->EG.RR=Get_RR(rate,RR(slot));
+ slot->EG.DL=0x1f-DL(slot);
+}
+
+int aica_device::EG_Update(AICA_SLOT *slot)
+{
+ switch(slot->EG.state)
+ {
+ case AICA_ATTACK:
+ slot->EG.volume+=slot->EG.AR;
+ if(slot->EG.volume>=(0x3ff<<EG_SHIFT))
+ {
+ if (!LPSLNK(slot) && slot->EG.D1R)
+ {
+ slot->EG.state=AICA_DECAY1;
+ if(slot->EG.D1R>=(1024<<EG_SHIFT) && slot->EG.D2R) //Skip DECAY1, go directly to DECAY2
+ slot->EG.state=AICA_DECAY2;
+ }
+ slot->EG.volume=0x3ff<<EG_SHIFT;
+ }
+ break;
+ case AICA_DECAY1:
+ slot->EG.volume-=slot->EG.D1R;
+ if(slot->EG.volume<=0)
+ slot->EG.volume=0;
+ if(slot->EG.volume>>(EG_SHIFT+5)<=slot->EG.DL)
+ slot->EG.state=AICA_DECAY2;
+ break;
+ case AICA_DECAY2:
+ if(D2R(slot)==0)
+ return (slot->EG.volume>>EG_SHIFT)<<(SHIFT-10);
+ slot->EG.volume-=slot->EG.D2R;
+ if(slot->EG.volume<=0)
+ slot->EG.volume=0;
+
+ break;
+ case AICA_RELEASE:
+ slot->EG.volume-=slot->EG.RR;
+ if(slot->EG.volume<=0)
+ {
+ slot->EG.volume=0;
+ StopSlot(slot,0);
+// slot->EG.volume=0x17f<<EG_SHIFT;
+// slot->EG.state=AICA_ATTACK;
+ }
+ break;
+ default:
+ return 1<<SHIFT;
+ }
+ return (slot->EG.volume>>EG_SHIFT)<<(SHIFT-10);
+}
+
+UINT32 aica_device::Step(AICA_SLOT *slot)
+{
+ int octave=(OCT(slot)^8)-8+SHIFT-10;
+ UINT32 Fn=FNS(slot) + (0x400);
+ if (octave >= 0)
+ Fn<<=octave;
+ else
+ Fn>>=-octave;
+ return Fn;
+}
+
+
+void aica_device::Compute_LFO(AICA_SLOT *slot)
+{
+ if(PLFOS(slot)!=0)
+ AICALFO_ComputeStep(&(slot->PLFO),LFOF(slot),PLFOWS(slot),PLFOS(slot),0);
+ if(ALFOS(slot)!=0)
+ AICALFO_ComputeStep(&(slot->ALFO),LFOF(slot),ALFOWS(slot),ALFOS(slot),1);
+}
+
+#define ADPCMSHIFT 8
+#define ADFIX(f) (int) ((float) f*(float) (1<<ADPCMSHIFT))
+
+static const int TableQuant[8]={ADFIX(0.8984375),ADFIX(0.8984375),ADFIX(0.8984375),ADFIX(0.8984375),ADFIX(1.19921875),ADFIX(1.59765625),ADFIX(2.0),ADFIX(2.3984375)};
+static const int quant_mul[16]= { 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15};
+
+void aica_device::InitADPCM(int *PrevSignal, int *PrevQuant)
+{
+ *PrevSignal=0;
+ *PrevQuant=0x7f;
+}
+
+signed short aica_device::DecodeADPCM(int *PrevSignal, unsigned char Delta, int *PrevQuant)
+{
+ int x = *PrevQuant * quant_mul [Delta & 15];
+ x = *PrevSignal + ((int)(x + ((UINT32)x >> 29)) >> 3);
+ *PrevSignal=ICLIP16(x);
+ *PrevQuant=(*PrevQuant*TableQuant[Delta&7])>>ADPCMSHIFT;
+ *PrevQuant=(*PrevQuant<0x7f)?0x7f:((*PrevQuant>0x6000)?0x6000:*PrevQuant);
+ return *PrevSignal;
+}
+
+void aica_device::StartSlot(AICA_SLOT *slot)
+{
+ UINT64 start_offset;
+
+ slot->active=1;
+ slot->Backwards=0;
+ slot->cur_addr=0; slot->nxt_addr=1<<SHIFT; slot->prv_addr=-1;
+ start_offset = SA(slot); // AICA can play 16-bit samples from any boundry
+ slot->base=&m_AICARAM[start_offset];
+ slot->step=Step(slot);
+ Compute_EG(slot);
+ slot->EG.state=AICA_ATTACK;
+ slot->EG.volume=0x17f<<EG_SHIFT;
+ Compute_LFO(slot);
+
+ if (PCMS(slot) >= 2)
+ {
+ slot->curstep = 0;
+ slot->adbase = (unsigned char *) (m_AICARAM+((SA(slot))&0x7fffff));
+ InitADPCM(&(slot->cur_sample), &(slot->cur_quant));
+ InitADPCM(&(slot->cur_lpsample), &(slot->cur_lpquant));
+
+ // on real hardware this creates undefined behavior.
+ if (LSA(slot) > LEA(slot))
+ {
+ slot->udata.data[0xc/2] = 0xffff;
+ }
+ }
+}
+
+void aica_device::StopSlot(AICA_SLOT *slot,int keyoff)
+{
+ if(keyoff /*&& slot->EG.state!=AICA_RELEASE*/)
+ {
+ slot->EG.state=AICA_RELEASE;
+ }
+ else
+ {
+ slot->active=0;
+ slot->lpend = 1;
+ }
+ slot->udata.data[0]&=~0x4000;
+}
+
+#define log_base_2(n) (log((float) n)/log((float) 2))
+
+void aica_device::Init()
+{
+ int i;
+
+ m_IrqTimA = m_IrqTimBC = m_IrqMidi = 0;
+ m_MidiR=m_MidiW=0;
+ m_MidiOutR=m_MidiOutW=0;
+
+ // get AICA RAM
+ {
+ m_AICARAM = region()->base();
+ if (m_AICARAM)
+ {
+ m_AICARAM += m_roffset;
+ m_AICARAM_LENGTH = region()->bytes();
+ m_RAM_MASK = m_AICARAM_LENGTH-1;
+ m_RAM_MASK16 = m_RAM_MASK & 0x7ffffe;
+ m_DSP.AICARAM = (UINT16 *)m_AICARAM;
+ m_DSP.AICARAM_LENGTH = m_AICARAM_LENGTH/2;
+ }
+ }
+
+ m_timerA = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(aica_device::timerA_cb), this));
+ m_timerB = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(aica_device::timerB_cb), this));
+ m_timerC = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(aica_device::timerC_cb), this));
+
+ for(i=0;i<0x400;++i)
+ {
+ float envDB=((float)(3*(i-0x3ff)))/32.0f;
+ float scale=(float)(1<<SHIFT);
+ m_EG_TABLE[i]=(INT32)(powf(10.0f,envDB/20.0f)*scale);
+ }
+
+ for(i=0;i<0x20000;++i)
+ {
+ int iTL =(i>>0x0)&0xff;
+ int iPAN=(i>>0x8)&0x1f;
+ int iSDL=(i>>0xD)&0x0F;
+ float TL=1.0;
+ float SegaDB=0;
+ float fSDL=1.0;
+ float PAN=1.0;
+ float LPAN,RPAN;
+
+ if(iTL&0x01) SegaDB-=0.4f;
+ if(iTL&0x02) SegaDB-=0.8f;
+ if(iTL&0x04) SegaDB-=1.5f;
+ if(iTL&0x08) SegaDB-=3.0f;
+ if(iTL&0x10) SegaDB-=6.0f;
+ if(iTL&0x20) SegaDB-=12.0f;
+ if(iTL&0x40) SegaDB-=24.0f;
+ if(iTL&0x80) SegaDB-=48.0f;
+
+ TL=powf(10.0f,SegaDB/20.0f);
+
+ SegaDB=0;
+ if(iPAN&0x1) SegaDB-=3.0f;
+ if(iPAN&0x2) SegaDB-=6.0f;
+ if(iPAN&0x4) SegaDB-=12.0f;
+ if(iPAN&0x8) SegaDB-=24.0f;
+
+ if((iPAN&0xf)==0xf) PAN=0.0;
+ else PAN=powf(10.0f,SegaDB/20.0f);
+
+ if(iPAN<0x10)
+ {
+ LPAN=PAN;
+ RPAN=1.0;
+ }
+ else
+ {
+ RPAN=PAN;
+ LPAN=1.0;
+ }
+
+ if(iSDL)
+ fSDL=powf(10.0f,(SDLT[iSDL])/20.0f);
+ else
+ fSDL=0.0;
+
+ m_LPANTABLE[i]=FIX((4.0f*LPAN*TL*fSDL));
+ m_RPANTABLE[i]=FIX((4.0f*RPAN*TL*fSDL));
+ }
+
+ m_ARTABLE[0]=m_DRTABLE[0]=0; //Infinite time
+ m_ARTABLE[1]=m_DRTABLE[1]=0; //Infinite time
+ for(i=2;i<64;++i)
+ {
+ double t,step,scale;
+ t=ARTimes[i]; //In ms
+ if(t!=0.0)
+ {
+ step=(1023*1000.0)/(44100.0*t);
+ scale=(double) (1<<EG_SHIFT);
+ m_ARTABLE[i]=(int) (step*scale);
+ }
+ else
+ m_ARTABLE[i]=1024<<EG_SHIFT;
+
+ t=DRTimes[i]; //In ms
+ step=(1023*1000.0)/(44100.0*t);
+ scale=(double) (1<<EG_SHIFT);
+ m_DRTABLE[i]=(int) (step*scale);
+ }
+
+ // make sure all the slots are off
+ for(i=0;i<64;++i)
+ {
+ m_Slots[i].slot=i;
+ m_Slots[i].active=0;
+ m_Slots[i].base=NULL;
+ m_Slots[i].EG.state=AICA_RELEASE;
+ m_Slots[i].lpend=1;
+ }
+
+ AICALFO_Init();
+ m_buffertmpl=auto_alloc_array_clear(machine(), signed int, 44100);
+ m_buffertmpr=auto_alloc_array_clear(machine(), signed int, 44100);
+
+ // no "pend"
+ m_udata.data[0xa0/2] = 0;
+ //AICA[1].udata.data[0x20/2] = 0;
+ m_TimCnt[0] = 0xffff;
+ m_TimCnt[1] = 0xffff;
+ m_TimCnt[2] = 0xffff;
+}
+
+void aica_device::UpdateSlotReg(int s,int r)
+{
+ AICA_SLOT *slot=m_Slots+s;
+ int sl;
+ switch(r&0x7f)
+ {
+ case 0:
+ case 1:
+ if(KEYONEX(slot))
+ {
+ for(sl=0;sl<64;++sl)
+ {
+ AICA_SLOT *s2=m_Slots+sl;
+ {
+ if(KEYONB(s2) && s2->EG.state==AICA_RELEASE/*&& !s2->active*/)
+ {
+ s2->lpend = 0;
+ StartSlot(s2);
+ #if 0
+ printf("StartSlot[%02X]: SSCTL %01X SA %06X LSA %04X LEA %04X PCMS %01X LPCTL %01X\n",sl,SSCTL(s2),SA(s2),LSA(s2),LEA(s2),PCMS(s2),LPCTL(s2));
+ printf(" AR %02X D1R %02X D2R %02X RR %02X DL %02X KRS %01X LPSLNK %01X\n",AR(s2),D1R(s2),D2R(s2),RR(s2),DL(s2),KRS(s2),LPSLNK(s2)>>14);
+ printf(" TL %02X OCT %01X FNS %03X\n",TL(s2),OCT(s2),FNS(s2));
+ printf(" LFORE %01X LFOF %02X ALFOWS %01X ALFOS %01X PLFOWS %01X PLFOS %01X\n",LFORE(s2),LFOF(s2),ALFOWS(s2),ALFOS(s2),PLFOWS(s2),PLFOS(s2));
+ printf(" IMXL %01X ISEL %01X DISDL %01X DIPAN %02X\n",IMXL(s2),ISEL(s2),DISDL(s2),DIPAN(s2));
+ printf("\n");
+ fflush(stdout);
+ #endif
+ }
+ if(!KEYONB(s2) /*&& s2->active*/)
+ {
+ StopSlot(s2,1);
+ }
+ }
+ }
+ slot->udata.data[0]&=~0x8000;
+ }
+ break;
+ case 0x18:
+ case 0x19:
+ slot->step=Step(slot);
+ break;
+ case 0x14:
+ case 0x15:
+ slot->EG.RR=Get_RR(0,RR(slot));
+ slot->EG.DL=0x1f-DL(slot);
+ break;
+ case 0x1c:
+ case 0x1d:
+ Compute_LFO(slot);
+ break;
+ case 0x24:
+// printf("[%02d]: %x to DISDL/DIPAN (PC=%x)\n", s, slot->udata.data[0x24/2], arm7_get_register(15));
+ break;
+ }
+}
+
+void aica_device::UpdateReg(address_space &space, int reg)
+{
+ switch(reg&0xff)
+ {
+ case 0x4:
+ case 0x5:
+ {
+ unsigned int v=RBL();
+ m_DSP.RBP=RBP();
+ if(v==0)
+ m_DSP.RBL=8*1024;
+ else if(v==1)
+ m_DSP.RBL=16*1024;
+ else if(v==2)
+ m_DSP.RBL=32*1024;
+ else if(v==3)
+ m_DSP.RBL=64*1024;
+ }
+ break;
+ case 0x8:
+ case 0x9:
+ midi_in(space, 0, m_udata.data[0x8/2]&0xff, 0xffff);
+ break;
+
+ //case 0x0c:
+ //case 0x0d:
+ // printf("%04x\n",m_udata.data[0xc/2]);
+ // break;
+
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ break;
+
+ case 0x80:
+ case 0x81:
+ m_dma.dmea = ((m_udata.data[0x80/2] & 0xfe00) << 7) | (m_dma.dmea & 0xfffc);
+ /* TODO: $TSCD - MRWINH regs */
+ break;
+
+ case 0x84:
+ case 0x85:
+ m_dma.dmea = (m_udata.data[0x84/2] & 0xfffc) | (m_dma.dmea & 0x7f0000);
+ break;
+
+ case 0x88:
+ case 0x89:
+ m_dma.drga = (m_udata.data[0x88/2] & 0x7ffc);
+ m_dma.dgate = (m_udata.data[0x88/2] & 0x8000) >> 15;
+ break;
+
+ case 0x8c:
+ case 0x8d:
+ m_dma.dlg = (m_udata.data[0x8c/2] & 0x7ffc);
+ m_dma.ddir = (m_udata.data[0x8c/2] & 0x8000) >> 15;
+ if(m_udata.data[0x8c/2] & 1) // dexe
+ aica_exec_dma(space);
+ break;
+
+ case 0x90:
+ case 0x91:
+ if(m_master)
+ {
+ UINT32 time;
+
+ m_TimPris[0]=1<<((m_udata.data[0x90/2]>>8)&0x7);
+ m_TimCnt[0]=(m_udata.data[0x90/2]&0xff)<<8;
+
+ if ((m_udata.data[0x90/2]&0xff) != 255)
+ {
+ time = (44100 / m_TimPris[0]) / (255-(m_udata.data[0x90/2]&0xff));
+ if (time)
+ {
+ m_timerA->adjust(attotime::from_hz(time));
+ }
+ }
+ }
+ break;
+ case 0x94:
+ case 0x95:
+ if(m_master)
+ {
+ UINT32 time;
+
+ m_TimPris[1]=1<<((m_udata.data[0x94/2]>>8)&0x7);
+ m_TimCnt[1]=(m_udata.data[0x94/2]&0xff)<<8;
+
+ if ((m_udata.data[0x94/2]&0xff) != 255)
+ {
+ time = (44100 / m_TimPris[1]) / (255-(m_udata.data[0x94/2]&0xff));
+ if (time)
+ {
+ m_timerB->adjust(attotime::from_hz(time));
+ }
+ }
+ }
+ break;
+ case 0x98:
+ case 0x99:
+ if(m_master)
+ {
+ UINT32 time;
+
+ m_TimPris[2]=1<<((m_udata.data[0x98/2]>>8)&0x7);
+ m_TimCnt[2]=(m_udata.data[0x98/2]&0xff)<<8;
+
+ if ((m_udata.data[0x98/2]&0xff) != 255)
+ {
+ time = (44100 / m_TimPris[2]) / (255-(m_udata.data[0x98/2]&0xff));
+ if (time)
+ {
+ m_timerC->adjust(attotime::from_hz(time));
+ }
+ }
+ }
+ break;
+
+ case 0x9c: //SCIEB
+ case 0x9d:
+ if(m_udata.data[0x9c/2] & 0x631)
+ popmessage("AICA: SCIEB enabled %04x, contact MAME/MESSdev",m_udata.data[0x9c/2]);
+ break;
+
+ case 0xa4: //SCIRE
+ case 0xa5:
+
+ if(m_master)
+ {
+ m_udata.data[0xa0/2] &= ~m_udata.data[0xa4/2];
+ ResetInterrupts();
+
+ // behavior from real hardware (SCSP, assumed to carry over): if you SCIRE a timer that's expired,
+ // it'll immediately pop up again
+ if (m_TimCnt[0] >= 0xff00)
+ {
+ m_udata.data[0xa0/2] |= 0x40;
+ }
+ if (m_TimCnt[1] >= 0xff00)
+ {
+ m_udata.data[0xa0/2] |= 0x80;
+ }
+ if (m_TimCnt[2] >= 0xff00)
+ {
+ m_udata.data[0xa0/2] |= 0x100;
+ }
+ }
+ break;
+ case 0xa8:
+ case 0xa9:
+ case 0xac:
+ case 0xad:
+ case 0xb0:
+ case 0xb1:
+ if(m_master)
+ {
+ m_IrqTimA=DecodeSCI(SCITMA);
+ m_IrqTimBC=DecodeSCI(SCITMB);
+ m_IrqMidi=DecodeSCI(SCIMID);
+ }
+ break;
+
+ case 0xb4: //MCIEB
+ case 0xb5:
+ if(m_udata.data[0xb4/2] & 0x7df)
+ popmessage("AICA: MCIEB enabled %04x, contact MAME/MESSdev",m_udata.data[0xb4/2]);
+ m_mcieb = m_udata.data[0xb4/2];
+ CheckPendingIRQ_SH4();
+ break;
+
+ case 0xb8:
+ case 0xb9:
+ if(m_udata.data[0xb8/2] & 0x20)
+ m_mcipd |= 0x20;
+ CheckPendingIRQ_SH4();
+ break;
+
+ case 0xbc:
+ case 0xbd:
+ m_mcipd &= ~m_udata.data[0xbc/2];
+ CheckPendingIRQ_SH4();
+ break;
+ }
+}
+
+void aica_device::UpdateSlotRegR(int slot,int reg)
+{
+}
+
+void aica_device::UpdateRegR(address_space &space, int reg)
+{
+ switch(reg&0xff)
+ {
+ case 8:
+ case 9:
+ {
+ unsigned short v=m_udata.data[0x8/2];
+ v&=0xff00;
+ v|=m_MidiStack[m_MidiR];
+ m_irq_cb(0); // cancel the IRQ
+ if(m_MidiR!=m_MidiW)
+ {
+ ++m_MidiR;
+ m_MidiR&=15;
+ }
+ m_udata.data[0x8/2]=v;
+ }
+ break;
+
+ case 0x10: // LP check
+ case 0x11:
+ {
+ int slotnum = MSLC();
+ AICA_SLOT *slot=m_Slots + slotnum;
+ UINT16 LP = 0;
+ if (!(AFSEL()))
+ {
+ UINT16 SGC;
+ int EG;
+
+ LP = slot->lpend ? 0x8000 : 0x0000;
+ slot->lpend = 0;
+ SGC = (slot->EG.state << 13) & 0x6000;
+ EG = slot->active ? slot->EG.volume : 0;
+ EG >>= (EG_SHIFT - 13);
+ EG = 0x1FFF - EG;
+ if (EG < 0) EG = 0;
+
+ m_udata.data[0x10/2] = (EG & 0x1FF8) | SGC | LP;
+ }
+ else
+ {
+ LP = slot->lpend ? 0x8000 : 0x0000;
+ m_udata.data[0x10/2] = LP;
+ }
+ }
+ break;
+
+ case 0x14: // CA (slot address)
+ case 0x15:
+ {
+ //m_stream->update();
+ int slotnum = MSLC();
+ AICA_SLOT *slot=m_Slots+slotnum;
+ unsigned int CA = 0;
+
+ if (PCMS(slot) == 0) // 16-bit samples
+ {
+ CA = (slot->cur_addr>>(SHIFT-1))&m_RAM_MASK16;
+ }
+ else // 8-bit PCM and 4-bit ADPCM
+ {
+ CA = (slot->cur_addr>>SHIFT)&m_RAM_MASK;
+ }
+
+ //printf("%08x %08x\n",CA,slot->cur_addr&m_RAM_MASK16);
+
+ m_udata.data[0x14/2] = CA;
+ }
+ break;
+ case 0xb8:
+ case 0xb9:
+ m_udata.data[0xb8/2] = m_mcipd;
+ break;
+ }
+}
+
+void aica_device::w16(address_space &space,unsigned int addr,unsigned short val)
+{
+ addr&=0xffff;
+ if(addr<0x2000)
+ {
+ int slot=addr/0x80;
+ addr&=0x7f;
+// printf("%x to slot %d offset %x\n", val, slot, addr);
+ *((unsigned short *) (m_Slots[slot].udata.datab+(addr))) = val;
+ UpdateSlotReg(slot,addr&0x7f);
+ }
+ else if (addr < 0x2800)
+ {
+ if (addr <= 0x2044)
+ {
+// printf("%x to EFSxx slot %d (addr %x)\n", val, (addr-0x2000)/4, addr&0x7f);
+ m_EFSPAN[addr&0x7f] = val;
+ }
+ }
+ else if(addr<0x3000)
+ {
+ if (addr < 0x28be)
+ {
+// printf("%x to AICA global @ %x\n", val, addr & 0xff);
+ *((unsigned short *) (m_udata.datab+((addr&0xff)))) = val;
+ UpdateReg(space, addr&0xff);
+
+ }
+ else if (addr == 0x2d00)
+ {
+ m_IRQL = val;
+ popmessage("AICA: write to IRQL?");
+ }
+ else if (addr == 0x2d04)
+ {
+ m_IRQR = val;
+
+ if (val & 1)
+ {
+ m_irq_cb(0);
+ }
+ if (val & 0x100)
+ popmessage("AICA: SH-4 write protection enabled!");
+
+ if (val & 0xfefe)
+ popmessage("AICA: IRQR %04x!",val);
+ }
+ }
+ else
+ {
+ //DSP
+ if(addr<0x3200) //COEF
+ *((unsigned short *) (m_DSP.COEF+(addr-0x3000)/2))=val;
+ else if(addr<0x3400)
+ *((unsigned short *) (m_DSP.MADRS+(addr-0x3200)/2))=val;
+ else if(addr<0x3c00)
+ {
+ *((unsigned short *) (m_DSP.MPRO+(addr-0x3400)/2))=val;
+
+ if (addr == 0x3bfe)
+ {
+ aica_dsp_start(&m_DSP);
+ }
+ }
+ else if(addr<0x4000)
+ {
+ popmessage("AICADSP write to undocumented reg %04x -> %04x",addr,val);
+ }
+ else if(addr<0x4400)
+ {
+ if(addr & 4)
+ m_DSP.TEMP[(addr >> 3) & 0x7f] = (m_DSP.TEMP[(addr >> 3) & 0x7f] & 0xffff0000) | (val & 0xffff);
+ else
+ m_DSP.TEMP[(addr >> 3) & 0x7f] = (m_DSP.TEMP[(addr >> 3) & 0x7f] & 0xffff) | (val << 16);
+ }
+ else if(addr<0x4500)
+ {
+ if(addr & 4)
+ m_DSP.MEMS[(addr >> 3) & 0x1f] = (m_DSP.MEMS[(addr >> 3) & 0x1f] & 0xffff0000) | (val & 0xffff);
+ else
+ m_DSP.MEMS[(addr >> 3) & 0x1f] = (m_DSP.MEMS[(addr >> 3) & 0x1f] & 0xffff) | (val << 16);
+ }
+ else if(addr<0x4580)
+ {
+ if(addr & 4)
+ m_DSP.MIXS[(addr >> 3) & 0xf] = (m_DSP.MIXS[(addr >> 3) & 0xf] & 0xffff0000) | (val & 0xffff);
+ else
+ m_DSP.MIXS[(addr >> 3) & 0xf] = (m_DSP.MIXS[(addr >> 3) & 0xf] & 0xffff) | (val << 16);
+ }
+ else if(addr<0x45c0)
+ *((unsigned short *) (m_DSP.EFREG+(addr-0x4580)/4))=val;
+ else if(addr<0x45c8)
+ *((unsigned short *) (m_DSP.EXTS+(addr-0x45c0)/2))=val;
+ }
+}
+
+unsigned short aica_device::r16(address_space &space, unsigned int addr)
+{
+ unsigned short v=0;
+ addr&=0xffff;
+ if(addr<0x2000)
+ {
+ int slot=addr/0x80;
+ addr&=0x7f;
+ UpdateSlotRegR(slot,addr&0x7f);
+ v=*((unsigned short *) (m_Slots[slot].udata.datab+(addr)));
+ }
+ else if(addr<0x3000)
+ {
+ if (addr <= 0x2044)
+ {
+ v = m_EFSPAN[addr&0x7f];
+ }
+ else if (addr < 0x28be)
+ {
+ UpdateRegR(space, addr&0xff);
+ v= *((unsigned short *) (m_udata.datab+((addr&0xff))));
+ if((addr&0xfffe)==0x2810) m_udata.data[0x10/2] &= 0x7FFF; // reset LP on read
+ }
+ else if (addr == 0x2d00)
+ {
+ return m_IRQL;
+ }
+ else if (addr == 0x2d04)
+ {
+ //popmessage("AICA: read to IRQR?");
+ return m_IRQR;
+ }
+ }
+ else
+ {
+ if(addr<0x3200) //COEF
+ v= *((unsigned short *) (m_DSP.COEF+(addr-0x3000)/2));
+ else if(addr<0x3400)
+ v= *((unsigned short *) (m_DSP.MADRS+(addr-0x3200)/2));
+ else if(addr<0x3c00)
+ v= *((unsigned short *) (m_DSP.MPRO+(addr-0x3400)/2));
+ else if(addr<0x4000)
+ {
+ v= 0xffff;
+ popmessage("AICADSP read to undocumented reg %04x",addr);
+ }
+ else if(addr<0x4400)
+ {
+ if(addr & 4)
+ v= m_DSP.TEMP[(addr >> 3) & 0x7f] & 0xffff;
+ else
+ v= m_DSP.TEMP[(addr >> 3) & 0x7f] >> 16;
+ }
+ else if(addr<0x4500)
+ {
+ if(addr & 4)
+ v= m_DSP.MEMS[(addr >> 3) & 0x1f] & 0xffff;
+ else
+ v= m_DSP.MEMS[(addr >> 3) & 0x1f] >> 16;
+ }
+ else if(addr<0x4580)
+ {
+ if(addr & 4)
+ v= m_DSP.MIXS[(addr >> 3) & 0xf] & 0xffff;
+ else
+ v= m_DSP.MIXS[(addr >> 3) & 0xf] >> 16;
+ }
+ else if(addr<0x45c0)
+ v = *((unsigned short *) (m_DSP.EFREG+(addr-0x4580)/4));
+ else if(addr<0x45c8)
+ v = *((unsigned short *) (m_DSP.EXTS+(addr-0x45c0)/2));
+ }
+// else if (addr<0x700)
+// v=m_RINGBUF[(addr-0x600)/2];
+ return v;
+}
+
+
+#define REVSIGN(v) ((~v)+1)
+
+#ifdef UNUSED_FUNCTION
+void aica_device::TimersAddTicks(int ticks)
+{
+ if(m_TimCnt[0]<=0xff00)
+ {
+ m_TimCnt[0] += ticks << (8-((m_udata.data[0x18/2]>>8)&0x7));
+ if (m_TimCnt[0] > 0xFF00)
+ {
+ m_TimCnt[0] = 0xFFFF;
+ m_udata.data[0xa0/2]|=0x40;
+ }
+ m_udata.data[0x90/2]&=0xff00;
+ m_udata.data[0x90/2]|=m_TimCnt[0]>>8;
+ }
+
+ if(m_TimCnt[1]<=0xff00)
+ {
+ m_TimCnt[1] += ticks << (8-((m_udata.data[0x1a/2]>>8)&0x7));
+ if (m_TimCnt[1] > 0xFF00)
+ {
+ m_TimCnt[1] = 0xFFFF;
+ m_udata.data[0xa0/2]|=0x80;
+ }
+ m_udata.data[0x94/2]&=0xff00;
+ m_udata.data[0x94/2]|=m_TimCnt[1]>>8;
+ }
+
+ if(m_TimCnt[2]<=0xff00)
+ {
+ m_TimCnt[2] += ticks << (8-((m_udata.data[0x1c/2]>>8)&0x7));
+ if (m_TimCnt[2] > 0xFF00)
+ {
+ m_TimCnt[2] = 0xFFFF;
+ m_udata.data[0xa0/2]|=0x100;
+ }
+ m_udata.data[0x98/2]&=0xff00;
+ m_udata.data[0x98/2]|=m_TimCnt[2]>>8;
+ }
+}
+#endif
+
+INT32 aica_device::UpdateSlot(AICA_SLOT *slot)
+{
+ INT32 sample;
+ int step=slot->step;
+ UINT32 addr1,addr2,addr_select; // current and next sample addresses
+ UINT32 *addr[2] = {&addr1, &addr2}; // used for linear interpolation
+ UINT32 *slot_addr[2] = {&(slot->cur_addr), &(slot->nxt_addr)}; //
+ UINT32 chanlea = LEA(slot);
+
+ if(SSCTL(slot)!=0) //no FM or noise yet
+ return 0;
+
+ if(PCMS(slot) == 3) // Red Dog music relies on this
+ chanlea = (chanlea + 3) & ~3;
+
+ if(PLFOS(slot)!=0)
+ {
+ step=step*AICAPLFO_Step(&(slot->PLFO));
+ step>>=SHIFT;
+ }
+
+ if(PCMS(slot) == 1)
+ {
+ addr1=slot->cur_addr>>SHIFT;
+ addr2=slot->nxt_addr>>SHIFT;
+ }
+ else if(PCMS(slot) == 0)
+ {
+ addr1=(slot->cur_addr>>(SHIFT-1))&m_RAM_MASK16;
+ addr2=(slot->nxt_addr>>(SHIFT-1))&m_RAM_MASK16;
+ }
+ else
+ {
+ addr1=slot->cur_addr>>SHIFT;
+ addr2=slot->nxt_addr>>SHIFT;
+ }
+
+ if(PCMS(slot) == 1) // 8-bit signed
+ {
+ INT8 *p1=(signed char *) (m_AICARAM+(((SA(slot)+addr1))&m_RAM_MASK));
+ INT8 *p2=(signed char *) (m_AICARAM+(((SA(slot)+addr2))&m_RAM_MASK));
+ INT32 s;
+ INT32 fpart=slot->cur_addr&((1<<SHIFT)-1);
+ s=(int) (p1[0]<<8)*((1<<SHIFT)-fpart)+(int) (p2[0]<<8)*fpart;
+ sample=(s>>SHIFT);
+ }
+ else if (PCMS(slot) == 0) //16 bit signed
+ {
+ UINT8 *p1=(UINT8 *) (m_AICARAM+((SA(slot)+addr1)&m_RAM_MASK));
+ UINT8 *p2=(UINT8 *) (m_AICARAM+((SA(slot)+addr2)&m_RAM_MASK));
+ INT32 s;
+ INT32 fpart=slot->cur_addr&((1<<SHIFT)-1);
+ s=(int) ((INT16)(p1[0] | (p1[1]<<8)))*((1<<SHIFT)-fpart)+(int) ((INT16)(p2[0] | (p2[1]<<8)))*fpart;
+ sample=(s>>SHIFT);
+ }
+ else // 4-bit ADPCM
+ {
+ UINT8 *base= slot->adbase;
+ INT32 s;
+ int cur_sample; //current ADPCM sample
+ int nxt_sample; //next ADPCM sample
+ INT32 fpart=slot->cur_addr&((1<<SHIFT)-1);
+ UINT32 steps_to_go = addr1 > addr2 ? chanlea : addr2, curstep = slot->curstep;
+
+ if (slot->adbase)
+ {
+ cur_sample = slot->cur_sample; // may already contains current decoded sample
+
+ // seek to the interpolation sample
+ while (curstep < steps_to_go)
+ {
+ int shift1 = 4 & (curstep << 2);
+ unsigned char delta1 = (*base>>shift1)&0xf;
+ DecodeADPCM(&(slot->cur_sample),delta1,&(slot->cur_quant));
+ if (!(++curstep & 1))
+ base++;
+ if (curstep == addr1)
+ cur_sample = slot->cur_sample;
+ if (curstep == LSA(slot))
+ {
+ slot->cur_lpsample = slot->cur_sample;
+ slot->cur_lpquant = slot->cur_quant;
+ }
+ }
+ nxt_sample = slot->cur_sample;
+
+ slot->adbase = base;
+ slot->curstep = curstep;
+
+ s=(int)cur_sample*((1<<SHIFT)-fpart)+(int)nxt_sample*fpart;
+ }
+ else
+ {
+ s = 0;
+ }
+
+ sample=(s>>SHIFT);
+ }
+
+ slot->prv_addr=slot->cur_addr;
+ slot->cur_addr+=step;
+ slot->nxt_addr=slot->cur_addr+(1<<SHIFT);
+
+ addr1=slot->cur_addr>>SHIFT;
+ addr2=slot->nxt_addr>>SHIFT;
+
+ if(addr1>=LSA(slot))
+ {
+ if(LPSLNK(slot) && slot->EG.state==AICA_ATTACK && slot->EG.D1R)
+ slot->EG.state = AICA_DECAY1;
+ }
+
+ for (addr_select=0; addr_select<2; addr_select++)
+ {
+ INT32 rem_addr;
+ switch(LPCTL(slot))
+ {
+ case 0: //no loop
+ if(*addr[addr_select]>=LSA(slot) && *addr[addr_select]>=chanlea)
+ {
+ StopSlot(slot,0);
+ }
+ break;
+ case 1: //normal loop
+ if(*addr[addr_select]>=chanlea)
+ {
+ slot->lpend = 1;
+ rem_addr = *slot_addr[addr_select] - (chanlea<<SHIFT);
+ *slot_addr[addr_select]=(LSA(slot)<<SHIFT) + rem_addr;
+
+ if(PCMS(slot)>=2 && addr_select == 0)
+ {
+ // restore the state @ LSA - the sampler will naturally walk to (LSA + remainder)
+ slot->adbase = &m_AICARAM[SA(slot)+(LSA(slot)/2)];
+ slot->curstep = LSA(slot);
+ if (PCMS(slot) == 2)
+ {
+ slot->cur_sample = slot->cur_lpsample;
+ slot->cur_quant = slot->cur_lpquant;
+ }
+
+// printf("Looping: slot_addr %x LSA %x LEA %x step %x base %x\n", *slot_addr[addr_select]>>SHIFT, LSA(slot), LEA(slot), slot->curstep, slot->adbase);
+ }
+ }
+ break;
+ }
+ }
+
+ if(ALFOS(slot)!=0)
+ {
+ sample=sample*AICAALFO_Step(&(slot->ALFO));
+ sample>>=SHIFT;
+ }
+
+ if(slot->EG.state==AICA_ATTACK)
+ sample=(sample*EG_Update(slot))>>SHIFT;
+ else
+ sample=(sample*m_EG_TABLE[EG_Update(slot)>>(SHIFT-10)])>>SHIFT;
+
+ return sample;
+}
+
+void aica_device::DoMasterSamples(int nsamples)
+{
+ stream_sample_t *bufr,*bufl;
+ int sl, s, i;
+
+ bufr=m_bufferr;
+ bufl=m_bufferl;
+
+ for(s=0;s<nsamples;++s)
+ {
+ INT32 smpl, smpr;
+
+ smpl = smpr = 0;
+
+ // mix slots' direct output
+ for(sl=0;sl<64;++sl)
+ {
+ AICA_SLOT *slot=m_Slots+sl;
+ m_RBUFDST=m_RINGBUF+m_BUFPTR;
+ if(m_Slots[sl].active)
+ {
+ unsigned int Enc;
+ signed int sample;
+
+ sample=UpdateSlot(slot);
+
+ Enc=((TL(slot))<<0x0)|((IMXL(slot))<<0xd);
+ aica_dsp_setsample(&m_DSP,(sample*m_LPANTABLE[Enc])>>(SHIFT-2),ISEL(slot),IMXL(slot));
+ Enc=((TL(slot))<<0x0)|((DIPAN(slot))<<0x8)|((DISDL(slot))<<0xd);
+ {
+ smpl+=(sample*m_LPANTABLE[Enc])>>SHIFT;
+ smpr+=(sample*m_RPANTABLE[Enc])>>SHIFT;
+ }
+ }
+
+ m_BUFPTR&=63;
+ }
+
+ // process the DSP
+ aica_dsp_step(&m_DSP);
+
+ // mix DSP output
+ for(i=0;i<16;++i)
+ {
+ if(EFSDL(i))
+ {
+ unsigned int Enc=((EFPAN(i))<<0x8)|((EFSDL(i))<<0xd);
+ smpl+=(m_DSP.EFREG[i]*m_LPANTABLE[Enc])>>SHIFT;
+ smpr+=(m_DSP.EFREG[i]*m_RPANTABLE[Enc])>>SHIFT;
+ }
+ }
+
+ *bufl++ = ICLIP16(smpl>>3);
+ *bufr++ = ICLIP16(smpr>>3);
+ }
+}
+
+/* TODO: this needs to be timer-ized */
+void aica_device::aica_exec_dma(address_space &space)
+{
+ static UINT16 tmp_dma[4];
+ int i;
+
+ printf("AICA: DMA transfer START\n"
+ "DMEA: %08x DRGA: %08x DLG: %04x\n"
+ "DGATE: %d DDIR: %d\n",m_dma.dmea,m_dma.drga,m_dma.dlg,m_dma.dgate,m_dma.ddir);
+
+ /* Copy the dma values in a temp storage for resuming later */
+ /* (DMA *can't* overwrite its parameters). */
+ if(!(m_dma.ddir))
+ {
+ for(i=0;i<4;i++)
+ tmp_dma[i] = m_udata.data[(0x80+(i*4))/2];
+ }
+
+ /* note: we don't use space.read_word / write_word because it can happen that SH-4 enables the DMA instead of ARM like in DCLP tester. */
+ /* TODO: don't know if params auto-updates, I guess not ... */
+ if(m_dma.ddir)
+ {
+ if(m_dma.dgate)
+ {
+ for(i=0;i < m_dma.dlg;i+=2)
+ {
+ m_AICARAM[m_dma.dmea] = 0;
+ m_AICARAM[m_dma.dmea+1] = 0;
+ m_dma.dmea+=2;
+ }
+ }
+ else
+ {
+ for(i=0;i < m_dma.dlg;i+=2)
+ {
+ UINT16 tmp;
+ tmp = r16(space, m_dma.drga);;
+ m_AICARAM[m_dma.dmea] = tmp & 0xff;
+ m_AICARAM[m_dma.dmea+1] = tmp>>8;
+ m_dma.dmea+=4;
+ m_dma.drga+=4;
+ }
+ }
+ }
+ else
+ {
+ if(m_dma.dgate)
+ {
+ for(i=0;i < m_dma.dlg;i+=2)
+ {
+ w16(space, m_dma.drga, 0);
+ m_dma.drga+=4;
+ }
+ }
+ else
+ {
+ for(i=0;i < m_dma.dlg;i+=2)
+ {
+ UINT16 tmp;
+ tmp = m_AICARAM[m_dma.dmea];
+ tmp|= m_AICARAM[m_dma.dmea+1]<<8;
+ w16(space, m_dma.drga, tmp);
+ m_dma.dmea+=4;
+ m_dma.drga+=4;
+ }
+ }
+ }
+
+ /*Resume the values*/
+ if(!(m_dma.ddir))
+ {
+ for(i=0;i<4;i++)
+ m_udata.data[(0x80+(i*4))/2] = tmp_dma[i];
+ }
+
+ /* Job done, clear DEXE */
+ m_udata.data[0x8c/2] &= ~1;
+ /* request a dma end irq */
+ m_mcipd |= 0x10;
+ CheckPendingIRQ_SH4();
+}
+
+#ifdef UNUSED_FUNCTION
+int aica_device::IRQCB(void *param)
+{
+ CheckPendingIRQ(param);
+ return -1;
+}
+#endif
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void aica_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ m_bufferl = outputs[0];
+ m_bufferr = outputs[1];
+ m_length = samples;
+ DoMasterSamples(samples);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void aica_device::device_start()
+{
+ // init the emulation
+ Init();
+
+ // set up the IRQ callbacks
+ m_irq_cb.resolve_safe();
+ m_main_irq_cb.resolve_safe();
+
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, 44100);
+
+ // save state
+ save_item(NAME(m_IrqTimA));
+ save_item(NAME(m_IrqTimBC));
+ save_item(NAME(m_IrqMidi));
+ save_item(NAME(m_MidiOutW));
+ save_item(NAME(m_MidiOutR));
+ save_item(NAME(m_MidiStack),16);
+ save_item(NAME(m_MidiW));
+ save_item(NAME(m_MidiR));
+ save_item(NAME(m_LPANTABLE),0x20000);
+ save_item(NAME(m_RPANTABLE),0x20000);
+ save_item(NAME(m_TimPris),3);
+ save_item(NAME(m_TimCnt),3);
+}
+
+void aica_device::set_ram_base(void *base, int size)
+{
+ m_AICARAM = (unsigned char *)base;
+ m_AICARAM_LENGTH = size;
+ m_RAM_MASK = m_AICARAM_LENGTH-1;
+ m_RAM_MASK16 = m_RAM_MASK & 0x7ffffe;
+ m_DSP.AICARAM = (UINT16 *)base;
+ m_DSP.AICARAM_LENGTH = size;
+}
+
+READ16_MEMBER( aica_device::read )
+{
+ return r16(space,offset*2);
+}
+
+WRITE16_MEMBER( aica_device::write )
+{
+ UINT16 tmp;
+
+ tmp = r16(space, offset*2);
+ COMBINE_DATA(&tmp);
+ w16(space, offset*2, tmp);
+}
+
+WRITE16_MEMBER( aica_device::midi_in )
+{
+ m_MidiStack[m_MidiW++]=data;
+ m_MidiW &= 15;
+}
+
+READ16_MEMBER( aica_device::midi_out_r )
+{
+ unsigned char val;
+
+ val=m_MidiStack[m_MidiR++];
+ m_MidiR&=7;
+ return val;
+}
+
+const device_type AICA = &device_creator<aica_device>;
+
+aica_device::aica_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, AICA, "AICA", tag, owner, clock, "aica", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_master(false),
+ m_roffset(0),
+ m_irq_cb(*this),
+ m_main_irq_cb(*this),
+ m_IRQL(0),
+ m_IRQR(0),
+ m_BUFPTR(0),
+ m_AICARAM(NULL),
+ m_AICARAM_LENGTH(0),
+ m_RAM_MASK(0),
+ m_RAM_MASK16(0),
+ m_buffertmpl(NULL),
+ m_buffertmpr(NULL),
+ m_IrqTimA(0),
+ m_IrqTimBC(0),
+ m_IrqMidi(0),
+ m_MidiOutW(0),
+ m_MidiOutR(0),
+ m_MidiW(0),
+ m_MidiR(0),
+ m_mcieb(0),
+ m_mcipd(0),
+ m_bufferl(NULL),
+ m_bufferr(NULL),
+ m_length(0),
+ m_RBUFDST(NULL)
+
+{
+ memset(&m_udata.data, 0, sizeof(m_udata.data));
+ memset(m_EFSPAN, 0, sizeof(m_EFSPAN));
+ memset(m_Slots, 0, sizeof(m_Slots));
+ memset(m_RINGBUF, 0, sizeof(m_RINGBUF));
+ memset(m_MidiStack, 0, sizeof(m_MidiStack));
+
+ memset(m_LPANTABLE, 0, sizeof(m_LPANTABLE));
+ memset(m_RPANTABLE, 0, sizeof(m_RPANTABLE));
+
+ memset(m_TimPris, 0, sizeof(m_TimPris));
+ memset(m_TimCnt, 0, sizeof(m_TimCnt));
+
+ memset(&m_dma, 0, sizeof(m_dma));
+
+ memset(m_ARTABLE, 0, sizeof(m_ARTABLE));
+ memset(m_DRTABLE, 0, sizeof(m_DRTABLE));
+
+ memset(&m_DSP, 0, sizeof(m_DSP));
+
+ memset(m_EG_TABLE, 0, sizeof(m_EG_TABLE));
+ memset(m_PLFO_TRI, 0, sizeof(m_PLFO_TRI));
+ memset(m_PLFO_SQR, 0, sizeof(m_PLFO_SQR));
+ memset(m_PLFO_SAW, 0, sizeof(m_PLFO_SAW));
+ memset(m_PLFO_NOI, 0, sizeof(m_PLFO_NOI));
+
+ memset(m_ALFO_TRI, 0, sizeof(m_ALFO_TRI));
+ memset(m_ALFO_SQR, 0, sizeof(m_ALFO_SQR));
+ memset(m_ALFO_SAW, 0, sizeof(m_ALFO_SAW));
+ memset(m_ALFO_NOI, 0, sizeof(m_ALFO_NOI));
+
+ memset(m_PSCALES, 0, sizeof(m_PSCALES));
+ memset(m_ASCALES, 0, sizeof(m_ASCALES));
+}
+
+
+static const float LFOFreq[32]={0.17f,0.19f,0.23f,0.27f,0.34f,0.39f,0.45f,0.55f,0.68f,0.78f,0.92f,1.10f,1.39f,1.60f,1.87f,2.27f,
+ 2.87f,3.31f,3.92f,4.79f,6.15f,7.18f,8.60f,10.8f,14.4f,17.2f,21.5f,28.7f,43.1f,57.4f,86.1f,172.3f};
+static const float ASCALE[8]={0.0f,0.4f,0.8f,1.5f,3.0f,6.0f,12.0f,24.0f};
+static const float PSCALE[8]={0.0f,7.0f,13.5f,27.0f,55.0f,112.0f,230.0f,494.0f};
+
+void aica_device::AICALFO_Init()
+{
+ int i,s;
+ for(i=0;i<256;++i)
+ {
+ int a,p;
+// float TL;
+ //Saw
+ a=255-i;
+ if(i<128)
+ p=i;
+ else
+ p=i-256;
+ m_ALFO_SAW[i]=a;
+ m_PLFO_SAW[i]=p;
+
+ //Square
+ if(i<128)
+ {
+ a=255;
+ p=127;
+ }
+ else
+ {
+ a=0;
+ p=-128;
+ }
+ m_ALFO_SQR[i]=a;
+ m_PLFO_SQR[i]=p;
+
+ //Tri
+ if(i<128)
+ a=255-(i*2);
+ else
+ a=(i*2)-256;
+ if(i<64)
+ p=i*2;
+ else if(i<128)
+ p=255-i*2;
+ else if(i<192)
+ p=256-i*2;
+ else
+ p=i*2-511;
+ m_ALFO_TRI[i]=a;
+ m_PLFO_TRI[i]=p;
+
+ //noise
+ //a=lfo_noise[i];
+ a=machine().rand()&0xff;
+ p=128-a;
+ m_ALFO_NOI[i]=a;
+ m_PLFO_NOI[i]=p;
+ }
+
+ for(s=0;s<8;++s)
+ {
+ float limit=PSCALE[s];
+ for(i=-128;i<128;++i)
+ {
+ m_PSCALES[s][i+128]=CENTS(((limit*(float) i)/128.0f));
+ }
+ limit=-ASCALE[s];
+ for(i=0;i<256;++i)
+ {
+ m_ASCALES[s][i]=DB(((limit*(float) i)/256.0f));
+ }
+ }
+}
+
+signed int aica_device::AICAPLFO_Step(AICA_LFO_t *LFO)
+{
+ int p;
+
+ LFO->phase+=LFO->phase_step;
+#if LFO_SHIFT!=8
+ LFO->phase&=(1<<(LFO_SHIFT+8))-1;
+#endif
+ p=LFO->table[LFO->phase>>LFO_SHIFT];
+ p=LFO->scale[p+128];
+ return p<<(SHIFT-LFO_SHIFT);
+}
+
+signed int aica_device::AICAALFO_Step(AICA_LFO_t *LFO)
+{
+ int p;
+ LFO->phase+=LFO->phase_step;
+#if LFO_SHIFT!=8
+ LFO->phase&=(1<<(LFO_SHIFT+8))-1;
+#endif
+ p=LFO->table[LFO->phase>>LFO_SHIFT];
+ p=LFO->scale[p];
+ return p<<(SHIFT-LFO_SHIFT);
+}
+
+void aica_device::AICALFO_ComputeStep(AICA_LFO_t *LFO,UINT32 LFOF,UINT32 LFOWS,UINT32 LFOS,int ALFO)
+{
+ float step=(float) LFOFreq[LFOF]*256.0f/(float)44100.0f;
+ LFO->phase_step=(unsigned int) ((float) (1<<LFO_SHIFT)*step);
+ if(ALFO)
+ {
+ switch(LFOWS)
+ {
+ case 0: LFO->table=m_ALFO_SAW; break;
+ case 1: LFO->table=m_ALFO_SQR; break;
+ case 2: LFO->table=m_ALFO_TRI; break;
+ case 3: LFO->table=m_ALFO_NOI; break;
+ default: printf("Unknown ALFO %d\n", LFOWS);
+ }
+ LFO->scale=m_ASCALES[LFOS];
+ }
+ else
+ {
+ switch(LFOWS)
+ {
+ case 0: LFO->table=m_PLFO_SAW; break;
+ case 1: LFO->table=m_PLFO_SQR; break;
+ case 2: LFO->table=m_PLFO_TRI; break;
+ case 3: LFO->table=m_PLFO_NOI; break;
+ default: printf("Unknown PLFO %d\n", LFOWS);
+ }
+ LFO->scale=m_PSCALES[LFOS];
+ }
+}
diff --git a/src/devices/sound/aica.h b/src/devices/sound/aica.h
new file mode 100644
index 00000000000..0c7d5caf4f4
--- /dev/null
+++ b/src/devices/sound/aica.h
@@ -0,0 +1,213 @@
+// license:???
+// copyright-holders:ElSemi, kingshriek, Deunan Knute, R. Belmont
+/*
+
+ Sega/Yamaha AICA emulation
+*/
+
+#ifndef __AICA_H__
+#define __AICA_H__
+
+#include "aicadsp.h"
+
+#define MCFG_AICA_MASTER \
+ aica_device::set_master(*device);
+
+#define MCFG_AICA_ROFFSET(_offs) \
+ aica_device::set_roffset(*device, _offs);
+
+#define MCFG_AICA_IRQ_CB(_devcb) \
+ devcb = &aica_device::set_irq_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_AICA_MAIN_IRQ_CB(_devcb) \
+ devcb = &aica_device::set_main_irq_callback(*device, DEVCB_##_devcb);
+
+enum AICA_STATE {AICA_ATTACK,AICA_DECAY1,AICA_DECAY2,AICA_RELEASE};
+
+struct AICA_LFO_t
+{
+ unsigned short phase;
+ UINT32 phase_step;
+ int *table;
+ int *scale;
+};
+
+
+struct AICA_EG_t
+{
+ int volume; //
+ AICA_STATE state;
+ int step;
+ //step vals
+ int AR; //Attack
+ int D1R; //Decay1
+ int D2R; //Decay2
+ int RR; //Release
+
+ int DL; //Decay level
+ UINT8 LPLINK;
+};
+
+struct AICA_SLOT
+{
+ union
+ {
+ UINT16 data[0x40]; //only 0x1a bytes used
+ UINT8 datab[0x80];
+ } udata;
+ UINT8 active; //this slot is currently playing
+ UINT8 *base; //samples base address
+ UINT32 prv_addr; // previous play address (for ADPCM)
+ UINT32 cur_addr; //current play address (24.8)
+ UINT32 nxt_addr; //next play address
+ UINT32 step; //pitch step (24.8)
+ UINT8 Backwards; //the wave is playing backwards
+ AICA_EG_t EG; //Envelope
+ AICA_LFO_t PLFO; //Phase LFO
+ AICA_LFO_t ALFO; //Amplitude LFO
+ int slot;
+ int cur_sample; //current ADPCM sample
+ int cur_quant; //current ADPCM step
+ int curstep;
+ int cur_lpquant, cur_lpsample, cur_lpstep;
+ UINT8 *adbase, *adlpbase;
+ UINT8 lpend;
+};
+
+class aica_device : public device_t,
+ public device_sound_interface
+{
+public:
+ aica_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void set_master(device_t &device) { downcast<aica_device &>(device).m_master = true; }
+ static void set_roffset(device_t &device, int roffset) { downcast<aica_device &>(device).m_roffset = roffset; }
+ template<class _Object> static devcb_base &set_irq_callback(device_t &device, _Object object) { return downcast<aica_device &>(device).m_irq_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_main_irq_callback(device_t &device, _Object object) { return downcast<aica_device &>(device).m_main_irq_cb.set_callback(object); }
+
+ // AICA register access
+ DECLARE_READ16_MEMBER( read );
+ DECLARE_WRITE16_MEMBER( write );
+
+ // MIDI I/O access
+ DECLARE_WRITE16_MEMBER( midi_in );
+ DECLARE_READ16_MEMBER( midi_out_r );
+
+ void set_ram_base(void *base, int size);
+
+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:
+ unsigned char DecodeSCI(unsigned char irq);
+ void ResetInterrupts();
+
+ void CheckPendingIRQ();
+ void CheckPendingIRQ_SH4();
+ TIMER_CALLBACK_MEMBER( timerA_cb );
+ TIMER_CALLBACK_MEMBER( timerB_cb );
+ TIMER_CALLBACK_MEMBER( timerC_cb );
+ int Get_AR(int base,int R);
+ int Get_DR(int base,int R);
+ int Get_RR(int base,int R);
+ void Compute_EG(AICA_SLOT *slot);
+ int EG_Update(AICA_SLOT *slot);
+ UINT32 Step(AICA_SLOT *slot);
+ void Compute_LFO(AICA_SLOT *slot);
+ void InitADPCM(int *PrevSignal, int *PrevQuant);
+ inline signed short DecodeADPCM(int *PrevSignal, unsigned char Delta, int *PrevQuant);
+ void StartSlot(AICA_SLOT *slot);
+ void StopSlot(AICA_SLOT *slot,int keyoff);
+ void Init();
+ void UpdateSlotReg(int s,int r);
+ void UpdateReg(address_space &space, int reg);
+ void UpdateSlotRegR(int slot,int reg);
+ void UpdateRegR(address_space &space, int reg);
+ void w16(address_space &space,unsigned int addr,unsigned short val);
+ unsigned short r16(address_space &space, unsigned int addr);
+ inline INT32 UpdateSlot(AICA_SLOT *slot);
+ void DoMasterSamples(int nsamples);
+ void aica_exec_dma(address_space &space);
+
+
+ void AICALFO_Init();
+ inline signed int AICAPLFO_Step(AICA_LFO_t *LFO);
+ inline signed int AICAALFO_Step(AICA_LFO_t *LFO);
+ void AICALFO_ComputeStep(AICA_LFO_t *LFO,UINT32 LFOF,UINT32 LFOWS,UINT32 LFOS,int ALFO);
+
+ bool m_master;
+ int m_roffset; /* offset in the region */
+ devcb_write_line m_irq_cb;
+ devcb_write_line m_main_irq_cb;
+
+ union
+ {
+ UINT16 data[0xc0/2];
+ UINT8 datab[0xc0];
+ } m_udata;
+
+ UINT16 m_IRQL, m_IRQR;
+ UINT16 m_EFSPAN[0x48];
+ AICA_SLOT m_Slots[64];
+ signed short m_RINGBUF[64];
+ unsigned char m_BUFPTR;
+ unsigned char *m_AICARAM;
+ UINT32 m_AICARAM_LENGTH, m_RAM_MASK, m_RAM_MASK16;
+ sound_stream * m_stream;
+
+ INT32 *m_buffertmpl, *m_buffertmpr;
+
+ UINT32 m_IrqTimA;
+ UINT32 m_IrqTimBC;
+ UINT32 m_IrqMidi;
+
+ UINT8 m_MidiOutW,m_MidiOutR;
+ UINT8 m_MidiStack[16];
+ UINT8 m_MidiW,m_MidiR;
+
+ int m_LPANTABLE[0x20000];
+ int m_RPANTABLE[0x20000];
+
+ int m_TimPris[3];
+ int m_TimCnt[3];
+
+ UINT16 m_mcieb, m_mcipd;
+
+ // timers
+ emu_timer *m_timerA, *m_timerB, *m_timerC;
+
+ // DMA stuff
+ struct{
+ UINT32 dmea;
+ UINT16 drga;
+ UINT16 dlg;
+ UINT8 dgate;
+ UINT8 ddir;
+ }m_dma;
+
+
+ int m_ARTABLE[64], m_DRTABLE[64];
+
+ AICADSP m_DSP;
+
+ stream_sample_t *m_bufferl;
+ stream_sample_t *m_bufferr;
+
+ int m_length;
+
+ signed short *m_RBUFDST; //this points to where the sample will be stored in the RingBuf
+ INT32 m_EG_TABLE[0x400];
+ int m_PLFO_TRI[256],m_PLFO_SQR[256],m_PLFO_SAW[256],m_PLFO_NOI[256];
+ int m_ALFO_TRI[256],m_ALFO_SQR[256],m_ALFO_SAW[256],m_ALFO_NOI[256];
+ int m_PSCALES[8][256];
+ int m_ASCALES[8][256];
+
+};
+
+extern const device_type AICA;
+
+
+#endif /* __AICA_H__ */
diff --git a/src/devices/sound/aicadsp.c b/src/devices/sound/aicadsp.c
new file mode 100644
index 00000000000..4f533125825
--- /dev/null
+++ b/src/devices/sound/aicadsp.c
@@ -0,0 +1,351 @@
+// license:???
+// copyright-holders:ElSemi, kingshriek, Deunan Knute, R. Belmont
+#include "emu.h"
+#include "aicadsp.h"
+
+static UINT16 PACK(INT32 val)
+{
+ UINT32 temp;
+ int sign,exponent,k;
+
+ sign = (val >> 23) & 0x1;
+ temp = (val ^ (val << 1)) & 0xFFFFFF;
+ exponent = 0;
+ for (k=0; k<12; k++)
+ {
+ if (temp & 0x800000)
+ break;
+ temp <<= 1;
+ exponent += 1;
+ }
+ if (exponent < 12)
+ val = (val << exponent) & 0x3FFFFF;
+ else
+ val <<= 11;
+ val >>= 11;
+ val &= 0x7FF;
+ val |= sign << 15;
+ val |= exponent << 11;
+
+ return (UINT16)val;
+}
+
+static INT32 UNPACK(UINT16 val)
+{
+ int sign,exponent,mantissa;
+ INT32 uval;
+
+ sign = (val >> 15) & 0x1;
+ exponent = (val >> 11) & 0xF;
+ mantissa = val & 0x7FF;
+ uval = mantissa << 11;
+ if (exponent > 11)
+ {
+ exponent = 11;
+ uval |= sign << 22;
+ }
+ else
+ uval |= (sign ^ 1) << 22;
+ uval |= sign << 23;
+ uval <<= 8;
+ uval >>= 8;
+ uval >>= exponent;
+
+ return uval;
+}
+
+void aica_dsp_init(AICADSP *DSP)
+{
+ memset(DSP,0,sizeof(AICADSP));
+ DSP->RBL=0x8000;
+ DSP->Stopped=1;
+}
+
+void aica_dsp_step(AICADSP *DSP)
+{
+ INT32 ACC=0; //26 bit
+ INT32 SHIFTED=0; //24 bit
+ INT32 X=0; //24 bit
+ INT32 Y=0; //13 bit
+ INT32 B=0; //26 bit
+ INT32 INPUTS=0; //24 bit
+ INT32 MEMVAL=0;
+ INT32 FRC_REG=0; //13 bit
+ INT32 Y_REG=0; //24 bit
+ UINT32 ADDR=0;
+ UINT32 ADRS_REG=0; //13 bit
+ int step;
+
+ if(DSP->Stopped)
+ return;
+
+ memset(DSP->EFREG,0,2*16);
+#if 0
+ int dump=0;
+ FILE *f=NULL;
+ if(dump)
+ f=fopen("dsp.txt","wt");
+#endif
+ for(step=0;step</*128*/DSP->LastStep;++step)
+ {
+ UINT16 *IPtr=DSP->MPRO+step*8;
+
+// if(IPtr[0]==0 && IPtr[1]==0 && IPtr[2]==0 && IPtr[3]==0)
+// break;
+
+ UINT32 TRA=(IPtr[0]>>9)&0x7F;
+ UINT32 TWT=(IPtr[0]>>8)&0x01;
+ UINT32 TWA=(IPtr[0]>>1)&0x7F;
+
+ UINT32 XSEL=(IPtr[2]>>15)&0x01;
+ UINT32 YSEL=(IPtr[2]>>13)&0x03;
+ UINT32 IRA=(IPtr[2]>>7)&0x3F;
+ UINT32 IWT=(IPtr[2]>>6)&0x01;
+ UINT32 IWA=(IPtr[2]>>1)&0x1F;
+
+ UINT32 TABLE=(IPtr[4]>>15)&0x01;
+ UINT32 MWT=(IPtr[4]>>14)&0x01;
+ UINT32 MRD=(IPtr[4]>>13)&0x01;
+ UINT32 EWT=(IPtr[4]>>12)&0x01;
+ UINT32 EWA=(IPtr[4]>>8)&0x0F;
+ UINT32 ADRL=(IPtr[4]>>7)&0x01;
+ UINT32 FRCL=(IPtr[4]>>6)&0x01;
+ UINT32 SHIFT=(IPtr[4]>>4)&0x03;
+ UINT32 YRL=(IPtr[4]>>3)&0x01;
+ UINT32 NEGB=(IPtr[4]>>2)&0x01;
+ UINT32 ZERO=(IPtr[4]>>1)&0x01;
+ UINT32 BSEL=(IPtr[4]>>0)&0x01;
+
+ UINT32 NOFL=(IPtr[6]>>15)&1; //????
+ UINT32 COEF=step;
+
+ UINT32 MASA=(IPtr[6]>>9)&0x1f; //???
+ UINT32 ADREB=(IPtr[6]>>8)&0x1;
+ UINT32 NXADR=(IPtr[6]>>7)&0x1;
+
+ INT64 v;
+
+ //operations are done at 24 bit precision
+#if 0
+ if(MASA)
+ int a=1;
+ if(NOFL)
+ int a=1;
+
+// int dump=0;
+
+ if(f)
+ {
+#define DUMP(v) fprintf(f," " #v ": %04X",v);
+
+ fprintf(f,"%d: ",step);
+ DUMP(ACC);
+ DUMP(SHIFTED);
+ DUMP(X);
+ DUMP(Y);
+ DUMP(B);
+ DUMP(INPUTS);
+ DUMP(MEMVAL);
+ DUMP(FRC_REG);
+ DUMP(Y_REG);
+ DUMP(ADDR);
+ DUMP(ADRS_REG);
+ fprintf(f,"\n");
+ }
+#endif
+ //INPUTS RW
+ assert(IRA<0x32);
+ if(IRA<=0x1f)
+ INPUTS=DSP->MEMS[IRA];
+ else if(IRA<=0x2F)
+ INPUTS=DSP->MIXS[IRA-0x20]<<4; //MIXS is 20 bit
+ else if(IRA<=0x31)
+ INPUTS=0;
+
+ INPUTS<<=8;
+ INPUTS>>=8;
+ //if(INPUTS&0x00800000)
+ // INPUTS|=0xFF000000;
+
+ if(IWT)
+ {
+ DSP->MEMS[IWA]=MEMVAL; //MEMVAL was selected in previous MRD
+ if(IRA==IWA)
+ INPUTS=MEMVAL;
+ }
+
+ //Operand sel
+ //B
+ if(!ZERO)
+ {
+ if(BSEL)
+ B=ACC;
+ else
+ {
+ B=DSP->TEMP[(TRA+DSP->DEC)&0x7F];
+ B<<=8;
+ B>>=8;
+ //if(B&0x00800000)
+ // B|=0xFF000000; //Sign extend
+ }
+ if(NEGB)
+ B=0-B;
+ }
+ else
+ B=0;
+
+ //X
+ if(XSEL)
+ X=INPUTS;
+ else
+ {
+ X=DSP->TEMP[(TRA+DSP->DEC)&0x7F];
+ X<<=8;
+ X>>=8;
+ //if(X&0x00800000)
+ // X|=0xFF000000;
+ }
+
+ //Y
+ if(YSEL==0)
+ Y=FRC_REG;
+ else if(YSEL==1)
+ Y=DSP->COEF[COEF<<1]>>3; //COEF is 16 bits
+ else if(YSEL==2)
+ Y=(Y_REG>>11)&0x1FFF;
+ else if(YSEL==3)
+ Y=(Y_REG>>4)&0x0FFF;
+
+ if(YRL)
+ Y_REG=INPUTS;
+
+ //Shifter
+ if(SHIFT==0)
+ {
+ SHIFTED=ACC;
+ if(SHIFTED>0x007FFFFF)
+ SHIFTED=0x007FFFFF;
+ if(SHIFTED<(-0x00800000))
+ SHIFTED=-0x00800000;
+ }
+ else if(SHIFT==1)
+ {
+ SHIFTED=ACC*2;
+ if(SHIFTED>0x007FFFFF)
+ SHIFTED=0x007FFFFF;
+ if(SHIFTED<(-0x00800000))
+ SHIFTED=-0x00800000;
+ }
+ else if(SHIFT==2)
+ {
+ SHIFTED=ACC*2;
+ SHIFTED<<=8;
+ SHIFTED>>=8;
+ //SHIFTED&=0x00FFFFFF;
+ //if(SHIFTED&0x00800000)
+ // SHIFTED|=0xFF000000;
+ }
+ else if(SHIFT==3)
+ {
+ SHIFTED=ACC;
+ SHIFTED<<=8;
+ SHIFTED>>=8;
+ //SHIFTED&=0x00FFFFFF;
+ //if(SHIFTED&0x00800000)
+ // SHIFTED|=0xFF000000;
+ }
+
+ //ACCUM
+ Y<<=19;
+ Y>>=19;
+ //if(Y&0x1000)
+ // Y|=0xFFFFF000;
+
+ v=(((INT64) X*(INT64) Y)>>12);
+ ACC=(int) v+B;
+
+ if(TWT)
+ DSP->TEMP[(TWA+DSP->DEC)&0x7F]=SHIFTED;
+
+ if(FRCL)
+ {
+ if(SHIFT==3)
+ FRC_REG=SHIFTED&0x0FFF;
+ else
+ FRC_REG=(SHIFTED>>11)&0x1FFF;
+ }
+
+ if(MRD || MWT)
+ //if(0)
+ {
+ ADDR=DSP->MADRS[MASA<<1];
+ if(!TABLE)
+ ADDR+=DSP->DEC;
+ if(ADREB)
+ ADDR+=ADRS_REG&0x0FFF;
+ if(NXADR)
+ ADDR++;
+ if(!TABLE)
+ ADDR&=DSP->RBL-1;
+ else
+ ADDR&=0xFFFF;
+ //ADDR<<=1;
+ //ADDR+=DSP->RBP<<13;
+ //MEMVAL=DSP->AICARAM[ADDR>>1];
+ ADDR+=DSP->RBP<<10;
+ if(MRD && (step&1)) //memory only allowed on odd? DoA inserts NOPs on even
+ {
+ if(NOFL)
+ MEMVAL=DSP->AICARAM[ADDR]<<8;
+ else
+ MEMVAL=UNPACK(DSP->AICARAM[ADDR]);
+ }
+ if(MWT && (step&1))
+ {
+ if(NOFL)
+ DSP->AICARAM[ADDR]=SHIFTED>>8;
+ else
+ DSP->AICARAM[ADDR]=PACK(SHIFTED);
+ }
+ }
+
+ if(ADRL)
+ {
+ if(SHIFT==3)
+ ADRS_REG=(SHIFTED>>12)&0xFFF;
+ else
+ ADRS_REG=(INPUTS>>16);
+ }
+
+ if(EWT)
+ DSP->EFREG[EWA]+=SHIFTED>>8;
+
+ }
+ --DSP->DEC;
+ memset(DSP->MIXS,0,4*16);
+// if(f)
+// fclose(f);
+}
+
+void aica_dsp_setsample(AICADSP *DSP,INT32 sample,int SEL,int MXL)
+{
+ //DSP->MIXS[SEL]+=sample<<(MXL+1)/*7*/;
+ DSP->MIXS[SEL]+=sample;
+// if(MXL)
+// int a=1;
+}
+
+void aica_dsp_start(AICADSP *DSP)
+{
+ int i;
+ DSP->Stopped=0;
+ for(i=127;i>=0;--i)
+ {
+ UINT16 *IPtr=DSP->MPRO+i*8;
+
+ if(IPtr[0]!=0 || IPtr[2]!=0 || IPtr[4]!=0 || IPtr[6]!=0)
+ break;
+ }
+ DSP->LastStep=i+1;
+
+}
diff --git a/src/devices/sound/aicadsp.h b/src/devices/sound/aicadsp.h
new file mode 100644
index 00000000000..e28301a6100
--- /dev/null
+++ b/src/devices/sound/aicadsp.h
@@ -0,0 +1,42 @@
+// license:???
+// copyright-holders:ElSemi, kingshriek, Deunan Knute, R. Belmont
+#pragma once
+
+#ifndef __AICADSP_H__
+#define __AICADSP_H__
+
+//the DSP Context
+struct AICADSP
+{
+//Config
+ UINT16 *AICARAM;
+ UINT32 AICARAM_LENGTH;
+ UINT32 RBP; //Ring buf pointer
+ UINT32 RBL; //Delay ram (Ring buffer) size in words
+
+//context
+
+ INT16 COEF[128*2]; //16 bit signed
+ UINT16 MADRS[64*2]; //offsets (in words), 16 bit
+ UINT16 MPRO[128*4*2*2]; //128 steps 64 bit
+ INT32 TEMP[128]; //TEMP regs,24 bit signed
+ INT32 MEMS[32]; //MEMS regs,24 bit signed
+ UINT32 DEC;
+
+//input
+ INT32 MIXS[16]; //MIXS, 24 bit signed
+ INT16 EXTS[2]; //External inputs (CDDA) 16 bit signed
+
+//output
+ INT16 EFREG[16]; //EFREG, 16 bit signed
+
+ int Stopped;
+ int LastStep;
+};
+
+void aica_dsp_init(AICADSP *DSP);
+void aica_dsp_setsample(AICADSP *DSP, INT32 sample, INT32 SEL, INT32 MXL);
+void aica_dsp_step(AICADSP *DSP);
+void aica_dsp_start(AICADSP *DSP);
+
+#endif /* __AICADSP_H__ */
diff --git a/src/devices/sound/amiga.c b/src/devices/sound/amiga.c
new file mode 100644
index 00000000000..84b761e73bd
--- /dev/null
+++ b/src/devices/sound/amiga.c
@@ -0,0 +1,245 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ Amiga audio hardware
+
+***************************************************************************/
+
+#include "amiga.h"
+#include "includes/amiga.h"
+
+
+//**************************************************************************
+// CONSTANTS / MACROS
+//**************************************************************************
+
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type AMIGA = &device_creator<amiga_sound_device>;
+
+
+//*************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// amiga_sound_device - constructor
+//-------------------------------------------------
+
+amiga_sound_device::amiga_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, AMIGA, "Amiga Paula", tag, owner, clock, "amiga_paula", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void amiga_sound_device::device_start()
+{
+ // initialize channels
+ for (int i = 0; i < 4; i++)
+ {
+ m_channel[i].index = i;
+ m_channel[i].curticks = 0;
+ m_channel[i].manualmode = false;
+ m_channel[i].curlocation = 0;
+ m_channel[i].irq_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(amiga_sound_device::signal_irq), this));
+ }
+
+ // create the stream
+ m_stream = machine().sound().stream_alloc(*this, 0, 4, clock() / CLOCK_DIVIDER);
+}
+
+
+//*************************************************************************
+// IMPLEMENTATION
+//**************************************************************************
+
+//-------------------------------------------------
+// signal_irq - irq signaling
+//-------------------------------------------------
+
+TIMER_CALLBACK_MEMBER( amiga_sound_device::signal_irq )
+{
+ amiga_state *state = machine().driver_data<amiga_state>();
+
+ state->custom_chip_w(REG_INTREQ, INTENA_SETCLR | (0x80 << param));
+}
+
+//-------------------------------------------------
+// dma_reload
+//-------------------------------------------------
+
+void amiga_sound_device::dma_reload(audio_channel *chan)
+{
+ amiga_state *state = machine().driver_data<amiga_state>();
+
+ chan->curlocation = CUSTOM_REG_LONG(REG_AUD0LCH + chan->index * 8);
+ chan->curlength = CUSTOM_REG(REG_AUD0LEN + chan->index * 8);
+ chan->irq_timer->adjust(attotime::from_hz(15750), chan->index);
+
+ LOG(("dma_reload(%d): offs=%05X len=%04X\n", chan->index, chan->curlocation, chan->curlength));
+}
+
+//-------------------------------------------------
+// data_w - manual mode data writer
+//-------------------------------------------------
+
+void amiga_sound_device::data_w(int which, UINT16 data)
+{
+ m_channel[which].manualmode = true;
+}
+
+//-------------------------------------------------
+// update - stream updater
+//-------------------------------------------------
+
+void amiga_sound_device::update()
+{
+ m_stream->update();
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void amiga_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ amiga_state *state = machine().driver_data<amiga_state>();
+ int channum, sampoffs = 0;
+
+ // if all DMA off, disable all channels
+ if (!(CUSTOM_REG(REG_DMACON) & 0x0200))
+ {
+ m_channel[0].dma_enabled =
+ m_channel[1].dma_enabled =
+ m_channel[2].dma_enabled =
+ m_channel[3].dma_enabled = false;
+
+ // clear the sample data to 0
+ for (channum = 0; channum < 4; channum++)
+ memset(outputs[channum], 0, sizeof(stream_sample_t) * samples);
+ return;
+ }
+
+ samples *= CLOCK_DIVIDER;
+
+ // update the DMA states on each channel and reload if fresh
+ for (channum = 0; channum < 4; channum++)
+ {
+ audio_channel *chan = &m_channel[channum];
+ if (!chan->dma_enabled && ((CUSTOM_REG(REG_DMACON) >> channum) & 1))
+ dma_reload(chan);
+ chan->dma_enabled = (CUSTOM_REG(REG_DMACON) >> channum) & 1;
+ }
+
+ // loop until done
+ while (samples > 0)
+ {
+ int nextper, nextvol;
+ int ticks = samples;
+
+ // determine the number of ticks we can do in this chunk
+ if (ticks > m_channel[0].curticks)
+ ticks = m_channel[0].curticks;
+ if (ticks > m_channel[1].curticks)
+ ticks = m_channel[1].curticks;
+ if (ticks > m_channel[2].curticks)
+ ticks = m_channel[2].curticks;
+ if (ticks > m_channel[3].curticks)
+ ticks = m_channel[3].curticks;
+
+ // loop over channels
+ nextper = nextvol = -1;
+ for (channum = 0; channum < 4; channum++)
+ {
+ int volume = (nextvol == -1) ? CUSTOM_REG(REG_AUD0VOL + channum * 8) : nextvol;
+ int period = (nextper == -1) ? CUSTOM_REG(REG_AUD0PER + channum * 8) : nextper;
+ audio_channel *chan = &m_channel[channum];
+ stream_sample_t sample;
+ int i;
+
+ // normalize the volume value
+ volume = (volume & 0x40) ? 64 : (volume & 0x3f);
+ volume *= 4;
+
+ // are we modulating the period of the next channel?
+ if ((CUSTOM_REG(REG_ADKCON) >> channum) & 0x10)
+ {
+ nextper = CUSTOM_REG(REG_AUD0DAT + channum * 8);
+ nextvol = -1;
+ sample = 0;
+ }
+
+ // are we modulating the volume of the next channel?
+ else if ((CUSTOM_REG(REG_ADKCON) >> channum) & 0x01)
+ {
+ nextper = -1;
+ nextvol = CUSTOM_REG(REG_AUD0DAT + channum * 8);
+ sample = 0;
+ }
+
+ // otherwise, we are generating data
+ else
+ {
+ nextper = nextvol = -1;
+ sample = chan->latched * volume;
+ }
+
+ // fill the buffer with the sample
+ for (i = 0; i < ticks; i += CLOCK_DIVIDER)
+ outputs[channum][(sampoffs + i) / CLOCK_DIVIDER] = sample;
+
+ // account for the ticks; if we hit 0, advance
+ chan->curticks -= ticks;
+ if (chan->curticks == 0)
+ {
+ // reset the clock and ensure we're above the minimum ticks
+ chan->curticks = period;
+ if (chan->curticks < 124)
+ chan->curticks = 124;
+
+ // move forward one byte; if we move to an even byte, fetch new
+ if (chan->dma_enabled || chan->manualmode)
+ chan->curlocation++;
+ if (chan->dma_enabled && !(chan->curlocation & 1))
+ {
+ CUSTOM_REG(REG_AUD0DAT + channum * 8) = state->chip_ram_r(chan->curlocation);
+ if (chan->curlength != 0)
+ chan->curlength--;
+
+ // if we run out of data, reload the dma
+ if (chan->curlength == 0)
+ dma_reload(chan);
+ }
+
+ // latch the next byte of the sample
+ if (!(chan->curlocation & 1))
+ chan->latched = CUSTOM_REG(REG_AUD0DAT + channum * 8) >> 8;
+ else
+ chan->latched = CUSTOM_REG(REG_AUD0DAT + channum * 8) >> 0;
+
+ // if we're in manual mode, signal an interrupt once we latch the low byte
+ if (!chan->dma_enabled && chan->manualmode && (chan->curlocation & 1))
+ {
+ signal_irq(NULL, channum);
+ chan->manualmode = false;
+ }
+ }
+ }
+
+ // bump ourselves forward by the number of ticks
+ sampoffs += ticks;
+ samples -= ticks;
+ }
+}
diff --git a/src/devices/sound/amiga.h b/src/devices/sound/amiga.h
new file mode 100644
index 00000000000..eb8b9ee436d
--- /dev/null
+++ b/src/devices/sound/amiga.h
@@ -0,0 +1,65 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ Amiga audio hardware
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __SOUND_AMIGA_H__
+#define __SOUND_AMIGA_H__
+
+#include "emu.h"
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> amiga_sound_device
+
+class amiga_sound_device : public device_t, public device_sound_interface
+{
+public:
+ amiga_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~amiga_sound_device() {}
+
+ void update();
+ void data_w(int which, UINT16 data);
+
+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:
+ static const int CLOCK_DIVIDER = 16;
+
+ struct audio_channel
+ {
+ emu_timer *irq_timer;
+ UINT32 curlocation;
+ UINT16 curlength;
+ UINT16 curticks;
+ UINT8 index;
+ bool dma_enabled;
+ bool manualmode;
+ INT8 latched;
+ };
+
+ void dma_reload(audio_channel *chan);
+
+ // internal state
+ audio_channel m_channel[4];
+ sound_stream *m_stream;
+
+ TIMER_CALLBACK_MEMBER( signal_irq );
+};
+
+extern const device_type AMIGA;
+
+#endif // __SOUND_AMIGA_H__
diff --git a/src/devices/sound/asc.c b/src/devices/sound/asc.c
new file mode 100644
index 00000000000..ad7559e0d3d
--- /dev/null
+++ b/src/devices/sound/asc.c
@@ -0,0 +1,597 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+/***************************************************************************
+
+ asc.c
+
+ Apple Sound Chip (ASC) 344S0063
+ Enhanced Apple Sound Chip (EASC) 343S1063
+
+ Emulation by R. Belmont
+
+ Registers:
+ 0x800: VERSION
+ 0x801: MODE (1=FIFO mode, 2=wavetable mode)
+ 0x802: CONTROL (bit 0=analog or PWM output, 1=stereo/mono, 7=processing time exceeded)
+ 0x803: FIFO MODE (bit 7=clear FIFO, bit 1="non-ROM companding", bit 0="ROM companding")
+ 0x804: FIFO IRQ STATUS (bit 0=ch A 1/2 full, 1=ch A full, 2=ch B 1/2 full, 3=ch B full)
+ 0x805: WAVETABLE CONTROL (bits 0-3 wavetables 0-3 start)
+ 0x806: VOLUME (bits 2-4 = 3 bit internal ASC volume, bits 5-7 = volume control sent to Sony sound chip)
+ 0x807: CLOCK RATE (0 = Mac 22257 Hz, 1 = undefined, 2 = 22050 Hz, 3 = 44100 Hz)
+ 0x80a: PLAY REC A
+ 0x80f: TEST (bits 6-7 = digital test, bits 4-5 = analog test)
+ 0x810: WAVETABLE 0 PHASE (big-endian 9.15 fixed-point, only 24 bits valid)
+ 0x814: WAVETABLE 0 INCREMENT (big-endian 9.15 fixed-point, only 24 bits valid)
+ 0x818: WAVETABLE 1 PHASE
+ 0x81C: WAVETABLE 1 INCREMENT
+ 0x820: WAVETABLE 2 PHASE
+ 0x824: WAVETABLE 2 INCREMENT
+ 0x828: WAVETABLE 3 PHASE
+ 0x82C: WAVETABLE 3 INCREMENT
+
+***************************************************************************/
+
+#include "emu.h"
+#include "asc.h"
+
+// device type definition
+const device_type ASC = &device_creator<asc_device>;
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// asc_device - constructor
+//-------------------------------------------------
+
+asc_device::asc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, ASC, "ASC", tag, owner, clock, "asc", __FILE__),
+ device_sound_interface(mconfig, *this),
+ write_irq(*this),
+ m_chip_type(0)
+{
+}
+
+
+//-------------------------------------------------
+// static_set_type - configuration helper to set
+// the chip type
+//-------------------------------------------------
+
+void asc_device::static_set_type(device_t &device, int type)
+{
+ asc_device &asc = downcast<asc_device &>(device);
+ asc.m_chip_type = type;
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void asc_device::device_start()
+{
+ // create the stream
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, 22257);
+
+ memset(m_regs, 0, sizeof(m_regs));
+
+ m_timer = timer_alloc(0, NULL);
+
+ save_item(NAME(m_fifo_a_rdptr));
+ save_item(NAME(m_fifo_b_rdptr));
+ save_item(NAME(m_fifo_a_wrptr));
+ save_item(NAME(m_fifo_b_wrptr));
+ save_item(NAME(m_fifo_cap_a));
+ save_item(NAME(m_fifo_cap_b));
+ save_item(NAME(m_fifo_a));
+ save_item(NAME(m_fifo_b));
+ save_item(NAME(m_regs));
+ save_item(NAME(m_phase));
+ save_item(NAME(m_incr));
+
+ write_irq.resolve_safe();
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void asc_device::device_reset()
+{
+ m_stream->update();
+
+ memset(m_regs, 0, sizeof(m_regs));
+ memset(m_fifo_a, 0, sizeof(m_fifo_a));
+ memset(m_fifo_b, 0, sizeof(m_fifo_b));
+ memset(m_phase, 0, sizeof(m_phase));
+ memset(m_incr, 0, sizeof(m_incr));
+
+ m_fifo_a_rdptr = m_fifo_b_rdptr = 0;
+ m_fifo_a_wrptr = m_fifo_b_wrptr = 0;
+ m_fifo_cap_a = m_fifo_cap_b = 0;
+}
+
+//-------------------------------------------------
+// device_timer - called when our device timer expires
+//-------------------------------------------------
+
+void asc_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
+{
+ m_stream->update();
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void asc_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *outL, *outR;
+ int i, ch;
+ static UINT32 wtoffs[2] = { 0, 0x200 };
+
+ outL = outputs[0];
+ outR = outputs[1];
+
+ switch (m_regs[R_MODE-0x800] & 3)
+ {
+ case 0: // chip off
+ for (i = 0; i < samples; i++)
+ {
+ outL[i] = outR[i] = 0;
+ }
+ break;
+
+ case 1: // FIFO mode
+ for (i = 0; i < samples; i++)
+ {
+ INT8 smpll, smplr;
+
+ smpll = (INT8)m_fifo_a[m_fifo_a_rdptr]^0x80;
+ smplr = (INT8)m_fifo_b[m_fifo_b_rdptr]^0x80;
+
+ // don't advance the sample pointer if there are no more samples
+ if (m_fifo_cap_a)
+ {
+ m_fifo_a_rdptr++;
+ m_fifo_a_rdptr &= 0x3ff;
+ m_fifo_cap_a--;
+ }
+
+ if (m_fifo_cap_b)
+ {
+ m_fifo_b_rdptr++;
+ m_fifo_b_rdptr &= 0x3ff;
+ m_fifo_cap_b--;
+ }
+
+ switch (m_chip_type)
+ {
+ case ASC_TYPE_SONORA:
+ if (m_fifo_cap_a < 0x200)
+ {
+ m_regs[R_FIFOSTAT-0x800] |= 0x4; // fifo less than half full
+ m_regs[R_FIFOSTAT-0x800] |= 0x8; // just pass the damn test
+ write_irq(ASSERT_LINE);
+ }
+ break;
+
+ default:
+ if (m_fifo_cap_a == 0x1ff)
+ {
+ m_regs[R_FIFOSTAT-0x800] |= 1; // fifo A half-empty
+ write_irq(ASSERT_LINE);
+ }
+ else if (m_fifo_cap_a == 0x1) // fifo A fully empty
+ {
+ m_regs[R_FIFOSTAT-0x800] |= 2; // fifo A empty
+ write_irq(ASSERT_LINE);
+ }
+
+ if (m_fifo_cap_b == 0x1ff)
+ {
+ m_regs[R_FIFOSTAT-0x800] |= 4; // fifo B half-empty
+ write_irq(ASSERT_LINE);
+ }
+ else if (m_fifo_cap_b == 0x1) // fifo B fully empty
+ {
+ m_regs[R_FIFOSTAT-0x800] |= 8; // fifo B empty
+ write_irq(ASSERT_LINE);
+ }
+ break;
+ }
+
+ outL[i] = smpll * 64;
+ outR[i] = smplr * 64;
+ }
+ break;
+
+ case 2: // wavetable mode
+ for (i = 0; i < samples; i++)
+ {
+ INT32 mixL, mixR;
+ INT8 smpl;
+
+ mixL = mixR = 0;
+
+ // update channel pointers
+ for (ch = 0; ch < 4; ch++)
+ {
+ m_phase[ch] += m_incr[ch];
+
+ if (ch < 2)
+ {
+ smpl = (INT8)m_fifo_a[((m_phase[ch]>>15)&0x1ff) + wtoffs[ch&1]];
+ }
+ else
+ {
+ smpl = (INT8)m_fifo_b[((m_phase[ch]>>15)&0x1ff) + wtoffs[ch&1]];
+ }
+
+ smpl ^= 0x80;
+ mixL += smpl*256;
+ mixR += smpl*256;
+ }
+
+ outL[i] = mixL>>2;
+ outR[i] = mixR>>2;
+ }
+ break;
+ }
+
+// printf("rdA %04x rdB %04x wrA %04x wrB %04x (capA %04x B %04x)\n", m_fifo_a_rdptr, m_fifo_b_rdptr, m_fifo_a_wrptr, m_fifo_b_wrptr, m_fifo_cap_a, m_fifo_cap_b);
+}
+
+//-------------------------------------------------
+// read - read from the chip's registers and internal RAM
+//-------------------------------------------------
+
+READ8_MEMBER( asc_device::read )
+{
+ UINT8 rv;
+
+// printf("ASC: read at %x\n", offset);
+
+ // not sure what actually happens when the CPU reads the FIFO...
+ if (offset < 0x400)
+ {
+ return m_fifo_a[offset];
+ }
+ else if (offset < 0x800)
+ {
+ return m_fifo_b[offset-0x400];
+ }
+ else
+ {
+ m_stream->update();
+ switch (offset)
+ {
+ case R_VERSION:
+ switch (m_chip_type)
+ {
+ case ASC_TYPE_ASC:
+ return 0;
+
+ case ASC_TYPE_V8:
+ case ASC_TYPE_EAGLE:
+ case ASC_TYPE_SPICE:
+ case ASC_TYPE_VASP:
+ return 0xe8;
+
+ case ASC_TYPE_SONORA:
+ return 0xbc;
+
+ default: // return the actual register value
+ break;
+ }
+ break;
+
+ case R_MODE:
+ switch (m_chip_type)
+ {
+ case ASC_TYPE_V8:
+ case ASC_TYPE_EAGLE:
+ case ASC_TYPE_SPICE:
+ case ASC_TYPE_VASP:
+ return 1;
+
+ default:
+ break;
+ }
+ break;
+
+ case R_CONTROL:
+ switch (m_chip_type)
+ {
+ case ASC_TYPE_V8:
+ case ASC_TYPE_EAGLE:
+ case ASC_TYPE_SPICE:
+ case ASC_TYPE_VASP:
+ return 1;
+
+ default:
+ break;
+ }
+ break;
+
+ case R_FIFOSTAT:
+ if (m_chip_type == ASC_TYPE_V8)
+ {
+ rv = 3;
+ }
+ else
+ {
+ rv = m_regs[R_FIFOSTAT-0x800];
+ }
+
+// printf("Read FIFO stat = %02x\n", rv);
+
+ // reading this register clears all bits
+ m_regs[R_FIFOSTAT-0x800] = 0;
+
+ // reading this clears interrupts
+ write_irq(CLEAR_LINE);
+
+ return rv;
+
+ default:
+ break;
+ }
+ }
+
+ // WT inc/phase registers - rebuild from "live" copies"
+ if ((offset >= 0x810) && (offset <= 0x82f))
+ {
+ m_regs[0x11] = m_phase[0]>>16;
+ m_regs[0x12] = m_phase[0]>>8;
+ m_regs[0x13] = m_phase[0];
+ m_regs[0x15] = m_incr[0]>>16;
+ m_regs[0x16] = m_incr[0]>>8;
+ m_regs[0x17] = m_incr[0];
+
+ m_regs[0x19] = m_phase[1]>>16;
+ m_regs[0x1a] = m_phase[1]>>8;
+ m_regs[0x1b] = m_phase[1];
+ m_regs[0x1d] = m_incr[1]>>16;
+ m_regs[0x1e] = m_incr[1]>>8;
+ m_regs[0x1f] = m_incr[1];
+
+ m_regs[0x21] = m_phase[2]>>16;
+ m_regs[0x22] = m_phase[2]>>8;
+ m_regs[0x23] = m_phase[2];
+ m_regs[0x25] = m_incr[2]>>16;
+ m_regs[0x26] = m_incr[2]>>8;
+ m_regs[0x27] = m_incr[2];
+
+ m_regs[0x29] = m_phase[3]>>16;
+ m_regs[0x2a] = m_phase[3]>>8;
+ m_regs[0x2b] = m_phase[3];
+ m_regs[0x2d] = m_incr[3]>>16;
+ m_regs[0x2e] = m_incr[3]>>8;
+ m_regs[0x2f] = m_incr[3];
+ }
+
+ if (offset >= 0x1000)
+ {
+ return 0xff;
+ }
+
+ return m_regs[offset-0x800];
+}
+
+//-------------------------------------------------
+// write - write to the chip's registers and internal RAM
+//-------------------------------------------------
+
+WRITE8_MEMBER( asc_device::write )
+{
+// printf("ASC: write %02x to %x\n", data, offset);
+
+ if (offset < 0x400)
+ {
+ if (m_regs[R_MODE-0x800] == 1)
+ {
+ m_fifo_a[m_fifo_a_wrptr++] = data;
+ m_fifo_cap_a++;
+
+ if (m_fifo_cap_a == 0x3ff)
+ {
+ m_regs[R_FIFOSTAT-0x800] |= 2; // fifo A full
+ }
+
+ m_fifo_a_wrptr &= 0x3ff;
+ }
+ else
+ {
+ m_fifo_a[offset] = data;
+ }
+ }
+ else if (offset < 0x800)
+ {
+ if (m_regs[R_MODE-0x800] == 1)
+ {
+ m_fifo_b[m_fifo_b_wrptr++] = data;
+ m_fifo_cap_b++;
+
+ if (m_fifo_cap_b == 0x3ff)
+ {
+ m_regs[R_FIFOSTAT-0x800] |= 8; // fifo B full
+ }
+
+ m_fifo_b_wrptr &= 0x3ff;
+ }
+ else
+ {
+ m_fifo_b[offset-0x400] = data;
+ }
+ }
+ else
+ {
+// printf("ASC: %02x to %x (was %x)\n", data, offset, m_regs[offset-0x800]);
+
+ m_stream->update();
+ switch (offset)
+ {
+ case R_MODE:
+ data &= 3; // only bits 0 and 1 can be written
+
+ if (data != m_regs[R_MODE-0x800])
+ {
+ m_fifo_a_rdptr = m_fifo_b_rdptr = 0;
+ m_fifo_a_wrptr = m_fifo_b_wrptr = 0;
+ m_fifo_cap_a = m_fifo_cap_b = 0;
+
+ if (data != 0)
+ {
+ m_timer->adjust(attotime::zero, 0, attotime::from_hz(22257/4));
+ }
+ else
+ {
+ m_timer->adjust(attotime::never);
+ }
+ }
+ break;
+
+ case R_FIFOMODE:
+ if (data & 0x80)
+ {
+ m_fifo_a_rdptr = m_fifo_b_rdptr = 0;
+ m_fifo_a_wrptr = m_fifo_b_wrptr = 0;
+ m_fifo_cap_a = m_fifo_cap_b = 0;
+ }
+ break;
+
+ case R_WTCONTROL:
+// printf("One-shot wavetable %02x\n", data);
+ break;
+
+ case 0x811:
+ m_phase[0] &= 0x00ffff;
+ m_phase[0] |= data<<16;
+ break;
+
+ case 0x812:
+ m_phase[0] &= 0xff00ff;
+ m_phase[0] |= data<<8;
+ break;
+
+ case 0x813:
+ m_phase[0] &= 0xffff00;
+ m_phase[0] |= data;
+ break;
+
+ case 0x815:
+ m_incr[0] &= 0x00ffff;
+ m_incr[0] |= data<<16;
+ break;
+
+ case 0x816:
+ m_incr[0] &= 0xff00ff;
+ m_incr[0] |= data<<8;
+ break;
+
+ case 0x817:
+ m_incr[0] &= 0xffff00;
+ m_incr[0] |= data;
+ break;
+
+ case 0x819:
+ m_phase[1] &= 0x00ffff;
+ m_phase[1] |= data<<16;
+ break;
+
+ case 0x81a:
+ m_phase[1] &= 0xff00ff;
+ m_phase[1] |= data<<8;
+ break;
+
+ case 0x81b:
+ m_phase[1] &= 0xffff00;
+ m_phase[1] |= data;
+ break;
+
+ case 0x81d:
+ m_incr[1] &= 0x00ffff;
+ m_incr[1] |= data<<16;
+ break;
+
+ case 0x81e:
+ m_incr[1] &= 0xff00ff;
+ m_incr[1] |= data<<8;
+ break;
+
+ case 0x81f:
+ m_incr[1] &= 0xffff00;
+ m_incr[1] |= data;
+ break;
+
+ case 0x821:
+ m_phase[2] &= 0x00ffff;
+ m_phase[2] |= data<<16;
+ break;
+
+ case 0x822:
+ m_phase[2] &= 0xff00ff;
+ m_phase[2] |= data<<8;
+ break;
+
+ case 0x823:
+ m_phase[2] &= 0xffff00;
+ m_phase[2] |= data;
+ break;
+
+ case 0x825:
+ m_incr[2] &= 0x00ffff;
+ m_incr[2] |= data<<16;
+ break;
+
+ case 0x826:
+ m_incr[2] &= 0xff00ff;
+ m_incr[2] |= data<<8;
+ break;
+
+ case 0x827:
+ m_incr[2] &= 0xffff00;
+ m_incr[2] |= data;
+ break;
+
+ case 0x829:
+ m_phase[3] &= 0x00ffff;
+ m_phase[3] |= data<<16;
+ break;
+
+ case 0x82a:
+ m_phase[3] &= 0xff00ff;
+ m_phase[3] |= data<<8;
+ break;
+
+ case 0x82b:
+ m_phase[3] &= 0xffff00;
+ m_phase[3] |= data;
+ break;
+
+ case 0x82d:
+ m_incr[3] &= 0x00ffff;
+ m_incr[3] |= data<<16;
+ break;
+
+ case 0x82e:
+ m_incr[3] &= 0xff00ff;
+ m_incr[3] |= data<<8;
+ break;
+
+ case 0x82f:
+ m_incr[3] &= 0xffff00;
+ m_incr[3] |= data;
+ break;
+ }
+
+ if (offset >= 0x800 && offset < 0x1000)
+ {
+ m_regs[offset-0x800] = data;
+ }
+ }
+}
diff --git a/src/devices/sound/asc.h b/src/devices/sound/asc.h
new file mode 100644
index 00000000000..24f085e50ac
--- /dev/null
+++ b/src/devices/sound/asc.h
@@ -0,0 +1,136 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+/***************************************************************************
+
+ asc.h
+
+ Apple Sound Chip (ASC) 344S0063
+ Enhanced Apple Sound Chip (EASC) 343S1063
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __ASC_H__
+#define __ASC_H__
+
+
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+// chip behavior types
+enum
+{
+ ASC_TYPE_ASC = 0, // original discrete Apple Sound Chip
+ ASC_TYPE_EASC = 1, // discrete Enhanced Apple Sound Chip
+ ASC_TYPE_V8 = 2, // Subset of ASC included in the V8 ASIC (LC/LCII)
+ ASC_TYPE_EAGLE = 3, // Subset of ASC included in the Eagle ASIC (Classic II)
+ ASC_TYPE_SPICE = 4, // Subset of ASC included in the Spice ASIC (Color Classic)
+ ASC_TYPE_SONORA = 5, // Subset of ASC included in the Sonora ASIC (LCIII)
+ ASC_TYPE_VASP = 6, // Subset of ASC included in the VASP ASIC (IIvx/IIvi)
+ ASC_TYPE_ARDBEG = 7 // Subset of ASC included in the Ardbeg ASIC (LC520)
+};
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_ASC_ADD(_tag, _clock, _type, _irqf) \
+ MCFG_DEVICE_ADD(_tag, ASC, _clock) \
+ MCFG_ASC_TYPE(_type) \
+ MCFG_IRQ_FUNC(_irqf)
+
+#define MCFG_ASC_REPLACE(_tag, _clock, _type, _irqf) \
+ MCFG_DEVICE_REPLACE(_tag, ASC, _clock) \
+ MCFG_ASC_TYPE(_type) \
+ MCFG_IRQ_FUNC(_irqf)
+
+#define MCFG_ASC_TYPE(_type) \
+ asc_device::static_set_type(*device, _type);
+#define MCFG_IRQ_FUNC(_irqf) \
+ downcast<asc_device *>(device)->set_irqf(DEVCB_##_irqf);
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> asc_device
+
+class asc_device : public device_t, public device_sound_interface
+{
+public:
+ // construction/destruction
+ asc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // inline configuration helpers
+ static void static_set_type(device_t &device, int type);
+
+
+ template<class _write> void set_irqf(_write wr)
+ {
+ write_irq.set_callback(wr);
+ }
+
+ devcb_write_line write_irq;
+
+ DECLARE_READ8_MEMBER(read);
+ DECLARE_WRITE8_MEMBER(write);
+
+ sound_stream *m_stream;
+
+protected:
+ enum
+ {
+ R_VERSION = 0x800,
+ R_MODE,
+ R_CONTROL,
+ R_FIFOMODE,
+ R_FIFOSTAT,
+ R_WTCONTROL,
+ R_VOLUME,
+ R_CLOCK,
+ R_REG8,
+ R_REG9,
+ R_PLAYRECA,
+ R_REGB,
+ R_REGC,
+ R_REGD,
+ R_REGE,
+ R_TEST
+ };
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ // inline data
+ UINT8 m_chip_type;
+
+ UINT8 m_fifo_a[0x400];
+ UINT8 m_fifo_b[0x400];
+
+ UINT8 m_regs[0x800];
+
+ UINT32 m_phase[4], m_incr[4];
+
+ int m_fifo_a_rdptr, m_fifo_b_rdptr;
+ int m_fifo_a_wrptr, m_fifo_b_wrptr;
+ int m_fifo_cap_a, m_fifo_cap_b;
+
+ emu_timer *m_timer;
+};
+
+
+// device type definition
+extern const device_type ASC;
+
+
+#endif /* __ASC_H__ */
diff --git a/src/devices/sound/astrocde.c b/src/devices/sound/astrocde.c
new file mode 100644
index 00000000000..0870347c6ae
--- /dev/null
+++ b/src/devices/sound/astrocde.c
@@ -0,0 +1,285 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles,Frank Palazzolo
+/***********************************************************
+
+ Astrocade custom 'IO' chip sound chip driver
+ Aaron Giles
+ based on original work by Frank Palazzolo
+
+************************************************************
+
+ Register Map
+ ============
+
+ Register 0:
+ D7..D0: Master oscillator frequency
+
+ Register 1:
+ D7..D0: Tone generator A frequency
+
+ Register 2:
+ D7..D0: Tone generator B frequency
+
+ Register 3:
+ D7..D0: Tone generator C frequency
+
+ Register 4:
+ D7..D6: Vibrato speed
+ D5..D0: Vibrato depth
+
+ Register 5:
+ D5: Noise AM enable
+ D4: Mux source (0=vibrato, 1=noise)
+ D3..D0: Tone generator C volume
+
+ Register 6:
+ D7..D4: Tone generator B volume
+ D3..D0: Tone generator A volume
+
+ Register 7:
+ D7..D0: Noise volume
+
+***********************************************************/
+
+#include "emu.h"
+#include "astrocde.h"
+
+
+// device type definition
+const device_type ASTROCADE = &device_creator<astrocade_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// astrocade_device - constructor
+//-------------------------------------------------
+
+astrocade_device::astrocade_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, ASTROCADE, "Astrocade", tag, owner, clock, "astrocade", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_master_count(0),
+ m_vibrato_clock(0),
+ m_noise_clock(0),
+ m_noise_state(0),
+ m_a_count(0),
+ m_a_state(0),
+ m_b_count(0),
+ m_b_state(0),
+ m_c_count(0),
+ m_c_state(0)
+{
+ memset(m_reg, 0, sizeof(UINT8)*8);
+ memset(m_bitswap, 0, sizeof(UINT8)*256);
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void astrocade_device::device_start()
+{
+ int i;
+
+ /* generate a bitswap table for the noise */
+ for (i = 0; i < 256; i++)
+ m_bitswap[i] = BITSWAP8(i, 0,1,2,3,4,5,6,7);
+
+ /* allocate a stream for output */
+ m_stream = stream_alloc(0, 1, clock());
+
+ /* reset state */
+ device_reset();
+ state_save_register();
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void astrocade_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *dest = outputs[0];
+ UINT16 noise_state;
+ UINT8 master_count;
+ UINT8 noise_clock;
+
+ /* load some locals */
+ master_count = m_master_count;
+ noise_clock = m_noise_clock;
+ noise_state = m_noise_state;
+
+ /* loop over samples */
+ while (samples > 0)
+ {
+ stream_sample_t cursample = 0;
+ int samples_this_time;
+ int samp;
+
+ /* compute the number of cycles until the next master oscillator reset */
+ /* or until the next noise boundary */
+ samples_this_time = MIN(samples, 256 - master_count);
+ samples_this_time = MIN(samples_this_time, 64 - noise_clock);
+ samples -= samples_this_time;
+
+ /* sum the output of the tone generators */
+ if (m_a_state)
+ cursample += m_reg[6] & 0x0f;
+ if (m_b_state)
+ cursample += m_reg[6] >> 4;
+ if (m_c_state)
+ cursample += m_reg[5] & 0x0f;
+
+ /* add in the noise if it is enabled, based on the top bit of the LFSR */
+ if ((m_reg[5] & 0x20) && (noise_state & 0x4000))
+ cursample += m_reg[7] >> 4;
+
+ /* scale to max and output */
+ cursample = cursample * 32767 / 60;
+ for (samp = 0; samp < samples_this_time; samp++)
+ *dest++ = cursample;
+
+ /* clock the noise; a 2-bit counter clocks a 4-bit counter which clocks the LFSR */
+ noise_clock += samples_this_time;
+ if (noise_clock >= 64)
+ {
+ /* update the noise state; this is a 15-bit LFSR with feedback from */
+ /* the XOR of the top two bits */
+ noise_state = (noise_state << 1) | (~((noise_state >> 14) ^ (noise_state >> 13)) & 1);
+ noise_clock -= 64;
+
+ /* the same clock also controls the vibrato clock, which is a 13-bit counter */
+ m_vibrato_clock++;
+ }
+
+ /* clock the master oscillator; this is an 8-bit up counter */
+ master_count += samples_this_time;
+ if (master_count == 0)
+ {
+ /* reload based on mux value -- the value from the register is negative logic */
+ master_count = ~m_reg[0];
+
+ /* mux value 0 means reload based on the vibrato control */
+ if ((m_reg[5] & 0x10) == 0)
+ {
+ /* vibrato speed (register 4 bits 6-7) selects one of the top 4 bits */
+ /* of the 13-bit vibrato clock to use (0=highest freq, 3=lowest) */
+ if (!((m_vibrato_clock >> (m_reg[4] >> 6)) & 0x0200))
+ {
+ /* if the bit is clear, we add the vibrato volume to the counter */
+ master_count += m_reg[4] & 0x3f;
+ }
+ }
+
+ /* mux value 1 means reload based on the noise control */
+ else
+ {
+ /* the top 8 bits of the noise LFSR are ANDed with the noise volume */
+ /* register and added to the count */
+ master_count += m_bitswap[(noise_state >> 7) & 0xff] & m_reg[7];
+ }
+
+ /* clock tone A */
+ if (++m_a_count == 0)
+ {
+ m_a_state ^= 1;
+ m_a_count = ~m_reg[1];
+ }
+
+ /* clock tone B */
+ if (++m_b_count == 0)
+ {
+ m_b_state ^= 1;
+ m_b_count = ~m_reg[2];
+ }
+
+ /* clock tone C */
+ if (++m_c_count == 0)
+ {
+ m_c_state ^= 1;
+ m_c_count = ~m_reg[3];
+ }
+ }
+ }
+
+ /* put back the locals */
+ m_master_count = master_count;
+ m_noise_clock = noise_clock;
+ m_noise_state = noise_state;
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void astrocade_device::device_reset()
+{
+ memset(m_reg, 0, sizeof(m_reg));
+
+ m_master_count = 0;
+ m_vibrato_clock = 0;
+
+ m_noise_clock = 0;
+ m_noise_state = 0;
+
+ m_a_count = 0;
+ m_a_state = 0;
+
+ m_b_count = 0;
+ m_b_state = 0;
+
+ m_c_count = 0;
+ m_c_state = 0;
+}
+
+
+//-------------------------------------------------
+// Save state registration
+//-------------------------------------------------
+
+void astrocade_device::state_save_register()
+{
+ save_item(NAME(m_reg));
+
+ save_item(NAME(m_master_count));
+ save_item(NAME(m_vibrato_clock));
+
+ save_item(NAME(m_noise_clock));
+ save_item(NAME(m_noise_state));
+
+ save_item(NAME(m_a_count));
+ save_item(NAME(m_a_state));
+
+ save_item(NAME(m_b_count));
+ save_item(NAME(m_b_state));
+
+ save_item(NAME(m_c_count));
+ save_item(NAME(m_c_state));
+}
+
+
+/*************************************
+ *
+ * Sound write accessors
+ *
+ *************************************/
+
+WRITE8_MEMBER( astrocade_device::astrocade_sound_w )
+{
+ if ((offset & 8) != 0)
+ offset = (offset >> 8) & 7;
+ else
+ offset &= 7;
+
+ /* update */
+ m_stream->update();
+
+ /* stash the new register value */
+ m_reg[offset & 7] = data;
+}
diff --git a/src/devices/sound/astrocde.h b/src/devices/sound/astrocde.h
new file mode 100644
index 00000000000..23e9a83c733
--- /dev/null
+++ b/src/devices/sound/astrocde.h
@@ -0,0 +1,73 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles,Frank Palazzolo
+#pragma once
+
+#ifndef __ASTROCDE_H__
+#define __ASTROCDE_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_ASTROCADE_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, ASTROCADE, _clock)
+#define MCFG_ASTROCADE_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, ASTROCADE, _clock)
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> astrocade_device
+
+class astrocade_device : public device_t,
+ public device_sound_interface
+{
+public:
+ astrocade_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~astrocade_device() { }
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+public:
+ DECLARE_WRITE8_MEMBER( astrocade_sound_w );
+
+private:
+ void state_save_register();
+
+private:
+ sound_stream *m_stream; /* sound stream */
+
+ UINT8 m_reg[8]; /* 8 control registers */
+
+ UINT8 m_master_count; /* current master oscillator count */
+ UINT16 m_vibrato_clock; /* current vibrato clock */
+
+ UINT8 m_noise_clock; /* current noise generator clock */
+ UINT16 m_noise_state; /* current noise LFSR state */
+
+ UINT8 m_a_count; /* current tone generator A count */
+ UINT8 m_a_state; /* current tone generator A state */
+
+ UINT8 m_b_count; /* current tone generator B count */
+ UINT8 m_b_state; /* current tone generator B state */
+
+ UINT8 m_c_count; /* current tone generator C count */
+ UINT8 m_c_state; /* current tone generator C state */
+
+ UINT8 m_bitswap[256]; /* bitswap table */
+};
+
+extern const device_type ASTROCADE;
+
+
+#endif /* __ASTROCDE_H__ */
diff --git a/src/devices/sound/awacs.c b/src/devices/sound/awacs.c
new file mode 100644
index 00000000000..57668acaef5
--- /dev/null
+++ b/src/devices/sound/awacs.c
@@ -0,0 +1,176 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+/***************************************************************************
+
+ awacs.c
+
+ AWACS/Singer style 16-bit audio I/O for '040 and PowerPC Macs
+
+ Emulation by R. Belmont
+
+***************************************************************************/
+
+#include "emu.h"
+#include "awacs.h"
+
+// device type definition
+const device_type AWACS = &device_creator<awacs_device>;
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// awacs_device - constructor
+//-------------------------------------------------
+
+awacs_device::awacs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, AWACS, "AWACS", tag, owner, clock, "awacs", __FILE__),
+ device_sound_interface(mconfig, *this)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void awacs_device::device_start()
+{
+ // create the stream
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, 22050);
+
+ memset(m_regs, 0, sizeof(m_regs));
+
+ m_timer = timer_alloc(0, NULL);
+
+ save_item(NAME(m_play_ptr));
+ save_item(NAME(m_buffer_size));
+ save_item(NAME(m_playback_enable));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void awacs_device::device_reset()
+{
+ m_stream->update();
+
+ memset(m_regs, 0, sizeof(m_regs));
+
+ m_play_ptr = 0;
+ m_buffer_size = 0;
+ m_playback_enable = false;
+ m_dma_space = NULL;
+ m_dma_offset_0 = m_dma_offset_1 = 0;
+ m_buffer_num = 0;
+}
+
+//-------------------------------------------------
+// device_timer - called when our device timer expires
+//-------------------------------------------------
+
+void awacs_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
+{
+ m_stream->update();
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void awacs_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *outL, *outR;
+ int offset = (m_buffer_num == 0) ? m_dma_offset_0 : m_dma_offset_1;
+
+ outL = outputs[0];
+ outR = outputs[1];
+
+ if (m_playback_enable)
+ {
+ for (int i = 0; i < samples; i++)
+ {
+ outL[i] = (INT16)m_dma_space->read_word(offset + m_play_ptr);
+ outR[i] = (INT16)m_dma_space->read_word(offset + m_play_ptr + 2);
+ m_play_ptr += 4;
+ }
+
+ // out of buffer?
+ if (m_play_ptr >= m_buffer_size)
+ {
+ UINT8 bufflag[2] = { 0x40, 0x80 };
+
+ m_regs[0x18] |= bufflag[m_buffer_num];
+ m_buffer_num ^= 1;
+ m_play_ptr = 0;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < samples; i++)
+ {
+ outL[i] = 0;
+ outR[i] = 0;
+ }
+ }
+}
+
+//-------------------------------------------------
+// read - read from the chip's registers and internal RAM
+//-------------------------------------------------
+
+READ8_MEMBER( awacs_device::read )
+{
+ return m_regs[offset];
+}
+
+//-------------------------------------------------
+// write - write to the chip's registers and internal RAM
+//-------------------------------------------------
+
+WRITE8_MEMBER( awacs_device::write )
+{
+ switch (offset)
+ {
+ case 0x8:
+ case 0x9:
+ m_regs[offset] = data;
+ m_buffer_size = (m_regs[8]<<6) | (m_regs[9]>>2);
+// printf("buffer size = %x samples, %x bytes\n", m_buffer_size, m_buffer_size*4);
+ m_buffer_size *= 4; // samples * 16 bits * stereo
+ break;
+
+ case 0x10:
+ {
+ static const int rates[4] = { 22100, 29400, 44100, 22100 };
+ m_stream->set_sample_rate(rates[(data>>1)&3]);
+// printf("rate %d, enable: %d\n", rates[(data>>1)&3], data & 1);
+ m_playback_enable = (data & 1) ? true : false;
+
+ if (m_playback_enable && !(m_regs[0x10]&1))
+ {
+ m_play_ptr = 0;
+ m_buffer_num = 0;
+ }
+ }
+ break;
+
+ case 0x18:
+ m_regs[offset] &= 0xf0;
+ m_regs[offset] |= (data & 0x0f);
+ m_regs[offset] &= ~(data & 0xf0);
+ return;
+ }
+
+ m_regs[offset] = data;
+}
+
+void awacs_device::set_dma_base(address_space &space, int offset0, int offset1)
+{
+ m_dma_space = &space;
+ m_dma_offset_0 = offset0;
+ m_dma_offset_1 = offset1;
+}
diff --git a/src/devices/sound/awacs.h b/src/devices/sound/awacs.h
new file mode 100644
index 00000000000..93b9da2d5d9
--- /dev/null
+++ b/src/devices/sound/awacs.h
@@ -0,0 +1,77 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+/***************************************************************************
+
+ awacs.h
+
+ AWACS/Singer style 16-bit audio I/O for '040 and PowerPC Macs
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __AWACS_H__
+#define __AWACS_H__
+
+
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_AWACS_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, AWACS, _clock)
+
+#define MCFG_AWACS_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, AWACS, _clock)
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> awacs_device
+
+class awacs_device : public device_t, public device_sound_interface
+{
+public:
+ // construction/destruction
+ awacs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_READ8_MEMBER(read);
+ DECLARE_WRITE8_MEMBER(write);
+
+ void set_dma_base(address_space &space, int offset0, int offset1);
+
+ sound_stream *m_stream;
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ // inline data
+ UINT8 m_regs[0x100];
+
+ int m_play_ptr, m_buffer_size, m_buffer_num;
+ bool m_playback_enable;
+
+ address_space *m_dma_space;
+ int m_dma_offset_0, m_dma_offset_1;
+
+ emu_timer *m_timer;
+};
+
+
+// device type definition
+extern const device_type AWACS;
+
+
+#endif /* __AWACS_H__ */
diff --git a/src/devices/sound/ay8910.c b/src/devices/sound/ay8910.c
new file mode 100644
index 00000000000..9e8d397ff7a
--- /dev/null
+++ b/src/devices/sound/ay8910.c
@@ -0,0 +1,1270 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+/*
+ * Couriersud, July 2014:
+ *
+ * This documents recent work on the AY8910. A YM2149 is now on it's way from
+ * Hong Kong as well.
+ *
+ * TODO:
+ *
+ * - Create a true sound device nAY8910 driver.
+ * - implement approach outlined below in this driver.
+ *
+ * For years I had a AY8910 in my drawer. Arduinos were around as well.
+ * Using the approach documented in this blog post
+ * http://www.986-studio.com/2014/05/18/another-ay-entry/#more-476
+ * I measured the output voltages using a Extech 520.
+ *
+ * Measurement Setup
+ *
+ * Laptop <--> Arduino <---> AY8910
+ *
+ * AY8910 Registers:
+ * 0x07: 3f
+ * 0x08: RV
+ * 0x09: RV
+ * 0x0A: RV
+ *
+ * Output was measured on Analog Output B with a resistor RD to
+ * ground.
+ *
+ * Measurement results:
+ *
+ * RD 983 9.830k 99.5k 1.001M open
+ *
+ * RV B B B B B
+ * 0 0.0000 0.0000 0.0001 0.0011 0.0616
+ * 1 0.0106 0.0998 0.6680 1.8150 2.7260
+ * 2 0.0150 0.1377 0.8320 1.9890 2.8120
+ * 3 0.0222 0.1960 1.0260 2.1740 2.9000
+ * 4 0.0320 0.2708 1.2320 2.3360 2.9760
+ * 5 0.0466 0.3719 1.4530 2.4880 3.0440
+ * 6 0.0665 0.4938 1.6680 2.6280 3.1130
+ * 7 0.1039 0.6910 1.9500 2.7900 3.1860
+ * 8 0.1237 0.7790 2.0500 2.8590 3.2340
+ * 9 0.1986 1.0660 2.3320 3.0090 3.3090
+ * 10 0.2803 1.3010 2.5050 3.0850 3.3380
+ * 11 0.3548 1.4740 2.6170 3.1340 3.3590
+ * 12 0.4702 1.6870 2.7340 3.1800 3.3730
+ * 13 0.6030 1.8870 2.8410 3.2300 3.4050
+ * 14 0.7530 2.0740 2.9280 3.2580 3.4170
+ * 15 0.9250 2.2510 3.0040 3.2940 3.4380
+ *
+ * Using an equivalent model approach with two resistors
+ *
+ * 5V
+ * |
+ * Z
+ * Z Resistor Value for RV
+ * Z
+ * |
+ * +---> Output signal
+ * |
+ * Z
+ * Z External RD
+ * Z
+ * |
+ * GND
+ *
+ * will NOT work out of the box since RV = RV(RD).
+ *
+ * The following approach will be used going forward based on die pictures
+ * of the AY8910 done by Dr. Stack van Hay:
+ *
+ *
+ * 5V
+ * _| D
+ * G | NMOS
+ * Vg ---|| Kn depends on volume selected
+ * |_ S Vs
+ * |
+ * |
+ * +---> VO Output signal
+ * |
+ * Z
+ * Z External RD
+ * Z
+ * |
+ * GND
+ *
+ * Whilst conducting, the FET operates in saturation mode:
+ *
+ * Id = Kn * (Vgs - Vth)^2
+ *
+ * Using Id = Vs / RD
+ *
+ * Vs = Kn * RD * (Vg - Vs - Vth)^2
+ *
+ * finally using Vg' = Vg - Vth
+ *
+ * Vs = Vg' + 1 / (2 * Kn * RD) - sqrt((Vg' + 1 / (2 * Kn * RD))^2 - Vg'^2)
+ *
+ * and finally
+ *
+ * VO = Vs
+ *
+ * and this can be used to re-Thenevin to 5V
+ *
+ * RVequiv = RD * ( 5V / VO - 1)
+ *
+ * The RV and Kn parameter are derived using least squares to match
+ * calculation results with measurements.
+ *
+ * FIXME:
+ * There is voltage of 60 mV measured with the EX520 (Ri ~ 10M). This may
+ * be induced by cutoff currents from the 15 FETs.
+ *
+ */
+
+
+/***************************************************************************
+
+ ay8910.c
+
+ Emulation of the AY-3-8910 / YM2149 sound chip.
+
+ Based on various code snippets by Ville Hallik, Michael Cuddy,
+ Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria.
+
+ Mostly rewritten by couriersud in 2008
+
+ Public documentation:
+
+ - http://privatfrickler.de/blick-auf-den-chip-soundchip-general-instruments-ay-3-8910/
+ Die pictures of the AY8910
+
+ - US Patent 4933980
+
+ Games using ADSR: gyruss
+
+ A list with more games using ADSR can be found here:
+ http://mametesters.org/view.php?id=3043
+
+ TODO:
+ * The AY8930 has an extended mode which is currently
+ not emulated.
+ * YM2610 & YM2608 will need a separate flag in their config structures
+ to distinguish between legacy and discrete mode.
+
+ The rewrite also introduces a generic model for the DAC. This model is
+ not perfect, but allows channel mixing based on a parametrized approach.
+ This model also allows to factor in different loads on individual channels.
+ If a better model is developped in the future or better measurements are
+ available, the driver should be easy to change. The model is described
+ later.
+
+ In order to not break hundreds of existing drivers by default the flag
+ AY8910_LEGACY_OUTPUT is used by drivers not changed to take into account the
+ new model. All outputs are normalized to the old output range (i.e. 0 .. 7ffff).
+ In the case of channel mixing, output range is 0...3 * 7fff.
+
+ The main difference between the AY-3-8910 and the YM2149 is, that the
+ AY-3-8910 datasheet mentions, that fixed volume level 0, which is set by
+ registers 8 to 10 is "channel off". The YM2149 mentions, that the generated
+ signal has a 2V DC component. This is confirmed by measurements. The approach
+ taken here is to assume the 2V DC offset for all outputs for the YM2149.
+ For the AY-3-8910, an offset is used if envelope is active for a channel.
+ This is backed by oscilloscope pictures from the datasheet. If a fixed volume
+ is set, i.e. envelope is disabled, the output voltage is set to 0V. Recordings
+ I found on the web for gyruss indicate, that the AY-3-8910 offset should
+ be around 0.2V. This will also make sound levels more compatible with
+ user observations for scramble.
+
+ The Model:
+ 5V 5V
+ | |
+ / |
+ Volume Level x >---| Z
+ > Z Pullup Resistor RU
+ | Z
+ Z |
+ Rx Z |
+ Z |
+ | |
+ '-----+--------> >---+----> Output signal
+ | |
+ Z Z
+ Pulldown RD Z Z Load RL
+ Z Z
+ | |
+ GND GND
+
+Each Volume level x will select a different resistor Rx. Measurements from fpgaarcade.com
+where used to calibrate channel mixing for the YM2149. This was done using
+a least square approach using a fixed RL of 1K Ohm.
+
+For the AY measurements cited in e.g. openmsx as "Hacker Kay" for a single
+channel were taken. These were normalized to 0 ... 65535 and consequently
+adapted to an offset of 0.2V and a VPP of 1.3V. These measurements are in
+line e.g. with the formula used by pcmenc for the volume: vol(i) = exp(i/2-7.5).
+
+The following is documentation from the code moved here and amended to reflect
+the changes done:
+
+Careful studies of the chip output prove that the chip counts up from 0
+until the counter becomes greater or equal to the period. This is an
+important difference when the program is rapidly changing the period to
+modulate the sound. This is worthwhile noting, since the datasheets
+say, that the chip counts down.
+Also, note that period = 0 is the same as period = 1. This is mentioned
+in the YM2203 data sheets. However, this does NOT apply to the Envelope
+period. In that case, period = 0 is half as period = 1.
+
+Envelope shapes:
+ C AtAlH
+ 0 0 x x \___
+ 0 1 x x /___
+ 1 0 0 0 \\\\
+ 1 0 0 1 \___
+ 1 0 1 0 \/\/
+ 1 0 1 1 \```
+ 1 1 0 0 ////
+ 1 1 0 1 /```
+ 1 1 1 0 /\/\
+ 1 1 1 1 /___
+
+The envelope counter on the AY-3-8910 has 16 steps. On the YM2149 it
+has twice the steps, happening twice as fast.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "ay8910.h"
+
+/*************************************
+ *
+ * Defines
+ *
+ *************************************/
+
+#define ENABLE_REGISTER_TEST (0) /* Enable preprogrammed registers */
+
+#define MAX_OUTPUT 0x7fff
+
+/* register id's */
+#define AY_AFINE (0)
+#define AY_ACOARSE (1)
+#define AY_BFINE (2)
+#define AY_BCOARSE (3)
+#define AY_CFINE (4)
+#define AY_CCOARSE (5)
+#define AY_NOISEPER (6)
+#define AY_ENABLE (7)
+#define AY_AVOL (8)
+#define AY_BVOL (9)
+#define AY_CVOL (10)
+#define AY_EFINE (11)
+#define AY_ECOARSE (12)
+#define AY_ESHAPE (13)
+
+#define AY_PORTA (14)
+#define AY_PORTB (15)
+
+#define NOISE_ENABLEQ(_chan) ((m_regs[AY_ENABLE] >> (3 + _chan)) & 1)
+#define TONE_ENABLEQ(_chan) ((m_regs[AY_ENABLE] >> (_chan)) & 1)
+#define TONE_PERIOD(_chan) ( m_regs[(_chan) << 1] | ((m_regs[((_chan) << 1) | 1] & 0x0f) << 8) )
+#define NOISE_PERIOD() ( m_regs[AY_NOISEPER] & 0x1f)
+#define TONE_VOLUME(_chan) ( m_regs[AY_AVOL + (_chan)] & 0x0f)
+#define TONE_ENVELOPE(_chan) ((m_regs[AY_AVOL + (_chan)] >> 4) & ((type() == AY8914) ? 3 : 1))
+#define ENVELOPE_PERIOD() ((m_regs[AY_EFINE] | (m_regs[AY_ECOARSE]<<8)))
+#define NOISE_OUTPUT() (m_rng & 1)
+
+/*************************************
+ *
+ * Type definitions
+ *
+ *************************************/
+
+
+/*************************************
+ *
+ * Static
+ *
+ *************************************/
+
+static const ay8910_device::ay_ym_param ym2149_param =
+{
+ 630, 801,
+ 16,
+ { 73770, 37586, 27458, 21451, 15864, 12371, 8922, 6796,
+ 4763, 3521, 2403, 1737, 1123, 762, 438, 251 },
+};
+
+static const ay8910_device::ay_ym_param ym2149_param_env =
+{
+ 630, 801,
+ 32,
+ { 103350, 73770, 52657, 37586, 32125, 27458, 24269, 21451,
+ 18447, 15864, 14009, 12371, 10506, 8922, 7787, 6796,
+ 5689, 4763, 4095, 3521, 2909, 2403, 2043, 1737,
+ 1397, 1123, 925, 762, 578, 438, 332, 251 },
+};
+
+#if 0
+/* RL = 1000, Hacker Kay normalized, 2.1V to 3.2V */
+static const ay8910_device::ay_ym_param ay8910_param =
+{
+ 664, 913,
+ 16,
+ { 85785, 34227, 26986, 20398, 14886, 10588, 7810, 4856,
+ 4120, 2512, 1737, 1335, 1005, 747, 586, 451 },
+};
+
+/*
+ * RL = 3000, Hacker Kay normalized pattern, 1.5V to 2.8V
+ * These values correspond with guesses based on Gyruss schematics
+ * They work well with scramble as well.
+ */
+static const ay8910_device::ay_ym_param ay8910_param =
+{
+ 930, 454,
+ 16,
+ { 85066, 34179, 27027, 20603, 15046, 10724, 7922, 4935,
+ 4189, 2557, 1772, 1363, 1028, 766, 602, 464 },
+};
+
+/*
+ * RL = 1000, Hacker Kay normalized pattern, 0.75V to 2.05V
+ * These values correspond with guesses based on Gyruss schematics
+ * They work well with scramble as well.
+ */
+static const ay8910_device::ay_ym_param ay8910_param =
+{
+ 1371, 313,
+ 16,
+ { 93399, 33289, 25808, 19285, 13940, 9846, 7237, 4493,
+ 3814, 2337, 1629, 1263, 962, 727, 580, 458 },
+};
+
+/*
+ * RL = 1000, Hacker Kay normalized pattern, 0.2V to 1.5V
+ */
+static const ay8910_device::ay_ym_param ay8910_param =
+{
+ 5806, 300,
+ 16,
+ { 118996, 42698, 33105, 24770, 17925, 12678, 9331, 5807,
+ 4936, 3038, 2129, 1658, 1271, 969, 781, 623 }
+};
+#endif
+
+/*
+ * RL = 2000, Based on Matthew Westcott's measurements from Dec 2001.
+ * -------------------------------------------------------------------
+ *
+ * http://groups.google.com/group/comp.sys.sinclair/browse_thread/thread/fb3091da4c4caf26/d5959a800cda0b5e?lnk=gst&q=Matthew+Westcott#d5959a800cda0b5e
+ * After what Russell mentioned a couple of weeks back about the lack of
+ * publicised measurements of AY chip volumes - I've finally got round to
+ * making these readings, and I'm placing them in the public domain - so
+ * anyone's welcome to use them in emulators or anything else.
+
+ * To make the readings, I set up the chip to produce a constant voltage on
+ * channel C (setting bits 2 and 5 of register 6), and varied the amplitude
+ * (the low 4 bits of register 10). The voltages were measured between the
+ * channel C output (pin 1) and ground (pin 6).
+ *
+ * Level Voltage
+ * 0 1.147
+ * 1 1.162
+ * 2 1.169
+ * 3 1.178
+ * 4 1.192
+ * 5 1.213
+ * 6 1.238
+ * 7 1.299
+ * 8 1.336
+ * 9 1.457
+ * 10 1.573
+ * 11 1.707
+ * 12 1.882
+ * 13 2.06
+ * 14 2.32
+ * 15 2.58
+ * -------------------------------------------------------------------
+ *
+ * The ZX spectrum output circuit was modelled in SwitcherCAD and
+ * the resistor values below create the voltage levels above.
+ * RD was measured on a real chip to be 8m Ohm, RU was 0.8m Ohm.
+ */
+
+
+static const ay8910_device::ay_ym_param ay8910_param =
+{
+ 800000, 8000000,
+ 16,
+ { 15950, 15350, 15090, 14760, 14275, 13620, 12890, 11370,
+ 10600, 8590, 7190, 5985, 4820, 3945, 3017, 2345 }
+};
+
+static const ay8910_device::mosfet_param ay8910_mosfet_param =
+{
+ 1.465385778,
+ 4.9,
+ 16,
+ {
+ 0.00076,
+ 0.80536,
+ 1.13106,
+ 1.65952,
+ 2.42261,
+ 3.60536,
+ 5.34893,
+ 8.96871,
+ 10.97202,
+ 19.32370,
+ 29.01935,
+ 38.82026,
+ 55.50539,
+ 78.44395,
+ 109.49257,
+ 153.72985,
+ }
+};
+
+
+
+
+/*************************************
+ *
+ * Inline
+ *
+ *************************************/
+
+INLINE void build_3D_table(double rl, const ay8910_device::ay_ym_param *par, const ay8910_device::ay_ym_param *par_env, int normalize, double factor, int zero_is_off, INT32 *tab)
+{
+ double min = 10.0, max = 0.0;
+
+ std::vector<double> temp(8*32*32*32, 0);
+
+ for (int e=0; e < 8; e++)
+ {
+ const ay8910_device::ay_ym_param *par_ch1 = (e & 0x01) ? par_env : par;
+ const ay8910_device::ay_ym_param *par_ch2 = (e & 0x02) ? par_env : par;
+ const ay8910_device::ay_ym_param *par_ch3 = (e & 0x04) ? par_env : par;
+
+ for (int j1=0; j1 < par_ch1->res_count; j1++)
+ for (int j2=0; j2 < par_ch2->res_count; j2++)
+ for (int j3=0; j3 < par_ch3->res_count; j3++)
+ {
+ double n;
+ if (zero_is_off)
+ {
+ n = (j1 != 0 || (e & 0x01)) ? 1 : 0;
+ n += (j2 != 0 || (e & 0x02)) ? 1 : 0;
+ n += (j3 != 0 || (e & 0x04)) ? 1 : 0;
+ }
+ else
+ n = 3.0;
+
+ double rt = n / par->r_up + 3.0 / par->r_down + 1.0 / rl;
+ double rw = n / par->r_up;
+
+ rw += 1.0 / par_ch1->res[j1];
+ rt += 1.0 / par_ch1->res[j1];
+ rw += 1.0 / par_ch2->res[j2];
+ rt += 1.0 / par_ch2->res[j2];
+ rw += 1.0 / par_ch3->res[j3];
+ rt += 1.0 / par_ch3->res[j3];
+
+ int indx = (e << 15) | (j3<<10) | (j2<<5) | j1;
+ temp[indx] = rw / rt;
+ if (temp[indx] < min)
+ min = temp[indx];
+ if (temp[indx] > max)
+ max = temp[indx];
+ }
+ }
+
+ if (normalize)
+ {
+ for (int j=0; j < 32*32*32*8; j++)
+ tab[j] = MAX_OUTPUT * (((temp[j] - min)/(max-min))) * factor;
+ }
+ else
+ {
+ for (int j=0; j < 32*32*32*8; j++)
+ tab[j] = MAX_OUTPUT * temp[j];
+ }
+
+ /* for (e=0;e<16;e++) printf("%d %d\n",e<<10, tab[e<<10]); */
+}
+
+INLINE void build_single_table(double rl, const ay8910_device::ay_ym_param *par, int normalize, INT32 *tab, int zero_is_off)
+{
+ int j;
+ double rt, rw = 0;
+ double temp[32], min=10.0, max=0.0;
+
+ for (j=0; j < par->res_count; j++)
+ {
+ rt = 1.0 / par->r_down + 1.0 / rl;
+
+ rw = 1.0 / par->res[j];
+ rt += 1.0 / par->res[j];
+
+ if (!(zero_is_off && j == 0))
+ {
+ rw += 1.0 / par->r_up;
+ rt += 1.0 / par->r_up;
+ }
+
+ temp[j] = rw / rt;
+ if (temp[j] < min)
+ min = temp[j];
+ if (temp[j] > max)
+ max = temp[j];
+ }
+ if (normalize)
+ {
+ for (j=0; j < par->res_count; j++)
+ tab[j] = MAX_OUTPUT * (((temp[j] - min)/(max-min)) - 0.25) * 0.5;
+ }
+ else
+ {
+ for (j=0; j < par->res_count; j++)
+ tab[j] = MAX_OUTPUT * temp[j];
+ }
+
+}
+
+INLINE void build_mosfet_resistor_table(const ay8910_device::mosfet_param &par, const double rd, INT32 *tab)
+{
+ int j;
+
+ for (j=0; j < par.m_count; j++)
+ {
+ const double Vd = 5.0;
+ const double Vg = par.m_Vg - par.m_Vth;
+ const double kn = par.m_Kn[j] / 1.0e6;
+ const double p2 = 1.0 / (2.0 * kn * rd) + Vg;
+ const double Vs = p2 - sqrt(p2 * p2 - Vg * Vg);
+
+ const double res = rd * ( Vd / Vs - 1.0);
+ /* That's the biggest value we can stream on to netlist. */
+
+ if (res > (1 << 28))
+ tab[j] = (1 << 28);
+ else
+ tab[j] = res;
+ //printf("%d %f %10d\n", j, rd / (res + rd) * 5.0, tab[j]);
+ }
+}
+
+
+UINT16 ay8910_device::mix_3D()
+{
+ int indx = 0, chan;
+
+ for (chan = 0; chan < AY8910_NUM_CHANNELS; chan++)
+ if (TONE_ENVELOPE(chan) != 0)
+ {
+ if (type() == AY8914) // AY8914 Has a two bit tone_envelope field
+ {
+ indx |= (1 << (chan+15)) | ( m_vol_enabled[chan] ? ((m_env_volume >> (3-TONE_ENVELOPE(chan))) << (chan*5)) : 0);
+ }
+ else
+ {
+ indx |= (1 << (chan+15)) | ( m_vol_enabled[chan] ? m_env_volume << (chan*5) : 0);
+ }
+ }
+ else
+ {
+ indx |= (m_vol_enabled[chan] ? TONE_VOLUME(chan) << (chan*5) : 0);
+ }
+ return m_vol3d_table[indx];
+}
+
+/*************************************
+ *
+ * Static functions
+ *
+ *************************************/
+
+void ay8910_device::ay8910_write_reg(int r, int v)
+{
+ //if (r >= 11 && r <= 13 ) printf("%d %x %02x\n", PSG->index, r, v);
+ m_regs[r] = v;
+
+ switch( r )
+ {
+ case AY_AFINE:
+ case AY_ACOARSE:
+ case AY_BFINE:
+ case AY_BCOARSE:
+ case AY_CFINE:
+ case AY_CCOARSE:
+ case AY_NOISEPER:
+ case AY_AVOL:
+ case AY_BVOL:
+ case AY_CVOL:
+ case AY_EFINE:
+ /* No action required */
+ break;
+ case AY_ECOARSE:
+ #ifdef MAME_DEBUG
+ if ( (v & 0x0f) > 0)
+ popmessage("ECoarse");
+ #endif
+ /* No action required */
+ break;
+ case AY_ENABLE:
+ if ((m_last_enable == -1) ||
+ ((m_last_enable & 0x40) != (m_regs[AY_ENABLE] & 0x40)))
+ {
+ /* write out 0xff if port set to input */
+ if (!m_port_a_write_cb.isnull())
+ m_port_a_write_cb((offs_t)0, (m_regs[AY_ENABLE] & 0x40) ? m_regs[AY_PORTA] : 0xff);
+ }
+
+ if ((m_last_enable == -1) ||
+ ((m_last_enable & 0x80) != (m_regs[AY_ENABLE] & 0x80)))
+ {
+ /* write out 0xff if port set to input */
+ if (!m_port_b_write_cb.isnull())
+ m_port_b_write_cb((offs_t)0, (m_regs[AY_ENABLE] & 0x80) ? m_regs[AY_PORTB] : 0xff);
+ }
+ m_last_enable = m_regs[AY_ENABLE];
+ break;
+ case AY_ESHAPE:
+ #ifdef MAME_DEBUG
+ if ( (v & 0x0f) > 0)
+ popmessage("EShape");
+ #endif
+ m_attack = (m_regs[AY_ESHAPE] & 0x04) ? m_env_step_mask : 0x00;
+ if ((m_regs[AY_ESHAPE] & 0x08) == 0)
+ {
+ /* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */
+ m_hold = 1;
+ m_alternate = m_attack;
+ }
+ else
+ {
+ m_hold = m_regs[AY_ESHAPE] & 0x01;
+ m_alternate = m_regs[AY_ESHAPE] & 0x02;
+ }
+ m_env_step = m_env_step_mask;
+ m_holding = 0;
+ m_env_volume = (m_env_step ^ m_attack);
+ break;
+ case AY_PORTA:
+ if (m_regs[AY_ENABLE] & 0x40)
+ {
+ if (!m_port_a_write_cb.isnull())
+ m_port_a_write_cb((offs_t)0, m_regs[AY_PORTA]);
+ else
+ logerror("warning - write %02x to 8910 '%s' Port A\n",m_regs[AY_PORTA],tag());
+ }
+ else
+ {
+ logerror("warning: write to 8910 '%s' Port A set as input - ignored\n",tag());
+ }
+ break;
+ case AY_PORTB:
+ if (m_regs[AY_ENABLE] & 0x80)
+ {
+ if (!m_port_b_write_cb.isnull())
+ m_port_b_write_cb((offs_t)0, m_regs[AY_PORTB]);
+ else
+ logerror("warning - write %02x to 8910 '%s' Port B\n",m_regs[AY_PORTB],tag());
+ }
+ else
+ {
+ logerror("warning: write to 8910 '%s' Port B set as input - ignored\n",tag());
+ }
+ break;
+ }
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void ay8910_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *buf[AY8910_NUM_CHANNELS];
+ int chan;
+
+ buf[0] = outputs[0];
+ buf[1] = NULL;
+ buf[2] = NULL;
+ if (m_streams == AY8910_NUM_CHANNELS)
+ {
+ buf[1] = outputs[1];
+ buf[2] = outputs[2];
+ }
+
+ /* hack to prevent us from hanging when starting filtered outputs */
+ if (!m_ready)
+ {
+ for (chan = 0; chan < AY8910_NUM_CHANNELS; chan++)
+ if (buf[chan] != NULL)
+ memset(buf[chan], 0, samples * sizeof(*buf[chan]));
+ }
+
+ /* The 8910 has three outputs, each output is the mix of one of the three */
+ /* tone generators and of the (single) noise generator. The two are mixed */
+ /* BEFORE going into the DAC. The formula to mix each channel is: */
+ /* (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable). */
+ /* Note that this means that if both tone and noise are disabled, the output */
+ /* is 1, not 0, and can be modulated changing the volume. */
+
+ /* buffering loop */
+ while (samples)
+ {
+ for (chan = 0; chan < AY8910_NUM_CHANNELS; chan++)
+ {
+ m_count[chan]++;
+ if (m_count[chan] >= TONE_PERIOD(chan))
+ {
+ m_output[chan] ^= 1;
+ m_count[chan] = 0;
+ }
+ }
+
+ m_count_noise++;
+ if (m_count_noise >= NOISE_PERIOD())
+ {
+ /* toggle the prescaler output. Noise is no different to
+ * channels.
+ */
+ m_count_noise = 0;
+ m_prescale_noise ^= 1;
+
+ if ( m_prescale_noise)
+ {
+ /* The Random Number Generator of the 8910 is a 17-bit shift */
+ /* register. The input to the shift register is bit0 XOR bit3 */
+ /* (bit0 is the output). This was verified on AY-3-8910 and YM2149 chips. */
+
+ m_rng ^= (((m_rng & 1) ^ ((m_rng >> 3) & 1)) << 17);
+ m_rng >>= 1;
+ }
+ }
+
+ for (chan = 0; chan < AY8910_NUM_CHANNELS; chan++)
+ {
+ m_vol_enabled[chan] = (m_output[chan] | TONE_ENABLEQ(chan)) & (NOISE_OUTPUT() | NOISE_ENABLEQ(chan));
+ }
+
+ /* update envelope */
+ if (m_holding == 0)
+ {
+ m_count_env++;
+ if (m_count_env >= ENVELOPE_PERIOD() * m_step )
+ {
+ m_count_env = 0;
+ m_env_step--;
+
+ /* check envelope current position */
+ if (m_env_step < 0)
+ {
+ if (m_hold)
+ {
+ if (m_alternate)
+ m_attack ^= m_env_step_mask;
+ m_holding = 1;
+ m_env_step = 0;
+ }
+ else
+ {
+ /* if CountEnv has looped an odd number of times (usually 1), */
+ /* invert the output. */
+ if (m_alternate && (m_env_step & (m_env_step_mask + 1)))
+ m_attack ^= m_env_step_mask;
+
+ m_env_step &= m_env_step_mask;
+ }
+ }
+
+ }
+ }
+ m_env_volume = (m_env_step ^ m_attack);
+
+ if (m_streams == 3)
+ {
+ for (chan = 0; chan < AY8910_NUM_CHANNELS; chan++)
+ if (TONE_ENVELOPE(chan) != 0)
+ {
+ if (type() == AY8914) // AY8914 Has a two bit tone_envelope field
+ {
+ *(buf[chan]++) = m_env_table[chan][m_vol_enabled[chan] ? m_env_volume >> (3-TONE_ENVELOPE(chan)) : 0];
+ }
+ else
+ {
+ *(buf[chan]++) = m_env_table[chan][m_vol_enabled[chan] ? m_env_volume : 0];
+ }
+ }
+ else
+ {
+ *(buf[chan]++) = m_vol_table[chan][m_vol_enabled[chan] ? TONE_VOLUME(chan) : 0];
+ }
+ }
+ else
+ {
+ *(buf[0]++) = mix_3D();
+ }
+ samples--;
+ }
+}
+
+void ay8910_device::build_mixer_table()
+{
+ int normalize = 0;
+ int chan;
+
+ if ((m_flags & AY8910_LEGACY_OUTPUT) != 0)
+ {
+ logerror("AY-3-8910/YM2149 using legacy output levels!\n");
+ normalize = 1;
+ }
+
+ if ((m_flags & AY8910_RESISTOR_OUTPUT) != 0)
+ {
+ if (m_type != PSG_TYPE_AY)
+ fatalerror("AY8910_RESISTOR_OUTPUT currently only supported for AY8910 devices.");
+
+ for (chan=0; chan < AY8910_NUM_CHANNELS; chan++)
+ {
+ build_mosfet_resistor_table(ay8910_mosfet_param, m_res_load[chan], m_vol_table[chan]);
+ build_mosfet_resistor_table(ay8910_mosfet_param, m_res_load[chan], m_env_table[chan]);
+ }
+ }
+ else if (m_streams == AY8910_NUM_CHANNELS)
+ {
+ for (chan=0; chan < AY8910_NUM_CHANNELS; chan++)
+ {
+ build_single_table(m_res_load[chan], m_par, normalize, m_vol_table[chan], m_zero_is_off);
+ build_single_table(m_res_load[chan], m_par_env, normalize, m_env_table[chan], 0);
+ }
+ }
+ /*
+ * The previous implementation added all three channels up instead of averaging them.
+ * The factor of 3 will force the same levels if normalizing is used.
+ */
+ else
+ {
+ build_3D_table(m_res_load[0], m_par, m_par_env, normalize, 3, m_zero_is_off, m_vol3d_table);
+ }
+}
+
+void ay8910_device::ay8910_statesave()
+{
+ save_item(NAME(m_register_latch));
+ save_item(NAME(m_regs));
+ save_item(NAME(m_last_enable));
+
+ save_item(NAME(m_count));
+ save_item(NAME(m_count_noise));
+ save_item(NAME(m_count_env));
+
+ save_item(NAME(m_env_volume));
+
+ save_item(NAME(m_output));
+ save_item(NAME(m_prescale_noise));
+
+ save_item(NAME(m_env_step));
+ save_item(NAME(m_hold));
+ save_item(NAME(m_alternate));
+ save_item(NAME(m_attack));
+ save_item(NAME(m_holding));
+ save_item(NAME(m_rng));
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ay8910_device::device_start()
+{
+ int master_clock = clock();
+
+ if (m_ioports < 1 && !(m_port_a_read_cb.isnull() && m_port_a_write_cb.isnull()))
+ fatalerror("Device '%s' is a %s and has no port A!", tag(), name());
+
+ if (m_ioports < 2 && !(m_port_b_read_cb.isnull() && m_port_b_write_cb.isnull()))
+ fatalerror("Device '%s' is a %s and has no port B!", tag(), name());
+
+ m_port_a_read_cb.resolve();
+ m_port_b_read_cb.resolve();
+ m_port_a_write_cb.resolve();
+ m_port_b_write_cb.resolve();
+
+ if ((m_flags & AY8910_SINGLE_OUTPUT) != 0)
+ {
+ logerror("%s device '%s' using single output!\n", name(), tag());
+ m_streams = 1;
+ }
+
+ // FIXME: this doesn't belong here, it should be an input pin exposed via devcb
+ if (type() == YM2149 && (m_flags & YM2149_PIN26_LOW))
+ {
+ master_clock /= 2;
+ }
+
+ build_mixer_table();
+
+ /* The envelope is pacing twice as fast for the YM2149 as for the AY-3-8910, */
+ /* This handled by the step parameter. Consequently we use a divider of 8 here. */
+ m_channel = machine().sound().stream_alloc(*this, 0, m_streams, master_clock / 8);
+
+ ay_set_clock(master_clock);
+ ay8910_statesave();
+}
+
+
+
+void ay8910_device::ay8910_reset_ym()
+{
+ int i;
+
+ m_register_latch = 0;
+ m_rng = 1;
+ m_output[0] = 0;
+ m_output[1] = 0;
+ m_output[2] = 0;
+ m_count[0] = 0;
+ m_count[1] = 0;
+ m_count[2] = 0;
+ m_count_noise = 0;
+ m_count_env = 0;
+ m_prescale_noise = 0;
+ m_last_enable = -1; /* force a write */
+ for (i = 0;i < AY_PORTA;i++)
+ ay8910_write_reg(i,0);
+ m_ready = 1;
+#if ENABLE_REGISTER_TEST
+ ay8910_write_reg(AY_AFINE, 0);
+ ay8910_write_reg(AY_ACOARSE, 1);
+ ay8910_write_reg(AY_BFINE, 0);
+ ay8910_write_reg(AY_BCOARSE, 2);
+ ay8910_write_reg(AY_CFINE, 0);
+ ay8910_write_reg(AY_CCOARSE, 4);
+ //#define AY_NOISEPER (6)
+ ay8910_write_reg(AY_ENABLE, ~7);
+ ay8910_write_reg(AY_AVOL, 10);
+ ay8910_write_reg(AY_BVOL, 10);
+ ay8910_write_reg(AY_CVOL, 10);
+ //#define AY_EFINE (11)
+ //#define AY_ECOARSE (12)
+ //#define AY_ESHAPE (13)
+#endif
+}
+
+void ay8910_device::set_volume(int channel,int volume)
+{
+ int ch;
+
+ for (ch = 0; ch < m_streams; ch++)
+ if (channel == ch || m_streams == 1 || channel == ALL_8910_CHANNELS)
+ m_channel->set_output_gain(ch, volume / 100.0);
+}
+
+void ay8910_device::ay_set_clock(int clock)
+{
+ m_channel->set_sample_rate( clock / 8 );
+}
+
+void ay8910_device::ay8910_write_ym(int addr, int data)
+{
+ if (addr & 1)
+ { /* Data port */
+ int r = m_register_latch;
+
+ if (r > 15) return;
+ if (r == AY_ESHAPE || m_regs[r] != data)
+ {
+ /* update the output buffer before changing the register */
+ m_channel->update();
+ }
+
+ ay8910_write_reg(r,data);
+ }
+ else
+ { /* Register port */
+ m_register_latch = data & 0x0f;
+ }
+}
+
+int ay8910_device::ay8910_read_ym()
+{
+ device_type chip_type = type();
+ int r = m_register_latch;
+
+ if (r > 15) return 0;
+
+ /* There are no state dependent register in the AY8910! */
+ /* m_channel->update(); */
+
+ switch (r)
+ {
+ case AY_PORTA:
+ if ((m_regs[AY_ENABLE] & 0x40) != 0)
+ logerror("warning: read from 8910 '%s' Port A set as output\n",tag());
+ /*
+ even if the port is set as output, we still need to return the external
+ data. Some games, like kidniki, need this to work.
+
+ FIXME: The io ports are designed as open collector outputs. Bits 7 and 8 of AY_ENABLE
+ only enable (low) or disable (high) the pull up resistors. The YM2149 datasheet
+ specifies those pull up resistors as 60k to 600k (min / max).
+ We do need a callback for those two flags. Kid Niki (Irem m62) is one such
+ case were it makes a difference in comparison to a standard TTL output.
+ */
+ if (!m_port_a_read_cb.isnull())
+ m_regs[AY_PORTA] = m_port_a_read_cb(0);
+ else
+ logerror("%s: warning - read 8910 '%s' Port A\n",machine().describe_context(),tag());
+ break;
+ case AY_PORTB:
+ if ((m_regs[AY_ENABLE] & 0x80) != 0)
+ logerror("warning: read from 8910 '%s' Port B set as output\n",tag());
+ if (!m_port_b_read_cb.isnull())
+ m_regs[AY_PORTB] = m_port_b_read_cb(0);
+ else
+ logerror("%s: warning - read 8910 '%s' Port B\n",machine().describe_context(),tag());
+ break;
+ }
+
+ /* Depending on chip type, unused bits in registers may or may not be accessible.
+ Untested chips are assumed to regard them as 'ram'
+ Tested and confirmed on hardware:
+ - AY-3-8910: inaccessible bits (see masks below) read back as 0
+ - YM2149: no anomaly
+ */
+ if (chip_type == AY8910) {
+ const UINT8 mask[0x10]={
+ 0xff,0x0f,0xff,0x0f,0xff,0x0f,0x1f,0xff,0x1f,0x1f,0x1f,0xff,0xff,0x0f,0xff,0xff
+ };
+
+ return m_regs[r] & mask[r];
+ }
+ else return m_regs[r];
+}
+
+/*************************************
+ *
+ * Sound Interface
+ *
+ *************************************/
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ay8910_device::device_reset()
+{
+ ay8910_reset_ym();
+}
+
+/*************************************
+ *
+ * Read/Write Handlers
+ *
+ *************************************/
+
+READ8_MEMBER( ay8910_device::data_r )
+{
+ return ay8910_read_ym();
+}
+
+WRITE8_MEMBER( ay8910_device::data_address_w )
+{
+ /* note that directly connecting BC1 to A0 puts data on 0 and address on 1 */
+ ay8910_write_ym(~offset & 1, data);
+}
+
+WRITE8_MEMBER( ay8910_device::address_data_w )
+{
+ ay8910_write_ym(offset & 1, data);
+}
+
+WRITE8_MEMBER( ay8910_device::address_w )
+{
+#if ENABLE_REGISTER_TEST
+ return;
+#else
+ data_address_w(space, 1, data);
+#endif
+}
+
+WRITE8_MEMBER( ay8910_device::data_w )
+{
+#if ENABLE_REGISTER_TEST
+ return;
+#else
+ data_address_w(space, 0, data);
+#endif
+}
+
+WRITE8_MEMBER( ay8910_device::reset_w )
+{
+ ay8910_reset_ym();
+}
+
+static const int mapping8914to8910[16] = { 0, 2, 4, 11, 1, 3, 5, 12, 7, 6, 13, 8, 9, 10, 14, 15 };
+
+READ8_MEMBER( ay8914_device::read )
+{
+ UINT16 rv;
+ address_w(space, 0, mapping8914to8910[offset & 0xf]);
+ rv = (UINT16) data_r(space, 0);
+ return rv;
+}
+
+WRITE8_MEMBER( ay8914_device::write )
+{
+ address_w(space, 0, mapping8914to8910[offset & 0xf]);
+ data_w(space, 0, data & 0xff);
+}
+
+
+
+const device_type AY8910 = &device_creator<ay8910_device>;
+
+ay8910_device::ay8910_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, AY8910, "AY-3-8910A", tag, owner, clock, "ay8910", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_type(PSG_TYPE_AY),
+ m_streams(3),
+ m_ioports(2),
+ m_ready(0),
+ m_channel(NULL),
+ m_register_latch(0),
+ m_last_enable(0),
+ m_prescale_noise(0),
+ m_count_noise(0),
+ m_count_env(0),
+ m_env_step(0),
+ m_env_volume(0),
+ m_hold(0),
+ m_alternate(0),
+ m_attack(0),
+ m_holding(0),
+ m_rng(0),
+ m_env_step_mask(0x0f),
+ m_step(2),
+ m_zero_is_off(1),
+ m_par(&ay8910_param),
+ m_par_env(&ay8910_param),
+ m_flags(AY8910_LEGACY_OUTPUT),
+ m_port_a_read_cb(*this),
+ m_port_b_read_cb(*this),
+ m_port_a_write_cb(*this),
+ m_port_b_write_cb(*this)
+{
+ memset(&m_regs,0,sizeof(m_regs));
+ memset(&m_count,0,sizeof(m_count));
+ memset(&m_output,0,sizeof(m_output));
+ memset(&m_vol_enabled,0,sizeof(m_vol_enabled));
+ memset(&m_vol_table,0,sizeof(m_vol_table));
+ memset(&m_env_table,0,sizeof(m_env_table));
+ memset(&m_vol3d_table,0,sizeof(m_vol3d_table));
+ m_res_load[0] = m_res_load[1] = m_res_load[2] = 1000; //Default values for resistor loads
+}
+
+ay8910_device::ay8910_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock,
+ psg_type_t psg_type, int streams, int ioports, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_sound_interface(mconfig, *this),
+ m_type(psg_type),
+ m_streams(streams),
+ m_ioports(ioports),
+ m_ready(0),
+ m_channel(NULL),
+ m_register_latch(0),
+ m_last_enable(0),
+ m_prescale_noise(0),
+ m_count_noise(0),
+ m_count_env(0),
+ m_env_step(0),
+ m_env_volume(0),
+ m_hold(0),
+ m_alternate(0),
+ m_attack(0),
+ m_holding(0),
+ m_rng(0),
+ m_env_step_mask(psg_type == PSG_TYPE_AY ? 0x0f : 0x1f),
+ m_step( psg_type == PSG_TYPE_AY ? 2 : 1),
+ m_zero_is_off( psg_type == PSG_TYPE_AY ? 1 : 0),
+ m_par( psg_type == PSG_TYPE_AY ? &ay8910_param : &ym2149_param),
+ m_par_env( psg_type == PSG_TYPE_AY ? &ay8910_param : &ym2149_param_env),
+ m_flags(AY8910_LEGACY_OUTPUT),
+ m_port_a_read_cb(*this),
+ m_port_b_read_cb(*this),
+ m_port_a_write_cb(*this),
+ m_port_b_write_cb(*this)
+{
+ memset(&m_regs,0,sizeof(m_regs));
+ memset(&m_count,0,sizeof(m_count));
+ memset(&m_output,0,sizeof(m_output));
+ memset(&m_vol_enabled,0,sizeof(m_vol_enabled));
+ memset(&m_vol_table,0,sizeof(m_vol_table));
+ memset(&m_env_table,0,sizeof(m_env_table));
+ memset(&m_vol3d_table,0,sizeof(m_vol3d_table));
+ m_res_load[0] = m_res_load[1] = m_res_load[2] = 1000; //Default values for resistor loads
+}
+
+const device_type AY8912 = &device_creator<ay8912_device>;
+
+ay8912_device::ay8912_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ay8910_device(mconfig, AY8912, "AY-3-8912A", tag, owner, clock, PSG_TYPE_AY, 3, 1, "ay8912", __FILE__)
+{
+}
+
+
+const device_type AY8913 = &device_creator<ay8913_device>;
+
+ay8913_device::ay8913_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ay8910_device(mconfig, AY8913, "AY-3-8913A", tag, owner, clock, PSG_TYPE_AY, 3, 0, "ay8913", __FILE__)
+{
+}
+
+
+const device_type AY8914 = &device_creator<ay8914_device>;
+
+ay8914_device::ay8914_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ay8910_device(mconfig, AY8914, "AY-3-8914", tag, owner, clock, PSG_TYPE_AY, 3, 2, "ay8914", __FILE__)
+{
+}
+
+
+const device_type AY8930 = &device_creator<ay8930_device>;
+
+ay8930_device::ay8930_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ay8910_device(mconfig, AY8930, "AY8930", tag, owner, clock, PSG_TYPE_AY, 3, 2, "ay8930", __FILE__)
+{
+}
+
+
+const device_type YM2149 = &device_creator<ym2149_device>;
+
+ym2149_device::ym2149_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ay8910_device(mconfig, YM2149, "YM2149", tag, owner, clock, PSG_TYPE_YM, 3, 2, "ym2149", __FILE__)
+{
+}
+
+
+const device_type YM3439 = &device_creator<ym3439_device>;
+
+ym3439_device::ym3439_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ay8910_device(mconfig, YM3439, "YM3439", tag, owner, clock, PSG_TYPE_YM, 3, 2, "ym3429", __FILE__)
+{
+}
+
+
+const device_type YMZ284 = &device_creator<ymz284_device>;
+
+ymz284_device::ymz284_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ay8910_device(mconfig, YMZ284, "YMZ284", tag, owner, clock, PSG_TYPE_YM, 1, 0, "ymz284", __FILE__)
+{
+}
+
+
+const device_type YMZ294 = &device_creator<ymz294_device>;
+
+ymz294_device::ymz294_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : ay8910_device(mconfig, YMZ294, "YMZ294", tag, owner, clock, PSG_TYPE_YM, 1, 0, "ymz294", __FILE__)
+{
+}
diff --git a/src/devices/sound/ay8910.h b/src/devices/sound/ay8910.h
new file mode 100644
index 00000000000..30ef634b766
--- /dev/null
+++ b/src/devices/sound/ay8910.h
@@ -0,0 +1,273 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+#pragma once
+
+#ifndef __AY8910_H__
+#define __AY8910_H__
+
+#include "emu.h"
+
+/*
+AY-3-8910A: 2 I/O ports
+AY-3-8912A: 1 I/O port
+AY-3-8913A: 0 I/O port
+AY-3-8914: same as 8910 except for different register mapping and two bit envelope enable / volume field
+AY8930: upper compatible with 8910.
+In extended mode, it has higher resolution and duty ratio setting
+YM2149: higher resolution, selectable clock divider
+YM3439: same as 2149
+YMZ284: 0 I/O port, different clock divider
+YMZ294: 0 I/O port
+*/
+
+#define ALL_8910_CHANNELS -1
+
+/* Internal resistance at Volume level 7. */
+
+#define AY8910_INTERNAL_RESISTANCE (356)
+#define YM2149_INTERNAL_RESISTANCE (353)
+
+/*
+ * The following is used by all drivers not reviewed yet.
+ * This will like the old behaviour, output between
+ * 0 and 7FFF
+ */
+#define AY8910_LEGACY_OUTPUT (0x01)
+
+/*
+ * Specifing the next define will simulate the special
+ * cross channel mixing if outputs are tied together.
+ * The driver will only provide one stream in this case.
+ */
+#define AY8910_SINGLE_OUTPUT (0x02)
+
+/*
+ * The following define is the default behaviour.
+ * Output level 0 is 0V and 7ffff corresponds to 5V.
+ * Use this to specify that a discrete mixing stage
+ * follows.
+ */
+#define AY8910_DISCRETE_OUTPUT (0x04)
+
+/*
+ * The following define causes the driver to output
+ * resistor values. Intended to be used for
+ * netlist interfacing.
+ */
+
+#define AY8910_RESISTOR_OUTPUT (0x08)
+
+/*
+ * This define specifies the initial state of YM2149
+ * pin 26 (SEL pin). By default it is set to high,
+ * compatible with AY8910.
+ */
+/* TODO: make it controllable while it's running (used by any hw???) */
+#define YM2149_PIN26_HIGH (0x00) /* or N/C */
+#define YM2149_PIN26_LOW (0x10)
+
+
+#define AY8910_NUM_CHANNELS 3
+
+
+#define MCFG_AY8910_OUTPUT_TYPE(_flag) \
+ ay8910_device::set_flags(*device, _flag);
+
+#define MCFG_AY8910_RES_LOADS(_res0, _res1, _res2) \
+ ay8910_device::set_resistors_load(*device, _res0, _res1, _res2);
+
+#define MCFG_AY8910_PORT_A_READ_CB(_devcb) \
+ devcb = &ay8910_device::set_port_a_read_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_AY8910_PORT_B_READ_CB(_devcb) \
+ devcb = &ay8910_device::set_port_b_read_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_AY8910_PORT_A_WRITE_CB(_devcb) \
+ devcb = &ay8910_device::set_port_a_write_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_AY8910_PORT_B_WRITE_CB(_devcb) \
+ devcb = &ay8910_device::set_port_b_write_callback(*device, DEVCB_##_devcb);
+
+
+class ay8910_device : public device_t,
+ public device_sound_interface
+{
+public:
+ enum psg_type_t
+ {
+ PSG_TYPE_AY,
+ PSG_TYPE_YM
+ };
+
+ // construction/destruction
+ ay8910_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ay8910_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner,
+ UINT32 clock, psg_type_t psg_type, int streams, int ioports, const char *shortname, const char *source);
+
+ // static configuration helpers
+ static void set_flags(device_t &device, int flags) { downcast<ay8910_device &>(device).m_flags = flags; }
+ static void set_resistors_load(device_t &device, int res_load0, int res_load1, int res_load2) { downcast<ay8910_device &>(device).m_res_load[0] = res_load0; downcast<ay8910_device &>(device).m_res_load[1] = res_load1; downcast<ay8910_device &>(device).m_res_load[2] = res_load2; }
+ template<class _Object> static devcb_base &set_port_a_read_callback(device_t &device, _Object object) { return downcast<ay8910_device &>(device).m_port_a_read_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_port_b_read_callback(device_t &device, _Object object) { return downcast<ay8910_device &>(device).m_port_b_read_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_port_a_write_callback(device_t &device, _Object object) { return downcast<ay8910_device &>(device).m_port_a_write_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_port_b_write_callback(device_t &device, _Object object) { return downcast<ay8910_device &>(device).m_port_b_write_cb.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( data_r );
+ DECLARE_WRITE8_MEMBER( address_w );
+ DECLARE_WRITE8_MEMBER( data_w );
+
+ /* /RES */
+ DECLARE_WRITE8_MEMBER( reset_w );
+
+ /* use this when BC1 == A0; here, BC1=0 selects 'data' and BC1=1 selects 'latch address' */
+ DECLARE_WRITE8_MEMBER( data_address_w );
+
+ /* use this when BC1 == !A0; here, BC1=0 selects 'latch address' and BC1=1 selects 'data' */
+ DECLARE_WRITE8_MEMBER( address_data_w );
+
+ void set_volume(int channel,int volume);
+ void ay_set_clock(int clock);
+
+ struct ay_ym_param
+ {
+ double r_up;
+ double r_down;
+ int res_count;
+ double res[32];
+ };
+
+ struct mosfet_param
+ {
+ double m_Vth;
+ double m_Vg;
+ int m_count;
+ double m_Kn[32];
+ };
+
+ void ay8910_write_ym(int addr, int data);
+ int ay8910_read_ym();
+ void ay8910_reset_ym();
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ // internal helpers
+ inline UINT16 mix_3D();
+ void ay8910_write_reg(int r, int v);
+ void build_mixer_table();
+ void ay8910_statesave();
+
+ // internal state
+ psg_type_t m_type;
+ int m_streams;
+ int m_ioports;
+ int m_ready;
+ sound_stream *m_channel;
+ INT32 m_register_latch;
+ UINT8 m_regs[16];
+ INT32 m_last_enable;
+ INT32 m_count[AY8910_NUM_CHANNELS];
+ UINT8 m_output[AY8910_NUM_CHANNELS];
+ UINT8 m_prescale_noise;
+ INT32 m_count_noise;
+ INT32 m_count_env;
+ INT8 m_env_step;
+ UINT32 m_env_volume;
+ UINT8 m_hold,m_alternate,m_attack,m_holding;
+ INT32 m_rng;
+ UINT8 m_env_step_mask;
+ /* init parameters ... */
+ int m_step;
+ int m_zero_is_off;
+ UINT8 m_vol_enabled[AY8910_NUM_CHANNELS];
+ const ay_ym_param *m_par;
+ const ay_ym_param *m_par_env;
+ INT32 m_vol_table[AY8910_NUM_CHANNELS][16];
+ INT32 m_env_table[AY8910_NUM_CHANNELS][32];
+ INT32 m_vol3d_table[8*32*32*32];
+ int m_flags; /* Flags */
+ int m_res_load[3]; /* Load on channel in ohms */
+ devcb_read8 m_port_a_read_cb;
+ devcb_read8 m_port_b_read_cb;
+ devcb_write8 m_port_a_write_cb;
+ devcb_write8 m_port_b_write_cb;
+};
+
+extern const device_type AY8910;
+
+class ay8912_device : public ay8910_device
+{
+public:
+ ay8912_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+extern const device_type AY8912;
+
+class ay8913_device : public ay8910_device
+{
+public:
+ ay8913_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+extern const device_type AY8913;
+
+class ay8914_device : public ay8910_device
+{
+public:
+ ay8914_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ /* AY8914 handlers needed due to different register map */
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+};
+
+extern const device_type AY8914;
+
+class ay8930_device : public ay8910_device
+{
+public:
+ ay8930_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+extern const device_type AY8930;
+
+class ym2149_device : public ay8910_device
+{
+public:
+ ym2149_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+extern const device_type YM2149;
+
+class ym3439_device : public ay8910_device
+{
+public:
+ ym3439_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+extern const device_type YM3439;
+
+class ymz284_device : public ay8910_device
+{
+public:
+ ymz284_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+extern const device_type YMZ284;
+
+class ymz294_device : public ay8910_device
+{
+public:
+ ymz294_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+extern const device_type YMZ294;
+
+
+#endif /* __AY8910_H__ */
diff --git a/src/devices/sound/beep.c b/src/devices/sound/beep.c
new file mode 100644
index 00000000000..ce5558f3320
--- /dev/null
+++ b/src/devices/sound/beep.c
@@ -0,0 +1,147 @@
+// license:BSD-3-Clause
+// copyright-holders:Kevin Thacker
+/***************************************************************************
+
+ beep.c
+
+ This is used for computers/systems which can only output a constant tone.
+ This tone can be turned on and off.
+ e.g. PCW and PCW16 computer systems
+ KT - 25-Jun-2000
+
+ Sound handler
+
+****************************************************************************/
+
+#include "emu.h"
+#include "sound/beep.h"
+
+#define BEEP_RATE (48000)
+
+
+// device type definition
+const device_type BEEP = &device_creator<beep_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// beep_device - constructor
+//-------------------------------------------------
+
+beep_device::beep_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, BEEP, "Beep", tag, owner, clock, "beep", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_enable(0),
+ m_frequency(0),
+ m_incr(0),
+ m_signal(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void beep_device::device_start()
+{
+ m_stream = stream_alloc(0, 1, BEEP_RATE);
+ m_enable = 0;
+ m_frequency = 3250;
+ m_incr = 0;
+ m_signal = 0x07fff;
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void beep_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *buffer = outputs[0];
+ INT16 signal = m_signal;
+ int clock = 0, rate = BEEP_RATE / 2;
+
+ /* get progress through wave */
+ int incr = m_incr;
+
+ if (m_frequency > 0)
+ clock = m_frequency;
+
+ /* if we're not enabled, just fill with 0 */
+ if ( !m_enable || clock == 0 )
+ {
+ memset( buffer, 0, samples * sizeof(*buffer) );
+ return;
+ }
+
+ /* fill in the sample */
+ while( samples-- > 0 )
+ {
+ *buffer++ = signal;
+ incr -= clock;
+ while( incr < 0 )
+ {
+ incr += rate;
+ signal = -signal;
+ }
+ }
+
+ /* store progress through wave */
+ m_incr = incr;
+ m_signal = signal;
+}
+
+
+//-------------------------------------------------
+// changing state to on from off will restart tone
+//-------------------------------------------------
+
+void beep_device::set_state(int on)
+{
+ /* only update if new state is not the same as old state */
+ if (m_enable == on)
+ return;
+
+ m_stream->update();
+ m_enable = on;
+
+ /* restart wave from beginning */
+ m_incr = 0;
+ m_signal = 0x07fff;
+}
+
+
+
+//-------------------------------------------------
+// setting new frequency starts from beginning
+//-------------------------------------------------
+
+void beep_device::set_frequency(int frequency)
+{
+ if (m_frequency == frequency)
+ return;
+
+ m_stream->update();
+ m_frequency = frequency;
+ m_signal = 0x07fff;
+ m_incr = 0;
+}
+
+
+
+//-------------------------------------------------
+// change a channel volume
+//-------------------------------------------------
+
+void beep_device::set_volume(int volume)
+{
+ m_stream->update();
+ volume = 100 * volume / 7;
+ set_output_gain(0, volume);
+}
diff --git a/src/devices/sound/beep.h b/src/devices/sound/beep.h
new file mode 100644
index 00000000000..4b587bd3c89
--- /dev/null
+++ b/src/devices/sound/beep.h
@@ -0,0 +1,44 @@
+// license:BSD-3-Clause
+// copyright-holders:Kevin Thacker
+#pragma once
+
+#ifndef __BEEP_H__
+#define __BEEP_H__
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> beep_device
+
+class beep_device : public device_t,
+ public device_sound_interface
+{
+public:
+ beep_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~beep_device() { }
+
+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);
+
+public:
+ void set_state(int on);
+ void set_frequency(int frequency);
+ void set_volume(int volume);
+
+private:
+ sound_stream *m_stream; /* stream number */
+ int m_enable; /* enable beep */
+ int m_frequency; /* set frequency - this can be changed using the appropiate function */
+ int m_incr; /* initial wave state */
+ INT16 m_signal; /* current signal */
+};
+
+extern const device_type BEEP;
+
+
+#endif /* __BEEP_H__ */
diff --git a/src/devices/sound/bsmt2000.c b/src/devices/sound/bsmt2000.c
new file mode 100644
index 00000000000..0b1dea55354
--- /dev/null
+++ b/src/devices/sound/bsmt2000.c
@@ -0,0 +1,358 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ bsmt2000.c
+
+ BSMT2000 device emulator.
+
+****************************************************************************
+
+ Chip is actually a TMS320C15 DSP with embedded mask rom
+ Trivia: BSMT stands for "Brian Schmidt's Mouse Trap"
+
+***************************************************************************/
+
+#include "emu.h"
+#include "bsmt2000.h"
+
+
+// device type definition
+const device_type BSMT2000 = &device_creator<bsmt2000_device>;
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// program map for the DSP (points to internal ROM)
+static ADDRESS_MAP_START( tms_program_map, AS_PROGRAM, 16, bsmt2000_device)
+ ADDRESS_MAP_UNMAP_HIGH
+ AM_RANGE(0x000, 0xfff) AM_ROM
+ADDRESS_MAP_END
+
+
+// I/O map for the DSP
+static ADDRESS_MAP_START( tms_io_map, AS_IO, 16, bsmt2000_device)
+ AM_RANGE(0, 0) AM_READWRITE(tms_register_r, tms_rom_addr_w)
+ AM_RANGE(1, 1) AM_READWRITE(tms_data_r, tms_rom_bank_w)
+ AM_RANGE(2, 2) AM_READ(tms_rom_r)
+ AM_RANGE(3, 3) AM_WRITE(tms_left_w)
+ AM_RANGE(7, 7) AM_WRITE(tms_right_w)
+ AM_RANGE(TMS32010_BIO, TMS32010_BIO) AM_READ(tms_write_pending_r)
+ADDRESS_MAP_END
+
+
+// machine fragment
+static MACHINE_CONFIG_FRAGMENT( bsmt2000 )
+ MCFG_CPU_ADD("bsmt2000", TMS32015, DERIVED_CLOCK(1,1))
+ MCFG_CPU_PROGRAM_MAP(tms_program_map)
+ // data map is internal to the CPU
+ MCFG_CPU_IO_MAP(tms_io_map)
+MACHINE_CONFIG_END
+
+
+// default address map for the external memory interface
+// the BSMT can address a full 32 bits but typically only 24 are used
+static ADDRESS_MAP_START( bsmt2000, AS_0, 8, bsmt2000_device)
+ AM_RANGE(0x00000, 0xffffff) AM_ROM
+ADDRESS_MAP_END
+
+
+// ROM definition for the BSMT2000 program ROM
+ROM_START( bsmt2000 )
+ ROM_REGION( 0x2000, "bsmt2000", 0 )
+ ROM_LOAD16_WORD( "bsmt2000.bin", 0x0000, 0x2000, CRC(c2a265af) SHA1(6ec9eb038fb8eb842c5482aebe1d149daf49f2e6) )
+ROM_END
+
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// bsmt2000_device - constructor
+//-------------------------------------------------
+
+bsmt2000_device::bsmt2000_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, BSMT2000, "BSMT2000", tag, owner, clock, "bsmt2000", __FILE__),
+ device_sound_interface(mconfig, *this),
+ device_memory_interface(mconfig, *this),
+ m_space_config("samples", ENDIANNESS_LITTLE, 8, 32, 0, NULL),
+ m_ready_callback(NULL),
+ m_stream(NULL),
+ m_direct(NULL),
+ m_cpu(NULL),
+ m_register_select(0),
+ m_write_data(0),
+ m_rom_address(0),
+ m_rom_bank(0),
+ m_left_data(0),
+ m_right_data(0),
+ m_write_pending(false)
+{
+ m_address_map[0] = *ADDRESS_MAP_NAME(bsmt2000);
+}
+
+
+//-------------------------------------------------
+// static_set_ready_callback - configuration
+// helper to set the ready callback
+//-------------------------------------------------
+
+void bsmt2000_device::static_set_ready_callback(device_t &device, ready_callback callback)
+{
+ bsmt2000_device &bsmt = downcast<bsmt2000_device &>(device);
+ bsmt.m_ready_callback = callback;
+}
+
+
+//-------------------------------------------------
+// rom_region - return a pointer to the device's
+// internal ROM region
+//-------------------------------------------------
+
+const rom_entry *bsmt2000_device::device_rom_region() const
+{
+ return ROM_NAME( bsmt2000 );
+}
+
+
+//-------------------------------------------------
+// machine_config_additions - return a pointer to
+// the device's machine fragment
+//-------------------------------------------------
+
+machine_config_constructor bsmt2000_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( bsmt2000 );
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void bsmt2000_device::device_start()
+{
+ // find our CPU
+ m_cpu = subdevice<tms32015_device>("bsmt2000");
+
+ // find our direct access
+ m_direct = &space().direct();
+
+ // create the stream; BSMT typically runs at 24MHz and writes to a DAC, so
+ // in theory we should generate a 24MHz stream, but that's certainly overkill
+ // internally at 24MHz the max output sample rate is 32kHz
+ // divided by 128 gives us 6x the max output rate which is plenty for oversampling
+ m_stream = stream_alloc(0, 2, clock() / 128);
+
+ // register for save states
+ save_item(NAME(m_register_select));
+ save_item(NAME(m_write_data));
+ save_item(NAME(m_rom_address));
+ save_item(NAME(m_rom_bank));
+ save_item(NAME(m_left_data));
+ save_item(NAME(m_right_data));
+ save_item(NAME(m_write_pending));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void bsmt2000_device::device_reset()
+{
+ synchronize(TIMER_ID_RESET);
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *bsmt2000_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == 0) ? &m_space_config : NULL;
+}
+
+
+//-------------------------------------------------
+// device_timer - handle deferred writes and
+// resets as a timer callback
+//-------------------------------------------------
+
+void bsmt2000_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ // deferred reset
+ case TIMER_ID_RESET:
+ m_stream->update();
+ m_cpu->reset();
+ break;
+
+ // deferred register write
+ case TIMER_ID_REG_WRITE:
+ m_register_select = param & 0xffff;
+ break;
+
+ // deferred data write
+ case TIMER_ID_DATA_WRITE:
+ m_write_data = param & 0xffff;
+ if (m_write_pending) logerror("BSMT2000: Missed data\n");
+ m_write_pending = true;
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests
+// for our sound stream
+//-------------------------------------------------
+
+void bsmt2000_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ // just fill with current left/right values
+ for (int samp = 0; samp < samples; samp++)
+ {
+ outputs[0][samp] = m_left_data * 16;
+ outputs[1][samp] = m_right_data * 16;
+ }
+}
+
+
+//-------------------------------------------------
+// read_status - return the write pending status
+//-------------------------------------------------
+
+UINT16 bsmt2000_device::read_status()
+{
+ return m_write_pending ? 0 : 1;
+}
+
+
+//-------------------------------------------------
+// write_reg - handle writes to the BSMT2000
+// register select interface
+//-------------------------------------------------
+
+void bsmt2000_device::write_reg(UINT16 data)
+{
+ synchronize(TIMER_ID_REG_WRITE, data);
+}
+
+
+//-------------------------------------------------
+// write_data - handle writes to the BSMT2000
+// data port
+//-------------------------------------------------
+
+void bsmt2000_device::write_data(UINT16 data)
+{
+ synchronize(TIMER_ID_DATA_WRITE, data);
+
+ // boost the interleave on a write so that the caller detects the status more accurately
+ machine().scheduler().boost_interleave(attotime::from_usec(1), attotime::from_usec(10));
+}
+
+
+//-------------------------------------------------
+// tms_register_r - return the value written to
+// the register select port
+//-------------------------------------------------
+
+READ16_MEMBER( bsmt2000_device::tms_register_r )
+{
+ return m_register_select;
+}
+
+
+//-------------------------------------------------
+// tms_data_r - return the value written to the
+// data port
+//-------------------------------------------------
+
+READ16_MEMBER( bsmt2000_device::tms_data_r )
+{
+ // also implicitly clear the write pending flag
+ m_write_pending = false;
+ if (m_ready_callback != NULL)
+ (*m_ready_callback)(*this);
+ return m_write_data;
+}
+
+
+//-------------------------------------------------
+// tms_rom_r - read a byte from the currently
+// selected ROM bank and address
+//-------------------------------------------------
+
+READ16_MEMBER( bsmt2000_device::tms_rom_r )
+{
+ // underlying logic assumes this is a sign-extended value
+ return (INT8)m_direct->read_byte((m_rom_bank << 16) + m_rom_address);
+}
+
+
+//-------------------------------------------------
+// tms_rom_addr_w - selects which byte within the
+// current ROM bank to access
+//-------------------------------------------------
+
+WRITE16_MEMBER( bsmt2000_device::tms_rom_addr_w )
+{
+ m_rom_address = data;
+}
+
+
+//-------------------------------------------------
+// tms_rom_bank_w - selects which bank of ROM to
+// access
+//-------------------------------------------------
+
+WRITE16_MEMBER( bsmt2000_device::tms_rom_bank_w )
+{
+ m_rom_bank = data;
+}
+
+
+//-------------------------------------------------
+// tms_left_w - handle writes to the left channel
+// DAC
+//-------------------------------------------------
+
+WRITE16_MEMBER( bsmt2000_device::tms_left_w )
+{
+ m_stream->update();
+ m_left_data = data;
+}
+
+
+//-------------------------------------------------
+// tms_right_w - handle writes to the right
+// channel DAC
+//-------------------------------------------------
+
+WRITE16_MEMBER( bsmt2000_device::tms_right_w )
+{
+ m_stream->update();
+ m_right_data = data;
+}
+
+
+//-------------------------------------------------
+// tms_write_pending_r - return whether a write
+// is pending; this data is fed into the BIO line
+// on the TMS32015
+//-------------------------------------------------
+
+READ16_MEMBER( bsmt2000_device::tms_write_pending_r )
+{
+ return m_write_pending ? 1 : 0;
+}
diff --git a/src/devices/sound/bsmt2000.h b/src/devices/sound/bsmt2000.h
new file mode 100644
index 00000000000..fb993e982e0
--- /dev/null
+++ b/src/devices/sound/bsmt2000.h
@@ -0,0 +1,112 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ bsmt2000.h
+
+ BSMT2000 device emulator.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __BSMT2000_H__
+#define __BSMT2000_H__
+
+#include "cpu/tms32010/tms32010.h"
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_BSMT2000_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, BSMT2000, _clock)
+#define MCFG_BSMT2000_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, BSMT2000, _clock)
+#define MCFG_BSMT2000_READY_CALLBACK(_callback) \
+ bsmt2000_device::static_set_ready_callback(*device, _callback);
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+
+// ======================> bsmt2000_device
+
+class bsmt2000_device : public device_t,
+ public device_sound_interface,
+ public device_memory_interface
+{
+ typedef void (*ready_callback)(bsmt2000_device &device);
+
+public:
+ // construction/destruction
+ bsmt2000_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // inline configuration helpers
+ static void static_set_ready_callback(device_t &device, ready_callback callback);
+
+ // public interface
+ UINT16 read_status();
+ void write_reg(UINT16 data);
+ void write_data(UINT16 data);
+
+protected:
+ // device-level overrides
+ virtual const rom_entry *device_rom_region() const;
+ virtual machine_config_constructor device_mconfig_additions() const;
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+public:
+ // internal TMS I/O callbacks
+ DECLARE_READ16_MEMBER( tms_register_r );
+ DECLARE_READ16_MEMBER( tms_data_r );
+ DECLARE_READ16_MEMBER( tms_rom_r );
+ DECLARE_WRITE16_MEMBER( tms_rom_addr_w );
+ DECLARE_WRITE16_MEMBER( tms_rom_bank_w );
+ DECLARE_WRITE16_MEMBER( tms_left_w );
+ DECLARE_WRITE16_MEMBER( tms_right_w );
+ DECLARE_READ16_MEMBER( tms_write_pending_r );
+
+private:
+ // timers
+ enum
+ {
+ TIMER_ID_RESET,
+ TIMER_ID_REG_WRITE,
+ TIMER_ID_DATA_WRITE
+ };
+
+ // configuration state
+ const address_space_config m_space_config;
+ ready_callback m_ready_callback;
+
+ // internal state
+ sound_stream * m_stream;
+ direct_read_data * m_direct;
+ tms32015_device * m_cpu;
+ UINT16 m_register_select;
+ UINT16 m_write_data;
+ UINT16 m_rom_address;
+ UINT16 m_rom_bank;
+ INT16 m_left_data;
+ INT16 m_right_data;
+ bool m_write_pending;
+};
+
+
+// device type definition
+extern const device_type BSMT2000;
+
+
+#endif /* __BSMT2000_H__ */
diff --git a/src/devices/sound/c140.c b/src/devices/sound/c140.c
new file mode 100644
index 00000000000..6649f53616a
--- /dev/null
+++ b/src/devices/sound/c140.c
@@ -0,0 +1,489 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+/*
+C140.c
+
+Simulator based on AMUSE sources.
+The C140 sound chip is used by Namco System 2 and System 21
+The 219 ASIC (which incorporates a modified C140) is used by Namco NA-1 and NA-2
+This chip controls 24 channels (C140) or 16 (219) of PCM.
+16 bytes are associated with each channel.
+Channels can be 8 bit signed PCM, or 12 bit signed PCM.
+
+Timer behavior is not yet handled.
+
+Unmapped registers:
+ 0x1f8:timer interval? (Nx0.1 ms)
+ 0x1fa:irq ack? timer restart?
+ 0x1fe:timer switch?(0:off 1:on)
+
+--------------
+
+ ASIC "219" notes
+
+ On the 219 ASIC used on NA-1 and NA-2, the high registers have the following
+ meaning instead:
+ 0x1f7: bank for voices 0-3
+ 0x1f1: bank for voices 4-7
+ 0x1f3: bank for voices 8-11
+ 0x1f5: bank for voices 12-15
+
+ Some games (bkrtmaq, xday2) write to 0x1fd for voices 12-15 instead. Probably the bank registers
+ mirror at 1f8, in which case 1ff is also 0-3, 1f9 is also 4-7, 1fb is also 8-11, and 1fd is also 12-15.
+
+ Each bank is 0x20000 (128k), and the voice addresses on the 219 are all multiplied by 2.
+ Additionally, the 219's base pitch is the same as the C352's (42667). But these changes
+ are IMO not sufficient to make this a separate file - all the other registers are
+ fully compatible.
+
+ Finally, the 219 only has 16 voices.
+*/
+/*
+ 2000.06.26 CAB fixed compressed pcm playback
+ 2002.07.20 R. Belmont added support for multiple banking types
+ 2006.01.08 R. Belmont added support for NA-1/2 "219" derivative
+*/
+
+
+#include "emu.h"
+#include "c140.h"
+
+struct voice_registers
+{
+ UINT8 volume_right;
+ UINT8 volume_left;
+ UINT8 frequency_msb;
+ UINT8 frequency_lsb;
+ UINT8 bank;
+ UINT8 mode;
+ UINT8 start_msb;
+ UINT8 start_lsb;
+ UINT8 end_msb;
+ UINT8 end_lsb;
+ UINT8 loop_msb;
+ UINT8 loop_lsb;
+ UINT8 reserved[4];
+};
+
+
+// device type definition
+const device_type C140 = &device_creator<c140_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+INLINE int limit(INT32 in)
+{
+ if(in>0x7fff) return 0x7fff;
+ else if(in<-0x8000) return -0x8000;
+ return in;
+}
+
+
+//-------------------------------------------------
+// c140_device - constructor
+//-------------------------------------------------
+
+c140_device::c140_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, C140, "C140", tag, owner, clock, "c140", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_sample_rate(0),
+ m_stream(NULL),
+ m_banking_type(0),
+ m_mixer_buffer_left(NULL),
+ m_mixer_buffer_right(NULL),
+ m_baserate(0),
+ m_pRom(NULL)
+{
+ memset(m_REG, 0, sizeof(UINT8)*0x200);
+ memset(m_pcmtbl, 0, sizeof(INT16)*8);
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void c140_device::device_start()
+{
+ m_sample_rate=m_baserate=clock();
+
+ m_stream = stream_alloc(0, 2, m_sample_rate);
+
+ m_pRom = (INT8 *)region()->base();
+
+ /* make decompress pcm table */ //2000.06.26 CAB
+ {
+ int i;
+ INT32 segbase=0;
+ for(i=0;i<8;i++)
+ {
+ m_pcmtbl[i]=segbase; //segment base value
+ segbase += 16<<i;
+ }
+ }
+
+ memset(m_REG,0,sizeof(m_REG));
+ {
+ int i;
+ for(i=0;i<C140_MAX_VOICE;i++) init_voice( &m_voi[i] );
+ }
+
+ /* allocate a pair of buffers to mix into - 1 second's worth should be more than enough */
+ m_mixer_buffer_left = auto_alloc_array(machine(), INT16, 2 * m_sample_rate);
+ m_mixer_buffer_right = m_mixer_buffer_left + m_sample_rate;
+
+ save_item(NAME(m_REG));
+
+ for (int i = 0; i < C140_MAX_VOICE; i++)
+ {
+ save_item(NAME(m_voi[i].ptoffset), i);
+ save_item(NAME(m_voi[i].pos), i);
+ save_item(NAME(m_voi[i].key), i);
+ save_item(NAME(m_voi[i].lastdt), i);
+ save_item(NAME(m_voi[i].prevdt), i);
+ save_item(NAME(m_voi[i].dltdt), i);
+ save_item(NAME(m_voi[i].rvol), i);
+ save_item(NAME(m_voi[i].lvol), i);
+ save_item(NAME(m_voi[i].frequency), i);
+ save_item(NAME(m_voi[i].bank), i);
+ save_item(NAME(m_voi[i].mode), i);
+ save_item(NAME(m_voi[i].sample_start), i);
+ save_item(NAME(m_voi[i].sample_end), i);
+ save_item(NAME(m_voi[i].sample_loop), i);
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void c140_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int i,j;
+
+ INT32 rvol,lvol;
+ INT32 dt;
+ INT32 sdt;
+ INT32 st,ed,sz;
+
+ INT8 *pSampleData;
+ INT32 frequency,delta,offset,pos;
+ INT32 cnt, voicecnt;
+ INT32 lastdt,prevdt,dltdt;
+ float pbase=(float)m_baserate*2.0f / (float)m_sample_rate;
+
+ INT16 *lmix, *rmix;
+
+ if(samples>m_sample_rate) samples=m_sample_rate;
+
+ /* zap the contents of the mixer buffer */
+ memset(m_mixer_buffer_left, 0, samples * sizeof(INT16));
+ memset(m_mixer_buffer_right, 0, samples * sizeof(INT16));
+
+ /* get the number of voices to update */
+ voicecnt = (m_banking_type == C140_TYPE_ASIC219) ? 16 : 24;
+
+ //--- audio update
+ for( i=0;i<voicecnt;i++ )
+ {
+ C140_VOICE *v = &m_voi[i];
+ const struct voice_registers *vreg = (struct voice_registers *)&m_REG[i*16];
+
+ if( v->key )
+ {
+ frequency= vreg->frequency_msb*256 + vreg->frequency_lsb;
+
+ /* Abort voice if no frequency value set */
+ if(frequency==0) continue;
+
+ /* Delta = frequency * ((8MHz/374)*2 / sample rate) */
+ delta=(long)((float)frequency * pbase);
+
+ /* Calculate left/right channel volumes */
+ lvol=(vreg->volume_left*32)/C140_MAX_VOICE; //32ch -> 24ch
+ rvol=(vreg->volume_right*32)/C140_MAX_VOICE;
+
+ /* Set mixer outputs base pointers */
+ lmix = m_mixer_buffer_left;
+ rmix = m_mixer_buffer_right;
+
+ /* Retrieve sample start/end and calculate size */
+ st=v->sample_start;
+ ed=v->sample_end;
+ sz=ed-st;
+
+ /* Retrieve base pointer to the sample data */
+ pSampleData = m_pRom + find_sample(st, v->bank, i);
+
+ /* Fetch back previous data pointers */
+ offset=v->ptoffset;
+ pos=v->pos;
+ lastdt=v->lastdt;
+ prevdt=v->prevdt;
+ dltdt=v->dltdt;
+
+ /* Switch on data type - compressed PCM is only for C140 */
+ if ((v->mode&8) && (m_banking_type != C140_TYPE_ASIC219))
+ {
+ //compressed PCM (maybe correct...)
+ /* Loop for enough to fill sample buffer as requested */
+ for(j=0;j<samples;j++)
+ {
+ offset += delta;
+ cnt = (offset>>16)&0x7fff;
+ offset &= 0xffff;
+ pos+=cnt;
+ //for(;cnt>0;cnt--)
+ {
+ /* Check for the end of the sample */
+ if(pos >= sz)
+ {
+ /* Check if its a looping sample, either stop or loop */
+ if(v->mode&0x10)
+ {
+ pos = (v->sample_loop - st);
+ }
+ else
+ {
+ v->key=0;
+ break;
+ }
+ }
+
+ /* Read the chosen sample byte */
+ dt=pSampleData[pos];
+
+ /* decompress to 13bit range */ //2000.06.26 CAB
+ sdt=dt>>3; //signed
+ if(sdt<0) sdt = (sdt<<(dt&7)) - m_pcmtbl[dt&7];
+ else sdt = (sdt<<(dt&7)) + m_pcmtbl[dt&7];
+
+ prevdt=lastdt;
+ lastdt=sdt;
+ dltdt=(lastdt - prevdt);
+ }
+
+ /* Caclulate the sample value */
+ dt=((dltdt*offset)>>16)+prevdt;
+
+ /* Write the data to the sample buffers */
+ *lmix++ +=(dt*lvol)>>(5+5);
+ *rmix++ +=(dt*rvol)>>(5+5);
+ }
+ }
+ else
+ {
+ /* linear 8bit signed PCM */
+ for(j=0;j<samples;j++)
+ {
+ offset += delta;
+ cnt = (offset>>16)&0x7fff;
+ offset &= 0xffff;
+ pos += cnt;
+ /* Check for the end of the sample */
+ if(pos >= sz)
+ {
+ /* Check if its a looping sample, either stop or loop */
+ if( v->mode&0x10 )
+ {
+ pos = (v->sample_loop - st);
+ }
+ else
+ {
+ v->key=0;
+ break;
+ }
+ }
+
+ if( cnt )
+ {
+ prevdt=lastdt;
+
+ if (m_banking_type == C140_TYPE_ASIC219)
+ {
+ lastdt = pSampleData[BYTE_XOR_BE(pos)];
+
+ // Sign + magnitude format
+ if ((v->mode & 0x01) && (lastdt & 0x80))
+ lastdt = -(lastdt & 0x7f);
+
+ // Sign flip
+ if (v->mode & 0x40)
+ lastdt = -lastdt;
+ }
+ else
+ {
+ lastdt=pSampleData[pos];
+ }
+
+ dltdt = (lastdt - prevdt);
+ }
+
+ /* Caclulate the sample value */
+ dt=((dltdt*offset)>>16)+prevdt;
+
+ /* Write the data to the sample buffers */
+ *lmix++ +=(dt*lvol)>>5;
+ *rmix++ +=(dt*rvol)>>5;
+ }
+ }
+
+ /* Save positional data for next callback */
+ v->ptoffset=offset;
+ v->pos=pos;
+ v->lastdt=lastdt;
+ v->prevdt=prevdt;
+ v->dltdt=dltdt;
+ }
+ }
+
+ /* render to MAME's stream buffer */
+ lmix = m_mixer_buffer_left;
+ rmix = m_mixer_buffer_right;
+ {
+ stream_sample_t *dest1 = outputs[0];
+ stream_sample_t *dest2 = outputs[1];
+ for (i = 0; i < samples; i++)
+ {
+ INT32 val;
+
+ val = 8 * (*lmix++);
+ *dest1++ = limit(val);
+ val = 8 * (*rmix++);
+ *dest2++ = limit(val);
+ }
+ }
+}
+
+
+READ8_MEMBER( c140_device::c140_r )
+{
+ offset&=0x1ff;
+ return m_REG[offset];
+}
+
+
+WRITE8_MEMBER( c140_device::c140_w )
+{
+ m_stream->update();
+
+ offset&=0x1ff;
+
+ // mirror the bank registers on the 219, fixes bkrtmaq (and probably xday2 based on notes in the HLE)
+ if ((offset >= 0x1f8) && (m_banking_type == C140_TYPE_ASIC219))
+ {
+ offset -= 8;
+ }
+
+ m_REG[offset]=data;
+ if( offset<0x180 )
+ {
+ C140_VOICE *v = &m_voi[offset>>4];
+
+ if( (offset&0xf)==0x5 )
+ {
+ if( data&0x80 )
+ {
+ const struct voice_registers *vreg = (struct voice_registers *) &m_REG[offset&0x1f0];
+ v->key=1;
+ v->ptoffset=0;
+ v->pos=0;
+ v->lastdt=0;
+ v->prevdt=0;
+ v->dltdt=0;
+ v->bank = vreg->bank;
+ v->mode = data;
+
+ // on the 219 asic, addresses are in words
+ if (m_banking_type == C140_TYPE_ASIC219)
+ {
+ v->sample_loop = (vreg->loop_msb*256 + vreg->loop_lsb)*2;
+ v->sample_start = (vreg->start_msb*256 + vreg->start_lsb)*2;
+ v->sample_end = (vreg->end_msb*256 + vreg->end_lsb)*2;
+
+ #if 0
+ logerror("219: play v %d mode %02x start %x loop %x end %x\n",
+ offset>>4, v->mode,
+ find_sample(v->sample_start, v->bank, offset>>4),
+ find_sample(v->sample_loop, v->bank, offset>>4),
+ find_sample(v->sample_end, v->bank, offset>>4));
+ #endif
+ }
+ else
+ {
+ v->sample_loop = vreg->loop_msb*256 + vreg->loop_lsb;
+ v->sample_start = vreg->start_msb*256 + vreg->start_lsb;
+ v->sample_end = vreg->end_msb*256 + vreg->end_lsb;
+ }
+ }
+ else
+ {
+ v->key=0;
+ }
+ }
+ }
+}
+
+
+void c140_device::set_base(void *base)
+{
+ m_pRom = (INT8 *)base;
+}
+
+
+void c140_device::init_voice( C140_VOICE *v )
+{
+ v->key=0;
+ v->ptoffset=0;
+ v->rvol=0;
+ v->lvol=0;
+ v->frequency=0;
+ v->bank=0;
+ v->mode=0;
+ v->sample_start=0;
+ v->sample_end=0;
+ v->sample_loop=0;
+}
+
+
+/*
+ find_sample: compute the actual address of a sample given it's
+ address and banking registers, as well as the board type.
+
+ I suspect in "real life" this works like the Sega MultiPCM where the banking
+ is done by a small PAL or GAL external to the sound chip, which can be switched
+ per-game or at least per-PCB revision as addressing range needs grow.
+ */
+long c140_device::find_sample(long adrs, long bank, int voice)
+{
+ long newadr = 0;
+
+ static const INT16 asic219banks[4] = { 0x1f7, 0x1f1, 0x1f3, 0x1f5 };
+
+ adrs=(bank<<16)+adrs;
+
+ switch (m_banking_type)
+ {
+ case C140_TYPE_SYSTEM2:
+ // System 2 banking
+ newadr = ((adrs&0x200000)>>2)|(adrs&0x7ffff);
+ break;
+
+ case C140_TYPE_SYSTEM21:
+ // System 21 banking.
+ // similar to System 2's.
+ newadr = ((adrs&0x300000)>>1)+(adrs&0x7ffff);
+ break;
+
+ case C140_TYPE_ASIC219:
+ // ASIC219's banking is fairly simple
+ newadr = ((m_REG[asic219banks[voice/4]]&0x3) * 0x20000) + adrs;
+ break;
+ }
+
+ return (newadr);
+}
diff --git a/src/devices/sound/c140.h b/src/devices/sound/c140.h
new file mode 100644
index 00000000000..13b6bfbcc7c
--- /dev/null
+++ b/src/devices/sound/c140.h
@@ -0,0 +1,124 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+/* C140.h */
+
+#pragma once
+
+#ifndef __C140_H__
+#define __C140_H__
+
+#define C140_MAX_VOICE 24
+
+enum
+{
+ C140_TYPE_SYSTEM2,
+ C140_TYPE_SYSTEM21,
+ C140_TYPE_ASIC219
+};
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_C140_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, C140, _clock)
+#define MCFG_C140_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, C140, _clock)
+
+#define MCFG_C140_BANK_TYPE(_type) \
+ c140_device::set_bank_type(*device, _type);
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+struct C140_VOICE
+{
+ C140_VOICE() :
+ ptoffset(0),
+ pos(0),
+ key(0),
+ lastdt(0),
+ prevdt(0),
+ dltdt(0),
+ rvol(0),
+ lvol(0),
+ frequency(0),
+ bank(0),
+ mode(0),
+ sample_start(0),
+ sample_end(0),
+ sample_loop(0) {}
+
+ INT32 ptoffset;
+ INT32 pos;
+ INT32 key;
+ //--work
+ INT32 lastdt;
+ INT32 prevdt;
+ INT32 dltdt;
+ //--reg
+ INT32 rvol;
+ INT32 lvol;
+ INT32 frequency;
+ INT32 bank;
+ INT32 mode;
+
+ INT32 sample_start;
+ INT32 sample_end;
+ INT32 sample_loop;
+};
+
+
+// ======================> c140_device
+
+class c140_device : public device_t,
+ public device_sound_interface
+{
+public:
+ c140_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~c140_device() { }
+
+ // static configuration
+ static void set_bank_type(device_t &device, int bank) { downcast<c140_device &>(device).m_banking_type = bank; }
+
+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);
+
+public:
+ DECLARE_READ8_MEMBER( c140_r );
+ DECLARE_WRITE8_MEMBER( c140_w );
+
+public:
+ void set_base(void *base);
+
+private:
+ void init_voice( C140_VOICE *v );
+ long find_sample(long adrs, long bank, int voice);
+
+private:
+ int m_sample_rate;
+ sound_stream *m_stream;
+ int m_banking_type;
+ /* internal buffers */
+ INT16 *m_mixer_buffer_left;
+ INT16 *m_mixer_buffer_right;
+
+ int m_baserate;
+ INT8 *m_pRom;
+ UINT8 m_REG[0x200];
+
+ INT16 m_pcmtbl[8]; //2000.06.26 CAB
+
+ C140_VOICE m_voi[C140_MAX_VOICE];
+};
+
+extern const device_type C140;
+
+
+#endif /* __C140_H__ */
diff --git a/src/devices/sound/c352.c b/src/devices/sound/c352.c
new file mode 100644
index 00000000000..98f8f3a9fed
--- /dev/null
+++ b/src/devices/sound/c352.c
@@ -0,0 +1,551 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+/*
+ c352.c - Namco C352 custom PCM chip emulation
+ v1.2
+ By R. Belmont
+ Additional code by cync and the hoot development team
+
+ Thanks to Cap of VivaNonno for info and The_Author for preliminary reverse-engineering
+
+ Chip specs:
+ 32 voices
+ Supports 8-bit linear and 8-bit muLaw samples
+ Output: digital, 16 bit, 4 channels
+ Output sample rate is the input clock / (288 * 2).
+
+ superctr: The clock divider appears to be configurable for each system.
+ Below is a list of the divider values followed by the systems that use it.
+
+ * 228: System 11.
+ * 288: System 22, Super 22, NB-1/2, ND-1, FL.
+ * 296: System 23, Super 23.
+ * 332: System 12.
+ */
+
+#include "emu.h"
+#include "c352.h"
+
+#define VERBOSE (0)
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+// device type definition
+const device_type C352 = &device_creator<c352_device>;
+
+// default address map
+static ADDRESS_MAP_START( c352, AS_0, 8, c352_device )
+ AM_RANGE(0x000000, 0xffffff) AM_ROM
+ADDRESS_MAP_END
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// c352_device - constructor
+//-------------------------------------------------
+
+c352_device::c352_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, C352, "C352", tag, owner, clock, "c352", __FILE__),
+ device_sound_interface(mconfig, *this),
+ device_memory_interface(mconfig, *this),
+ m_space_config("samples", ENDIANNESS_LITTLE, 8, 24, 0, NULL, *ADDRESS_MAP_NAME(c352))
+{
+}
+
+//-------------------------------------------------
+// static_set_dividder - configuration helper to
+// set the divider setting
+//-------------------------------------------------
+
+void c352_device::static_set_divider(device_t &device, int setting)
+{
+ c352_device &c352 = downcast<c352_device &>(device);
+ c352.m_divider = setting;
+}
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *c352_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == 0) ? &m_space_config : NULL;
+}
+
+// noise generator
+int c352_device::get_mseq_bit()
+{
+ unsigned int mask = (1 << (7 - 1));
+ unsigned int reg = m_mseq_reg;
+ unsigned int bit = reg & (1 << (17 - 1));
+
+ if (bit)
+ {
+ reg = ((reg ^ mask) << 1) | 1;
+ }
+ else
+ {
+ reg = reg << 1;
+ }
+
+ m_mseq_reg = reg;
+
+ return (reg & 1);
+}
+
+void c352_device::mix_one_channel(unsigned long ch, long sample_count)
+{
+ int i;
+
+ signed short sample, nextsample;
+ signed short noisebuf;
+ UINT16 noisecnt;
+ INT32 frequency, delta, offset, cnt, flag;
+ UINT32 bank;
+ UINT32 pos;
+
+ frequency = m_c352_ch[ch].pitch;
+ delta=frequency;
+
+ pos = m_c352_ch[ch].current_addr; // sample pointer
+ offset = m_c352_ch[ch].pos; // 16.16 fixed-point offset into the sample
+ flag = m_c352_ch[ch].flag;
+ bank = m_c352_ch[ch].bank << 16;
+
+ noisecnt = m_c352_ch[ch].noisecnt;
+ noisebuf = m_c352_ch[ch].noisebuf;
+
+ for(i = 0 ; (i < sample_count) && (flag & C352_FLG_BUSY) ; i++)
+ {
+ offset += delta;
+ cnt = (offset>>16)&0x7fff;
+ if (cnt) // if there is a whole sample part, chop it off now that it's been applied
+ {
+ offset &= 0xffff;
+ }
+
+ if (pos > 0x1000000)
+ {
+ m_c352_ch[ch].flag &= ~C352_FLG_BUSY;
+ return;
+ }
+
+ sample = (char)m_direct->read_byte(pos);
+ nextsample = (char)m_direct->read_byte(pos+cnt);
+
+ // sample is muLaw, not 8-bit linear (Fighting Layer uses this extensively)
+ if (flag & C352_FLG_MULAW)
+ {
+ sample = m_mulaw_table[(unsigned char)sample];
+ nextsample = m_mulaw_table[(unsigned char)nextsample];
+ }
+ else
+ {
+ sample <<= 8;
+ nextsample <<= 8;
+ }
+
+ // play noise instead of sample data
+ if (flag & C352_FLG_NOISE)
+ {
+ int noise_level = 0x8000;
+ sample = m_c352_ch[ch].noise = (m_c352_ch[ch].noise << 1) | get_mseq_bit();
+ sample = (sample & (noise_level - 1)) - (noise_level >> 1);
+ if (sample > 0x7f)
+ {
+ sample = 0x7f;
+ }
+ else if (sample < 0)
+ {
+ sample = 0xff;
+ }
+ sample = m_mulaw_table[(unsigned char)sample];
+
+ if ( (pos+cnt) == pos )
+ {
+ noisebuf += sample;
+ noisecnt++;
+ sample = noisebuf / noisecnt;
+ }
+ else
+ {
+ if ( noisecnt )
+ {
+ sample = noisebuf / noisecnt;
+ }
+ else
+ {
+ sample = m_mulaw_table[0x7f]; // Nearest sound(s) is here.
+ }
+ noisebuf = 0;
+ noisecnt = ( flag & C352_FLG_FILTER ) ? 0 : 1;
+ }
+ }
+
+ // apply linear interpolation
+ if ( (flag & (C352_FLG_FILTER | C352_FLG_NOISE)) == 0 )
+ {
+ sample = (short)(sample + ((nextsample-sample) * (((double)(0x0000ffff&offset) )/0x10000)));
+ }
+
+ if ( flag & C352_FLG_PHASEFL )
+ {
+ m_channel_l[i] += ((-sample * m_c352_ch[ch].vol_l)>>8);
+ }
+ else
+ {
+ m_channel_l[i] += ((sample * m_c352_ch[ch].vol_l)>>8);
+ }
+
+ if ( flag & C352_FLG_PHASEFR )
+ {
+ m_channel_r[i] += ((-sample * m_c352_ch[ch].vol_r)>>8);
+ }
+ else
+ {
+ m_channel_r[i] += ((sample * m_c352_ch[ch].vol_r)>>8);
+ }
+
+ if ( flag & C352_FLG_PHASERL )
+ {
+ m_channel_l2[i] += ((-sample * m_c352_ch[ch].vol_l2)>>8);
+ }
+ else
+ {
+ m_channel_l2[i] += ((sample * m_c352_ch[ch].vol_l2)>>8);
+ }
+ m_channel_r2[i] += ((sample * m_c352_ch[ch].vol_r2)>>8);
+
+ if ( (flag & C352_FLG_REVERSE) && (flag & C352_FLG_LOOP) )
+ {
+ if ( !(flag & C352_FLG_LDIR) )
+ {
+ pos += cnt;
+ if (
+ (((pos&0xFFFF) > m_c352_ch[ch].end_addr) && ((pos&0xFFFF) < m_c352_ch[ch].start) && (m_c352_ch[ch].start > m_c352_ch[ch].end_addr) ) ||
+ (((pos&0xFFFF) > m_c352_ch[ch].end_addr) && ((pos&0xFFFF) > m_c352_ch[ch].start) && (m_c352_ch[ch].start < m_c352_ch[ch].end_addr) ) ||
+ ((pos > (bank|0xFFFF)) && (m_c352_ch[ch].end_addr == 0xFFFF))
+ )
+ {
+ m_c352_ch[ch].flag |= C352_FLG_LDIR;
+ m_c352_ch[ch].flag |= C352_FLG_LOOPHIST;
+ }
+ }
+ else
+ {
+ pos -= cnt;
+ if (
+ (((pos&0xFFFF) < m_c352_ch[ch].repeat) && ((pos&0xFFFF) < m_c352_ch[ch].end_addr) && (m_c352_ch[ch].end_addr > m_c352_ch[ch].start) ) ||
+ (((pos&0xFFFF) < m_c352_ch[ch].repeat) && ((pos&0xFFFF) > m_c352_ch[ch].end_addr) && (m_c352_ch[ch].end_addr < m_c352_ch[ch].start) ) ||
+ ((pos < bank) && (m_c352_ch[ch].repeat == 0x0000))
+ )
+ {
+ m_c352_ch[ch].flag &= ~C352_FLG_LDIR;
+ m_c352_ch[ch].flag |= C352_FLG_LOOPHIST;
+ }
+ }
+ }
+ else if ( flag & C352_FLG_REVERSE )
+ {
+ pos -= cnt;
+ if (
+ (((pos&0xFFFF) < m_c352_ch[ch].end_addr) && ((pos&0xFFFF) < m_c352_ch[ch].start) && (m_c352_ch[ch].start > m_c352_ch[ch].end_addr) ) ||
+ (((pos&0xFFFF) < m_c352_ch[ch].end_addr) && ((pos&0xFFFF) > m_c352_ch[ch].start) && (m_c352_ch[ch].start < m_c352_ch[ch].end_addr) ) ||
+ ((pos < bank) && (m_c352_ch[ch].end_addr == 0x0000))
+ )
+ {
+ if ( (flag & C352_FLG_LINK) && (flag & C352_FLG_LOOP) )
+ {
+ m_c352_ch[ch].bank = m_c352_ch[ch].start_addr & 0xFF;
+ m_c352_ch[ch].start_addr = m_c352_ch[ch].repeat_addr;
+ m_c352_ch[ch].start = m_c352_ch[ch].start_addr;
+ m_c352_ch[ch].repeat = m_c352_ch[ch].repeat_addr;
+ pos = (m_c352_ch[ch].bank<<16) + m_c352_ch[ch].start_addr;
+ m_c352_ch[ch].flag |= C352_FLG_LOOPHIST;
+ }
+ else if (flag & C352_FLG_LOOP)
+ {
+ pos = (pos & 0xFF0000) + m_c352_ch[ch].repeat;
+ m_c352_ch[ch].flag |= C352_FLG_LOOPHIST;
+ }
+ else
+ {
+ m_c352_ch[ch].flag |= C352_FLG_KEYOFF;
+ m_c352_ch[ch].flag &= ~C352_FLG_BUSY;
+ return;
+ }
+ }
+ } else {
+ pos += cnt;
+ if (
+ (((pos&0xFFFF) > m_c352_ch[ch].end_addr) && ((pos&0xFFFF) < m_c352_ch[ch].start) && (m_c352_ch[ch].start > m_c352_ch[ch].end_addr) ) ||
+ (((pos&0xFFFF) > m_c352_ch[ch].end_addr) && ((pos&0xFFFF) > m_c352_ch[ch].start) && (m_c352_ch[ch].start < m_c352_ch[ch].end_addr) ) ||
+ ((pos > (bank|0xFFFF)) && (m_c352_ch[ch].end_addr == 0xFFFF))
+ )
+ {
+ if ( (flag & C352_FLG_LINK) && (flag & C352_FLG_LOOP) )
+ {
+ m_c352_ch[ch].bank = m_c352_ch[ch].start_addr & 0xFF;
+ m_c352_ch[ch].start_addr = m_c352_ch[ch].repeat_addr;
+ m_c352_ch[ch].start = m_c352_ch[ch].start_addr;
+ m_c352_ch[ch].repeat = m_c352_ch[ch].repeat_addr;
+ pos = (m_c352_ch[ch].bank<<16) + m_c352_ch[ch].start_addr;
+ m_c352_ch[ch].flag |= C352_FLG_LOOPHIST;
+ }
+ else if (flag & C352_FLG_LOOP)
+ {
+ pos = (pos & 0xFF0000) + m_c352_ch[ch].repeat;
+ m_c352_ch[ch].flag |= C352_FLG_LOOPHIST;
+ }
+ else
+ {
+ m_c352_ch[ch].flag |= C352_FLG_KEYOFF;
+ m_c352_ch[ch].flag &= ~C352_FLG_BUSY;
+ return;
+ }
+ }
+ }
+ }
+
+ m_c352_ch[ch].noisecnt = noisecnt;
+ m_c352_ch[ch].noisebuf = noisebuf;
+ m_c352_ch[ch].pos = offset;
+ m_c352_ch[ch].current_addr = pos;
+}
+
+
+void c352_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int i, j;
+ stream_sample_t *bufferl = outputs[0];
+ stream_sample_t *bufferr = outputs[1];
+ stream_sample_t *bufferl2 = outputs[2];
+ stream_sample_t *bufferr2 = outputs[3];
+
+ for(i = 0 ; i < samples ; i++)
+ {
+ m_channel_l[i] = m_channel_r[i] = m_channel_l2[i] = m_channel_r2[i] = 0;
+ }
+
+ for (j = 0 ; j < 32 ; j++)
+ {
+ mix_one_channel(j, samples);
+ }
+
+ for(i = 0 ; i < samples ; i++)
+ {
+ *bufferl++ = (short) (m_channel_l[i] >>3);
+ *bufferr++ = (short) (m_channel_r[i] >>3);
+ *bufferl2++ = (short) (m_channel_l2[i] >>3);
+ *bufferr2++ = (short) (m_channel_r2[i] >>3);
+ }
+}
+
+unsigned short c352_device::read_reg16(unsigned long address)
+{
+ unsigned long chan;
+ unsigned short val;
+
+ m_stream->update();
+
+ chan = (address >> 4) & 0xfff;
+ if (chan > 31)
+ {
+ val = 0;
+ }
+ else
+ {
+ if ((address & 0xf) == 6)
+ {
+ val = m_c352_ch[chan].flag;
+ }
+ else
+ {
+ val = 0;
+ }
+ }
+ return val;
+}
+
+void c352_device::write_reg16(unsigned long address, unsigned short val)
+{
+ unsigned long chan;
+ int i;
+
+ m_stream->update();
+
+ chan = (address >> 4) & 0xfff;
+
+ if ( address >= 0x400 )
+ {
+ switch(address)
+ {
+ case 0x404: // execute key-ons/offs
+ for ( i = 0 ; i <= 31 ; i++ )
+ {
+ if ( m_c352_ch[i].flag & C352_FLG_KEYON )
+ {
+ if (m_c352_ch[i].start_addr != m_c352_ch[i].end_addr)
+ {
+ m_c352_ch[i].current_addr = (m_c352_ch[i].bank << 16) + m_c352_ch[i].start_addr;
+ m_c352_ch[i].start = m_c352_ch[i].start_addr;
+ m_c352_ch[i].repeat = m_c352_ch[i].repeat_addr;
+ m_c352_ch[i].noisebuf = 0;
+ m_c352_ch[i].noisecnt = 0;
+ m_c352_ch[i].flag &= ~(C352_FLG_KEYON | C352_FLG_LOOPHIST);
+ m_c352_ch[i].flag |= C352_FLG_BUSY;
+ }
+ }
+ else if ( m_c352_ch[i].flag & C352_FLG_KEYOFF )
+ {
+ m_c352_ch[i].flag &= ~C352_FLG_BUSY;
+ m_c352_ch[i].flag &= ~(C352_FLG_KEYOFF);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return;
+ }
+
+ if (chan > 31)
+ {
+ LOG(("C352 CTRL %08lx %04x\n", address, val));
+ return;
+ }
+ switch(address & 0xf)
+ {
+ case 0x0:
+ // volumes (output 1)
+ LOG(("CH %02ld LVOL %02x RVOL %02x\n", chan, val & 0xff, val >> 8));
+ m_c352_ch[chan].vol_l = val & 0xff;
+ m_c352_ch[chan].vol_r = val >> 8;
+ break;
+
+ case 0x2:
+ // volumes (output 2)
+ LOG(("CH %02ld RLVOL %02x RRVOL %02x\n", chan, val & 0xff, val >> 8));
+ m_c352_ch[chan].vol_l2 = val & 0xff;
+ m_c352_ch[chan].vol_r2 = val >> 8;
+ break;
+
+ case 0x4:
+ // pitch
+ LOG(("CH %02ld PITCH %04x\n", chan, val));
+ m_c352_ch[chan].pitch = val;
+ break;
+
+ case 0x6:
+ // flags
+ LOG(("CH %02ld FLAG %02x\n", chan, val));
+ m_c352_ch[chan].flag = val;
+ break;
+
+ case 0x8:
+ // bank (bits 16-31 of address);
+ m_c352_ch[chan].bank = val & 0xff;
+ LOG(("CH %02ld BANK %02x", chan, m_c352_ch[chan].bank));
+ break;
+
+ case 0xa:
+ // start address
+ LOG(("CH %02ld SADDR %04x\n", chan, val));
+ m_c352_ch[chan].start_addr = val;
+ break;
+
+ case 0xc:
+ // end address
+ LOG(("CH %02ld EADDR %04x\n", chan, val));
+ m_c352_ch[chan].end_addr = val;
+ break;
+
+ case 0xe:
+ // loop address
+ LOG(("CH %02ld LADDR %04x\n", chan, val));
+ m_c352_ch[chan].repeat_addr = val;
+ break;
+
+ default:
+ LOG(("CH %02ld UNKN %01lx %04x", chan, address & 0xf, val));
+ break;
+ }
+}
+
+void c352_device::device_start()
+{
+ int i;
+ double x_max = 32752.0;
+ double y_max = 127.0;
+ double u = 10.0;
+
+ // find our direct access
+ m_direct = &space().direct();
+
+ m_sample_rate_base = clock() / m_divider;
+
+ m_stream = machine().sound().stream_alloc(*this, 0, 4, m_sample_rate_base);
+
+ // generate mulaw table for mulaw format samples
+ for (i = 0; i < 256; i++)
+ {
+ double y = (double) (i & 0x7f);
+ double x = (exp (y / y_max * log (1.0 + u)) - 1.0) * x_max / u;
+
+ if (i & 0x80)
+ {
+ x = -x;
+ }
+ m_mulaw_table[i] = (short)x;
+ }
+
+ // register save state info
+ for (i = 0; i < 32; i++)
+ {
+ save_item(NAME(m_c352_ch[i].vol_l), i);
+ save_item(NAME(m_c352_ch[i].vol_r), i);
+ save_item(NAME(m_c352_ch[i].vol_l2), i);
+ save_item(NAME(m_c352_ch[i].vol_r2), i);
+ save_item(NAME(m_c352_ch[i].bank), i);
+ save_item(NAME(m_c352_ch[i].noise), i);
+ save_item(NAME(m_c352_ch[i].noisebuf), i);
+ save_item(NAME(m_c352_ch[i].noisecnt), i);
+ save_item(NAME(m_c352_ch[i].pitch), i);
+ save_item(NAME(m_c352_ch[i].start_addr), i);
+ save_item(NAME(m_c352_ch[i].end_addr), i);
+ save_item(NAME(m_c352_ch[i].repeat_addr), i);
+ save_item(NAME(m_c352_ch[i].flag), i);
+ save_item(NAME(m_c352_ch[i].start), i);
+ save_item(NAME(m_c352_ch[i].repeat), i);
+ save_item(NAME(m_c352_ch[i].current_addr), i);
+ save_item(NAME(m_c352_ch[i].pos), i);
+ }
+}
+
+void c352_device::device_reset()
+{
+ // clear all channels states
+ memset(m_c352_ch, 0, sizeof(c352_ch_t)*32);
+
+ // init noise generator
+ m_mseq_reg = 0x12345678;
+}
+
+READ16_MEMBER( c352_device::read )
+{
+ return(read_reg16(offset*2));
+}
+
+WRITE16_MEMBER( c352_device::write )
+{
+ if (mem_mask == 0xffff)
+ {
+ write_reg16(offset*2, data);
+ }
+ else
+ {
+ logerror("C352: byte-wide write unsupported at this time!\n");
+ }
+}
diff --git a/src/devices/sound/c352.h b/src/devices/sound/c352.h
new file mode 100644
index 00000000000..24a2a1cbe74
--- /dev/null
+++ b/src/devices/sound/c352.h
@@ -0,0 +1,121 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+#pragma once
+
+#ifndef __C352_H__
+#define __C352_H__
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_C352_ADD(_tag, _clock, _setting) \
+ MCFG_DEVICE_ADD(_tag, C352, _clock) \
+ MCFG_C352_DIVIDER(_setting)
+
+#define MCFG_C352_DIVIDER(_setting) \
+ c352_device::static_set_divider(*device, _setting);
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> asc_device
+
+class c352_device : public device_t,
+ public device_sound_interface,
+ public device_memory_interface
+{
+public:
+ // construction/destruction
+ c352_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // inline configuration helpers
+ static void static_set_divider(device_t &device, int setting);
+
+ DECLARE_READ16_MEMBER(read);
+ DECLARE_WRITE16_MEMBER(write);
+
+ sound_stream *m_stream;
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ const address_space_config m_space_config;
+
+private:
+ enum {
+ C352_FLG_BUSY = 0x8000, // channel is busy
+ C352_FLG_KEYON = 0x4000, // Keyon
+ C352_FLG_KEYOFF = 0x2000, // Keyoff
+ C352_FLG_LOOPTRG = 0x1000, // Loop Trigger
+ C352_FLG_LOOPHIST = 0x0800, // Loop History
+ C352_FLG_FM = 0x0400, // Frequency Modulation
+ C352_FLG_PHASERL = 0x0200, // Rear Left invert phase 180 degrees
+ C352_FLG_PHASEFL = 0x0100, // Front Left invert phase 180 degrees
+ C352_FLG_PHASEFR = 0x0080, // invert phase 180 degrees (e.g. flip sign of sample)
+ C352_FLG_LDIR = 0x0040, // loop direction
+ C352_FLG_LINK = 0x0020, // "long-format" sample (can't loop, not sure what else it means)
+ C352_FLG_NOISE = 0x0010, // play noise instead of sample
+ C352_FLG_MULAW = 0x0008, // sample is mulaw instead of linear 8-bit PCM
+ C352_FLG_FILTER = 0x0004, // don't apply filter
+ C352_FLG_REVLOOP = 0x0003, // loop backwards
+ C352_FLG_LOOP = 0x0002, // loop forward
+ C352_FLG_REVERSE = 0x0001 // play sample backwards
+ };
+
+ struct c352_ch_t
+ {
+ UINT8 vol_l;
+ UINT8 vol_r;
+ UINT8 vol_l2;
+ UINT8 vol_r2;
+ UINT8 bank;
+ INT16 noise;
+ INT16 noisebuf;
+ UINT16 noisecnt;
+ UINT16 pitch;
+ UINT16 start_addr;
+ UINT16 end_addr;
+ UINT16 repeat_addr;
+ UINT32 flag;
+
+ UINT16 start;
+ UINT16 repeat;
+ UINT32 current_addr;
+ UINT32 pos;
+ };
+
+ c352_ch_t m_c352_ch[32];
+ int m_sample_rate_base;
+ int m_divider;
+
+ long m_channel_l[2048*2];
+ long m_channel_r[2048*2];
+ long m_channel_l2[2048*2];
+ long m_channel_r2[2048*2];
+
+ short m_mulaw_table[256];
+ unsigned int m_mseq_reg;
+ direct_read_data *m_direct;
+
+ // private functions
+ int get_mseq_bit(void);
+ void mix_one_channel(unsigned long ch, long sample_count);
+ unsigned short read_reg16(unsigned long address);
+ void write_reg16(unsigned long address, unsigned short val);
+};
+
+
+// device type definition
+extern const device_type C352;
+
+#endif /* __C352_H__ */
diff --git a/src/devices/sound/c6280.c b/src/devices/sound/c6280.c
new file mode 100644
index 00000000000..d484b8281c0
--- /dev/null
+++ b/src/devices/sound/c6280.c
@@ -0,0 +1,302 @@
+// license:BSD-3-Clause
+// copyright-holders:Charles MacDonald
+/*
+ HuC6280 sound chip emulator
+ by Charles MacDonald
+ E-mail: cgfm2@hotmail.com
+ WWW: http://cgfm2.emuviews.com
+
+ Thanks to:
+
+ - Paul Clifford for his PSG documentation.
+ - Richard Bannister for the TGEmu-specific sound updating code.
+ - http://www.uspto.gov for the PSG patents.
+ - All contributors to the tghack-list.
+
+ Changes:
+
+ (03/30/2003)
+ - Removed TGEmu specific code and added support functions for MAME.
+ - Modified setup code to handle multiple chips with different clock and
+ volume settings.
+
+ Missing features / things to do:
+
+ - Add LFO support. But do any games actually use it?
+
+ - Add shared index for waveform playback and sample writes. Almost every
+ game will reset the index prior to playback so this isn't an issue.
+
+ - While the noise emulation is complete, the data for the pseudo-random
+ bitstream is calculated by machine.rand() and is not a representation of what
+ the actual hardware does.
+
+ For some background on Hudson Soft's C62 chipset:
+
+ - http://www.hudsonsoft.net/ww/about/about.html
+ - http://www.hudson.co.jp/corp/eng/coinfo/history.html
+
+*/
+
+#include "emu.h"
+#include "c6280.h"
+
+/* only needed for io_buffer */
+#include "cpu/h6280/h6280.h"
+
+
+void c6280_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ static const int scale_tab[] = {
+ 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
+ 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F
+ };
+
+ int lmal = (m_balance >> 4) & 0x0F;
+ int rmal = (m_balance >> 0) & 0x0F;
+
+ lmal = scale_tab[lmal];
+ rmal = scale_tab[rmal];
+
+ /* Clear buffer */
+ for (int i = 0; i < samples; i++)
+ {
+ outputs[0][i] = 0;
+ outputs[1][i] = 0;
+ }
+
+ for (int ch = 0; ch < 6; ch++)
+ {
+ /* Only look at enabled channels */
+ if(m_channel[ch].m_control & 0x80)
+ {
+ int lal = (m_channel[ch].m_balance >> 4) & 0x0F;
+ int ral = (m_channel[ch].m_balance >> 0) & 0x0F;
+ int al = m_channel[ch].m_control & 0x1F;
+
+ lal = scale_tab[lal];
+ ral = scale_tab[ral];
+
+ /* Calculate volume just as the patent says */
+ int vll = (0x1F - lal) + (0x1F - al) + (0x1F - lmal);
+ if(vll > 0x1F) vll = 0x1F;
+
+ int vlr = (0x1F - ral) + (0x1F - al) + (0x1F - rmal);
+ if(vlr > 0x1F) vlr = 0x1F;
+
+ vll = m_volume_table[vll];
+ vlr = m_volume_table[vlr];
+
+ /* Check channel mode */
+ if((ch >= 4) && (m_channel[ch].m_noise_control & 0x80))
+ {
+ /* Noise mode */
+ UINT32 step = m_noise_freq_tab[(m_channel[ch].m_noise_control & 0x1F) ^ 0x1F];
+ for (int i = 0; i < samples; i += 1)
+ {
+ static int data = 0;
+ m_channel[ch].m_noise_counter += step;
+ if(m_channel[ch].m_noise_counter >= 0x800)
+ {
+ data = (machine().rand() & 1) ? 0x1F : 0;
+ }
+ m_channel[ch].m_noise_counter &= 0x7FF;
+ outputs[0][i] += (INT16)(vll * (data - 16));
+ outputs[1][i] += (INT16)(vlr * (data - 16));
+ }
+ }
+ else
+ if(m_channel[ch].m_control & 0x40)
+ {
+ /* DDA mode */
+ for (int i = 0; i < samples; i++)
+ {
+ outputs[0][i] += (INT16)(vll * (m_channel[ch].m_dda - 16));
+ outputs[1][i] += (INT16)(vlr * (m_channel[ch].m_dda - 16));
+ }
+ }
+ else
+ {
+ /* Waveform mode */
+ UINT32 step = m_wave_freq_tab[m_channel[ch].m_frequency];
+ for (int i = 0; i < samples; i += 1)
+ {
+ int offset;
+ INT16 data;
+ offset = (m_channel[ch].m_counter >> 12) & 0x1F;
+ m_channel[ch].m_counter += step;
+ m_channel[ch].m_counter &= 0x1FFFF;
+ data = m_channel[ch].m_waveform[offset];
+ outputs[0][i] += (INT16)(vll * (data - 16));
+ outputs[1][i] += (INT16)(vlr * (data - 16));
+ }
+ }
+ }
+ }
+}
+
+
+/*--------------------------------------------------------------------------*/
+/* MAME specific code */
+/*--------------------------------------------------------------------------*/
+
+READ8_MEMBER( c6280_device::c6280_r )
+{
+ return m_cpudevice->io_get_buffer();
+}
+
+WRITE8_MEMBER( c6280_device::c6280_w )
+{
+ m_cpudevice->io_set_buffer(data);
+
+ channel *chan = &m_channel[m_select];
+
+ /* Update stream */
+ m_stream->update();
+
+ switch(offset & 0x0F)
+ {
+ case 0x00: /* Channel select */
+ m_select = data & 0x07;
+ break;
+
+ case 0x01: /* Global balance */
+ m_balance = data;
+ break;
+
+ case 0x02: /* Channel frequency (LSB) */
+ chan->m_frequency = (chan->m_frequency & 0x0F00) | data;
+ chan->m_frequency &= 0x0FFF;
+ break;
+
+ case 0x03: /* Channel frequency (MSB) */
+ chan->m_frequency = (chan->m_frequency & 0x00FF) | (data << 8);
+ chan->m_frequency &= 0x0FFF;
+ break;
+
+ case 0x04: /* Channel control (key-on, DDA mode, volume) */
+
+ /* 1-to-0 transition of DDA bit resets waveform index */
+ if((chan->m_control & 0x40) && ((data & 0x40) == 0))
+ {
+ chan->m_index = 0;
+ }
+ chan->m_control = data;
+ break;
+
+ case 0x05: /* Channel balance */
+ chan->m_balance = data;
+ break;
+
+ case 0x06: /* Channel waveform data */
+
+ switch(chan->m_control & 0xC0)
+ {
+ case 0x00:
+ chan->m_waveform[chan->m_index & 0x1F] = data & 0x1F;
+ chan->m_index = (chan->m_index + 1) & 0x1F;
+ break;
+
+ case 0x40:
+ break;
+
+ case 0x80:
+ chan->m_waveform[chan->m_index & 0x1F] = data & 0x1F;
+ chan->m_index = (chan->m_index + 1) & 0x1F;
+ break;
+
+ case 0xC0:
+ chan->m_dda = data & 0x1F;
+ break;
+ }
+
+ break;
+
+ case 0x07: /* Noise control (enable, frequency) */
+ chan->m_noise_control = data;
+ break;
+
+ case 0x08: /* LFO frequency */
+ m_lfo_frequency = data;
+ break;
+
+ case 0x09: /* LFO control (enable, mode) */
+ m_lfo_control = data;
+ break;
+
+ default:
+ break;
+ }
+}
+
+const device_type C6280 = &device_creator<c6280_device>;
+
+c6280_device::c6280_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, C6280, "HuC6280", tag, owner, clock, "c6280", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_cpudevice(*this)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void c6280_device::device_start()
+{
+ int rate = clock() / 16;
+
+ /* Create stereo stream */
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, rate);
+
+ /* Loudest volume level for table */
+ double level = 65535.0 / 6.0 / 32.0;
+
+ /* Clear context */
+ m_select = 0;
+ m_balance = 0;
+ m_lfo_frequency = 0;
+ m_lfo_control = 0;
+ memset(m_channel, 0, sizeof(channel) * 8);
+
+ /* Make waveform frequency table */
+ for (int i = 0; i < 4096; i += 1)
+ {
+ double step = ((clock() / rate) * 4096) / (i + 1);
+ m_wave_freq_tab[(1 + i) & 0xFFF] = (UINT32)step;
+ }
+
+ /* Make noise frequency table */
+ for (int i = 0; i < 32; i += 1)
+ {
+ double step = ((clock() / rate) * 32) / (i+1);
+ m_noise_freq_tab[i] = (UINT32)step;
+ }
+
+ /* Make volume table */
+ /* PSG has 48dB volume range spread over 32 steps */
+ double step = 48.0 / 32.0;
+ for (int i = 0; i < 31; i++)
+ {
+ m_volume_table[i] = (UINT16)level;
+ level /= pow(10.0, step / 20.0);
+ }
+ m_volume_table[31] = 0;
+
+ save_item(NAME(m_select));
+ save_item(NAME(m_balance));
+ save_item(NAME(m_lfo_frequency));
+ save_item(NAME(m_lfo_control));
+ for (int chan = 0; chan < 8; chan++)
+ {
+ save_item(NAME(m_channel[chan].m_frequency), chan);
+ save_item(NAME(m_channel[chan].m_control), chan);
+ save_item(NAME(m_channel[chan].m_balance), chan);
+ save_item(NAME(m_channel[chan].m_waveform), chan);
+ save_item(NAME(m_channel[chan].m_index), chan);
+ save_item(NAME(m_channel[chan].m_dda), chan);
+ save_item(NAME(m_channel[chan].m_noise_control), chan);
+ save_item(NAME(m_channel[chan].m_noise_counter), chan);
+ save_item(NAME(m_channel[chan].m_counter), chan);
+ }
+}
diff --git a/src/devices/sound/c6280.h b/src/devices/sound/c6280.h
new file mode 100644
index 00000000000..af53962a83e
--- /dev/null
+++ b/src/devices/sound/c6280.h
@@ -0,0 +1,61 @@
+// license:BSD-3-Clause
+// copyright-holders:Charles MacDonald
+#pragma once
+
+#ifndef __C6280_H__
+#define __C6280_H__
+
+#include "cpu/h6280/h6280.h"
+
+class c6280_device : public device_t,
+ public device_sound_interface
+{
+public:
+ c6280_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void set_devicecpu_tag(device_t &device, const char *tag) { downcast<c6280_device &>(device).m_cpudevice.set_tag(tag); }
+
+ // read/write
+ DECLARE_READ8_MEMBER( c6280_r );
+ DECLARE_WRITE8_MEMBER( c6280_w );
+
+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 channel {
+ UINT16 m_frequency;
+ UINT8 m_control;
+ UINT8 m_balance;
+ UINT8 m_waveform[32];
+ UINT8 m_index;
+ INT16 m_dda;
+ UINT8 m_noise_control;
+ UINT32 m_noise_counter;
+ UINT32 m_counter;
+ };
+
+ // internal state
+ sound_stream *m_stream;
+ required_device<h6280_device> m_cpudevice;
+ UINT8 m_select;
+ UINT8 m_balance;
+ UINT8 m_lfo_frequency;
+ UINT8 m_lfo_control;
+ channel m_channel[8];
+ INT16 m_volume_table[32];
+ UINT32 m_noise_freq_tab[32];
+ UINT32 m_wave_freq_tab[4096];
+};
+
+extern const device_type C6280;
+
+#define MCFG_C6280_CPU(_tag) \
+ c6280_device::set_devicecpu_tag(*device, "^" _tag);
+
+
+#endif /* __C6280_H__ */
diff --git a/src/devices/sound/cdda.c b/src/devices/sound/cdda.c
new file mode 100644
index 00000000000..1b7c81c1172
--- /dev/null
+++ b/src/devices/sound/cdda.c
@@ -0,0 +1,271 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles,smf
+/*
+ CD-DA "Red Book" audio sound hardware handler
+ Relies on the actual CD logic and reading in cdrom.c.
+*/
+
+#include "emu.h"
+#include "cdda.h"
+
+#define MAX_SECTORS ( 4 )
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void cdda_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ get_audio_data(&outputs[0][0], &outputs[1][0], samples);
+ m_audio_volume[0] = (INT16)outputs[0][0];
+ m_audio_volume[1] = (INT16)outputs[1][0];
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void cdda_device::device_start()
+{
+ /* allocate an audio cache */
+ m_audio_cache = auto_alloc_array( machine(), UINT8, CD_MAX_SECTOR_DATA * MAX_SECTORS );
+
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, 44100);
+
+ m_audio_playing = 0;
+ m_audio_pause = 0;
+ m_audio_ended_normally = 0;
+ m_audio_lba = 0;
+ m_audio_length = 0;
+ m_audio_samples = 0;
+ m_audio_bptr = 0;
+ m_disc = NULL;
+
+ save_item( NAME(m_audio_playing) );
+ save_item( NAME(m_audio_pause) );
+ save_item( NAME(m_audio_ended_normally) );
+ save_item( NAME(m_audio_lba) );
+ save_item( NAME(m_audio_length) );
+ save_pointer( NAME(m_audio_cache), CD_MAX_SECTOR_DATA * MAX_SECTORS );
+ save_item( NAME(m_audio_samples) );
+ save_item( NAME(m_audio_bptr) );
+}
+
+
+/*-------------------------------------------------
+ cdda_set_cdrom - set the CD-ROM file for the
+ given CDDA stream
+-------------------------------------------------*/
+
+void cdda_device::set_cdrom(void *file)
+{
+ m_disc = (cdrom_file *)file;
+}
+
+
+/*-------------------------------------------------
+ cdda_start_audio - begin playback of a Red
+ Book audio track
+-------------------------------------------------*/
+
+void cdda_device::start_audio(UINT32 startlba, UINT32 numblocks)
+{
+ m_stream->update();
+ m_audio_playing = TRUE;
+ m_audio_pause = FALSE;
+ m_audio_ended_normally = FALSE;
+ m_audio_lba = startlba;
+ m_audio_length = numblocks;
+ m_audio_samples = 0;
+}
+
+
+/*-------------------------------------------------
+ cdda_stop_audio - stop playback of a Red Book
+ audio track
+-------------------------------------------------*/
+
+void cdda_device::stop_audio()
+{
+ m_stream->update();
+ m_audio_playing = FALSE;
+ m_audio_ended_normally = TRUE;
+}
+
+
+/*-------------------------------------------------
+ cdda_pause_audio - pause/unpause playback of
+ a Red Book audio track
+-------------------------------------------------*/
+
+void cdda_device::pause_audio(int pause)
+{
+ m_stream->update();
+ m_audio_pause = pause;
+}
+
+
+/*-------------------------------------------------
+ cdda_get_audio_lba - returns the current LBA
+ (physical sector) during Red Book playback
+-------------------------------------------------*/
+
+UINT32 cdda_device::get_audio_lba()
+{
+ m_stream->update();
+ return m_audio_lba;
+}
+
+
+/*-------------------------------------------------
+ cdda_audio_active - returns Red Book audio
+ playback status
+-------------------------------------------------*/
+
+int cdda_device::audio_active()
+{
+ m_stream->update();
+ return m_audio_playing;
+}
+
+
+/*-------------------------------------------------
+ cdda_audio_paused - returns if Red Book
+ playback is paused
+-------------------------------------------------*/
+
+int cdda_device::audio_paused()
+{
+ return m_audio_pause;
+}
+
+
+/*-------------------------------------------------
+ cdda_audio_ended - returns if a Red Book
+ track reached it's natural end
+-------------------------------------------------*/
+
+int cdda_device::audio_ended()
+{
+ return m_audio_ended_normally;
+}
+
+
+/*-------------------------------------------------
+ get_audio_data - reads Red Book data off
+ the disc if playback is in progress and
+ converts it to 2 16-bit 44.1 kHz streams
+-------------------------------------------------*/
+
+void cdda_device::get_audio_data(stream_sample_t *bufL, stream_sample_t *bufR, UINT32 samples_wanted)
+{
+ int i;
+ INT16 *audio_cache = (INT16 *) m_audio_cache;
+
+ while (samples_wanted > 0)
+ {
+ /* if no file, audio not playing, audio paused, or out of disc data,
+ just zero fill */
+ if (!m_disc || !m_audio_playing || m_audio_pause || (!m_audio_length && !m_audio_samples))
+ {
+ if( m_disc && m_audio_playing && !m_audio_pause && !m_audio_length )
+ {
+ m_audio_playing = FALSE;
+ m_audio_ended_normally = TRUE;
+ }
+
+ memset(bufL, 0, sizeof(stream_sample_t)*samples_wanted);
+ memset(bufR, 0, sizeof(stream_sample_t)*samples_wanted);
+ return;
+ }
+
+ int samples = samples_wanted;
+ if (samples > m_audio_samples)
+ {
+ samples = m_audio_samples;
+ }
+
+ for (i = 0; i < samples; i++)
+ {
+ /* CD-DA data on the disc is big-endian */
+ *bufL++ = (INT16) BIG_ENDIANIZE_INT16( audio_cache[ m_audio_bptr ] ); m_audio_bptr++;
+ *bufR++ = (INT16) BIG_ENDIANIZE_INT16( audio_cache[ m_audio_bptr ] ); m_audio_bptr++;
+ }
+
+ samples_wanted -= samples;
+ m_audio_samples -= samples;
+
+ if (m_audio_samples == 0)
+ {
+ int sectors = m_audio_length;
+ if (sectors > MAX_SECTORS)
+ {
+ sectors = MAX_SECTORS;
+ }
+
+ for (i = 0; i < sectors; i++)
+ {
+ cdrom_read_data(m_disc, m_audio_lba, &m_audio_cache[CD_MAX_SECTOR_DATA*i], CD_TRACK_AUDIO);
+
+ m_audio_lba++;
+ }
+
+ m_audio_samples = (CD_MAX_SECTOR_DATA*sectors)/4;
+ m_audio_length -= sectors;
+
+ /* reset feedout ptr */
+ m_audio_bptr = 0;
+ }
+ }
+}
+
+/*-------------------------------------------------
+ cdda_set_volume - sets CD-DA volume level
+ for both speakers, used for fade in/out effects
+-------------------------------------------------*/
+
+void cdda_device::set_volume(int volume)
+{
+ m_stream->set_output_gain(0,volume / 100.0);
+ m_stream->set_output_gain(1,volume / 100.0);
+}
+
+/*-------------------------------------------------
+ cdda_set_channel_volume - sets CD-DA volume level
+ for either speaker, used for fade in/out effects
+-------------------------------------------------*/
+
+void cdda_device::set_channel_volume(int channel, int volume)
+{
+ m_stream->set_output_gain(channel,volume / 100.0);
+}
+
+
+/*-------------------------------------------------
+ cdda_get_channel_volume - sets CD-DA volume level
+ for either speaker, used for volume control display
+-------------------------------------------------*/
+
+INT16 cdda_device::get_channel_volume(int channel)
+{
+ return m_audio_volume[channel];
+}
+
+const device_type CDDA = &device_creator<cdda_device>;
+
+cdda_device::cdda_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, CDDA, "CD/DA", tag, owner, clock, "cdda", __FILE__),
+ device_sound_interface(mconfig, *this)
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void cdda_device::device_config_complete()
+{
+}
diff --git a/src/devices/sound/cdda.h b/src/devices/sound/cdda.h
new file mode 100644
index 00000000000..7650348a099
--- /dev/null
+++ b/src/devices/sound/cdda.h
@@ -0,0 +1,58 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles,smf
+#pragma once
+
+#ifndef __CDDA_H__
+#define __CDDA_H__
+
+#include "cdrom.h"
+
+class cdda_device : public device_t,
+ public device_sound_interface
+{
+public:
+ cdda_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ void set_cdrom(void *file);
+
+ void start_audio(UINT32 startlba, UINT32 numblocks);
+ void stop_audio();
+ void pause_audio(int pause);
+ void set_volume(int volume);
+ void set_channel_volume(int channel, int volume);
+ INT16 get_channel_volume(int channel);
+
+ UINT32 get_audio_lba();
+ int audio_active();
+ int audio_paused();
+ int audio_ended();
+
+ cdrom_file * m_disc;
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ 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:
+ void get_audio_data(stream_sample_t *bufL, stream_sample_t *bufR, UINT32 samples_wanted);
+
+ // internal state
+ sound_stream * m_stream;
+
+ INT8 m_audio_playing, m_audio_pause, m_audio_ended_normally;
+ UINT32 m_audio_lba, m_audio_length;
+
+ UINT8 * m_audio_cache;
+ UINT32 m_audio_samples;
+ UINT32 m_audio_bptr;
+ INT16 m_audio_volume[2];
+};
+
+extern const device_type CDDA;
+
+
+#endif /* __CDDA_H__ */
diff --git a/src/devices/sound/cdp1863.c b/src/devices/sound/cdp1863.c
new file mode 100644
index 00000000000..08a88f914ce
--- /dev/null
+++ b/src/devices/sound/cdp1863.c
@@ -0,0 +1,198 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ RCA CDP1863 CMOS 8-Bit Programmable Frequency Generator emulation
+
+**********************************************************************/
+
+/*
+
+ TODO:
+
+ - what happens if you connect both clocks?
+
+*/
+
+#include "cdp1863.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+#define CDP1863_DEFAULT_LATCH 0x35
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// devices
+const device_type CDP1863 = &device_creator<cdp1863_device>;
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// cdp1863_device - constructor
+//-------------------------------------------------
+
+cdp1863_device::cdp1863_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, CDP1863, "CDP1863", tag, owner, clock, "cdp1863", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_clock1(clock),
+ m_clock2(0)
+{
+}
+
+
+//-------------------------------------------------
+// static_set_clock2 - configuration helper
+//-------------------------------------------------
+
+void cdp1863_device::static_set_clock2(device_t &device, int clock2)
+{
+ cdp1863_device &cdp1863 = downcast<cdp1863_device &>(device);
+
+ cdp1863.m_clock2 = clock2;
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void cdp1863_device::device_start()
+{
+ // create sound stream
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate());
+
+ // register for state saving
+ save_item(NAME(m_clock1));
+ save_item(NAME(m_clock2));
+ save_item(NAME(m_oe));
+ save_item(NAME(m_latch));
+ save_item(NAME(m_signal));
+ save_item(NAME(m_incr));
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void cdp1863_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ // reset the output stream
+ memset(outputs[0], 0, samples * sizeof(*outputs[0]));
+
+ INT16 signal = m_signal;
+ stream_sample_t *buffer = outputs[0];
+
+ memset( buffer, 0, samples * sizeof(*buffer) );
+
+ if (m_oe)
+ {
+ double frequency;
+ int rate = machine().sample_rate() / 2;
+
+ // get progress through wave
+ int incr = m_incr;
+
+ if (m_clock1 > 0)
+ {
+ // CLK1 is pre-divided by 4
+ frequency = m_clock1 / 4 / (m_latch + 1) / 2;
+ }
+ else
+ {
+ // CLK2 is pre-divided by 8
+ frequency = m_clock2 / 8 / (m_latch + 1) / 2;
+ }
+
+ if (signal < 0)
+ {
+ signal = -0x7fff;
+ }
+ else
+ {
+ signal = 0x7fff;
+ }
+
+ while( samples-- > 0 )
+ {
+ *buffer++ = signal;
+ incr -= frequency;
+ while( incr < 0 )
+ {
+ incr += rate;
+ signal = -signal;
+ }
+ }
+
+ // store progress through wave
+ m_incr = incr;
+ m_signal = signal;
+ }
+}
+
+
+//-------------------------------------------------
+// str_w - latch write
+//-------------------------------------------------
+
+WRITE8_MEMBER( cdp1863_device::str_w )
+{
+ m_latch = data;
+}
+
+
+//-------------------------------------------------
+// str_w - latch write
+//-------------------------------------------------
+
+void cdp1863_device::str_w(UINT8 data)
+{
+ m_latch = data;
+}
+
+
+//-------------------------------------------------
+// oe_w - output enable write
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( cdp1863_device::oe_w )
+{
+ m_oe = state;
+}
+
+
+//-------------------------------------------------
+// set_clk1 - set clock 1
+//-------------------------------------------------
+
+void cdp1863_device::set_clk1(int clock)
+{
+ m_clock1 = clock;
+}
+
+
+//-------------------------------------------------
+// set_clk2 - set clock 2
+//-------------------------------------------------
+
+void cdp1863_device::set_clk2(int clock)
+{
+ m_clock2 = clock;
+}
diff --git a/src/devices/sound/cdp1863.h b/src/devices/sound/cdp1863.h
new file mode 100644
index 00000000000..910be5153fd
--- /dev/null
+++ b/src/devices/sound/cdp1863.h
@@ -0,0 +1,89 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ RCA CDP1863 CMOS 8-Bit Programmable Frequency Generator emulation
+
+**********************************************************************
+ _____ _____
+ _RESET 1 |* \_/ | 16 Vdd
+ CLK 2 2 | | 15 OE
+ CLK 1 3 | | 14 OUT
+ STR 4 | CDP1863 | 13 DO7
+ DI0 5 | | 12 DI6
+ DI1 6 | | 11 DI5
+ DI2 7 | | 10 DI4
+ Vss 8 |_____________| 9 DI3
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __CDP1863__
+#define __CDP1863__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_CDP1863_ADD(_tag, _clock, _clock2) \
+ MCFG_DEVICE_ADD(_tag, CDP1863, _clock) \
+ cdp1863_device::static_set_clock2(*device, _clock2);
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> cdp1863_device
+
+class cdp1863_device : public device_t,
+ public device_sound_interface
+{
+public:
+ // construction/destruction
+ cdp1863_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // inline configuration helpers
+ static void static_set_clock2(device_t &device, int clock2);
+
+ DECLARE_WRITE8_MEMBER( str_w );
+ void str_w(UINT8 data);
+
+ DECLARE_WRITE_LINE_MEMBER( oe_w );
+
+ void set_clk1(int clock);
+ void set_clk2(int clock);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+
+ // internal callbacks
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ sound_stream *m_stream;
+
+ int m_clock1; // clock 1
+ int m_clock2; // clock 2
+
+ // sound state
+ int m_oe; // output enable
+ int m_latch; // sound latch
+ INT16 m_signal; // current signal
+ int m_incr; // initial wave state
+};
+
+
+// device type definition
+extern const device_type CDP1863;
+
+
+
+#endif
diff --git a/src/devices/sound/cdp1864.c b/src/devices/sound/cdp1864.c
new file mode 100644
index 00000000000..b4859cee4a3
--- /dev/null
+++ b/src/devices/sound/cdp1864.c
@@ -0,0 +1,467 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ RCA CDP1864C COS/MOS PAL Compatible Color TV Interface
+
+**********************************************************************/
+
+/*
+
+ TODO:
+
+ - interlace mode
+ - PAL output, currently using RGB
+ - cpu synchronization
+
+ SC1 and SC0 are used to provide CDP1864C-to-CPU synchronization for a jitter-free display.
+ During every horizontal sync the CDP1864C samples SC0 and SC1 for SC0 = 1 and SC1 = 0
+ (CDP1800 execute state). Detection of a fetch cycle causes the CDP1864C to skip cycles to
+ attain synchronization. (i.e. picture moves 8 pixels to the right)
+
+*/
+
+#include "cdp1864.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define CDP1864_DEFAULT_LATCH 0x35
+
+#define CDP1864_CYCLES_DMA_START 2*8
+#define CDP1864_CYCLES_DMA_ACTIVE 8*8
+#define CDP1864_CYCLES_DMA_WAIT 6*8
+
+const int cdp1864_device::bckgnd[] = { 2, 0, 4, 1 };
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+// devices
+const device_type CDP1864 = &device_creator<cdp1864_device>;
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// cdp1864_device - constructor
+//-------------------------------------------------
+
+cdp1864_device::cdp1864_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, CDP1864, "CDP1864", tag, owner, clock, "cdp1864", __FILE__),
+ device_sound_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_read_inlace(*this),
+ m_read_rdata(*this),
+ m_read_bdata(*this),
+ m_read_gdata(*this),
+ m_write_irq(*this),
+ m_write_dma_out(*this),
+ m_write_efx(*this),
+ m_write_hsync(*this),
+ m_disp(0),
+ m_dmaout(0),
+ m_bgcolor(0),
+ m_con(0),
+ m_aoe(0),
+ m_latch(CDP1864_DEFAULT_LATCH)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void cdp1864_device::device_start()
+{
+ // resolve callbacks
+ m_read_inlace.resolve_safe(1);
+ m_read_rdata.resolve_safe(0);
+ m_read_bdata.resolve_safe(0);
+ m_read_gdata.resolve_safe(0);
+ m_write_irq.resolve_safe();
+ m_write_dma_out.resolve_safe();
+ m_write_efx.resolve_safe();
+ m_write_hsync.resolve_safe();
+
+ // initialize palette
+ initialize_palette();
+
+ // create sound stream
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate());
+
+ // allocate timers
+ m_int_timer = timer_alloc(TIMER_INT);
+ m_efx_timer = timer_alloc(TIMER_EFX);
+ m_dma_timer = timer_alloc(TIMER_DMA);
+ m_hsync_timer = timer_alloc(TIMER_HSYNC);
+
+ // find devices
+ m_screen->register_screen_bitmap(m_bitmap);
+
+ // register for state saving
+ save_item(NAME(m_disp));
+ save_item(NAME(m_dmaout));
+ save_item(NAME(m_bgcolor));
+ save_item(NAME(m_con));
+ save_item(NAME(m_aoe));
+ save_item(NAME(m_latch));
+ save_item(NAME(m_signal));
+ save_item(NAME(m_incr));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void cdp1864_device::device_reset()
+{
+ m_int_timer->adjust(m_screen->time_until_pos(CDP1864_SCANLINE_INT_START, 0));
+ m_efx_timer->adjust(m_screen->time_until_pos(CDP1864_SCANLINE_EFX_TOP_START, 0));
+ m_dma_timer->adjust(clocks_to_attotime(CDP1864_CYCLES_DMA_START));
+
+ m_disp = 0;
+ m_dmaout = 0;
+
+ m_write_irq(CLEAR_LINE);
+ m_write_dma_out(CLEAR_LINE);
+ m_write_efx(CLEAR_LINE);
+ m_write_hsync(CLEAR_LINE);
+}
+
+
+//-------------------------------------------------
+// device_timer - handle timer events
+//-------------------------------------------------
+
+void cdp1864_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ int scanline = m_screen->vpos();
+
+ switch (id)
+ {
+ case TIMER_INT:
+ if (scanline == CDP1864_SCANLINE_INT_START)
+ {
+ if (m_disp)
+ {
+ m_write_irq(ASSERT_LINE);
+ }
+
+ m_int_timer->adjust(m_screen->time_until_pos( CDP1864_SCANLINE_INT_END, 0));
+ }
+ else
+ {
+ if (m_disp)
+ {
+ m_write_irq(CLEAR_LINE);
+ }
+
+ m_int_timer->adjust(m_screen->time_until_pos(CDP1864_SCANLINE_INT_START, 0));
+ }
+ break;
+
+ case TIMER_EFX:
+ switch (scanline)
+ {
+ case CDP1864_SCANLINE_EFX_TOP_START:
+ m_write_efx(ASSERT_LINE);
+ m_efx_timer->adjust(m_screen->time_until_pos(CDP1864_SCANLINE_EFX_TOP_END, 0));
+ break;
+
+ case CDP1864_SCANLINE_EFX_TOP_END:
+ m_write_efx(CLEAR_LINE);
+ m_efx_timer->adjust(m_screen->time_until_pos(CDP1864_SCANLINE_EFX_BOTTOM_START, 0));
+ break;
+
+ case CDP1864_SCANLINE_EFX_BOTTOM_START:
+ m_write_efx(ASSERT_LINE);
+ m_efx_timer->adjust(m_screen->time_until_pos(CDP1864_SCANLINE_EFX_BOTTOM_END, 0));
+ break;
+
+ case CDP1864_SCANLINE_EFX_BOTTOM_END:
+ m_write_efx(CLEAR_LINE);
+ m_efx_timer->adjust(m_screen->time_until_pos(CDP1864_SCANLINE_EFX_TOP_START, 0));
+ break;
+ }
+ break;
+
+ case TIMER_DMA:
+ if (m_dmaout)
+ {
+ if (m_disp)
+ {
+ if (scanline >= CDP1864_SCANLINE_DISPLAY_START && scanline < CDP1864_SCANLINE_DISPLAY_END)
+ {
+ m_write_dma_out(CLEAR_LINE);
+ }
+ }
+
+ m_dma_timer->adjust(clocks_to_attotime(CDP1864_CYCLES_DMA_WAIT));
+
+ m_dmaout = 0;
+ }
+ else
+ {
+ if (m_disp)
+ {
+ if (scanline >= CDP1864_SCANLINE_DISPLAY_START && scanline < CDP1864_SCANLINE_DISPLAY_END)
+ {
+ m_write_dma_out(ASSERT_LINE);
+ }
+ }
+
+ m_dma_timer->adjust(clocks_to_attotime(CDP1864_CYCLES_DMA_ACTIVE));
+
+ m_dmaout = 1;
+ }
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void cdp1864_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ // reset the output stream
+ memset(outputs[0], 0, samples * sizeof(*outputs[0]));
+
+ INT16 signal = m_signal;
+ stream_sample_t *buffer = outputs[0];
+
+ memset( buffer, 0, samples * sizeof(*buffer) );
+
+ if (m_aoe)
+ {
+ double frequency = unscaled_clock() / 8 / 4 / (m_latch + 1) / 2;
+ int rate = machine().sample_rate() / 2;
+
+ /* get progress through wave */
+ int incr = m_incr;
+
+ if (signal < 0)
+ {
+ signal = -0x7fff;
+ }
+ else
+ {
+ signal = 0x7fff;
+ }
+
+ while( samples-- > 0 )
+ {
+ *buffer++ = signal;
+ incr -= frequency;
+ while( incr < 0 )
+ {
+ incr += rate;
+ signal = -signal;
+ }
+ }
+
+ /* store progress through wave */
+ m_incr = incr;
+ m_signal = signal;
+ }
+}
+
+
+//-------------------------------------------------
+// dispon_r -
+//-------------------------------------------------
+
+READ8_MEMBER( cdp1864_device::dispon_r )
+{
+ m_disp = 1;
+
+ return 0xff;
+}
+
+
+//-------------------------------------------------
+// dispoff_r -
+//-------------------------------------------------
+
+READ8_MEMBER( cdp1864_device::dispoff_r )
+{
+ m_disp = 0;
+
+ m_write_irq(CLEAR_LINE);
+ m_write_dma_out(CLEAR_LINE);
+
+ return 0xff;
+}
+
+
+//-------------------------------------------------
+// step_bgcolor_w -
+//-------------------------------------------------
+
+WRITE8_MEMBER( cdp1864_device::step_bgcolor_w )
+{
+ m_disp = 1;
+
+ m_bgcolor++;
+ m_bgcolor &= 0x03;
+}
+
+
+//-------------------------------------------------
+// tone_latch_w -
+//-------------------------------------------------
+
+WRITE8_MEMBER( cdp1864_device::tone_latch_w )
+{
+ m_latch = data;
+}
+
+
+//-------------------------------------------------
+// dma_w -
+//-------------------------------------------------
+
+WRITE8_MEMBER( cdp1864_device::dma_w )
+{
+ int rdata = 1, bdata = 1, gdata = 1;
+ int sx = m_screen->hpos() + 4;
+ int y = m_screen->vpos();
+
+ if (!m_con)
+ {
+ rdata = m_read_rdata();
+ bdata = m_read_bdata();
+ gdata = m_read_gdata();
+ }
+
+ for (int x = 0; x < 8; x++)
+ {
+ int color = bckgnd[m_bgcolor] + 8;
+
+ if (BIT(data, 7))
+ {
+ color = (gdata << 2) | (bdata << 1) | rdata;
+ }
+
+ m_bitmap.pix32(y, sx + x) = m_palette[color];
+
+ data <<= 1;
+ }
+}
+
+
+//-------------------------------------------------
+// con_w - color on write
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( cdp1864_device::con_w )
+{
+ if (!state)
+ {
+ m_con = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// aoe_w - audio output enable write
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( cdp1864_device::aoe_w )
+{
+ if (!state)
+ {
+ m_latch = CDP1864_DEFAULT_LATCH;
+ }
+
+ m_aoe = state;
+}
+
+
+//-------------------------------------------------
+// evs_w - external vertical sync write
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( cdp1864_device::evs_w )
+{
+}
+
+
+//-------------------------------------------------
+// update_screen -
+//-------------------------------------------------
+
+UINT32 cdp1864_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ if (m_disp)
+ {
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+ m_bitmap.fill(m_palette[bckgnd[m_bgcolor] + 8], cliprect);
+ }
+ else
+ {
+ bitmap.fill(rgb_t::black, cliprect);
+ }
+
+ return 0;
+}
+
+
+//-------------------------------------------------
+// initialize_palette -
+//-------------------------------------------------
+
+void cdp1864_device::initialize_palette()
+{
+ const int resistances_r[] = { m_chr_r };
+ const int resistances_g[] = { m_chr_g };
+ const int resistances_b[] = { m_chr_b };
+
+ double color_weights_r[1], color_weights_g[1], color_weights_b[1];
+ double color_weights_bkg_r[1], color_weights_bkg_g[1], color_weights_bkg_b[1];
+
+ compute_resistor_weights(0, 0xff, -1.0,
+ 1, resistances_r, color_weights_r, 0, m_chr_bkg,
+ 1, resistances_g, color_weights_g, 0, m_chr_bkg,
+ 1, resistances_b, color_weights_b, 0, m_chr_bkg);
+
+ compute_resistor_weights(0, 0xff, -1.0,
+ 1, resistances_r, color_weights_bkg_r, m_chr_bkg, 0,
+ 1, resistances_g, color_weights_bkg_g, m_chr_bkg, 0,
+ 1, resistances_b, color_weights_bkg_b, m_chr_bkg, 0);
+
+ for (int i = 0; i < 8; i++)
+ {
+ // foreground colors
+ UINT8 r = 0, g = 0, b = 0;
+
+ if (m_chr_r != RES_INF) r = combine_1_weights(color_weights_r, BIT(i, 0));
+ if (m_chr_b != RES_INF) b = combine_1_weights(color_weights_b, BIT(i, 1));
+ if (m_chr_g != RES_INF) g = combine_1_weights(color_weights_g, BIT(i, 2));
+
+ m_palette[i] = rgb_t(r, g, b);
+
+ // background colors
+ r = 0, g = 0, b = 0;
+
+ if (m_chr_r != RES_INF) r = combine_1_weights(color_weights_bkg_r, BIT(i, 0));
+ if (m_chr_b != RES_INF) b = combine_1_weights(color_weights_bkg_b, BIT(i, 1));
+ if (m_chr_g != RES_INF) g = combine_1_weights(color_weights_bkg_g, BIT(i, 2));
+
+ m_palette[i + 8] = rgb_t(r, g, b);
+ }
+}
diff --git a/src/devices/sound/cdp1864.h b/src/devices/sound/cdp1864.h
new file mode 100644
index 00000000000..ab9f384dc1a
--- /dev/null
+++ b/src/devices/sound/cdp1864.h
@@ -0,0 +1,208 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ RCA CDP1864C COS/MOS PAL Compatible Color TV Interface
+
+**********************************************************************
+ _____ _____
+ INLACE 1 |* \_/ | 40 Vdd
+ CLK IN_ 2 | | 39 AUD
+ CLR OUT_ 3 | | 38 CLR IN_
+ AOE 4 | | 37 DMA0_
+ SC1 5 | | 36 INT_
+ SC0 6 | | 35 TPA
+ MRD_ 7 | | 34 TPB
+ BUS 7 8 | | 33 EVS
+ BUS 6 9 | | 32 V SYNC
+ BUS 5 10 | CDP1864 | 31 H SYNC
+ BUS 4 11 | | 30 C SYNC_
+ BUS 3 12 | | 29 RED
+ BUS 2 13 | | 28 BLUE
+ BUS 1 14 | | 27 GREEN
+ BUS 0 15 | | 26 BCK GND_
+ CON_ 16 | | 25 BURST
+ N2 17 | | 24 ALT
+ EF_ 18 | | 23 R DATA
+ N0 19 | | 22 G DATA
+ Vss 20 |_____________| 21 B DATA
+
+
+ http://homepage.mac.com/ruske/cosmacelf/cdp1864.pdf
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __CDP1864__
+#define __CDP1864__
+
+#include "emu.h"
+#include "machine/rescap.h"
+#include "video/resnet.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define CDP1864_CLOCK XTAL_1_75MHz
+
+#define CDP1864_VISIBLE_COLUMNS 64
+#define CDP1864_VISIBLE_LINES 192
+
+#define CDP1864_HBLANK_END 1 * 8
+#define CDP1864_HBLANK_START 13 * 8
+#define CDP1864_HSYNC_START 0 * 8
+#define CDP1864_HSYNC_END 1 * 8
+#define CDP1864_SCREEN_START 4 * 8
+#define CDP1864_SCREEN_END 12 * 8
+#define CDP1864_SCREEN_WIDTH 14 * 8
+
+#define CDP1864_TOTAL_SCANLINES 312
+
+#define CDP1864_SCANLINE_VBLANK_START CDP1864_TOTAL_SCANLINES - 4
+#define CDP1864_SCANLINE_VBLANK_END 20
+#define CDP1864_SCANLINE_VSYNC_START 0
+#define CDP1864_SCANLINE_VSYNC_END 4
+#define CDP1864_SCANLINE_DISPLAY_START 60 // ???
+#define CDP1864_SCANLINE_DISPLAY_END CDP1864_SCANLINE_DISPLAY_START + CDP1864_VISIBLE_LINES
+#define CDP1864_SCANLINE_INT_START CDP1864_SCANLINE_DISPLAY_START - 2
+#define CDP1864_SCANLINE_INT_END CDP1864_SCANLINE_DISPLAY_START
+#define CDP1864_SCANLINE_EFX_TOP_START CDP1864_SCANLINE_DISPLAY_START - 4
+#define CDP1864_SCANLINE_EFX_TOP_END CDP1864_SCANLINE_DISPLAY_START
+#define CDP1864_SCANLINE_EFX_BOTTOM_START CDP1864_SCANLINE_DISPLAY_END - 4
+#define CDP1864_SCANLINE_EFX_BOTTOM_END CDP1864_SCANLINE_DISPLAY_END
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_CDP1864_ADD(_tag, _screen_tag, _clock, _inlace, _irq, _dma_out, _efx, _hsync, _rdata, _bdata, _gdata) \
+ MCFG_SOUND_ADD(_tag, CDP1864, _clock) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag) \
+ downcast<cdp1864_device *>(device)->set_inlace_callback(DEVCB_##_inlace); \
+ downcast<cdp1864_device *>(device)->set_irq_callback(DEVCB_##_irq); \
+ downcast<cdp1864_device *>(device)->set_dma_out_callback(DEVCB_##_dma_out); \
+ downcast<cdp1864_device *>(device)->set_efx_callback(DEVCB_##_efx); \
+ downcast<cdp1864_device *>(device)->set_hsync_callback(DEVCB_##_hsync); \
+ downcast<cdp1864_device *>(device)->set_rdata_callback(DEVCB_##_rdata); \
+ downcast<cdp1864_device *>(device)->set_bdata_callback(DEVCB_##_bdata); \
+ downcast<cdp1864_device *>(device)->set_gdata_callback(DEVCB_##_gdata);
+
+#define MCFG_CDP1864_CHROMINANCE(_r, _b, _g, _bkg) \
+ downcast<cdp1864_device *>(device)->set_chrominance_resistors(_r, _b, _g, _bkg);
+
+#define MCFG_CDP1864_SCREEN_ADD(_tag, _clock) \
+ MCFG_SCREEN_ADD(_tag, RASTER) \
+ MCFG_SCREEN_RAW_PARAMS(_clock, CDP1864_SCREEN_WIDTH, CDP1864_HBLANK_END, CDP1864_HBLANK_START, CDP1864_TOTAL_SCANLINES, CDP1864_SCANLINE_VBLANK_END, CDP1864_SCANLINE_VBLANK_START)
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> cdp1864_device
+
+class cdp1864_device : public device_t,
+ public device_sound_interface,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ cdp1864_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _inlace> void set_inlace_callback(_inlace inlace) { m_read_inlace.set_callback(inlace); }
+ template<class _irq> void set_irq_callback(_irq irq) { m_write_irq.set_callback(irq); }
+ template<class _dma_out> void set_dma_out_callback(_dma_out dma_out) { m_write_dma_out.set_callback(dma_out); }
+ template<class _efx> void set_efx_callback(_efx efx) { m_write_efx.set_callback(efx); }
+ template<class _hsync> void set_hsync_callback(_hsync hsync) { m_write_hsync.set_callback(hsync); }
+ template<class _rdata> void set_rdata_callback(_rdata rdata) { m_read_rdata.set_callback(rdata); }
+ template<class _bdata> void set_bdata_callback(_bdata bdata) { m_read_bdata.set_callback(bdata); }
+ template<class _gdata> void set_gdata_callback(_gdata gdata) { m_read_gdata.set_callback(gdata); }
+ void set_chrominance_resistors(double r, double b, double g, double bkg) { m_chr_r = r; m_chr_b = b; m_chr_g = g; m_chr_bkg = bkg; }
+
+ DECLARE_READ8_MEMBER( dispon_r );
+ DECLARE_READ8_MEMBER( dispoff_r );
+
+ DECLARE_WRITE8_MEMBER( step_bgcolor_w );
+ DECLARE_WRITE8_MEMBER( tone_latch_w );
+
+ DECLARE_WRITE8_MEMBER( dma_w );
+
+ DECLARE_WRITE_LINE_MEMBER( con_w );
+ DECLARE_WRITE_LINE_MEMBER( aoe_w );
+ DECLARE_WRITE_LINE_MEMBER( evs_w );
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // internal callbacks
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ enum
+ {
+ TIMER_INT,
+ TIMER_EFX,
+ TIMER_DMA,
+ TIMER_HSYNC
+ };
+
+ void initialize_palette();
+
+ static const int bckgnd[];
+
+ devcb_read_line m_read_inlace;
+ devcb_read_line m_read_rdata;
+ devcb_read_line m_read_bdata;
+ devcb_read_line m_read_gdata;
+ devcb_write_line m_write_irq;
+ devcb_write_line m_write_dma_out;
+ devcb_write_line m_write_efx;
+ devcb_write_line m_write_hsync;
+
+ bitmap_rgb32 m_bitmap; // bitmap
+ sound_stream *m_stream; // sound output
+
+ // video state
+ double m_chr_r; // red chrominance resistor value
+ double m_chr_b; // blue chrominance resistor value
+ double m_chr_g; // green chrominance resistor value
+ double m_chr_bkg; // background chrominance resistor value
+
+ rgb_t m_palette[16];
+ int m_disp; // display on
+ int m_dmaout; // DMA request active
+ int m_bgcolor; // background color
+ int m_con; // color on
+
+ // sound state
+ int m_aoe; // audio on
+ int m_latch; // sound latch
+ INT16 m_signal; // current signal
+ int m_incr; // initial wave state
+
+ // timers
+ emu_timer *m_int_timer;
+ emu_timer *m_efx_timer;
+ emu_timer *m_dma_timer;
+ emu_timer *m_hsync_timer;
+};
+
+
+// device type definition
+extern const device_type CDP1864;
+
+
+
+#endif
diff --git a/src/devices/sound/cdp1869.c b/src/devices/sound/cdp1869.c
new file mode 100644
index 00000000000..383d7eb5d88
--- /dev/null
+++ b/src/devices/sound/cdp1869.c
@@ -0,0 +1,1004 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ RCA CDP1869/1870/1876 Video Interface System (VIS) emulation
+
+**********************************************************************/
+
+/*
+
+ TODO:
+
+ - white noise
+ - scanline based update
+ - CMSEL output
+
+*/
+
+#include "emu.h"
+#include "cdp1869.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+#define CDP1869_WEIGHT_RED 30 // % of max luminance
+#define CDP1869_WEIGHT_GREEN 59
+#define CDP1869_WEIGHT_BLUE 11
+
+#define CDP1869_COLUMNS_HALF 20
+#define CDP1869_COLUMNS_FULL 40
+#define CDP1869_ROWS_HALF 12
+#define CDP1869_ROWS_FULL_PAL 25
+#define CDP1869_ROWS_FULL_NTSC 24
+
+enum
+{
+ CDB0 = 0,
+ CDB1,
+ CDB2,
+ CDB3,
+ CDB4,
+ CDB5,
+ CCB0,
+ CCB1
+};
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+// device type definition
+const device_type CDP1869 = &device_creator<cdp1869_device>;
+
+// I/O map
+DEVICE_ADDRESS_MAP_START( io_map, 8, cdp1869_device )
+ AM_RANGE(0x03, 0x03) AM_WRITE(out3_w)
+ AM_RANGE(0x04, 0x04) AM_WRITE(out4_w)
+ AM_RANGE(0x05, 0x05) AM_WRITE(out5_w)
+ AM_RANGE(0x06, 0x06) AM_WRITE(out6_w)
+ AM_RANGE(0x07, 0x07) AM_WRITE(out7_w)
+ADDRESS_MAP_END
+
+// character RAM map
+DEVICE_ADDRESS_MAP_START( char_map, 8, cdp1869_device )
+ AM_RANGE(0x000, 0x3ff) AM_READWRITE(char_ram_r, char_ram_w)
+ADDRESS_MAP_END
+
+// page RAM map
+DEVICE_ADDRESS_MAP_START( page_map, 8, cdp1869_device )
+ AM_RANGE(0x000, 0x7ff) AM_READWRITE(page_ram_r, page_ram_w)
+ADDRESS_MAP_END
+
+// default address map
+static ADDRESS_MAP_START( cdp1869, AS_0, 8, cdp1869_device )
+ AM_RANGE(0x000, 0x7ff) AM_RAM
+ADDRESS_MAP_END
+
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// is_ntsc - is device in NTSC mode
+//-------------------------------------------------
+
+inline bool cdp1869_device::is_ntsc()
+{
+ return m_read_pal_ntsc() ? false : true;
+}
+
+
+//-------------------------------------------------
+// read_page_ram_byte - read a page RAM byte at
+// the given address
+//-------------------------------------------------
+
+inline UINT8 cdp1869_device::read_page_ram_byte(offs_t pma)
+{
+ return space().read_byte(pma);
+}
+
+
+//-------------------------------------------------
+// write_page_ram_byte - write a page RAM byte at
+// the given address
+//-------------------------------------------------
+
+inline void cdp1869_device::write_page_ram_byte(offs_t pma, UINT8 data)
+{
+ space().write_byte(pma, data);
+}
+
+
+//-------------------------------------------------
+// read_char_ram_byte - read a char RAM byte at
+// the given address
+//-------------------------------------------------
+
+inline UINT8 cdp1869_device::read_char_ram_byte(offs_t pma, offs_t cma, UINT8 pmd)
+{
+ UINT8 data = 0;
+
+ if (!m_in_char_ram_func.isnull())
+ {
+ data = m_in_char_ram_func(pma, cma, pmd);
+ }
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// write_char_ram_byte - write a char RAM byte at
+// the given address
+//-------------------------------------------------
+
+inline void cdp1869_device::write_char_ram_byte(offs_t pma, offs_t cma, UINT8 pmd, UINT8 data)
+{
+ if (!m_out_char_ram_func.isnull())
+ {
+ m_out_char_ram_func(pma, cma, pmd, data);
+ }
+}
+
+
+//-------------------------------------------------
+// read_pcb - read page control bit
+//-------------------------------------------------
+
+inline int cdp1869_device::read_pcb(offs_t pma, offs_t cma, UINT8 pmd)
+{
+ int pcb = 0;
+
+ if (!m_in_pcb_func.isnull())
+ {
+ pcb = m_in_pcb_func(pma, cma, pmd);
+ }
+
+ return pcb;
+}
+
+
+//-------------------------------------------------
+// update_prd_changed_timer -
+//-------------------------------------------------
+
+inline void cdp1869_device::update_prd_changed_timer()
+{
+ int start = CDP1869_SCANLINE_PREDISPLAY_START_PAL;
+ int end = CDP1869_SCANLINE_PREDISPLAY_END_PAL;
+ int next_state;
+ int scanline = m_screen->vpos();
+ int next_scanline;
+
+ if (is_ntsc())
+ {
+ start = CDP1869_SCANLINE_PREDISPLAY_START_NTSC;
+ end = CDP1869_SCANLINE_PREDISPLAY_END_NTSC;
+ }
+
+ if (scanline < start)
+ {
+ next_scanline = start;
+ next_state = ASSERT_LINE;
+ }
+ else if (scanline < end)
+ {
+ next_scanline = end;
+ next_state = CLEAR_LINE;
+ }
+ else
+ {
+ next_scanline = start;
+ next_state = ASSERT_LINE;
+ }
+
+ if (m_dispoff)
+ {
+ next_state = CLEAR_LINE;
+ }
+
+ attotime duration = m_screen->time_until_pos(next_scanline);
+ m_prd_timer->adjust(duration, next_state);
+}
+
+
+//-------------------------------------------------
+// get_rgb - get RGB value
+//-------------------------------------------------
+
+inline rgb_t cdp1869_device::get_rgb(int i, int c, int l)
+{
+ int luma = 0, r, g, b;
+
+ luma += (l & 4) ? CDP1869_WEIGHT_RED : 0;
+ luma += (l & 1) ? CDP1869_WEIGHT_GREEN : 0;
+ luma += (l & 2) ? CDP1869_WEIGHT_BLUE : 0;
+
+ luma = (luma * 0xff) / 100;
+
+ r = (c & 4) ? luma : 0;
+ g = (c & 1) ? luma : 0;
+ b = (c & 2) ? luma : 0;
+
+ return rgb_t(r, g, b);
+}
+
+
+//-------------------------------------------------
+// get_lines - get number of character lines
+//-------------------------------------------------
+
+inline int cdp1869_device::get_lines()
+{
+ if (m_line16 && !m_dblpage)
+ {
+ return 16;
+ }
+ else if (!m_line9)
+ {
+ return 9;
+ }
+ else
+ {
+ return 8;
+ }
+}
+
+
+//-------------------------------------------------
+// get_pmemsize - get page memory size
+//-------------------------------------------------
+
+inline UINT16 cdp1869_device::get_pmemsize(int cols, int rows)
+{
+ int pmemsize = cols * rows;
+
+ if (m_dblpage) pmemsize *= 2;
+ if (m_line16) pmemsize *= 2;
+
+ return pmemsize;
+}
+
+
+//-------------------------------------------------
+// get_pma - get page memory address
+//-------------------------------------------------
+
+inline UINT16 cdp1869_device::get_pma()
+{
+ if (m_dblpage)
+ {
+ return m_pma;
+ }
+ else
+ {
+ return m_pma & 0x3ff;
+ }
+}
+
+
+//-------------------------------------------------
+// get_pen - get pen for color bits
+//-------------------------------------------------
+
+inline int cdp1869_device::get_pen(int ccb0, int ccb1, int pcb)
+{
+ int r = 0, g = 0, b = 0;
+
+ switch (m_col)
+ {
+ case 0:
+ r = ccb0;
+ b = ccb1;
+ g = pcb;
+ break;
+
+ case 1:
+ r = ccb0;
+ b = pcb;
+ g = ccb1;
+ break;
+
+ case 2:
+ case 3:
+ r = pcb;
+ b = ccb0;
+ g = ccb1;
+ break;
+ }
+
+ int color = (r << 2) + (b << 1) + g;
+
+ if (m_cfc)
+ {
+ return color + ((m_bkg + 1) * 8);
+ }
+ else
+ {
+ return color;
+ }
+}
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// cdp1869_device - constructor
+//-------------------------------------------------
+
+cdp1869_device::cdp1869_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, CDP1869, "RCA CDP1869", tag, owner, clock, "cdp1869", __FILE__),
+ device_sound_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ device_memory_interface(mconfig, *this),
+ m_read_pal_ntsc(*this),
+ m_write_prd(*this),
+ m_color_clock(0),
+ m_stream(NULL),
+ m_palette(*this, "palette"),
+ m_space_config("pageram", ENDIANNESS_LITTLE, 8, 11, 0, NULL, *ADDRESS_MAP_NAME(cdp1869))
+{
+}
+
+static MACHINE_CONFIG_FRAGMENT( cdp1869 )
+ MCFG_PALETTE_ADD("palette", 8+64)
+ MCFG_PALETTE_INIT_OWNER(cdp1869_device, cdp1869)
+MACHINE_CONFIG_END
+
+//-------------------------------------------------
+// machine_config_additions - return a pointer to
+// the device's machine fragment
+//-------------------------------------------------
+
+machine_config_constructor cdp1869_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( cdp1869 );
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void cdp1869_device::device_start()
+{
+ // resolve callbacks
+ m_read_pal_ntsc.resolve_safe(0);
+ m_write_prd.resolve_safe();
+ m_in_pcb_func.bind_relative_to(*owner());
+ m_in_char_ram_func.bind_relative_to(*owner());
+ m_out_char_ram_func.bind_relative_to(*owner());
+
+ // allocate timers
+ m_prd_timer = timer_alloc();
+ m_dispoff = 0;
+ update_prd_changed_timer();
+
+ // initialize palette
+ m_bkg = 0;
+
+ // create sound stream
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate());
+
+ // initialize other
+ m_tonediv = 0;
+ m_tonefreq = 0;
+ m_toneamp = 0;
+ m_dblpage = 0;
+ m_line16 = 0;
+ m_line9 = 0;
+ m_fresvert = 0;
+ m_freshorz = 0;
+ m_hma = 0;
+ m_col = 0;
+ m_incr = 0;
+ m_signal = 0;
+ m_cfc = 0;
+ m_toneoff = 0;
+ m_cmem = 0;
+
+ // register for state saving
+ save_item(NAME(m_prd));
+ save_item(NAME(m_dispoff));
+ save_item(NAME(m_fresvert));
+ save_item(NAME(m_freshorz));
+ save_item(NAME(m_cmem));
+ save_item(NAME(m_dblpage));
+ save_item(NAME(m_line16));
+ save_item(NAME(m_line9));
+ save_item(NAME(m_cfc));
+ save_item(NAME(m_col));
+ save_item(NAME(m_bkg));
+ save_item(NAME(m_pma));
+ save_item(NAME(m_hma));
+ save_item(NAME(m_signal));
+ save_item(NAME(m_incr));
+ save_item(NAME(m_toneoff));
+ save_item(NAME(m_wnoff));
+ save_item(NAME(m_tonediv));
+ save_item(NAME(m_tonefreq));
+ save_item(NAME(m_toneamp));
+ save_item(NAME(m_wnfreq));
+ save_item(NAME(m_wnamp));
+}
+
+
+//-------------------------------------------------
+// device_post_load - device-specific post-load
+//-------------------------------------------------
+
+void cdp1869_device::device_post_load()
+{
+ update_prd_changed_timer();
+}
+
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+
+void cdp1869_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ m_write_prd(param);
+ m_prd = param;
+
+ update_prd_changed_timer();
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *cdp1869_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == 0) ? &m_space_config : NULL;
+}
+
+
+//-------------------------------------------------
+// initialize_palette - initialize palette
+//-------------------------------------------------
+
+PALETTE_INIT_MEMBER(cdp1869_device, cdp1869)
+{
+ // color-on-color display (CFC=0)
+ int i;
+
+ for (i = 0; i < 8; i++)
+ {
+ palette.set_pen_color(i, get_rgb(i, i, 15));
+ }
+
+ // tone-on-tone display (CFC=1)
+ for (int c = 0; c < 8; c++)
+ {
+ for (int l = 0; l < 8; l++)
+ {
+ palette.set_pen_color(i, get_rgb(i, c, l));
+ i++;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void cdp1869_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ // reset the output stream
+ memset(outputs[0], 0, samples * sizeof(*outputs[0]));
+
+ INT16 signal = m_signal;
+ stream_sample_t *buffer = outputs[0];
+
+ if (!m_toneoff && m_toneamp)
+ {
+ double frequency = (clock() / 2) / (512 >> m_tonefreq) / (m_tonediv + 1);
+// double amplitude = m_toneamp * ((0.78*5) / 15);
+
+ int rate = machine().sample_rate() / 2;
+
+ /* get progress through wave */
+ int incr = m_incr;
+
+ if (signal < 0)
+ {
+ signal = -(m_toneamp * (0x07fff / 15));
+ }
+ else
+ {
+ signal = m_toneamp * (0x07fff / 15);
+ }
+
+ while( samples-- > 0 )
+ {
+ *buffer++ = signal;
+ incr -= frequency;
+ while( incr < 0 )
+ {
+ incr += rate;
+ signal = -signal;
+ }
+ }
+
+ /* store progress through wave */
+ m_incr = incr;
+ m_signal = signal;
+ }
+/*
+ if (!m_wnoff)
+ {
+ double amplitude = m_wnamp * ((0.78*5) / 15);
+
+ for (int wndiv = 0; wndiv < 128; wndiv++)
+ {
+ double frequency = (clock() / 2) / (4096 >> m_wnfreq) / (wndiv + 1):
+
+ sum_square_wave(buffer, frequency, amplitude);
+ }
+ }
+*/
+
+}
+
+
+//-------------------------------------------------
+// draw_line - draw character line
+//-------------------------------------------------
+
+void cdp1869_device::draw_line(bitmap_rgb32 &bitmap, const rectangle &rect, int x, int y, UINT8 data, int color)
+{
+ int i;
+ pen_t fg = m_palette->pen(color);
+
+ data <<= 2;
+
+ for (i = 0; i < CDP1869_CHAR_WIDTH; i++)
+ {
+ if (data & 0x80)
+ {
+ bitmap.pix32(y, x) = fg;
+
+ if (!m_fresvert)
+ {
+ bitmap.pix32(y + 1, x) = fg;
+ }
+
+ if (!m_freshorz)
+ {
+ bitmap.pix32(y, x + 1) = fg;
+
+ if (!m_fresvert)
+ {
+ bitmap.pix32(y + 1, x + 1) = fg;
+ }
+ }
+ }
+
+ if (!m_freshorz)
+ {
+ x++;
+ }
+
+ x++;
+
+ data <<= 1;
+ }
+}
+
+
+//-------------------------------------------------
+// draw_char - draw character
+//-------------------------------------------------
+
+void cdp1869_device::draw_char(bitmap_rgb32 &bitmap, const rectangle &rect, int x, int y, UINT16 pma)
+{
+ UINT8 pmd = read_page_ram_byte(pma);
+
+ for (UINT8 cma = 0; cma < get_lines(); cma++)
+ {
+ UINT8 data = read_char_ram_byte(pma, cma, pmd);
+
+ int ccb0 = BIT(data, CCB0);
+ int ccb1 = BIT(data, CCB1);
+ int pcb = read_pcb(pma, cma, pmd);
+
+ int color = get_pen(ccb0, ccb1, pcb);
+
+ draw_line(bitmap, rect, rect.min_x + x, rect.min_y + y, data, color);
+
+ y++;
+
+ if (!m_fresvert)
+ {
+ y++;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// out3_w - register 3 write
+//-------------------------------------------------
+
+WRITE8_MEMBER( cdp1869_device::out3_w )
+{
+ /*
+ bit description
+
+ 0 bkg green
+ 1 bkg blue
+ 2 bkg red
+ 3 cfc
+ 4 disp off
+ 5 colb0
+ 6 colb1
+ 7 fres horz
+ */
+
+ m_bkg = data & 0x07;
+ m_cfc = BIT(data, 3);
+ m_dispoff = BIT(data, 4);
+ m_col = (data & 0x60) >> 5;
+ m_freshorz = BIT(data, 7);
+}
+
+
+//-------------------------------------------------
+// out4_w - register 4 write
+//-------------------------------------------------
+
+WRITE8_MEMBER( cdp1869_device::out4_w )
+{
+ /*
+ bit description
+
+ 0 tone amp 2^0
+ 1 tone amp 2^1
+ 2 tone amp 2^2
+ 3 tone amp 2^3
+ 4 tone freq sel0
+ 5 tone freq sel1
+ 6 tone freq sel2
+ 7 tone off
+ 8 tone / 2^0
+ 9 tone / 2^1
+ 10 tone / 2^2
+ 11 tone / 2^3
+ 12 tone / 2^4
+ 13 tone / 2^5
+ 14 tone / 2^6
+ 15 always 0
+ */
+
+ m_toneamp = offset & 0x0f;
+ m_tonefreq = (offset & 0x70) >> 4;
+ m_toneoff = BIT(offset, 7);
+ m_tonediv = (offset & 0x7f00) >> 8;
+
+ m_stream->update();
+}
+
+
+//-------------------------------------------------
+// out5_w - register 5 write
+//-------------------------------------------------
+
+WRITE8_MEMBER( cdp1869_device::out5_w )
+{
+ /*
+ bit description
+
+ 0 cmem access mode
+ 1 x
+ 2 x
+ 3 9-line
+ 4 x
+ 5 16 line hi-res
+ 6 double page
+ 7 fres vert
+ 8 wn amp 2^0
+ 9 wn amp 2^1
+ 10 wn amp 2^2
+ 11 wn amp 2^3
+ 12 wn freq sel0
+ 13 wn freq sel1
+ 14 wn freq sel2
+ 15 wn off
+ */
+
+ m_cmem = BIT(offset, 0);
+ m_line9 = BIT(offset, 3);
+ m_line16 = BIT(offset, 5);
+ m_dblpage = BIT(offset, 6);
+ m_fresvert = BIT(offset, 7);
+ m_wnamp = (offset & 0x0f00) >> 8;
+ m_wnfreq = (offset & 0x7000) >> 12;
+ m_wnoff = BIT(offset, 15);
+
+ m_stream->update();
+
+ if (m_cmem)
+ {
+ m_pma = offset;
+ }
+ else
+ {
+ m_pma = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// out6_w - register 6 write
+//-------------------------------------------------
+
+WRITE8_MEMBER( cdp1869_device::out6_w )
+{
+ /*
+ bit description
+
+ 0 pma0 reg
+ 1 pma1 reg
+ 2 pma2 reg
+ 3 pma3 reg
+ 4 pma4 reg
+ 5 pma5 reg
+ 6 pma6 reg
+ 7 pma7 reg
+ 8 pma8 reg
+ 9 pma9 reg
+ 10 pma10 reg
+ 11 x
+ 12 x
+ 13 x
+ 14 x
+ 15 x
+ */
+
+ m_pma = offset & 0x7ff;
+}
+
+
+//-------------------------------------------------
+// out7_w - register 7 write
+//-------------------------------------------------
+
+WRITE8_MEMBER( cdp1869_device::out7_w )
+{
+ /*
+ bit description
+
+ 0 x
+ 1 x
+ 2 hma2 reg
+ 3 hma3 reg
+ 4 hma4 reg
+ 5 hma5 reg
+ 6 hma6 reg
+ 7 hma7 reg
+ 8 hma8 reg
+ 9 hma9 reg
+ 10 hma10 reg
+ 11 x
+ 12 x
+ 13 x
+ 14 x
+ 15 x
+ */
+
+ m_hma = offset & 0x7fc;
+}
+
+
+//-------------------------------------------------
+// char_ram_r - character RAM read
+//-------------------------------------------------
+
+READ8_MEMBER( cdp1869_device::char_ram_r )
+{
+ UINT8 cma = offset & 0x0f;
+ UINT16 pma;
+
+ if (m_cmem)
+ {
+ pma = get_pma();
+ }
+ else
+ {
+ pma = offset;
+ }
+
+ if (m_dblpage)
+ {
+ cma &= 0x07;
+ }
+
+ UINT8 pmd = read_page_ram_byte(pma);
+
+ return read_char_ram_byte(pma, cma, pmd);
+}
+
+
+//-------------------------------------------------
+// char_ram_w - character RAM write
+//-------------------------------------------------
+
+WRITE8_MEMBER( cdp1869_device::char_ram_w )
+{
+ UINT8 cma = offset & 0x0f;
+ UINT16 pma;
+
+ if (m_cmem)
+ {
+ pma = get_pma();
+ }
+ else
+ {
+ pma = offset;
+ }
+
+ if (m_dblpage)
+ {
+ cma &= 0x07;
+ }
+
+ UINT8 pmd = read_page_ram_byte(pma);
+
+ write_char_ram_byte(pma, cma, pmd, data);
+}
+
+
+//-------------------------------------------------
+// page_ram_r - page RAM read
+//-------------------------------------------------
+
+READ8_MEMBER( cdp1869_device::page_ram_r )
+{
+ UINT16 pma;
+
+ if (m_cmem)
+ {
+ pma = get_pma();
+ }
+ else
+ {
+ pma = offset;
+ }
+
+ return read_page_ram_byte(pma);
+}
+
+
+//-------------------------------------------------
+// page_ram_w - page RAM write
+//-------------------------------------------------
+
+WRITE8_MEMBER( cdp1869_device::page_ram_w )
+{
+ UINT16 pma;
+
+ if (m_cmem)
+ {
+ pma = get_pma();
+ }
+ else
+ {
+ pma = offset;
+ }
+
+ write_page_ram_byte(pma, data);
+}
+
+
+//-------------------------------------------------
+// page_ram_w - predisplay
+//-------------------------------------------------
+
+READ_LINE_MEMBER( cdp1869_device::predisplay_r )
+{
+ return m_prd;
+}
+
+
+//-------------------------------------------------
+// pal_ntsc_r - PAL/NTSC
+//-------------------------------------------------
+
+READ_LINE_MEMBER( cdp1869_device::pal_ntsc_r )
+{
+ return m_read_pal_ntsc();
+}
+
+
+//-------------------------------------------------
+// update_screen - update screen
+//-------------------------------------------------
+
+UINT32 cdp1869_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ rectangle screen_rect, outer;
+
+ if (is_ntsc())
+ {
+ outer.min_x = CDP1869_HBLANK_END;
+ outer.max_x = CDP1869_HBLANK_START - 1;
+ outer.min_y = CDP1869_SCANLINE_VBLANK_END_NTSC;
+ outer.max_y = CDP1869_SCANLINE_VBLANK_START_NTSC - 1;
+ screen_rect.min_x = CDP1869_SCREEN_START_NTSC;
+ screen_rect.max_x = CDP1869_SCREEN_END - 1;
+ screen_rect.min_y = CDP1869_SCANLINE_DISPLAY_START_NTSC;
+ screen_rect.max_y = CDP1869_SCANLINE_DISPLAY_END_NTSC - 1;
+ }
+ else
+ {
+ outer.min_x = CDP1869_HBLANK_END;
+ outer.max_x = CDP1869_HBLANK_START - 1;
+ outer.min_y = CDP1869_SCANLINE_VBLANK_END_PAL;
+ outer.max_y = CDP1869_SCANLINE_VBLANK_START_PAL - 1;
+ screen_rect.min_x = CDP1869_SCREEN_START_PAL;
+ screen_rect.max_x = CDP1869_SCREEN_END - 1;
+ screen_rect.min_y = CDP1869_SCANLINE_DISPLAY_START_PAL;
+ screen_rect.max_y = CDP1869_SCANLINE_DISPLAY_END_PAL - 1;
+ }
+
+ outer &= cliprect;
+ bitmap.fill(m_palette->pen(m_bkg), outer);
+
+ if (!m_dispoff)
+ {
+ int width = CDP1869_CHAR_WIDTH;
+ int height = get_lines();
+
+ if (!m_freshorz)
+ {
+ width *= 2;
+ }
+
+ if (!m_fresvert)
+ {
+ height *= 2;
+ }
+
+ int cols = m_freshorz ? CDP1869_COLUMNS_FULL : CDP1869_COLUMNS_HALF;
+ int rows = screen_rect.height() / height;
+
+ UINT16 pmemsize = get_pmemsize(cols, rows);
+ UINT16 addr = m_hma;
+
+ for (int sy = 0; sy < rows; sy++)
+ {
+ for (int sx = 0; sx < cols; sx++)
+ {
+ int x = sx * width;
+ int y = sy * height;
+
+ draw_char(bitmap, screen_rect, x, y, addr);
+
+ addr++;
+
+ if (addr == pmemsize) addr = 0;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/src/devices/sound/cdp1869.h b/src/devices/sound/cdp1869.h
new file mode 100644
index 00000000000..96185021ac4
--- /dev/null
+++ b/src/devices/sound/cdp1869.h
@@ -0,0 +1,302 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ RCA CDP1869/1870/1876 Video Interface System (VIS) emulation
+
+**********************************************************************
+ _____ _____
+ TPA 1 |* \_/ | 40 Vdd
+ TPB 2 | | 39 PMSEL
+ _MRD 3 | | 38 _PMWR
+ _MWR 4 | | 37 CMSEL
+ MA0/8 5 | | 36 _CMWR
+ MA1/9 6 | | 35 PMA0
+ MA2/10 7 | | 34 PMA1
+ MA3/11 8 | | 33 PMA2
+ MA4/12 9 | | 32 PMA3
+ MA5/13 10 | | 31 PMA4
+ MA6/14 11 | CDP1869 | 30 PMA5
+ MA7/15 12 | | 29 PMA6
+ N0 13 | | 28 PMA7
+ N1 14 | | 27 PMA8
+ N2 15 | | 26 PMA9
+ _H SYNC 16 | | 25 CMA3/PMA10
+ _DISPLAY 17 | | 24 CMA2
+ _ADDRSTB 18 | | 23 CMA1
+ SOUND 19 | | 22 CMA0
+ Vss 20 |_____________| 21 _N=3
+
+ _____ _____
+ _PREDISPLAY 1 |* \_/ | 40 Vdd
+ _DISPLAY 2 | | 39 PAL/_NTSC
+ PCB 3 | | 38 CPUCLK
+ CCB1 4 | | 37 XTAL (DOT)
+ BUS7 5 | | 36 _XTAL (DOT)
+ CCB0 6 | | 35 _ADDRSTB
+ BUS6 7 | | 34 _MRD
+ CDB5 8 | | 33 TPB
+ BUS5 9 | | 32 CMSEL
+ CDB4 10 | | 31 BURST
+ BUS4 11 | CDP1870 | 30 _H SYNC
+ CDB3 12 | | 29 _COMPSYNC
+ BUS3 13 | | 28 LUM
+ CDB2 14 | | 27 PAL CHROM
+ BUS2 15 | | 26 NTSC CHROM
+ CDB1 16 | | 25 _XTAL (CHROM)
+ BUS1 17 | | 24 XTAL (CHROM)
+ CDB0 18 | | 23 _EMS
+ BUS0 19 | | 22 _EVS
+ Vss 20 |_____________| 21 _N=3
+
+ _____ _____
+ _PREDISPLAY 1 |* \_/ | 40 Vdd
+ _DISPLAY 2 | | 39 PAL/_NTSC
+ PCB 3 | | 38 CPUCLK
+ CCB1 4 | | 37 XTAL (DOT)
+ BUS7 5 | | 36 _XTAL (DOT)
+ CCB0 6 | | 35 _ADDRSTB
+ BUS6 7 | | 34 _MRD
+ CDB5 8 | | 33 TPB
+ BUS5 9 | | 32 CMSEL
+ CDB4 10 | | 31 BURST
+ BUS4 11 | CDP1876 | 30 _H SYNC
+ CDB3 12 | | 29 _COMPSYNC
+ BUS3 13 | | 28 RED
+ CDB2 14 | | 27 BLUE
+ BUS2 15 | | 26 GREEN
+ CDB1 16 | | 25 _XTAL (CHROM)
+ BUS1 17 | | 24 XTAL (CHROM)
+ CDB0 18 | | 23 _EMS
+ BUS0 19 | | 22 _EVS
+ Vss 20 |_____________| 21 _N=3
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __CDP1869__
+#define __CDP1869__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define CDP1869_DOT_CLK_PAL (float)XTAL_5_626MHz
+#define CDP1869_DOT_CLK_NTSC (float)XTAL_5_67MHz
+#define CDP1869_COLOR_CLK_PAL (float)XTAL_8_867236MHz
+#define CDP1869_COLOR_CLK_NTSC (float)XTAL_7_15909MHz
+
+#define CDP1869_CPU_CLK_PAL (CDP1869_DOT_CLK_PAL / 2)
+#define CDP1869_CPU_CLK_NTSC (CDP1869_DOT_CLK_NTSC / 2)
+
+#define CDP1869_CHAR_WIDTH 6
+
+#define CDP1869_HSYNC_START (56 * CDP1869_CHAR_WIDTH)
+#define CDP1869_HSYNC_END (60 * CDP1869_CHAR_WIDTH)
+#define CDP1869_HBLANK_START (54 * CDP1869_CHAR_WIDTH)
+#define CDP1869_HBLANK_END ( 5 * CDP1869_CHAR_WIDTH)
+#define CDP1869_SCREEN_START_PAL ( 9 * CDP1869_CHAR_WIDTH)
+#define CDP1869_SCREEN_START_NTSC (10 * CDP1869_CHAR_WIDTH)
+#define CDP1869_SCREEN_START (10 * CDP1869_CHAR_WIDTH)
+#define CDP1869_SCREEN_END (50 * CDP1869_CHAR_WIDTH)
+#define CDP1869_SCREEN_WIDTH (60 * CDP1869_CHAR_WIDTH)
+
+#define CDP1869_TOTAL_SCANLINES_PAL 312
+#define CDP1869_SCANLINE_VBLANK_START_PAL 304
+#define CDP1869_SCANLINE_VBLANK_END_PAL 10
+#define CDP1869_SCANLINE_VSYNC_START_PAL 308
+#define CDP1869_SCANLINE_VSYNC_END_PAL 312
+#define CDP1869_SCANLINE_DISPLAY_START_PAL 44
+#define CDP1869_SCANLINE_DISPLAY_END_PAL 260
+#define CDP1869_SCANLINE_PREDISPLAY_START_PAL 43
+#define CDP1869_SCANLINE_PREDISPLAY_END_PAL 260
+#define CDP1869_VISIBLE_SCANLINES_PAL (CDP1869_SCANLINE_DISPLAY_END_PAL - CDP1869_SCANLINE_DISPLAY_START_PAL)
+
+#define CDP1869_TOTAL_SCANLINES_NTSC 262
+#define CDP1869_SCANLINE_VBLANK_START_NTSC 252
+#define CDP1869_SCANLINE_VBLANK_END_NTSC 10
+#define CDP1869_SCANLINE_VSYNC_START_NTSC 258
+#define CDP1869_SCANLINE_VSYNC_END_NTSC 262
+#define CDP1869_SCANLINE_DISPLAY_START_NTSC 36
+#define CDP1869_SCANLINE_DISPLAY_END_NTSC 228
+#define CDP1869_SCANLINE_PREDISPLAY_START_NTSC 35
+#define CDP1869_SCANLINE_PREDISPLAY_END_NTSC 228
+#define CDP1869_VISIBLE_SCANLINES_NTSC (CDP1869_SCANLINE_DISPLAY_END_NTSC - CDP1869_SCANLINE_DISPLAY_START_NTSC)
+
+#define CDP1869_PALETTE_LENGTH 8+64
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_CDP1869_ADD(_tag, _pixclock, _map) \
+ MCFG_DEVICE_ADD(_tag, CDP1869, _pixclock) \
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, _map)
+
+#define MCFG_CDP1869_SCREEN_PAL_ADD(_cdptag, _tag, _clock) \
+ MCFG_SCREEN_ADD(_tag, RASTER) \
+ MCFG_SCREEN_UPDATE_DEVICE(_cdptag, cdp1869_device, screen_update) \
+ MCFG_SCREEN_RAW_PARAMS(_clock, CDP1869_SCREEN_WIDTH, CDP1869_HBLANK_END, CDP1869_HBLANK_START, CDP1869_TOTAL_SCANLINES_PAL, CDP1869_SCANLINE_VBLANK_END_PAL, CDP1869_SCANLINE_VBLANK_START_PAL)
+
+#define MCFG_CDP1869_SCREEN_NTSC_ADD(_cdptag, _tag, _clock) \
+ MCFG_SCREEN_ADD(_tag, RASTER) \
+ MCFG_SCREEN_UPDATE_DEVICE(_cdptag, cdp1869_device, screen_update) \
+ MCFG_SCREEN_RAW_PARAMS(_clock, CDP1869_SCREEN_WIDTH, CDP1869_HBLANK_END, CDP1869_HBLANK_START, CDP1869_TOTAL_SCANLINES_NTSC, CDP1869_SCANLINE_VBLANK_END_NTSC, CDP1869_SCANLINE_VBLANK_START_NTSC)
+
+#define MCFG_CDP1869_SET_SCREEN MCFG_VIDEO_SET_SCREEN
+
+#define CDP1869_CHAR_RAM_READ_MEMBER(name) UINT8 name(UINT16 pma, UINT8 cma, UINT8 pmd)
+#define CDP1869_CHAR_RAM_WRITE_MEMBER(name) void name(UINT16 pma, UINT8 cma, UINT8 pmd, UINT8 data)
+#define CDP1869_PCB_READ_MEMBER(name) int name(UINT16 pma, UINT8 cma, UINT8 pmd)
+
+#define MCFG_CDP1869_PAL_NTSC_CALLBACK(_read) \
+ devcb = &cdp1869_device::set_pal_ntsc_rd_callback(*device, DEVCB_##_read);
+
+#define MCFG_CDP1869_PRD_CALLBACK(_write) \
+ devcb = &cdp1869_device::set_prd_wr_callback(*device, DEVCB_##_write);
+
+#define MCFG_CDP1869_COLOR_CLOCK(_clk) \
+ cdp1869_device::static_set_color_clock(*device, _clk);
+
+#define MCFG_CDP1869_CHAR_RAM_READ_OWNER(_class, _method) \
+ cdp1869_device::static_set_char_ram_read(*device, cdp1869_char_ram_read_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_CDP1869_CHAR_RAM_WRITE_OWNER(_class, _method) \
+ cdp1869_device::static_set_char_ram_write(*device, cdp1869_char_ram_write_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_CDP1869_CHAR_PCB_READ_OWNER(_class, _method) \
+ cdp1869_device::static_set_pcb_read(*device, cdp1869_pcb_read_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+typedef device_delegate<UINT8 (UINT16 pma, UINT8 cma, UINT8 pmd)> cdp1869_char_ram_read_delegate;
+typedef device_delegate<void (UINT16 pma, UINT8 cma, UINT8 pmd, UINT8 data)> cdp1869_char_ram_write_delegate;
+typedef device_delegate<int (UINT16 pma, UINT8 cma, UINT8 pmd)> cdp1869_pcb_read_delegate;
+
+// ======================> cdp1869_device
+
+class cdp1869_device : public device_t,
+ public device_sound_interface,
+ public device_video_interface,
+ public device_memory_interface
+{
+public:
+ // construction/destruction
+ cdp1869_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _Object> static devcb_base &set_pal_ntsc_rd_callback(device_t &device, _Object object) { return downcast<cdp1869_device &>(device).m_read_pal_ntsc.set_callback(object); }
+ template<class _Object> static devcb_base &set_prd_wr_callback(device_t &device, _Object object) { return downcast<cdp1869_device &>(device).m_write_prd.set_callback(object); }
+ static void static_set_char_ram_read(device_t &device, cdp1869_char_ram_read_delegate callback) { downcast<cdp1869_device &>(device).m_in_char_ram_func = callback; }
+ static void static_set_char_ram_write(device_t &device, cdp1869_char_ram_write_delegate callback) { downcast<cdp1869_device &>(device).m_out_char_ram_func = callback; }
+ static void static_set_pcb_read(device_t &device, cdp1869_pcb_read_delegate callback) { downcast<cdp1869_device &>(device).m_in_pcb_func = callback; }
+ static void static_set_color_clock(device_t &device, int color_clock) { downcast<cdp1869_device &>(device).m_color_clock = color_clock; }
+
+ DECLARE_PALETTE_INIT(cdp1869);
+
+ virtual DECLARE_ADDRESS_MAP(io_map, 8);
+ virtual DECLARE_ADDRESS_MAP(char_map, 8);
+ virtual DECLARE_ADDRESS_MAP(page_map, 8);
+
+ DECLARE_WRITE8_MEMBER( out3_w );
+ DECLARE_WRITE8_MEMBER( out4_w );
+ DECLARE_WRITE8_MEMBER( out5_w );
+ DECLARE_WRITE8_MEMBER( out6_w );
+ DECLARE_WRITE8_MEMBER( out7_w );
+
+ DECLARE_READ8_MEMBER( char_ram_r );
+ DECLARE_WRITE8_MEMBER( char_ram_w );
+
+ DECLARE_READ8_MEMBER( page_ram_r );
+ DECLARE_WRITE8_MEMBER( page_ram_w );
+
+ DECLARE_READ_LINE_MEMBER( predisplay_r );
+ DECLARE_READ_LINE_MEMBER( pal_ntsc_r );
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+protected:
+ // device-level overrides
+ virtual machine_config_constructor device_mconfig_additions() const;
+ virtual void device_start();
+ virtual void device_post_load();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ // device_sound_interface callbacks
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ inline bool is_ntsc();
+ inline UINT8 read_page_ram_byte(offs_t address);
+ inline void write_page_ram_byte(offs_t address, UINT8 data);
+ inline UINT8 read_char_ram_byte(offs_t pma, offs_t cma, UINT8 pmd);
+ inline void write_char_ram_byte(offs_t pma, offs_t cma, UINT8 pmd, UINT8 data);
+ inline int read_pcb(offs_t pma, offs_t cma, UINT8 pmd);
+ inline void update_prd_changed_timer();
+ inline rgb_t get_rgb(int i, int c, int l);
+ inline int get_lines();
+ inline UINT16 get_pmemsize(int cols, int rows);
+ inline UINT16 get_pma();
+ inline int get_pen(int ccb0, int ccb1, int pcb);
+
+ void draw_line(bitmap_rgb32 &bitmap, const rectangle &rect, int x, int y, UINT8 data, int color);
+ void draw_char(bitmap_rgb32 &bitmap, const rectangle &rect, int x, int y, UINT16 pma);
+
+private:
+ devcb_read_line m_read_pal_ntsc;
+ devcb_write_line m_write_prd;
+ cdp1869_pcb_read_delegate m_in_pcb_func;
+ cdp1869_char_ram_read_delegate m_in_char_ram_func;
+ cdp1869_char_ram_write_delegate m_out_char_ram_func;
+ int m_color_clock;
+
+ //address_space *m_page_ram;
+ emu_timer *m_prd_timer;
+ sound_stream *m_stream;
+ required_device<palette_device> m_palette;
+ const address_space_config m_space_config;
+
+ // video state
+ int m_prd; // predisplay
+ int m_dispoff; // display off
+ int m_fresvert; // full resolution vertical
+ int m_freshorz; // full resolution horizontal
+ int m_cmem; // character memory access mode
+ int m_dblpage; // double page mode
+ int m_line16; // 16-line hi-res mode
+ int m_line9; // 9 line mode
+ int m_cfc; // color format control
+ UINT8 m_col; // character color control
+ UINT8 m_bkg; // background color
+ UINT16 m_pma; // page memory address
+ UINT16 m_hma; // home memory address
+
+ // sound state
+ INT16 m_signal; // current signal
+ int m_incr; // initial wave state
+ int m_toneoff; // tone off
+ int m_wnoff; // white noise off
+ UINT8 m_tonediv; // tone divisor
+ UINT8 m_tonefreq; // tone range select
+ UINT8 m_toneamp; // tone output amplitude
+ UINT8 m_wnfreq; // white noise range select
+ UINT8 m_wnamp; // white noise output amplitude
+};
+
+
+// device type definition
+extern const device_type CDP1869;
+
+
+
+#endif
diff --git a/src/devices/sound/cem3394.c b/src/devices/sound/cem3394.c
new file mode 100644
index 00000000000..f5af2dd2b86
--- /dev/null
+++ b/src/devices/sound/cem3394.c
@@ -0,0 +1,552 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ CEM3394 sound driver.
+
+ This driver handles CEM-3394 analog synth chip. Very crudely.
+
+ Still to do:
+ - adjust the overall volume when multiple waves are being generated
+ - filter internal sound
+ - support resonance (don't understand how it works)
+
+***************************************************************************/
+
+#include "emu.h"
+#include "cem3394.h"
+
+
+
+/* waveform generation parameters */
+#define ENABLE_PULSE 1
+#define ENABLE_TRIANGLE 1
+#define ENABLE_SAWTOOTH 1
+#define ENABLE_EXTERNAL 1
+
+
+/* pulse shaping parameters */
+/* examples: */
+/* hat trick - skidding ice sounds too loud if minimum width is too big */
+/* snake pit - melody during first level too soft if minimum width is too small */
+/* snake pit - bonus counter at the end of level */
+/* snacks'n jaxson - laugh at end of level is too soft if minimum width is too small */
+
+#define LIMIT_WIDTH 1
+#define MINIMUM_WIDTH 0.25
+#define MAXIMUM_WIDTH 0.75
+
+
+/********************************************************************************
+
+ From the datasheet:
+
+ CEM3394_VCO_FREQUENCY:
+ -4.0 ... +4.0
+ -0.75 V/octave
+ f = exp(V) * 431.894
+
+ CEM3394_MODULATION_AMOUNT
+ 0.0 ... +3.5
+ 0.0 == 0.01 x frequency
+ 3.5 == 2.00 x frequency
+
+ CEM3394_WAVE_SELECT
+ -0.5 ... -0.2 == triangle
+ +0.9 ... +1.5 == triangle + sawtooth
+ +2.3 ... +3.9 == sawtooth
+
+ CEM3394_PULSE_WIDTH
+ 0.0 ... +2.0
+ 0.0 == 0% duty cycle
+ +2.0 == 100% duty cycle
+
+ CEM3394_MIXER_BALANCE
+ -4.0 ... +4.0
+ 0.0 both at -6dB
+ -20 dB/V
+
+ CEM3394_FILTER_RESONANCE
+ 0.0 ... +2.5
+ 0.0 == no resonance
+ +2.5 == oscillation
+
+ CEM3394_FILTER_FREQENCY
+ -3.0 ... +4.0
+ -0.375 V/octave
+ 0.0 == 1300Hz
+
+ CEM3394_FINAL_GAIN
+ 0.0 ... +4.0
+ -20 dB/V
+ 0.0 == -90dB
+ 4.0 == 0dB
+
+ Square wave output = 160 (average is constant regardless of duty cycle)
+ Sawtooth output = 200
+ Triangle output = 250
+ Sawtooth + triangle output = 330
+ Maximum output = 400
+
+********************************************************************************/
+
+
+// various waveforms
+#define WAVE_TRIANGLE 1
+#define WAVE_SAWTOOTH 2
+#define WAVE_PULSE 4
+
+// keep lots of fractional bits
+#define FRACTION_BITS 28
+#define FRACTION_ONE (1 << FRACTION_BITS)
+#define FRACTION_ONE_D ((double)(1 << FRACTION_BITS))
+#define FRACTION_MASK (FRACTION_ONE - 1)
+#define FRACTION_MULT(a,b) (((a) >> (FRACTION_BITS / 2)) * ((b) >> (FRACTION_BITS - FRACTION_BITS / 2)))
+
+
+// device type definition
+const device_type CEM3394 = &device_creator<cem3394_device>;
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// cem3394_device - constructor
+//-------------------------------------------------
+
+cem3394_device::cem3394_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, CEM3394, "CEM3394", tag, owner, clock, "cem3394", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_vco_zero_freq(0.0),
+ m_filter_zero_freq(0.0),
+ m_wave_select(0),
+ m_volume(0),
+ m_mixer_internal(0),
+ m_mixer_external(0),
+ m_position(0),
+ m_step(0),
+ m_filter_position(0),
+ m_filter_step(0),
+ m_modulation_depth(0),
+ m_last_ext(0),
+ m_pulse_width(0),
+ m_inv_sample_rate(0.0),
+ m_sample_rate(0),
+ m_mixer_buffer(NULL),
+ m_external_buffer(NULL)
+{
+ memset(m_values, 0, 8*sizeof(double));
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - generate sound to the mix buffer in mono
+//-------------------------------------------------
+
+void cem3394_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int int_volume = (m_volume * m_mixer_internal) / 256;
+ int ext_volume = (m_volume * m_mixer_external) / 256;
+ UINT32 step = m_step, position, end_position = 0;
+ stream_sample_t *buffer = outputs[0];
+ INT16 *mix, *ext;
+ int i;
+
+ /* external volume is effectively 0 if no external function */
+ if (m_ext_cb.isnull() || !ENABLE_EXTERNAL)
+ ext_volume = 0;
+
+ /* adjust the volume for the filter */
+ if (step > m_filter_step)
+ int_volume /= step - m_filter_step;
+
+ /* bail if nothing's going on */
+ if (int_volume == 0 && ext_volume == 0)
+ {
+ memset(buffer, 0, sizeof(*buffer) * samples);
+ return;
+ }
+
+ /* if there's external stuff, fetch and process it now */
+ if (ext_volume != 0)
+ {
+ UINT32 fposition = m_filter_position, fstep = m_filter_step, depth;
+ INT16 last_ext = m_last_ext;
+
+ /* fetch the external data */
+ m_ext_cb(samples, m_external_buffer);
+
+ /* compute the modulation depth, and adjust fstep to the maximum frequency */
+ /* we lop off 13 bits of depth so that we can multiply by stepadjust, below, */
+ /* which has 13 bits of precision */
+ depth = FRACTION_MULT(fstep, m_modulation_depth);
+ fstep += depth;
+ depth >>= 13;
+
+ /* "apply" the filter: note this is pretty cheesy; it basically just downsamples the
+ external sample to filter_freq by allowing only 2 transitions for every cycle */
+ for (i = 0, ext = m_external_buffer, position = m_position; i < samples; i++, ext++)
+ {
+ UINT32 newposition;
+ INT32 stepadjust;
+
+ /* update the position and compute the adjustment from a triangle wave */
+ if (position & (1 << (FRACTION_BITS - 1)))
+ stepadjust = 0x2000 - ((position >> (FRACTION_BITS - 14)) & 0x1fff);
+ else
+ stepadjust = (position >> (FRACTION_BITS - 14)) & 0x1fff;
+ position += step;
+
+ /* if we cross a half-step boundary, allow the next byte of the external input */
+ newposition = fposition + fstep - (stepadjust * depth);
+ if ((newposition ^ fposition) & ~(FRACTION_MASK >> 1))
+ last_ext = *ext;
+ else
+ *ext = last_ext;
+ fposition = newposition & FRACTION_MASK;
+ }
+
+ /* update the final filter values */
+ m_filter_position = fposition;
+ m_last_ext = last_ext;
+ }
+
+ /* if there's internal stuff, generate it */
+ if (int_volume != 0)
+ {
+ if (m_wave_select == 0 && !ext_volume)
+ logerror("%f V didn't cut it\n", m_values[CEM3394_WAVE_SELECT]);
+
+ /* handle the pulse component; it maxes out at 0x1932, which is 27% smaller than */
+ /* the sawtooth (since the value is constant, this is the best place to have an */
+ /* odd value for volume) */
+ if (ENABLE_PULSE && (m_wave_select & WAVE_PULSE))
+ {
+ UINT32 pulse_width = m_pulse_width;
+
+ /* if the width is wider than the step, we're guaranteed to hit it once per cycle */
+ if (pulse_width >= step)
+ {
+ for (i = 0, mix = m_mixer_buffer, position = m_position; i < samples; i++, mix++)
+ {
+ if (position < pulse_width)
+ *mix = 0x1932;
+ else
+ *mix = 0x0000;
+ position = (position + step) & FRACTION_MASK;
+ }
+ }
+
+ /* otherwise, we compute a volume and watch for cycle boundary crossings */
+ else
+ {
+ INT16 volume = 0x1932 * pulse_width / step;
+ for (i = 0, mix = m_mixer_buffer, position = m_position; i < samples; i++, mix++)
+ {
+ UINT32 newposition = position + step;
+ if ((newposition ^ position) & ~FRACTION_MASK)
+ *mix = volume;
+ else
+ *mix = 0x0000;
+ position = newposition & FRACTION_MASK;
+ }
+ }
+ end_position = position;
+ }
+
+ /* otherwise, clear the mixing buffer */
+ else
+ memset(m_mixer_buffer, 0, sizeof(INT16) * samples);
+
+ /* handle the sawtooth component; it maxes out at 0x2000, which is 27% larger */
+ /* than the pulse */
+ if (ENABLE_SAWTOOTH && (m_wave_select & WAVE_SAWTOOTH))
+ {
+ for (i = 0, mix = m_mixer_buffer, position = m_position; i < samples; i++, mix++)
+ {
+ *mix += ((position >> (FRACTION_BITS - 14)) & 0x3fff) - 0x2000;
+ position += step;
+ }
+ end_position = position & FRACTION_MASK;
+ }
+
+ /* handle the triangle component; it maxes out at 0x2800, which is 25% larger */
+ /* than the sawtooth (should be 27% according to the specs, but 25% saves us */
+ /* a multiplication) */
+ if (ENABLE_TRIANGLE && (m_wave_select & WAVE_TRIANGLE))
+ {
+ for (i = 0, mix = m_mixer_buffer, position = m_position; i < samples; i++, mix++)
+ {
+ INT16 value;
+ if (position & (1 << (FRACTION_BITS - 1)))
+ value = 0x2000 - ((position >> (FRACTION_BITS - 14)) & 0x1fff);
+ else
+ value = (position >> (FRACTION_BITS - 14)) & 0x1fff;
+ *mix += value + (value >> 2);
+ position += step;
+ }
+ end_position = position & FRACTION_MASK;
+ }
+
+ /* update the final position */
+ m_position = end_position;
+ }
+
+ /* mix it down */
+ mix = m_mixer_buffer;
+ ext = m_external_buffer;
+ {
+ /* internal + external */
+ if (ext_volume != 0 && int_volume != 0)
+ {
+ for (i = 0; i < samples; i++, mix++, ext++)
+ *buffer++ = (*mix * int_volume + *ext * ext_volume) / 128;
+ }
+ /* internal only */
+ else if (int_volume != 0)
+ {
+ for (i = 0; i < samples; i++, mix++)
+ *buffer++ = *mix * int_volume / 128;
+ }
+ /* external only */
+ else
+ {
+ for (i = 0; i < samples; i++, ext++)
+ *buffer++ = *ext * ext_volume / 128;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void cem3394_device::device_start()
+{
+ /* copy global parameters */
+ m_sample_rate = CEM3394_SAMPLE_RATE;
+ m_inv_sample_rate = 1.0 / (double)m_sample_rate;
+
+ /* allocate stream channels, 1 per chip */
+ m_stream = stream_alloc(0, 1, m_sample_rate);
+
+ m_ext_cb.bind_relative_to(*owner());
+
+ /* allocate memory for a mixer buffer and external buffer (1 second should do it!) */
+ m_mixer_buffer = auto_alloc_array(machine(), INT16, m_sample_rate);
+ m_external_buffer = auto_alloc_array(machine(), INT16, m_sample_rate);
+
+ save_item(NAME(m_values));
+ save_item(NAME(m_wave_select));
+ save_item(NAME(m_volume));
+ save_item(NAME(m_mixer_internal));
+ save_item(NAME(m_mixer_external));
+ save_item(NAME(m_position));
+ save_item(NAME(m_step));
+ save_item(NAME(m_filter_position));
+ save_item(NAME(m_filter_step));
+ save_item(NAME(m_modulation_depth));
+ save_item(NAME(m_last_ext));
+ save_item(NAME(m_pulse_width));
+}
+
+
+double cem3394_device::compute_db(double voltage)
+{
+ /* assumes 0.0 == full off, 4.0 == full on, with linear taper, as described in the datasheet */
+
+ /* above 4.0, maximum volume */
+ if (voltage >= 4.0)
+ return 0.0;
+
+ /* below 0.0, minimum volume */
+ else if (voltage <= 0.0)
+ return 90.0;
+
+ /* between 2.5 and 4.0, linear from 20dB to 0dB */
+ else if (voltage >= 2.5)
+ return (4.0 - voltage) * (1.0 / 1.5) * 20.0;
+
+ /* between 0.0 and 2.5, exponential to 20dB */
+ else
+ {
+ double temp = 20.0 * pow(2.0, 2.5 - voltage);
+ if (temp < 90.0) return 90.0;
+ else return temp;
+ }
+}
+
+
+UINT32 cem3394_device::compute_db_volume(double voltage)
+{
+ double temp;
+
+ /* assumes 0.0 == full off, 4.0 == full on, with linear taper, as described in the datasheet */
+
+ /* above 4.0, maximum volume */
+ if (voltage >= 4.0)
+ return 256;
+
+ /* below 0.0, minimum volume */
+ else if (voltage <= 0.0)
+ return 0;
+
+ /* between 2.5 and 4.0, linear from 20dB to 0dB */
+ else if (voltage >= 2.5)
+ temp = (4.0 - voltage) * (1.0 / 1.5) * 20.0;
+
+ /* between 0.0 and 2.5, exponential to 20dB */
+ else
+ {
+ temp = 20.0 * pow(2.0, 2.5 - voltage);
+ if (temp < 50.0) return 0;
+ }
+
+ /* convert from dB to volume and return */
+ return (UINT32)(256.0 * pow(0.891251, temp));
+}
+
+
+void cem3394_device::set_voltage(int input, double voltage)
+{
+ double temp;
+
+ /* don't do anything if no change */
+ if (voltage == m_values[input])
+ return;
+ m_values[input] = voltage;
+
+ /* update the stream first */
+ m_stream->update();
+
+ /* switch off the input */
+ switch (input)
+ {
+ /* frequency varies from -4.0 to +4.0, at 0.75V/octave */
+ case CEM3394_VCO_FREQUENCY:
+ temp = m_vco_zero_freq * pow(2.0, -voltage * (1.0 / 0.75));
+ m_step = (UINT32)(temp * m_inv_sample_rate * FRACTION_ONE_D);
+ break;
+
+ /* wave select determines triangle/sawtooth enable */
+ case CEM3394_WAVE_SELECT:
+ m_wave_select &= ~(WAVE_TRIANGLE | WAVE_SAWTOOTH);
+ if (voltage >= -0.5 && voltage <= -0.2)
+ m_wave_select |= WAVE_TRIANGLE;
+ else if (voltage >= 0.9 && voltage <= 1.5)
+ m_wave_select |= WAVE_TRIANGLE | WAVE_SAWTOOTH;
+ else if (voltage >= 2.3 && voltage <= 3.9)
+ m_wave_select |= WAVE_SAWTOOTH;
+ break;
+
+ /* pulse width determines duty cycle; 0.0 means 0%, 2.0 means 100% */
+ case CEM3394_PULSE_WIDTH:
+ if (voltage < 0.0)
+ {
+ m_pulse_width = 0;
+ m_wave_select &= ~WAVE_PULSE;
+ }
+ else
+ {
+ temp = voltage * 0.5;
+ if (LIMIT_WIDTH)
+ temp = MINIMUM_WIDTH + (MAXIMUM_WIDTH - MINIMUM_WIDTH) * temp;
+ m_pulse_width = (UINT32)(temp * FRACTION_ONE_D);
+ m_wave_select |= WAVE_PULSE;
+ }
+ break;
+
+ /* final gain is pretty self-explanatory; 0.0 means ~90dB, 4.0 means 0dB */
+ case CEM3394_FINAL_GAIN:
+ m_volume = compute_db_volume(voltage);
+ break;
+
+ /* mixer balance is a pan between the external input and the internal input */
+ /* 0.0 is equal parts of both; positive values favor external, negative favor internal */
+ case CEM3394_MIXER_BALANCE:
+ if (voltage >= 0.0)
+ {
+ m_mixer_internal = compute_db_volume(3.55 - voltage);
+ m_mixer_external = compute_db_volume(3.55 + 0.45 * (voltage * 0.25));
+ }
+ else
+ {
+ m_mixer_internal = compute_db_volume(3.55 - 0.45 * (voltage * 0.25));
+ m_mixer_external = compute_db_volume(3.55 + voltage);
+ }
+ break;
+
+ /* filter frequency varies from -4.0 to +4.0, at 0.375V/octave */
+ case CEM3394_FILTER_FREQENCY:
+ temp = m_filter_zero_freq * pow(2.0, -voltage * (1.0 / 0.375));
+ m_filter_step = (UINT32)(temp * m_inv_sample_rate * FRACTION_ONE_D);
+ break;
+
+ /* modulation depth is 0.01 at 0V and 2.0 at 3.5V; how it grows from one to the other */
+ /* is still unclear at this point */
+ case CEM3394_MODULATION_AMOUNT:
+ if (voltage < 0.0)
+ m_modulation_depth = (UINT32)(0.01 * FRACTION_ONE_D);
+ else if (voltage > 3.5)
+ m_modulation_depth = (UINT32)(2.00 * FRACTION_ONE_D);
+ else
+ m_modulation_depth = (UINT32)(((voltage * (1.0 / 3.5)) * 1.99 + 0.01) * FRACTION_ONE_D);
+ break;
+
+ /* this is not yet implemented */
+ case CEM3394_FILTER_RESONANCE:
+ break;
+ }
+}
+
+
+double cem3394_device::get_parameter(int input)
+{
+ double voltage = m_values[input];
+
+ switch (input)
+ {
+ case CEM3394_VCO_FREQUENCY:
+ return m_vco_zero_freq * pow(2.0, -voltage * (1.0 / 0.75));
+
+ case CEM3394_WAVE_SELECT:
+ return voltage;
+
+ case CEM3394_PULSE_WIDTH:
+ if (voltage <= 0.0)
+ return 0.0;
+ else if (voltage >= 2.0)
+ return 1.0;
+ else
+ return voltage * 0.5;
+
+ case CEM3394_FINAL_GAIN:
+ return compute_db(voltage);
+
+ case CEM3394_MIXER_BALANCE:
+ return voltage * 0.25;
+
+ case CEM3394_MODULATION_AMOUNT:
+ if (voltage < 0.0)
+ return 0.01;
+ else if (voltage > 3.5)
+ return 2.0;
+ else
+ return (voltage * (1.0 / 3.5)) * 1.99 + 0.01;
+
+ case CEM3394_FILTER_RESONANCE:
+ if (voltage < 0.0)
+ return 0.0;
+ else if (voltage > 2.5)
+ return 1.0;
+ else
+ return voltage * (1.0 / 2.5);
+
+ case CEM3394_FILTER_FREQENCY:
+ return m_filter_zero_freq * pow(2.0, -voltage * (1.0 / 0.375));
+ }
+ return 0.0;
+}
diff --git a/src/devices/sound/cem3394.h b/src/devices/sound/cem3394.h
new file mode 100644
index 00000000000..65290aefce9
--- /dev/null
+++ b/src/devices/sound/cem3394.h
@@ -0,0 +1,117 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+#pragma once
+
+#ifndef __CEM3394_H__
+#define __CEM3394_H__
+
+#define CEM3394_SAMPLE_RATE (44100*4)
+
+// inputs
+enum
+{
+ CEM3394_VCO_FREQUENCY = 0,
+ CEM3394_MODULATION_AMOUNT,
+ CEM3394_WAVE_SELECT,
+ CEM3394_PULSE_WIDTH,
+ CEM3394_MIXER_BALANCE,
+ CEM3394_FILTER_RESONANCE,
+ CEM3394_FILTER_FREQENCY,
+ CEM3394_FINAL_GAIN
+};
+
+typedef device_delegate<void (int count, short *buffer)> cem3394_ext_input_delegate;
+
+#define CEM3394_EXT_INPUT(_name) void _name(int count, short *buffer)
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_CEM3394_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, CEM3394, _clock)
+#define MCFG_CEM3394_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, CEM3394, _clock)
+
+#define MCFG_CEM3394_EXT_INPUT_CB(_class, _method) \
+ cem3394_device::set_ext_input_callback(*device, cem3394_ext_input_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_CEM3394_VCO_ZERO(_freq) \
+ cem3394_device::set_vco_zero_freq(*device, _freq);
+
+#define MCFG_CEM3394_FILTER_ZERO(_freq) \
+ cem3394_device::set_filter_zero_freq(*device, _freq);
+
+
+class cem3394_device : public device_t,
+ public device_sound_interface
+{
+public:
+ cem3394_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~cem3394_device() { }
+
+ static void set_ext_input_callback(device_t &device, cem3394_ext_input_delegate callback) { downcast<cem3394_device &>(device).m_ext_cb = callback; }
+ static void set_vco_zero_freq(device_t &device, double freq) { downcast<cem3394_device &>(device).m_vco_zero_freq = freq; }
+ static void set_filter_zero_freq(device_t &device, double freq) { downcast<cem3394_device &>(device).m_filter_zero_freq = freq; }
+
+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);
+
+public:
+ // Set the voltage going to a particular parameter
+ void set_voltage(int input, double voltage);
+
+ // Get the translated parameter associated with the given input as follows:
+ // CEM3394_VCO_FREQUENCY: frequency in Hz
+ // CEM3394_MODULATION_AMOUNT: scale factor, 0.0 to 2.0
+ // CEM3394_WAVE_SELECT: voltage from this line
+ // CEM3394_PULSE_WIDTH: width fraction, from 0.0 to 1.0
+ // CEM3394_MIXER_BALANCE: balance, from -1.0 to 1.0
+ // CEM3394_FILTER_RESONANCE: resonance, from 0.0 to 1.0
+ // CEM3394_FILTER_FREQENCY: frequency, in Hz
+ // CEM3394_FINAL_GAIN: gain, in dB
+ double get_parameter(int input);
+
+private:
+ double compute_db(double voltage);
+ UINT32 compute_db_volume(double voltage);
+
+private:
+ cem3394_ext_input_delegate m_ext_cb; /* callback to generate external samples */
+
+ sound_stream *m_stream; /* our stream */
+ double m_vco_zero_freq; /* frequency of VCO at 0.0V */
+ double m_filter_zero_freq; /* frequency of filter at 0.0V */
+
+ double m_values[8]; /* raw values of registers */
+ UINT8 m_wave_select; /* flags which waveforms are enabled */
+
+ UINT32 m_volume; /* linear overall volume (0-256) */
+ UINT32 m_mixer_internal; /* linear internal volume (0-256) */
+ UINT32 m_mixer_external; /* linear external volume (0-256) */
+
+ UINT32 m_position; /* current VCO frequency position (0.FRACTION_BITS) */
+ UINT32 m_step; /* per-sample VCO step (0.FRACTION_BITS) */
+
+ UINT32 m_filter_position; /* current filter frequency position (0.FRACTION_BITS) */
+ UINT32 m_filter_step; /* per-sample filter step (0.FRACTION_BITS) */
+ UINT32 m_modulation_depth; /* fraction of total by which we modulate (0.FRACTION_BITS) */
+ INT16 m_last_ext; /* last external sample we read */
+
+ UINT32 m_pulse_width; /* fractional pulse width (0.FRACTION_BITS) */
+
+ double m_inv_sample_rate;
+ int m_sample_rate;
+
+ INT16 *m_mixer_buffer;
+ INT16 *m_external_buffer;
+};
+
+extern const device_type CEM3394;
+
+
+#endif /* __CEM3394_H__ */
diff --git a/src/devices/sound/dac.c b/src/devices/sound/dac.c
new file mode 100644
index 00000000000..8e4baffe3aa
--- /dev/null
+++ b/src/devices/sound/dac.c
@@ -0,0 +1,92 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ dac.c
+
+ DAC device emulator.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "dac.h"
+
+
+// device type definition
+const device_type DAC = &device_creator<dac_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// dac_device - constructor
+//-------------------------------------------------
+
+dac_device::dac_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, DAC, "DAC", tag, owner, clock, "dac", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_output(0)
+{
+}
+
+
+//-------------------------------------------------
+// write_unsigned8 - write an 8-bit value,
+// keeping the scaled result unsigned
+//-------------------------------------------------
+
+WRITE8_MEMBER( dac_device::write_unsigned8 )
+{
+ write_unsigned8(data);
+}
+
+
+//-------------------------------------------------
+// write_signed8 - write an 8-bit value,
+// keeping the scaled result signed
+//-------------------------------------------------
+
+WRITE8_MEMBER( dac_device::write_signed8 )
+{
+ write_signed8(data);
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void dac_device::device_start()
+{
+ // create the stream
+ m_stream = stream_alloc(0, 1, DEFAULT_SAMPLE_RATE);
+
+ // register for save states
+ save_item(NAME(m_output));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void dac_device::device_reset()
+{
+ m_output = 0;
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests
+// for our sound stream
+//-------------------------------------------------
+
+void dac_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ // just fill with current value
+ for (int samp = 0; samp < samples; samp++)
+ outputs[0][samp] = m_output;
+}
diff --git a/src/devices/sound/dac.h b/src/devices/sound/dac.h
new file mode 100644
index 00000000000..fadc1a7e324
--- /dev/null
+++ b/src/devices/sound/dac.h
@@ -0,0 +1,70 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ dac.h
+
+ DAC device emulator.
+
+***************************************************************************/
+
+#ifndef __DAC_H__
+#define __DAC_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_DAC_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, DAC, 0)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+
+// ======================> dac_device
+
+class dac_device : public device_t,
+ public device_sound_interface
+{
+ // default to 4x oversampling
+ static const UINT32 DEFAULT_SAMPLE_RATE = 48000 * 4;
+
+public:
+ // construction/destruction
+ dac_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // public interface
+ INT16 output() const { return m_output; }
+ void write(INT16 data) { m_stream->update(); m_output = data; }
+ void write_unsigned8(UINT8 data) { write(data * 32767 / 255); } // 0..255, mapped to 0..32767
+ void write_signed8(UINT8 data) { write((data - 0x80) * 32767 / 128); } // 0..255, mapped to -32767..32767
+ void write_unsigned16(UINT16 data) { write(data / 2); } // 0..65535, mapped to 0..32767
+ void write_signed16(UINT16 data) { write(data - 0x8000); } // 0..65535, mapped to -32768..32767
+
+ // wrappers
+ DECLARE_WRITE8_MEMBER( write_unsigned8 );
+ DECLARE_WRITE8_MEMBER( write_signed8 );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ // internal state
+ sound_stream * m_stream;
+ INT16 m_output;
+};
+
+
+// device type definition
+extern const device_type DAC;
+
+
+#endif /* __DAC_H__ */
diff --git a/src/devices/sound/digitalk.c b/src/devices/sound/digitalk.c
new file mode 100644
index 00000000000..dcd0a965408
--- /dev/null
+++ b/src/devices/sound/digitalk.c
@@ -0,0 +1,692 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+#include "emu.h"
+#include "digitalk.h"
+
+/*
+ National Semiconductor's Digitalker, also known as MM54104.
+
+This is a sample decompression chip where the codec is very
+specialized for speech.
+
+ - Driver history
+
+The history of this driver is a little strange. The real
+reverse-engineering work has been done by Kevin Horton
+(single-stepping the chip and everything) with assistance by Lord
+Nightmare who had done the work (with help from Mr. Horton) on the tsi
+s14001a, a predecessor of the digitalker. Mr. Horton was not
+interested in publishing his findings, but provided full-rate
+resynthesized samples for the game scorpion. This driver is the
+result of analyzing these samples.
+
+
+ - The Chip
+
+Pinout from chipdir, added there by Agustin Yado. Added rompwr.
+Package is DIP40. Standard osc is 4MHz, maximum is 5Mhz.
+
+ +--()--+
+ osc in | 1 40| vdd
+ osc out | 2 39| speech out
+ cs | 3 38| adr 13
+ wr | 4 37| adr 12
+ rompwr | 5 36| adr 11
+ intr | 6 35| adr 10
+ cms | 7 34| adr 9
+ d0 | 8 33| adr 8
+ d1 | 9 32| adr 7
+ d2 |10 31| adr 6
+ d3 |11 30| adr 5
+ d4 |12 29| adr 4
+ d5 |13 28| adr 3
+ d6 |14 27| adr 2
+ d7 |15 26| adr 1
+ rdata 0 |16 25| adr 0
+ rdata 1 |17 24| rdata 7
+ rdata 2 |18 23| rdata 6
+ rdata 3 |19 22| rdata 5
+ vss |20 21| rdata 4
+ +------+
+
+Pin functions, excerpt from
+http://www.ski.org/Rehab/sktf/vol06no1Winter1985.html, slightly modified
+"Smith-Kettlewell Technical File, Vol 6, No 1, winter 1985"
+
+ On the controller chip, pin 40 is VCC, while pin 20 is ground. VCC
+ for this chip is between 7 and 11 VDC, and pin 40 is bypassed to
+ pin 20 by 0.1uF. Maximum current is listed at 45mA.
+
+ Pin 3 is called "Chip Select Not," and can be taken high to "open"
+ the input address and control lines. This is used in cases where
+ the Digitalker is connected to a computer bus, and the address
+ lines need to be floated while the bus is doing something else. In
+ other words, taking pin 3 high makes the Digitalker turn a deaf
+ ear to all of its inputs.
+
+ Pin 4 is "Write Not," and, as mentioned before, is brought low to
+ load an address into the controller, then brought high again to
+ start speech. In other words, this is the pin by which you
+ "trigger" the Digitalker.
+
+ Pin 5 is "Not ROM-Power Enable," an output which can be used to
+ control the power to the ROM's. This is used in cases of battery
+ supply where current drain is important; the ROM's will have their
+ power controlled by the controller.
+
+ Pin 6 is the "Interrupt Output," (equivalent to the "Busy Line" of
+ the old TSI Speech Board); this line goes low when an address is
+ loaded into the chip, then goes high again when speech is
+ finished. This signal can be used to control the driver circuitry
+ (or other controlling device), in which case it tells the driver
+ to "Hold the phone!" while the speech is running. Pin 7 is called
+ "CMS," and its state controls the action of the "Write Not"
+ line. With pin 7 low, the operation of pin 4 is as described. If
+ pin 7 is brought high, raising pin 4 high after loading an address
+ serves only to reset the interrupt and does not start speech. This
+ facility is probably intended for use where the interrupt line
+ really controls the hardware interrupt of a computer, and where
+ the program taking care of the interrupt may not have another word
+ to say every time the Digitalker is finished. I have found no
+ particular use for pin 7, and I simply ground it for normal
+ operation.
+
+ Pins 8 through 15 are the eight input address lines, with pin 8
+ being the most significant BIT and pin 15 being least
+ significant. These address lines are "active high." They should
+ never be left open. They are TTL-compatible; this means that logic
+ low is ground and logic high is plus 5VDC. (Actually, being MOS
+ inputs, you can take them as high as the VCC on the controller,
+ but a 5V supply is required for the ROM's anyhow -- it's there if
+ you want to use 5V.)
+
+ Pins 16 through 24 are the eight data lines which bring data from
+ the ROMs to the controller, with pin 16 being called "ROM Data
+ 1," and pin 24 being "ROM Data 8."
+
+ Pins 25 through 38 are the fourteen address lines which select
+ location in the ROM's to be read by the controller. Pin 25 is
+ "Address 0," pin 38 is "Address 13."
+
+
+ - Codec
+
+The codec stems from the standard model for voiced speech generation:
+a stream of impulses at the pitch frequency followed by an
+articulation filter. Both of those are considered slowly varying.
+
+ pitch filter voiced sound
+ |||||||||| * /\/\ = ~~~~
+
+The first compression effect is by forcing the filter to be
+zero-phase. That makes the periods perfectly symmetrical around the
+pitch pulse. The voiced speech is as a result extracted as a number
+of symmetric periods, centered on the pitch pulses.
+
+Following that, two quantizations are done. First, the pitch
+frequency is quantized to one of 32 values (see pitch_vals), going
+from ~80 to 200Hz. Then the volume is selected among 8 possible
+values in an exponential scale, and the amplitudes are quantized as a
+4-bit signed value. The period is time-warped to make it exactly 128
+samples long.
+
+The next step of the compression is to select which harnomics will be
+kept. The choices are to keep only the even ones or only the odd
+ones. Dropping half the harmonics allow to encode the period in only
+32 samples, using the fact that a period, for a zero-phase-at-center,
+half-harmonics signal, looks like:
+
+ even harmonics: /\/\ odd harmonics: _/\_
+
+Where / = block of 32 samples
+ \ = same block reversed
+ _ = 32 zeroes
+
+So we're left with 32 4-bit samples to encode, which is done using a
+2-bit adpcm. The adaptative part is done by using a fixed 16-deltas
+table indexed by the current and the previous encoded value.
+
+Added to all that is the possibility of repeating such a period while
+increasing or decreasing the pitch frequency.
+
+
+For non-voiced speech or non-speech an alternative mode is available
+where an equivalent period cutting, frequency and amplitude
+quantization is done, but the whole 128 samples are adpcm-encoded.
+
+
+Finally, silent zones are compressed specifically by storing their
+lenghts.
+
+
+Decoding is simpler. The 128-samples waveform is decoded using the
+adpcm data and mirroring/zeroing as needed in the voiced case. The
+pitch is taken into account by modulating a 1MHz (clock/4) signal at
+the pitch frequency multiplied by 128. pitch_vals in is practice this
+modulation interval, hence its 128us base unit to compute the pitch
+period.
+
+
+ - Rom organization
+
+The rom starts with a vector of 16-bits big endian values which are
+the addresses of the segments table for the samples. The segments data
+is a vector of 24-bits little-endian values organized as such:
+
+ adr+2 adr+1 adr
+ MMAAAAAA AAAAAAAA ERRRSSSS
+
+ M: Segment base waveforms compression mode (0-3)
+ A: Segment base waveforms data address (0-16383)
+ R: Repeat count (1-8)
+ S: Number of waveforms (1-16)
+ E: Last segment of the sample (flag)
+
+Decoding stops after having decoded a segment with the E bit set. A
+final 8.192ms silence is systematically added.
+
+A == 0 means silence. Duration is 5.12ms*(R+1)*(S+1), or in other
+terms a full decode of all-zero waveforms at maximal pitch frequency
+(pitch code 31).
+
+
+A != 0 means sound. The sound data starts at that offset. The
+encoding method is selected with M:
+
+ 0: odd-harmonics voiced mode
+ 2: even-harmonics voiced mode
+ 3: unvoiced/non-speech mode
+
+Mode 1 is not supported because it is not present in the available
+samples, hence unknown.
+
+
+ Voiced mode (9 bytes/waveform):
+
+ VVVPPPPP AAAAAAAAx8 - First waveform
+ VVVDCCCC AAAAAAAAx8 - Following waveforms
+
+V: Volume (first index in pcm_levels)
+P: Pitch index
+A: adpcm data
+D: Pitch index change direction (0=increase, 1=decrease)
+C: Pitch index maximum change
+
+The waveforms are encoded with a 2-bit adpcm, lowest pair of bits
+first. Deltas are a size-16 vector, indexed with the previous adpcm
+value in bits 0&1 and the current in bits 2&3. Voiced speech modes
+use table delta1 and initial "previous" value 2.
+
+Each waveform is repeated R times at volume V. First waveform has
+fixed pitch P. Subsequent waveforms change the pitch index by 1 every
+repeat (including the first) up to a change of C. D indicates whether
+it's an increment or a decrement.
+
+
+ Unvoiced mode (33 bytes/waveform):
+
+ VVVPPPPP AAAAAAAAx32 - All waveforms
+
+V: Volume (first index in pcm_levels)
+P: Pitch index
+A: adpcm data
+
+Adpcm encoding is identical but using delta2 table and an initial
+value of 1. Every waveform is played consecutively and the adpcm
+previous value or dac level is not reset between waveforms. The
+complete set of waveforms is repeated R times.
+
+*/
+
+// Quantized intensity values, first index is the volume, second the
+// intensity (positive half only, real value goes -8..7)
+static const short pcm_levels[8][8] = {
+ { 473, 945, 1418, 1890, 2363, 2835, 3308, 3781 },
+ { 655, 1310, 1966, 2621, 3276, 3931, 4586, 5242 },
+ { 925, 1851, 2776, 3702, 4627, 5553, 6478, 7404 },
+ { 1249, 2498, 3747, 4996, 6245, 7494, 8743, 9992 },
+ { 1638, 3276, 4914, 6552, 8190, 9828, 11466, 13104 },
+ { 2252, 4504, 6757, 9009, 11261, 13514, 15766, 18018 },
+ { 2989, 5979, 8968, 11957, 14947, 17936, 20925, 23915 },
+ { 4095, 8190, 12285, 16380, 20475, 24570, 28665, 32760 },
+};
+
+static const int delta1[16] = { -4, -4, -1, -1, -2, -2, 0, 0, 0, 0, 2, 2, 1, 1, 4, 4 };
+static const int delta2[16] = { 0, -1, -2, -3, 1, 0, -1, -2, 2, 1, 0, -1, 3, 2, 1, 0 };
+
+// Frequency quantizations, values are in units of 128us.
+static const int pitch_vals[32] = {
+ 97, 95, 92, 89, 87, 84, 82, 80, 77, 75, 73, 71, 69, 67, 65, 63,
+ 61, 60, 58, 56, 55, 53, 52, 50, 49, 48, 46, 45, 43, 42, 41, 40
+};
+
+
+const device_type DIGITALKER = &device_creator<digitalker_device>;
+
+digitalker_device::digitalker_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, DIGITALKER, "Digitalker", tag, owner, clock, "digitalker", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_rom(NULL),
+ m_stream(NULL),
+ m_data(0),
+ m_cs(0),
+ m_cms(0),
+ m_wr(0),
+ m_intr(0),
+ m_bpos(0),
+ m_apos(0),
+ m_mode(0),
+ m_cur_segment(0),
+ m_cur_repeat(0),
+ m_segments(0),
+ m_repeats(0),
+ m_prev_pitch(0),
+ m_pitch(0),
+ m_pitch_pos(0),
+ m_stop_after(0),
+ m_cur_dac(0),
+ m_cur_bits(0),
+ m_zero_count(0),
+ m_dac_index(0)
+{
+}
+
+
+void digitalker_device::digitalker_write(UINT8 *adr, UINT8 vol, INT8 dac)
+{
+ INT16 v;
+ dac &= 15;
+ if(dac >= 9)
+ v = -pcm_levels[vol][15-dac];
+ else if(dac)
+ v = pcm_levels[vol][dac-1];
+ else
+ v = 0;
+ m_dac[(*adr)++] = v;
+}
+
+UINT8 digitalker_device::digitalker_pitch_next(UINT8 val, UINT8 prev, int step)
+{
+ int delta, nv;
+
+ delta = val & 0xf;
+ if(delta > step + 1)
+ delta = step + 1;
+ if(val & 0x10)
+ delta = -delta;
+
+ nv = prev + delta;
+ if(nv < 0)
+ nv = 0;
+ else if(nv > 31)
+ nv = 31;
+ return nv;
+}
+
+void digitalker_device::digitalker_set_intr(UINT8 intr)
+{
+ m_intr = intr;
+}
+
+void digitalker_device::digitalker_start_command(UINT8 cmd)
+{
+ m_bpos = ((m_rom[cmd*2] << 8) | m_rom[cmd*2+1]) & 0x3fff;
+ m_cur_segment = m_segments = m_cur_repeat = m_repeats = 0;
+ m_dac_index = 128;
+ m_zero_count = 0;
+ digitalker_set_intr(0);
+}
+
+void digitalker_device::digitalker_step_mode_0()
+{
+ INT8 dac = 0;
+ int i, k, l;
+ UINT8 wpos = 0;
+ UINT8 h = m_rom[m_apos];
+ UINT16 bits = 0x80;
+ UINT8 vol = h >> 5;
+ UINT8 pitch_id = m_cur_segment ? digitalker_pitch_next(h, m_prev_pitch, m_cur_repeat) : h & 0x1f;
+
+ m_pitch = pitch_vals[pitch_id];
+
+ for(i=0; i<32; i++)
+ m_dac[wpos++] = 0;
+
+ for(k=1; k != 9; k++) {
+ bits |= m_rom[m_apos+k] << 8;
+ for(l=0; l<4; l++) {
+ dac += delta1[(bits >> (6+2*l)) & 15];
+ digitalker_write(&wpos, vol, dac);
+ }
+ bits >>= 8;
+ }
+
+ digitalker_write(&wpos, vol, dac);
+
+ for(k=7; k >= 0; k--) {
+ bits = (bits << 8) | (k ? m_rom[m_apos+k] : 0x80);
+ for(l=3; l>=0; l--) {
+ dac -= delta1[(bits >> (6+2*l)) & 15];
+ digitalker_write(&wpos, vol, dac);
+ }
+ }
+
+ for(i=0; i<31; i++)
+ m_dac[wpos++] = 0;
+
+ m_cur_repeat++;
+ if(m_cur_repeat == m_repeats) {
+ m_apos += 9;
+ m_prev_pitch = pitch_id;
+ m_cur_repeat = 0;
+ m_cur_segment++;
+ }
+}
+
+void digitalker_device::digitalker_step_mode_1()
+{
+ logerror("Digitalker mode 1 unsupported\n");
+ m_zero_count = 1;
+ m_cur_segment = m_segments;
+}
+
+void digitalker_device::digitalker_step_mode_2()
+{
+ INT8 dac = 0;
+ int k, l;
+ UINT8 wpos=0;
+ UINT8 h = m_rom[m_apos];
+ UINT16 bits = 0x80;
+ UINT8 vol = h >> 5;
+ UINT8 pitch_id = m_cur_segment ? digitalker_pitch_next(h, m_prev_pitch, m_cur_repeat) : h & 0x1f;
+
+ m_pitch = pitch_vals[pitch_id];
+
+ for(k=1; k != 9; k++) {
+ bits |= m_rom[m_apos+k] << 8;
+ for(l=0; l<4; l++) {
+ dac += delta1[(bits >> (6+2*l)) & 15];
+ digitalker_write(&wpos, vol, dac);
+ }
+ bits >>= 8;
+ }
+
+ digitalker_write(&wpos, vol, dac);
+
+ for(k=7; k >= 0; k--) {
+ int limit = k ? 0 : 1;
+ bits = (bits << 8) | (k ? m_rom[m_apos+k] : 0x80);
+ for(l=3; l>=limit; l--) {
+ dac -= delta1[(bits >> (6+2*l)) & 15];
+ digitalker_write(&wpos, vol, dac);
+ }
+ }
+
+ digitalker_write(&wpos, vol, dac);
+
+ for(k=1; k != 9; k++) {
+ int start = k == 1 ? 1 : 0;
+ bits |= m_rom[m_apos+k] << 8;
+ for(l=start; l<4; l++) {
+ dac += delta1[(bits >> (6+2*l)) & 15];
+ digitalker_write(&wpos, vol, dac);
+ }
+ bits >>= 8;
+ }
+
+ digitalker_write(&wpos, vol, dac);
+
+ for(k=7; k >= 0; k--) {
+ int limit = k ? 0 : 1;
+ bits = (bits << 8) | (k ? m_rom[m_apos+k] : 0x80);
+ for(l=3; l>=limit; l--) {
+ dac -= delta1[(bits >> (6+2*l)) & 15];
+ digitalker_write(&wpos, vol, dac);
+ }
+ }
+
+ m_cur_repeat++;
+ if(m_cur_repeat == m_repeats) {
+ m_apos += 9;
+ m_prev_pitch = pitch_id;
+ m_cur_repeat = 0;
+ m_cur_segment++;
+ }
+}
+
+void digitalker_device::digitalker_step_mode_3()
+{
+ UINT8 h = m_rom[m_apos];
+ UINT8 vol = h >> 5;
+ UINT16 bits;
+ UINT8 dac, apos, wpos;
+ int k, l;
+
+ m_pitch = pitch_vals[h & 0x1f];
+ if(m_cur_segment == 0 && m_cur_repeat == 0) {
+ m_cur_bits = 0x40;
+ m_cur_dac = 0;
+ }
+ bits = m_cur_bits;
+ dac = 0;
+
+ apos = m_apos + 1 + 32*m_cur_segment;
+ wpos = 0;
+ for(k=0; k != 32; k++) {
+ bits |= m_rom[apos++] << 8;
+ for(l=0; l<4; l++) {
+ dac += delta2[(bits >> (6+2*l)) & 15];
+ digitalker_write(&wpos, vol, dac);
+ }
+ bits >>= 8;
+ }
+
+ m_cur_bits = bits;
+ m_cur_dac = dac;
+
+ m_cur_segment++;
+ if(m_cur_segment == m_segments) {
+ m_cur_segment = 0;
+ m_cur_repeat++;
+ }
+}
+
+void digitalker_device::digitalker_step()
+{
+ if(m_cur_segment == m_segments || m_cur_repeat == m_repeats) {
+ if(m_stop_after == 0 && m_bpos == 0xffff)
+ return;
+ if(m_stop_after == 0) {
+ UINT8 v1 = m_rom[m_bpos++];
+ UINT8 v2 = m_rom[m_bpos++];
+ UINT8 v3 = m_rom[m_bpos++];
+ m_apos = v2 | ((v3 << 8) & 0x3f00);
+ m_segments = (v1 & 15) + 1;
+ m_repeats = ((v1 >> 4) & 7) + 1;
+ m_mode = (v3 >> 6) & 3;
+ m_stop_after = (v1 & 0x80) != 0;
+
+ m_cur_segment = 0;
+ m_cur_repeat = 0;
+
+ if(!m_apos) {
+ m_zero_count = 40*128*m_segments*m_repeats;
+ m_segments = 0;
+ m_repeats = 0;
+ return;
+ }
+ } else if(m_stop_after == 1) {
+ m_bpos = 0xffff;
+ m_zero_count = 81920;
+ m_stop_after = 2;
+ m_cur_segment = 0;
+ m_cur_repeat = 0;
+ m_segments = 0;
+ m_repeats = 0;
+ } else {
+ m_stop_after = 0;
+ digitalker_set_intr(1);
+ }
+ }
+
+ switch(m_mode) {
+ case 0: digitalker_step_mode_0(); break;
+ case 1: digitalker_step_mode_1(); break;
+ case 2: digitalker_step_mode_2(); break;
+ case 3: digitalker_step_mode_3(); break;
+ }
+ if(!m_zero_count)
+ m_dac_index = 0;
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void digitalker_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *sout = outputs[0];
+ int cpos = 0;
+ while(cpos != samples) {
+ if(m_zero_count == 0 && m_dac_index == 128)
+ digitalker_step();
+
+ if(m_zero_count) {
+ int n = samples - cpos;
+ int i;
+ if(n > m_zero_count)
+ n = m_zero_count;
+ for(i=0; i != n; i++)
+ sout[cpos++] = 0;
+ m_zero_count -= n;
+
+ } else if(m_dac_index != 128) {
+ while(cpos != samples && m_dac_index != 128) {
+ short v = m_dac[m_dac_index];
+ int pp = m_pitch_pos;
+ while(cpos != samples && pp != m_pitch) {
+ sout[cpos++] = v;
+ pp++;
+ }
+ if(pp == m_pitch) {
+ pp = 0;
+ m_dac_index++;
+ }
+ m_pitch_pos = pp;
+ }
+
+ } else {
+ while(cpos != samples)
+ sout[cpos++] = 0;
+ }
+ }
+}
+
+void digitalker_device::digitalker_cs_w(int line)
+{
+ UINT8 cs = line == ASSERT_LINE ? 1 : 0;
+ if(cs == m_cs)
+ return;
+ m_cs = cs;
+ if(cs)
+ return;
+ if(!m_wr) {
+ if(m_cms)
+ digitalker_set_intr(1);
+ else
+ digitalker_start_command(m_data);
+ }
+}
+
+void digitalker_device::digitalker_cms_w(int line)
+{
+ m_cms = line == ASSERT_LINE ? 1 : 0;
+}
+
+void digitalker_device::digitalker_wr_w(int line)
+{
+ UINT8 wr = line == ASSERT_LINE ? 1 : 0;
+ if(wr == m_wr)
+ return;
+ m_wr = wr;
+ if(wr || m_cs)
+ return;
+ if(m_cms)
+ digitalker_set_intr(1);
+ else
+ digitalker_start_command(m_data);
+}
+
+int digitalker_device::digitalker_intr_r()
+{
+ return m_intr ? ASSERT_LINE : CLEAR_LINE;
+}
+
+void digitalker_device::digitalker_register_for_save()
+{
+ save_item(NAME(m_data));
+ save_item(NAME(m_cs));
+ save_item(NAME(m_cms));
+ save_item(NAME(m_wr));
+ save_item(NAME(m_intr));
+ save_item(NAME(m_bpos));
+ save_item(NAME(m_apos));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_cur_segment));
+ save_item(NAME(m_cur_repeat));
+ save_item(NAME(m_segments));
+ save_item(NAME(m_repeats));
+ save_item(NAME(m_prev_pitch));
+ save_item(NAME(m_pitch));
+ save_item(NAME(m_pitch_pos));
+ save_item(NAME(m_stop_after));
+ save_item(NAME(m_cur_dac));
+ save_item(NAME(m_cur_bits));
+ save_item(NAME(m_zero_count));
+ save_item(NAME(m_dac_index));
+ save_item(NAME(m_dac));
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void digitalker_device::device_start()
+{
+ m_rom = m_region->base();
+ m_stream = stream_alloc(0, 1, clock()/4);
+ m_dac_index = 128;
+ m_data = 0xff;
+ m_cs = m_cms = m_wr = 1;
+ m_bpos = 0xffff;
+ digitalker_set_intr(1);
+
+ digitalker_register_for_save();
+}
+
+void digitalker_device::digitalker_0_cs_w(int line)
+{
+ digitalker_cs_w(line);
+}
+
+void digitalker_device::digitalker_0_cms_w(int line)
+{
+ digitalker_cms_w(line);
+}
+
+void digitalker_device::digitalker_0_wr_w(int line)
+{
+ digitalker_wr_w(line);
+}
+
+int digitalker_device::digitalker_0_intr_r()
+{
+ return digitalker_intr_r();
+}
+
+WRITE8_MEMBER( digitalker_device::digitalker_data_w )
+{
+ m_data = data;
+}
diff --git a/src/devices/sound/digitalk.h b/src/devices/sound/digitalk.h
new file mode 100644
index 00000000000..fc2dd497f09
--- /dev/null
+++ b/src/devices/sound/digitalk.h
@@ -0,0 +1,101 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+#ifndef _DIGITALKER_H_
+#define _DIGITALKER_H_
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_DIGITALKER_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, DIGITALKER, _clock)
+#define MCFG_DIGITALKER_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, DIGITALKER, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> digitalker_device
+
+class digitalker_device : public device_t,
+ public device_sound_interface
+{
+public:
+ digitalker_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~digitalker_device() { }
+
+ void digitalker_0_cs_w(int line);
+ void digitalker_0_cms_w(int line);
+ void digitalker_0_wr_w(int line);
+ int digitalker_0_intr_r();
+
+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);
+
+public:
+ DECLARE_WRITE8_MEMBER(digitalker_data_w);
+
+private:
+ void digitalker_write(UINT8 *adr, UINT8 vol, INT8 dac);
+ UINT8 digitalker_pitch_next(UINT8 val, UINT8 prev, int step);
+ void digitalker_set_intr(UINT8 intr);
+ void digitalker_start_command(UINT8 cmd);
+ void digitalker_step_mode_0();
+ void digitalker_step_mode_1();
+ void digitalker_step_mode_2();
+ void digitalker_step_mode_3();
+ void digitalker_step();
+ void digitalker_cs_w(int line);
+ void digitalker_cms_w(int line);
+ void digitalker_wr_w(int line);
+ int digitalker_intr_r();
+ void digitalker_register_for_save();
+
+private:
+ const UINT8 *m_rom;
+ sound_stream *m_stream;
+
+ // Port/lines state
+ UINT8 m_data;
+ UINT8 m_cs;
+ UINT8 m_cms;
+ UINT8 m_wr;
+ UINT8 m_intr;
+
+ // Current decoding state
+ UINT16 m_bpos;
+ UINT16 m_apos;
+
+ UINT8 m_mode;
+ UINT8 m_cur_segment;
+ UINT8 m_cur_repeat;
+ UINT8 m_segments;
+ UINT8 m_repeats;
+
+ UINT8 m_prev_pitch;
+ UINT8 m_pitch;
+ UINT8 m_pitch_pos;
+
+ UINT8 m_stop_after;
+ UINT8 m_cur_dac;
+ UINT8 m_cur_bits;
+
+ // Zero-range size
+ UINT32 m_zero_count; // 0 for done
+
+ // Waveform and current index in it
+ UINT8 m_dac_index; // 128 for done
+ INT16 m_dac[128];
+};
+
+extern const device_type DIGITALKER;
+
+
+#endif
diff --git a/src/devices/sound/disc_cls.h b/src/devices/sound/disc_cls.h
new file mode 100644
index 00000000000..255b0958c15
--- /dev/null
+++ b/src/devices/sound/disc_cls.h
@@ -0,0 +1,258 @@
+// license:???
+// copyright-holders:K.Wilkins, Derrick Renaud, F.Palazzolo, Couriersud
+#pragma once
+
+#ifndef __DISC_CLS_H__
+#define __DISC_CLS_H__
+
+/***********************************************************************
+ *
+ * MAME - Discrete sound system emulation library
+ *
+ * Written by K.Wilkins (mame@esplexo.co.uk)
+ *
+ * (c) K.Wilkins 2000
+ *
+ * Coding started in November 2000
+ *
+ * Additions/bugfix February 2003 - Derrick Renaud, F.Palazzolo, K.Wilkins
+ * Discrete parallel tasks 2009 - Couriersud
+ * Discrete classes 2010 - Couriersud
+ *
+ ***********************************************************************/
+
+#define DISCRETE_CLASS_NAME(_name) discrete_ ## _name ## _node
+
+#define DISCRETE_CLASS_INPUT(_name, _num) inline double _name (void) { return *(m_input[_num]); }
+
+#define DISCRETE_CLASS_CONSTRUCTOR(_name, _base) \
+ public: \
+ DISCRETE_CLASS_NAME(_name)() \
+ : DISCRETE_CLASS_NAME(_base)() { }
+
+#define DISCRETE_CLASS_DESTRUCTOR(_name) \
+ public: \
+ virtual ~ DISCRETE_CLASS_NAME(_name)(void) { }
+
+#define DISCRETE_CLASS_STEP_RESET(_name, _maxout, _priv) \
+class DISCRETE_CLASS_NAME(_name): public discrete_base_node, public discrete_step_interface \
+{ \
+ DISCRETE_CLASS_CONSTRUCTOR(_name, base) \
+ DISCRETE_CLASS_DESTRUCTOR(_name) \
+public: \
+ void step(void); \
+ void reset(void); \
+ int max_output(void) { return _maxout; } \
+private: \
+ _priv \
+}
+
+#define DISCRETE_CLASS_STEP(_name, _maxout, _priv) \
+class DISCRETE_CLASS_NAME(_name): public discrete_base_node, public discrete_step_interface \
+{ \
+ DISCRETE_CLASS_CONSTRUCTOR(_name, base) \
+ DISCRETE_CLASS_DESTRUCTOR(_name) \
+public: \
+ void step(void); \
+ void reset(void) { this->step(); } \
+ int max_output(void) { return _maxout; } \
+private: \
+ _priv \
+}
+
+#define DISCRETE_CLASS_RESET(_name, _maxout) \
+class DISCRETE_CLASS_NAME(_name): public discrete_base_node \
+{ \
+ DISCRETE_CLASS_CONSTRUCTOR(_name, base) \
+ DISCRETE_CLASS_DESTRUCTOR(_name) \
+public: \
+ void reset(void); \
+ int max_output(void) { return _maxout; } \
+}
+
+#define DISCRETE_CLASS(_name, _maxout, _priv) \
+class DISCRETE_CLASS_NAME(_name): public discrete_base_node, public discrete_step_interface \
+{ \
+ DISCRETE_CLASS_DESTRUCTOR(_name) \
+ DISCRETE_CLASS_CONSTRUCTOR(_name, base) \
+public: \
+ void step(void); \
+ void reset(void); \
+ void start(void); \
+ void stop(void); \
+ int max_output(void) { return _maxout; } \
+private: \
+ _priv \
+}
+
+class DISCRETE_CLASS_NAME(special): public discrete_base_node
+{
+ DISCRETE_CLASS_CONSTRUCTOR(special, base)
+ DISCRETE_CLASS_DESTRUCTOR(special)
+public:
+ int max_output(void) { return 0; }
+};
+
+class DISCRETE_CLASS_NAME(unimplemented): public discrete_base_node
+{
+ DISCRETE_CLASS_CONSTRUCTOR(unimplemented, base)
+ DISCRETE_CLASS_DESTRUCTOR(unimplemented)
+public:
+ int max_output(void) { return 0; }
+};
+
+/*************************************
+ *
+ * disc_sys.inc
+ *
+ *************************************/
+
+class DISCRETE_CLASS_NAME(dso_output): public discrete_base_node,
+ public discrete_sound_output_interface,
+ public discrete_step_interface
+{
+ DISCRETE_CLASS_CONSTRUCTOR(dso_output, base)
+ DISCRETE_CLASS_DESTRUCTOR(dso_output)
+public:
+ void step(void) {
+ /* Add gain to the output and put into the buffers */
+ /* Clipping will be handled by the main sound system */
+ double val = DISCRETE_INPUT(0) * DISCRETE_INPUT(1);
+ *m_ptr++ = val;
+ }
+ int max_output(void) { return 0; }
+ void set_output_ptr(stream_sample_t *ptr) { m_ptr = ptr; }
+private:
+ stream_sample_t *m_ptr;
+};
+
+DISCRETE_CLASS(dso_csvlog, 0,
+ FILE *m_csv_file;
+ INT64 m_sample_num;
+ char m_name[32];
+);
+
+DISCRETE_CLASS(dso_wavlog, 0,
+ wav_file *m_wavfile;
+ char m_name[32];
+);
+
+/*************************************
+ *
+ * disc_inp.inc
+ *
+ *************************************/
+
+class DISCRETE_CLASS_NAME(dss_adjustment): public discrete_base_node, public discrete_step_interface
+{
+ DISCRETE_CLASS_CONSTRUCTOR(dss_adjustment, base)
+ DISCRETE_CLASS_DESTRUCTOR(dss_adjustment)
+public:
+ void step(void);
+ void reset(void);
+private:
+ ioport_port *m_port;
+ INT32 m_lastpval;
+ INT32 m_pmin;
+ double m_pscale;
+ double m_min;
+ double m_scale;
+};
+
+DISCRETE_CLASS_RESET(dss_constant, 1);
+
+class DISCRETE_CLASS_NAME(dss_input_data): public discrete_base_node, public discrete_input_interface
+{
+ DISCRETE_CLASS_DESTRUCTOR(dss_input_data)
+ DISCRETE_CLASS_CONSTRUCTOR(dss_input_data, base)
+public:
+ void reset(void);
+ void input_write(int sub_node, UINT8 data );
+private:
+ double m_gain; /* node gain */
+ double m_offset; /* node offset */
+ UINT8 m_data; /* data written */
+};
+
+class DISCRETE_CLASS_NAME(dss_input_logic): public discrete_base_node, public discrete_input_interface
+{
+ DISCRETE_CLASS_CONSTRUCTOR(dss_input_logic, base)
+ DISCRETE_CLASS_DESTRUCTOR(dss_input_logic)
+public:
+ void reset(void);
+ void input_write(int sub_node, UINT8 data );
+private:
+ double m_gain; /* node gain */
+ double m_offset; /* node offset */
+ UINT8 m_data; /* data written */
+};
+
+class DISCRETE_CLASS_NAME(dss_input_not): public discrete_base_node, public discrete_input_interface
+{
+ DISCRETE_CLASS_CONSTRUCTOR(dss_input_not, base)
+ DISCRETE_CLASS_DESTRUCTOR(dss_input_not)
+public:
+ void reset(void);
+ void input_write(int sub_node, UINT8 data );
+private:
+ double m_gain; /* node gain */
+ double m_offset; /* node offset */
+ UINT8 m_data; /* data written */
+};
+
+class DISCRETE_CLASS_NAME(dss_input_pulse): public discrete_base_node, public discrete_input_interface, public discrete_step_interface
+{
+ DISCRETE_CLASS_CONSTRUCTOR(dss_input_pulse, base)
+ DISCRETE_CLASS_DESTRUCTOR(dss_input_pulse)
+public:
+ void step(void);
+ void reset(void);
+ void input_write(int sub_node, UINT8 data );
+private:
+ //double m_gain; /* node gain */
+ //double m_offset; /* node offset */
+ UINT8 m_data; /* data written */
+};
+
+class DISCRETE_CLASS_NAME(dss_input_stream): public discrete_base_node, public discrete_input_interface, public discrete_step_interface
+{
+ DISCRETE_CLASS_CONSTRUCTOR(dss_input_stream, base)
+ DISCRETE_CLASS_DESTRUCTOR(dss_input_stream)
+public:
+ void step(void);
+ void reset(void);
+ void start(void);
+ void input_write(int sub_node, UINT8 data );
+ virtual bool is_buffered(void) { return false; }
+
+ /* This is called by discrete_sound_device */
+ void stream_start(void);
+
+//protected:
+ UINT32 m_stream_in_number;
+ stream_sample_t *m_ptr; /* current in ptr for stream */
+private:
+ void stream_generate(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ double m_gain; /* node gain */
+ double m_offset; /* node offset */
+ UINT8 m_data; /* data written */
+ UINT8 m_is_buffered;
+ /* the buffer stream */
+ sound_stream *m_buffer_stream;
+};
+
+class DISCRETE_CLASS_NAME(dss_input_buffer): public DISCRETE_CLASS_NAME(dss_input_stream)
+{
+ DISCRETE_CLASS_CONSTRUCTOR(dss_input_buffer, dss_input_stream)
+ DISCRETE_CLASS_DESTRUCTOR(dss_input_buffer)
+public:
+ bool is_buffered(void) { return true; }
+};
+
+#include "disc_wav.h"
+#include "disc_mth.h"
+#include "disc_flt.h"
+#include "disc_dev.h"
+
+#endif /* __DISCRETE_H__ */
diff --git a/src/devices/sound/disc_dev.h b/src/devices/sound/disc_dev.h
new file mode 100644
index 00000000000..80af62a3ae3
--- /dev/null
+++ b/src/devices/sound/disc_dev.h
@@ -0,0 +1,129 @@
+// license:BSD-3-Clause
+// copyright-holders:K.Wilkins
+#pragma once
+
+#ifndef __DISC_DEV_H__
+#define __DISC_DEV_H__
+
+/***********************************************************************
+ *
+ * MAME - Discrete sound system emulation library
+ *
+ * Written by K.Wilkins (mame@esplexo.co.uk)
+ *
+ * (c) K.Wilkins 2000
+ *
+ * Coding started in November 2000
+ *
+ * Additions/bugfix February 2003 - Derrick Renaud, F.Palazzolo, K.Wilkins
+ * Discrete parallel tasks 2009 - Couriersud
+ * Discrete classes 2010 - Couriersud
+ *
+ ***********************************************************************/
+
+#include "discrete.h"
+
+DISCRETE_CLASS_STEP_RESET(dsd_555_astbl, 1,
+ int m_use_ctrlv;
+ int m_output_type;
+ int m_output_is_ac;
+ double m_ac_shift; /* DC shift needed to make waveform ac */
+ int m_flip_flop; /* 555 flip/flop output state */
+ double m_cap_voltage; /* voltage on cap */
+ double m_threshold;
+ double m_trigger;
+ double m_v_out_high; /* Logic 1 voltage level */
+ double m_v_charge;
+ const double * m_v_charge_node; /* point to output of node */
+ int m_has_rc_nodes;
+ double m_exp_bleed;
+ double m_exp_charge;
+ double m_exp_discharge;
+ double m_t_rc_bleed;
+ double m_t_rc_charge;
+ double m_t_rc_discharge;
+ double m_last_r1;
+ double m_last_r2;
+ double m_last_c;
+);
+
+DISCRETE_CLASS_STEP_RESET(dsd_555_mstbl, 1,
+ int m_trig_is_logic;
+ int m_trig_discharges_cap;
+ int m_output_type;
+ double m_ac_shift; /* DC shift needed to make waveform ac */
+ int m_flip_flop; /* 555 flip/flop output state */
+ int m_has_rc_nodes;
+ double m_exp_charge;
+ double m_cap_voltage; /* voltage on cap */
+ double m_threshold;
+ double m_trigger;
+ double m_v_out_high; /* Logic 1 voltage level */
+ double m_v_charge;
+);
+
+DISCRETE_CLASS_STEP_RESET(dsd_555_cc, 1,
+ unsigned int m_type; /* type of 555cc circuit */
+ int m_output_type;
+ int m_output_is_ac;
+ double m_ac_shift; /* DC shift needed to make waveform ac */
+ int m_flip_flop; /* 555 flip/flop output state */
+ double m_cap_voltage; /* voltage on cap */
+ double m_threshold;
+ double m_trigger;
+ double m_v_out_high; /* Logic 1 voltage level */
+ double m_v_cc_source;
+ int m_has_rc_nodes;
+ double m_exp_bleed;
+ double m_exp_charge;
+ double m_exp_discharge;
+ double m_exp_discharge_01;
+ double m_exp_discharge_no_i;
+ double m_t_rc_charge;
+ double m_t_rc_discharge;
+ double m_t_rc_discharge_01;
+ double m_t_rc_discharge_no_i;
+);
+
+DISCRETE_CLASS_STEP_RESET(dsd_555_vco1, 1,
+ int m_ctrlv_is_node;
+ int m_output_type;
+ int m_output_is_ac;
+ double m_ac_shift; /* DC shift needed to make waveform ac */
+ int m_flip_flop; /* flip/flop output state */
+ double m_v_out_high; /* 555 high voltage */
+ double m_threshold; /* falling threshold */
+ double m_trigger; /* rising threshold */
+ double m_i_charge; /* charge current */
+ double m_i_discharge; /* discharge current */
+ double m_cap_voltage; /* current capacitor voltage */
+);
+
+DISCRETE_CLASS_STEP_RESET(dsd_566, 1,
+ //unsigned int m_state[2]; /* keeps track of excess flip_flop changes during the current step */
+ int m_flip_flop; /* 566 flip/flop output state */
+ double m_cap_voltage; /* voltage on cap */
+ double m_v_sqr_low; /* voltage for a squarewave at low */
+ double m_v_sqr_high; /* voltage for a squarewave at high */
+ double m_v_sqr_diff;
+ double m_threshold_low; /* falling threshold */
+ double m_threshold_high; /* rising threshold */
+ double m_ac_shift; /* used to fake AC */
+ double m_v_osc_stable;
+ double m_v_osc_stop;
+ int m_fake_ac;
+ int m_out_type;
+);
+
+DISCRETE_CLASS_STEP_RESET(dsd_ls624, 1,
+ double m_exponent;
+ double m_t_used;
+ double m_v_cap_freq_in;
+ double m_v_freq_scale;
+ double m_v_rng_scale;
+ int m_flip_flop;
+ int m_has_freq_in_cap;
+ int m_out_type;
+);
+
+#endif /* __DISC_WAV_H__ */
diff --git a/src/devices/sound/disc_dev.inc b/src/devices/sound/disc_dev.inc
new file mode 100644
index 00000000000..47026747cd1
--- /dev/null
+++ b/src/devices/sound/disc_dev.inc
@@ -0,0 +1,1776 @@
+// license:BSD-3-Clause
+// copyright-holders:K.Wilkins
+/************************************************************************
+ *
+ * MAME - Discrete sound system emulation library
+ *
+ * Written by K.Wilkins (mame@esplexo.co.uk)
+ *
+ * (c) K.Wilkins 2000
+ * (c) Derrick Renaud 2003-2004
+ *
+ ************************************************************************
+ *
+ * DSD_555_ASTBL - NE555 Simulation - Astable mode
+ * DSD_555_MSTBL - NE555 Simulation - Monostable mode
+ * DSD_555_CC - NE555 Constant Current VCO
+ * DSD_555_VCO1 - Op-Amp linear ramp based 555 VCO
+ * DSD_566 - NE566 Simulation
+ * DSD_LS624 - 74LS624/629 Simulation
+ *
+ ************************************************************************
+ *
+ * You will notice that the code for a lot of these routines are similar.
+ * I tried to make a common charging routine, but there are too many
+ * minor differences that affect each module.
+ *
+ ************************************************************************/
+
+#define DEFAULT_555_BLEED_R RES_M(10)
+
+/************************************************************************
+ *
+ * DSD_555_ASTBL - - 555 Astable simulation
+ *
+ * input[0] - Reset value
+ * input[1] - R1 value
+ * input[2] - R2 value
+ * input[3] - C value
+ * input[4] - Control Voltage value
+ *
+ * also passed discrete_555_desc structure
+ *
+ * Jan 2004, D Renaud.
+ ************************************************************************/
+#define DSD_555_ASTBL__RESET (! DISCRETE_INPUT(0))
+#define DSD_555_ASTBL__R1 DISCRETE_INPUT(1)
+#define DSD_555_ASTBL__R2 DISCRETE_INPUT(2)
+#define DSD_555_ASTBL__C DISCRETE_INPUT(3)
+#define DSD_555_ASTBL__CTRLV DISCRETE_INPUT(4)
+
+/* bit mask of the above RC inputs */
+#define DSD_555_ASTBL_RC_MASK 0x0e
+
+/* charge/discharge constants */
+#define DSD_555_ASTBL_T_RC_BLEED (DEFAULT_555_BLEED_R * DSD_555_ASTBL__C)
+/* Use quick charge if specified. */
+#define DSD_555_ASTBL_T_RC_CHARGE ((DSD_555_ASTBL__R1 + ((info->options & DISC_555_ASTABLE_HAS_FAST_CHARGE_DIODE) ? 0 : DSD_555_ASTBL__R2)) * DSD_555_ASTBL__C)
+#define DSD_555_ASTBL_T_RC_DISCHARGE (DSD_555_ASTBL__R2 * DSD_555_ASTBL__C)
+
+DISCRETE_STEP(dsd_555_astbl)
+{
+ DISCRETE_DECLARE_INFO(discrete_555_desc)
+
+ int count_f = 0;
+ int count_r = 0;
+ double dt; /* change in time */
+ double x_time = 0; /* time since change happened */
+ double v_cap = m_cap_voltage; /* Current voltage on capacitor, before dt */
+ double v_cap_next = 0; /* Voltage on capacitor, after dt */
+ double v_charge, exponent = 0;
+ UINT8 flip_flop = m_flip_flop;
+ UINT8 update_exponent = 0;
+ double v_out = 0.0;
+
+ /* put commonly used stuff in local variables for speed */
+ double threshold = m_threshold;
+ double trigger = m_trigger;
+
+ if(DSD_555_ASTBL__RESET)
+ {
+ /* We are in RESET */
+ set_output(0, 0);
+ m_flip_flop = 1;
+ m_cap_voltage = 0;
+ return;
+ }
+
+ /* Check: if the Control Voltage node is connected. */
+ if (m_use_ctrlv)
+ {
+ /* If CV is less then .25V, the circuit will oscillate way out of range.
+ * So we will just ignore it when it happens. */
+ if (DSD_555_ASTBL__CTRLV < .25) return;
+ /* If it is a node then calculate thresholds based on Control Voltage */
+ threshold = DSD_555_ASTBL__CTRLV;
+ trigger = DSD_555_ASTBL__CTRLV / 2.0;
+ /* Since the thresholds may have changed we need to update the FF */
+ if (v_cap >= threshold)
+ {
+ flip_flop = 0;
+ count_f++;
+ }
+ else
+ if (v_cap <= trigger)
+ {
+ flip_flop = 1;
+ count_r++;
+ }
+ }
+
+ /* get the v_charge and update each step if it is a node */
+ if (m_v_charge_node != NULL)
+ {
+ v_charge = *m_v_charge_node;
+ if (info->options & DISC_555_ASTABLE_HAS_FAST_CHARGE_DIODE) v_charge -= 0.5;
+ }
+ else
+ v_charge = m_v_charge;
+
+
+ /* Calculate future capacitor voltage.
+ * ref@ http://www.physics.rutgers.edu/ugrad/205/capacitance.html
+ * The formulas from the ref pages have been modified to reflect that we are stepping the change.
+ * dt = time of sample (1/sample frequency)
+ * VC = Voltage across capacitor
+ * VC' = Future voltage across capacitor
+ * Vc = Voltage change
+ * Vr = is the voltage across the resistor. For charging it is Vcc - VC. Discharging it is VC - 0.
+ * R = R1+R2 (for charging) R = R2 for discharging.
+ * Vc = Vr*(1-exp(-dt/(R*C)))
+ * VC' = VC + Vc (for charging) VC' = VC - Vc for discharging.
+ *
+ * We will also need to calculate the amount of time we overshoot the thresholds
+ * dt = amount of time we overshot
+ * Vc = voltage change overshoot
+ * dt = R*C(log(1/(1-(Vc/Vr))))
+ */
+
+ dt = this->sample_time();
+
+ /* Sometimes a switching network is used to setup the capacitance.
+ * These may select no capacitor, causing oscillation to stop.
+ */
+ if (DSD_555_ASTBL__C == 0)
+ {
+ flip_flop = 1;
+ /* The voltage goes high because the cap circuit is open. */
+ v_cap_next = v_charge;
+ v_cap = v_charge;
+ m_cap_voltage = 0;
+ }
+ else
+ {
+ /* Update charge contstants and exponents if nodes changed */
+ if (m_has_rc_nodes && (DSD_555_ASTBL__R1 != m_last_r1 || DSD_555_ASTBL__C != m_last_c || DSD_555_ASTBL__R2 != m_last_r2))
+ {
+ m_t_rc_bleed = DSD_555_ASTBL_T_RC_BLEED;
+ m_t_rc_charge = DSD_555_ASTBL_T_RC_CHARGE;
+ m_t_rc_discharge = DSD_555_ASTBL_T_RC_DISCHARGE;
+ m_exp_bleed = RC_CHARGE_EXP(m_t_rc_bleed);
+ m_exp_charge = RC_CHARGE_EXP(m_t_rc_charge);
+ m_exp_discharge = RC_CHARGE_EXP(m_t_rc_discharge);
+ m_last_r1 = DSD_555_ASTBL__R1;
+ m_last_r2 = DSD_555_ASTBL__R2;
+ m_last_c = DSD_555_ASTBL__C;
+ }
+ /* Keep looping until all toggling in time sample is used up. */
+ do
+ {
+ if (flip_flop)
+ {
+ if (DSD_555_ASTBL__R1 == 0)
+ {
+ /* Oscillation disabled because there is no longer any charge resistor. */
+ /* Bleed the cap due to circuit losses. */
+ if (update_exponent)
+ exponent = RC_CHARGE_EXP_DT(m_t_rc_bleed, dt);
+ else
+ exponent = m_exp_bleed;
+ v_cap_next = v_cap - (v_cap * exponent);
+ dt = 0;
+ }
+ else
+ {
+ /* Charging */
+ if (update_exponent)
+ exponent = RC_CHARGE_EXP_DT(m_t_rc_charge, dt);
+ else
+ exponent = m_exp_charge;
+ v_cap_next = v_cap + ((v_charge - v_cap) * exponent);
+ dt = 0;
+
+ /* has it charged past upper limit? */
+ if (v_cap_next >= threshold)
+ {
+ /* calculate the overshoot time */
+ dt = m_t_rc_charge * log(1.0 / (1.0 - ((v_cap_next - threshold) / (v_charge - v_cap))));
+ x_time = dt;
+ v_cap_next = threshold;
+ flip_flop = 0;
+ count_f++;
+ update_exponent = 1;
+ }
+ }
+ }
+ else
+ {
+ /* Discharging */
+ if(DSD_555_ASTBL__R2 != 0)
+ {
+ if (update_exponent)
+ exponent = RC_CHARGE_EXP_DT(m_t_rc_discharge, dt);
+ else
+ exponent = m_exp_discharge;
+ v_cap_next = v_cap - (v_cap * exponent);
+ dt = 0;
+ }
+ else
+ {
+ /* no discharge resistor so we immediately discharge */
+ v_cap_next = trigger;
+ }
+
+ /* has it discharged past lower limit? */
+ if (v_cap_next <= trigger)
+ {
+ /* calculate the overshoot time */
+ if (v_cap_next < trigger)
+ dt = m_t_rc_discharge * log(1.0 / (1.0 - ((trigger - v_cap_next) / v_cap)));
+ x_time = dt;
+ v_cap_next = trigger;
+ flip_flop = 1;
+ count_r++;
+ update_exponent = 1;
+ }
+ }
+ v_cap = v_cap_next;
+ } while(dt);
+
+ m_cap_voltage = v_cap;
+ }
+
+ /* Convert last switch time to a ratio */
+ x_time = x_time / this->sample_time();
+
+ switch (m_output_type)
+ {
+ case DISC_555_OUT_SQW:
+ if (count_f + count_r >= 2)
+ /* force at least 1 toggle */
+ v_out = m_flip_flop ? 0 : m_v_out_high;
+ else
+ v_out = flip_flop * m_v_out_high;
+ v_out += m_ac_shift;
+ break;
+ case DISC_555_OUT_CAP:
+ v_out = v_cap;
+ /* Fake it to AC if needed */
+ if (m_output_is_ac)
+ v_out -= threshold * 3.0 /4.0;
+ break;
+ case DISC_555_OUT_ENERGY:
+ if (x_time == 0) x_time = 1.0;
+ v_out = m_v_out_high * (flip_flop ? x_time : (1.0 - x_time));
+ v_out += m_ac_shift;
+ break;
+ case DISC_555_OUT_LOGIC_X:
+ v_out = flip_flop + x_time;
+ break;
+ case DISC_555_OUT_COUNT_F_X:
+ v_out = count_f ? count_f + x_time : count_f;
+ break;
+ case DISC_555_OUT_COUNT_R_X:
+ v_out = count_r ? count_r + x_time : count_r;
+ break;
+ case DISC_555_OUT_COUNT_F:
+ v_out = count_f;
+ break;
+ case DISC_555_OUT_COUNT_R:
+ v_out = count_r;
+ break;
+ }
+ set_output(0, v_out);
+ m_flip_flop = flip_flop;
+}
+
+DISCRETE_RESET(dsd_555_astbl)
+{
+ DISCRETE_DECLARE_INFO(discrete_555_desc)
+
+ m_use_ctrlv = (this->input_is_node() >> 4) & 1;
+ m_output_type = info->options & DISC_555_OUT_MASK;
+
+ /* Use the defaults or supplied values. */
+ m_v_out_high = (info->v_out_high == DEFAULT_555_HIGH) ? info->v_pos - 1.2 : info->v_out_high;
+
+ /* setup v_charge or node */
+ m_v_charge_node = m_device->node_output_ptr(info->v_charge);
+ if (m_v_charge_node == NULL)
+ {
+ m_v_charge = (info->v_charge == DEFAULT_555_CHARGE) ? info->v_pos : info->v_charge;
+
+ if (info->options & DISC_555_ASTABLE_HAS_FAST_CHARGE_DIODE) m_v_charge -= 0.5;
+ }
+
+ if ((DSD_555_ASTBL__CTRLV != -1) && !m_use_ctrlv)
+ {
+ /* Setup based on supplied Control Voltage static value */
+ m_threshold = DSD_555_ASTBL__CTRLV;
+ m_trigger = DSD_555_ASTBL__CTRLV / 2.0;
+ }
+ else
+ {
+ /* Setup based on v_pos power source */
+ m_threshold = info->v_pos * 2.0 / 3.0;
+ m_trigger = info->v_pos / 3.0;
+ }
+
+ /* optimization if none of the values are nodes */
+ m_has_rc_nodes = 0;
+ if (this->input_is_node() & DSD_555_ASTBL_RC_MASK)
+ m_has_rc_nodes = 1;
+ else
+ {
+ m_t_rc_bleed = DSD_555_ASTBL_T_RC_BLEED;
+ m_exp_bleed = RC_CHARGE_EXP(m_t_rc_bleed);
+ m_t_rc_charge = DSD_555_ASTBL_T_RC_CHARGE;
+ m_exp_charge = RC_CHARGE_EXP(m_t_rc_charge);
+ m_t_rc_discharge = DSD_555_ASTBL_T_RC_DISCHARGE;
+ m_exp_discharge = RC_CHARGE_EXP(m_t_rc_discharge);
+ }
+
+ m_output_is_ac = info->options & DISC_555_OUT_AC;
+ /* Calculate DC shift needed to make squarewave waveform AC */
+ m_ac_shift = m_output_is_ac ? -m_v_out_high / 2.0 : 0;
+
+ m_flip_flop = 1;
+ m_cap_voltage = 0;
+
+ /* Step to set the output */
+ this->step();
+}
+
+
+/************************************************************************
+ *
+ * DSD_555_MSTBL - 555 Monostable simulation
+ *
+ * input[0] - Reset value
+ * input[1] - Trigger input
+ * input[2] - R2 value
+ * input[3] - C value
+ *
+ * also passed discrete_555_desc structure
+ *
+ * Oct 2004, D Renaud.
+ ************************************************************************/
+#define DSD_555_MSTBL__RESET (! DISCRETE_INPUT(0))
+#define DSD_555_MSTBL__TRIGGER DISCRETE_INPUT(1)
+#define DSD_555_MSTBL__R DISCRETE_INPUT(2)
+#define DSD_555_MSTBL__C DISCRETE_INPUT(3)
+
+/* bit mask of the above RC inputs */
+#define DSD_555_MSTBL_RC_MASK 0x0c
+
+DISCRETE_STEP(dsd_555_mstbl)
+{
+ DISCRETE_DECLARE_INFO(discrete_555_desc)
+
+ double v_cap; /* Current voltage on capacitor, before dt */
+ double x_time = 0; /* time since change happened */
+ double dt, exponent;
+ double out = 0;
+ int trigger = 0;
+ int trigger_type;
+ int update_exponent = m_has_rc_nodes;
+ int flip_flop;
+
+ if(UNEXPECTED(DSD_555_MSTBL__RESET))
+ {
+ /* We are in RESET */
+ set_output(0, 0);
+ m_flip_flop = 0;
+ m_cap_voltage = 0;
+ return;
+ }
+
+ dt = this->sample_time();
+ flip_flop = m_flip_flop;
+ trigger_type = info->options;
+ v_cap = m_cap_voltage;
+
+ switch (trigger_type & DSD_555_TRIGGER_TYPE_MASK)
+ {
+ case DISC_555_TRIGGER_IS_LOGIC:
+ trigger = ((int)DSD_555_MSTBL__TRIGGER) ? 0 : 1;
+ if (UNEXPECTED(trigger))
+ x_time = 1.0 - DSD_555_MSTBL__TRIGGER;
+ break;
+ case DISC_555_TRIGGER_IS_VOLTAGE:
+ trigger = (int)(DSD_555_MSTBL__TRIGGER < m_trigger);
+ break;
+ case DISC_555_TRIGGER_IS_COUNT:
+ trigger = (int)DSD_555_MSTBL__TRIGGER;
+ if (UNEXPECTED(trigger))
+ x_time = DSD_555_MSTBL__TRIGGER - trigger;
+ break;
+ }
+
+ if (UNEXPECTED(trigger && !flip_flop && x_time != 0))
+ {
+ /* adjust sample to after trigger */
+ update_exponent = 1;
+ dt *= x_time;
+ }
+ x_time = 0;
+
+ if ((trigger_type & DISC_555_TRIGGER_DISCHARGES_CAP) && trigger)
+ m_cap_voltage = 0;
+
+ /* Wait for trigger */
+ if (UNEXPECTED(!flip_flop && trigger))
+ {
+ flip_flop = 1;
+ m_flip_flop = 1;
+ }
+
+ if (flip_flop)
+ {
+ /* Sometimes a switching network is used to setup the capacitance.
+ * These may select 'no' capacitor, causing oscillation to stop.
+ */
+ if (UNEXPECTED(DSD_555_MSTBL__C == 0))
+ {
+ /* The trigger voltage goes high because the cap circuit is open.
+ * and the cap discharges */
+ v_cap = info->v_pos; /* needed for cap output type */
+ m_cap_voltage = 0;
+
+ if (!trigger)
+ {
+ flip_flop = 0;
+ m_flip_flop = 0;
+ }
+ }
+ else
+ {
+ /* Charging */
+ double v_diff = m_v_charge - v_cap;
+
+ if (UNEXPECTED(update_exponent))
+ exponent = RC_CHARGE_EXP_DT(DSD_555_MSTBL__R * DSD_555_MSTBL__C, dt);
+ else
+ exponent = m_exp_charge;
+ v_cap += v_diff * exponent;
+
+ /* Has it charged past upper limit? */
+ /* If trigger is still enabled, then we keep charging,
+ * regardless of threshold. */
+ if (UNEXPECTED((v_cap >= m_threshold) && !trigger))
+ {
+ dt = DSD_555_MSTBL__R * DSD_555_MSTBL__C * log(1.0 / (1.0 - ((v_cap - m_threshold) / v_diff)));
+ x_time = 1.0 - dt / this->sample_time();
+ v_cap = 0;
+ flip_flop = 0;
+ m_flip_flop = 0;
+ }
+ m_cap_voltage = v_cap;
+ }
+ }
+
+ switch (m_output_type)
+ {
+ case DISC_555_OUT_SQW:
+ out = flip_flop * m_v_out_high - m_ac_shift;
+ break;
+ case DISC_555_OUT_CAP:
+ if (x_time > 0)
+ out = v_cap * x_time;
+ else
+ out = v_cap;
+
+ out -= m_ac_shift;
+ break;
+ case DISC_555_OUT_ENERGY:
+ if (x_time > 0)
+ out = m_v_out_high * x_time;
+ else if (flip_flop)
+ out = m_v_out_high;
+ else
+ out = 0;
+
+ out -= m_ac_shift;
+ break;
+ }
+ set_output(0, out);
+}
+
+DISCRETE_RESET(dsd_555_mstbl)
+{
+ DISCRETE_DECLARE_INFO(discrete_555_desc)
+
+ m_output_type = info->options & DISC_555_OUT_MASK;
+ if ((m_output_type == DISC_555_OUT_COUNT_F) || (m_output_type == DISC_555_OUT_COUNT_R))
+ {
+ m_device->discrete_log("Invalid Output type in NODE_%d.\n", this->index());
+ m_output_type = DISC_555_OUT_SQW;
+ }
+
+ /* Use the defaults or supplied values. */
+ m_v_out_high = (info->v_out_high == DEFAULT_555_HIGH) ? info->v_pos - 1.2 : info->v_out_high;
+ m_v_charge = (info->v_charge == DEFAULT_555_CHARGE) ? info->v_pos : info->v_charge;
+
+ /* Setup based on v_pos power source */
+ m_threshold = info->v_pos * 2.0 / 3.0;
+ m_trigger = info->v_pos / 3.0;
+
+ /* Calculate DC shift needed to make waveform AC */
+ if (info->options & DISC_555_OUT_AC)
+ {
+ if (m_output_type == DISC_555_OUT_CAP)
+ m_ac_shift = m_threshold * 3.0 /4.0;
+ else
+ m_ac_shift = m_v_out_high / 2.0;
+ }
+ else
+ m_ac_shift = 0;
+
+ m_trig_is_logic = (info->options & DISC_555_TRIGGER_IS_VOLTAGE) ? 0: 1;
+ m_trig_discharges_cap = (info->options & DISC_555_TRIGGER_DISCHARGES_CAP) ? 1: 0;
+
+ m_flip_flop = 0;
+ m_cap_voltage = 0;
+
+ /* optimization if none of the values are nodes */
+ m_has_rc_nodes = 0;
+ if (this->input_is_node() & DSD_555_MSTBL_RC_MASK)
+ m_has_rc_nodes = 1;
+ else
+ m_exp_charge = RC_CHARGE_EXP(DSD_555_MSTBL__R * DSD_555_MSTBL__C);
+
+ set_output(0, 0);
+}
+
+
+/************************************************************************
+ *
+ * DSD_555_CC - Usage of node_description values
+ *
+ * input[0] - Reset input value
+ * input[1] - Voltage input for Constant current source.
+ * input[2] - R value to set CC current.
+ * input[3] - C value
+ * input[4] - rBias value
+ * input[5] - rGnd value
+ * input[6] - rDischarge value
+ *
+ * also passed discrete_555_cc_desc structure
+ *
+ * Mar 2004, D Renaud.
+ ************************************************************************/
+#define DSD_555_CC__RESET (! DISCRETE_INPUT(0))
+#define DSD_555_CC__VIN DISCRETE_INPUT(1)
+#define DSD_555_CC__R DISCRETE_INPUT(2)
+#define DSD_555_CC__C DISCRETE_INPUT(3)
+#define DSD_555_CC__RBIAS DISCRETE_INPUT(4)
+#define DSD_555_CC__RGND DISCRETE_INPUT(5)
+#define DSD_555_CC__RDIS DISCRETE_INPUT(6)
+
+/* bit mask of the above RC inputs not including DSD_555_CC__R */
+#define DSD_555_CC_RC_MASK 0x78
+
+/* charge/discharge constants */
+#define DSD_555_CC_T_RC_BLEED (DEFAULT_555_BLEED_R * DSD_555_CC__C)
+#define DSD_555_CC_T_RC_DISCHARGE_01 (DSD_555_CC__RDIS * DSD_555_CC__C)
+#define DSD_555_CC_T_RC_DISCHARGE_NO_I (DSD_555_CC__RGND * DSD_555_CC__C)
+#define DSD_555_CC_T_RC_CHARGE (r_charge * DSD_555_CC__C)
+#define DSD_555_CC_T_RC_DISCHARGE (r_discharge * DSD_555_CC__C)
+
+
+DISCRETE_STEP(dsd_555_cc)
+{
+ DISCRETE_DECLARE_INFO(discrete_555_cc_desc)
+
+ int count_f = 0;
+ int count_r = 0;
+ double i; /* Charging current created by vIn */
+ double r_charge = 0; /* Equivalent charging resistor */
+ double r_discharge = 0; /* Equivalent discharging resistor */
+ double vi = 0; /* Equivalent voltage from current source */
+ double v_bias = 0; /* Equivalent voltage from bias voltage */
+ double v = 0; /* Equivalent voltage total from current source and bias circuit if used */
+ double dt; /* change in time */
+ double x_time = 0; /* time since change happened */
+ double t_rc ; /* RC time constant */
+ double v_cap; /* Current voltage on capacitor, before dt */
+ double v_cap_next = 0; /* Voltage on capacitor, after dt */
+ double v_vcharge_limit; /* vIn and the junction voltage limit the max charging voltage from i */
+ double r_temp; /* play thing */
+ double exponent;
+ UINT8 update_exponent, update_t_rc;
+ UINT8 flip_flop = m_flip_flop;
+
+ double v_out = 0;
+
+
+ if (UNEXPECTED(DSD_555_CC__RESET))
+ {
+ /* We are in RESET */
+ set_output(0, 0);
+ m_flip_flop = 1;
+ m_cap_voltage = 0;
+ return;
+ }
+
+ dt = this->sample_time(); /* Change in time */
+ v_cap = m_cap_voltage; /* Set to voltage before change */
+ v_vcharge_limit = DSD_555_CC__VIN + info->v_cc_junction; /* the max v_cap can be and still be charged by i */
+ /* Calculate charging current */
+ i = (m_v_cc_source - v_vcharge_limit) / DSD_555_CC__R;
+ if ( i < 0) i = 0;
+
+ if (info->options & DISCRETE_555_CC_TO_CAP)
+ {
+ vi = i * DSD_555_CC__RDIS;
+ }
+ else
+ {
+ switch (m_type) /* see dsd_555_cc_reset for descriptions */
+ {
+ case 1:
+ r_discharge = DSD_555_CC__RDIS;
+ case 0:
+ break;
+ case 3:
+ r_discharge = RES_2_PARALLEL(DSD_555_CC__RDIS, DSD_555_CC__RGND);
+ case 2:
+ r_charge = DSD_555_CC__RGND;
+ vi = i * r_charge;
+ break;
+ case 4:
+ r_charge = DSD_555_CC__RBIAS;
+ vi = i * r_charge;
+ v_bias = info->v_pos;
+ break;
+ case 5:
+ r_charge = DSD_555_CC__RBIAS + DSD_555_CC__RDIS;
+ vi = i * DSD_555_CC__RBIAS;
+ v_bias = info->v_pos;
+ r_discharge = DSD_555_CC__RDIS;
+ break;
+ case 6:
+ r_charge = RES_2_PARALLEL(DSD_555_CC__RBIAS, DSD_555_CC__RGND);
+ vi = i * r_charge;
+ v_bias = info->v_pos * RES_VOLTAGE_DIVIDER(DSD_555_CC__RGND, DSD_555_CC__RBIAS);
+ break;
+ case 7:
+ r_temp = DSD_555_CC__RBIAS + DSD_555_CC__RDIS;
+ r_charge = RES_2_PARALLEL(r_temp, DSD_555_CC__RGND);
+ r_temp += DSD_555_CC__RGND;
+ r_temp = DSD_555_CC__RGND / r_temp; /* now has voltage divider ratio, not resistance */
+ vi = i * DSD_555_CC__RBIAS * r_temp;
+ v_bias = info->v_pos * r_temp;
+ r_discharge = RES_2_PARALLEL(DSD_555_CC__RGND, DSD_555_CC__RDIS);
+ break;
+ }
+ }
+
+ /* Keep looping until all toggling in time sample is used up. */
+ update_t_rc = m_has_rc_nodes;
+ update_exponent = update_t_rc;
+ do
+ {
+ if (m_type <= 1)
+ {
+ /* Standard constant current charge */
+ if (flip_flop)
+ {
+ if (i == 0)
+ {
+ /* No charging current, so we have to discharge the cap
+ * due to cap and circuit losses.
+ */
+ if (update_exponent)
+ {
+ t_rc = DSD_555_CC_T_RC_BLEED;
+ exponent = RC_CHARGE_EXP_DT(t_rc, dt);
+ }
+ else
+ exponent = m_exp_bleed;
+ v_cap_next = v_cap - (v_cap * exponent);
+ dt = 0;
+ }
+ else
+ {
+ /* Charging */
+ /* iC=C*dv/dt works out to dv=iC*dt/C */
+ v_cap_next = v_cap + (i * dt / DSD_555_CC__C);
+ /* Yes, if the cap voltage has reached the max voltage it can,
+ * and the 555 threshold has not been reached, then oscillation stops.
+ * This is the way the actual electronics works.
+ * This is why you never play with the pots after being factory adjusted
+ * to work in the proper range. */
+ if (v_cap_next > v_vcharge_limit) v_cap_next = v_vcharge_limit;
+ dt = 0;
+
+ /* has it charged past upper limit? */
+ if (v_cap_next >= m_threshold)
+ {
+ /* calculate the overshoot time */
+ dt = DSD_555_CC__C * (v_cap_next - m_threshold) / i;
+ x_time = dt;
+ v_cap_next = m_threshold;
+ flip_flop = 0;
+ count_f++;
+ update_exponent = 1;
+ }
+ }
+ }
+ else if (DSD_555_CC__RDIS != 0)
+ {
+ /* Discharging */
+ if (update_t_rc)
+ t_rc = DSD_555_CC_T_RC_DISCHARGE_01;
+ else
+ t_rc = m_t_rc_discharge_01;
+ if (update_exponent)
+ exponent = RC_CHARGE_EXP_DT(t_rc, dt);
+ else
+ exponent = m_exp_discharge_01;
+
+ if (info->options & DISCRETE_555_CC_TO_CAP)
+ {
+ /* Asteroids - Special Case */
+ /* Charging in discharge mode */
+ /* If the cap voltage is past the current source charging limit
+ * then only the bias voltage will charge the cap. */
+ v = (v_cap < v_vcharge_limit) ? vi : v_vcharge_limit;
+ v_cap_next = v_cap + ((v - v_cap) * exponent);
+ }
+ else
+ {
+ v_cap_next = v_cap - (v_cap * exponent);
+ }
+
+ dt = 0;
+ /* has it discharged past lower limit? */
+ if (v_cap_next <= m_trigger)
+ {
+ dt = t_rc * log(1.0 / (1.0 - ((m_trigger - v_cap_next) / v_cap)));
+ x_time = dt;
+ v_cap_next = m_trigger;
+ flip_flop = 1;
+ count_r++;
+ update_exponent = 1;
+ }
+ }
+ else /* Immediate discharge. No change in dt. */
+ {
+ x_time = dt;
+ v_cap_next = m_trigger;
+ flip_flop = 1;
+ count_r++;
+ }
+ }
+ else
+ {
+ /* The constant current gets changed to a voltage due to a load resistor. */
+ if (flip_flop)
+ {
+ if ((i == 0) && (DSD_555_CC__RBIAS == 0))
+ {
+ /* No charging current, so we have to discharge the cap
+ * due to rGnd.
+ */
+ if (update_t_rc)
+ t_rc = DSD_555_CC_T_RC_DISCHARGE_NO_I;
+ else
+ t_rc = m_t_rc_discharge_no_i;
+ if (update_exponent)
+ exponent = RC_CHARGE_EXP_DT(t_rc, dt);
+ else
+ exponent = m_exp_discharge_no_i;
+
+ v_cap_next = v_cap - (v_cap * exponent);
+ dt = 0;
+ }
+ else
+ {
+ /* Charging */
+ /* If the cap voltage is past the current source charging limit
+ * then only the bias voltage will charge the cap. */
+ v = v_bias;
+ if (v_cap < v_vcharge_limit) v += vi;
+ else if (m_type <= 3) v = v_vcharge_limit;
+
+ if (update_t_rc)
+ t_rc = DSD_555_CC_T_RC_CHARGE;
+ else
+ t_rc = m_t_rc_charge;
+ if (update_exponent)
+ exponent = RC_CHARGE_EXP_DT(t_rc, dt);
+ else
+ exponent = m_exp_charge;
+
+ v_cap_next = v_cap + ((v - v_cap) * exponent);
+ dt = 0;
+
+ /* has it charged past upper limit? */
+ if (v_cap_next >= m_threshold)
+ {
+ /* calculate the overshoot time */
+ dt = t_rc * log(1.0 / (1.0 - ((v_cap_next - m_threshold) / (v - v_cap))));
+ x_time = dt;
+ v_cap_next = m_threshold;
+ flip_flop = 0;
+ count_f++;
+ update_exponent = 1;
+ }
+ }
+ }
+ else /* Discharging */
+ if (r_discharge)
+ {
+ if (update_t_rc)
+ t_rc = DSD_555_CC_T_RC_DISCHARGE;
+ else
+ t_rc = m_t_rc_discharge;
+ if (update_exponent)
+ exponent = RC_CHARGE_EXP_DT(t_rc, dt);
+ else
+ exponent = m_exp_discharge;
+
+ v_cap_next = v_cap - (v_cap * exponent);
+ dt = 0;
+
+ /* has it discharged past lower limit? */
+ if (v_cap_next <= m_trigger)
+ {
+ /* calculate the overshoot time */
+ dt = t_rc * log(1.0 / (1.0 - ((m_trigger - v_cap_next) / v_cap)));
+ x_time = dt;
+ v_cap_next = m_trigger;
+ flip_flop = 1;
+ count_r++;
+ update_exponent = 1;
+ }
+ }
+ else /* Immediate discharge. No change in dt. */
+ {
+ x_time = dt;
+ v_cap_next = m_trigger;
+ flip_flop = 1;
+ count_r++;
+ }
+ }
+ v_cap = v_cap_next;
+ } while(dt);
+
+ m_cap_voltage = v_cap;
+
+ /* Convert last switch time to a ratio */
+ x_time = x_time / this->sample_time();
+
+ switch (m_output_type)
+ {
+ case DISC_555_OUT_SQW:
+ if (count_f + count_r >= 2)
+ /* force at least 1 toggle */
+ v_out = m_flip_flop ? 0 : m_v_out_high;
+ else
+ v_out = flip_flop * m_v_out_high;
+ /* Fake it to AC if needed */
+ v_out += m_ac_shift;
+ break;
+ case DISC_555_OUT_CAP:
+ v_out = v_cap + m_ac_shift;
+ break;
+ case DISC_555_OUT_ENERGY:
+ if (x_time == 0) x_time = 1.0;
+ v_out = m_v_out_high * (flip_flop ? x_time : (1.0 - x_time));
+ v_out += m_ac_shift;
+ break;
+ case DISC_555_OUT_LOGIC_X:
+ v_out = flip_flop + x_time;
+ break;
+ case DISC_555_OUT_COUNT_F_X:
+ v_out = count_f ? count_f + x_time : count_f;
+ break;
+ case DISC_555_OUT_COUNT_R_X:
+ v_out = count_r ? count_r + x_time : count_r;
+ break;
+ case DISC_555_OUT_COUNT_F:
+ v_out = count_f;
+ break;
+ case DISC_555_OUT_COUNT_R:
+ v_out = count_r;
+ break;
+ }
+ set_output(0, v_out);
+ m_flip_flop = flip_flop;
+}
+
+DISCRETE_RESET(dsd_555_cc)
+{
+ DISCRETE_DECLARE_INFO(discrete_555_cc_desc)
+
+ double r_temp, r_discharge = 0, r_charge = 0;
+
+ m_flip_flop = 1;
+ m_cap_voltage = 0;
+
+ m_output_type = info->options & DISC_555_OUT_MASK;
+
+ /* Use the defaults or supplied values. */
+ m_v_out_high = (info->v_out_high == DEFAULT_555_HIGH) ? info->v_pos - 1.2 : info->v_out_high;
+ m_v_cc_source = (info->v_cc_source == DEFAULT_555_CC_SOURCE) ? info->v_pos : info->v_cc_source;
+
+ /* Setup based on v_pos power source */
+ m_threshold = info->v_pos * 2.0 / 3.0;
+ m_trigger = info->v_pos / 3.0;
+
+ m_output_is_ac = info->options & DISC_555_OUT_AC;
+ /* Calculate DC shift needed to make squarewave waveform AC */
+ m_ac_shift = m_output_is_ac ? -m_v_out_high / 2.0 : 0;
+
+ /* There are 8 different types of basic oscillators
+ * depending on the resistors used. We will determine
+ * the type of circuit at reset, because the ciruit type
+ * is constant. See Below.
+ */
+ m_type = (DSD_555_CC__RDIS > 0) | ((DSD_555_CC__RGND > 0) << 1) | ((DSD_555_CC__RBIAS > 0) << 2);
+
+ /* optimization if none of the values are nodes */
+ m_has_rc_nodes = 0;
+ if (this->input_is_node() & DSD_555_CC_RC_MASK)
+ m_has_rc_nodes = 1;
+ else
+ {
+ switch (m_type) /* see dsd_555_cc_reset for descriptions */
+ {
+ case 1:
+ r_discharge = DSD_555_CC__RDIS;
+ case 0:
+ break;
+ case 3:
+ r_discharge = RES_2_PARALLEL(DSD_555_CC__RDIS, DSD_555_CC__RGND);
+ case 2:
+ r_charge = DSD_555_CC__RGND;
+ break;
+ case 4:
+ r_charge = DSD_555_CC__RBIAS;
+ break;
+ case 5:
+ r_charge = DSD_555_CC__RBIAS + DSD_555_CC__RDIS;
+ r_discharge = DSD_555_CC__RDIS;
+ break;
+ case 6:
+ r_charge = RES_2_PARALLEL(DSD_555_CC__RBIAS, DSD_555_CC__RGND);
+ break;
+ case 7:
+ r_temp = DSD_555_CC__RBIAS + DSD_555_CC__RDIS;
+ r_charge = RES_2_PARALLEL(r_temp, DSD_555_CC__RGND);
+ r_discharge = RES_2_PARALLEL(DSD_555_CC__RGND, DSD_555_CC__RDIS);
+ break;
+ }
+
+ m_exp_bleed = RC_CHARGE_EXP(DSD_555_CC_T_RC_BLEED);
+ m_t_rc_discharge_01 = DSD_555_CC_T_RC_DISCHARGE_01;
+ m_exp_discharge_01 = RC_CHARGE_EXP(m_t_rc_discharge_01);
+ m_t_rc_discharge_no_i = DSD_555_CC_T_RC_DISCHARGE_NO_I;
+ m_exp_discharge_no_i = RC_CHARGE_EXP(m_t_rc_discharge_no_i);
+ m_t_rc_charge = DSD_555_CC_T_RC_CHARGE;
+ m_exp_charge = RC_CHARGE_EXP(m_t_rc_charge);
+ m_t_rc_discharge = DSD_555_CC_T_RC_DISCHARGE;
+ m_exp_discharge = RC_CHARGE_EXP(m_t_rc_discharge);
+ }
+
+ /* Step to set the output */
+ this->step();
+
+ /*
+ * TYPES:
+ * Note: These are equivalent circuits shown without the 555 circuitry.
+ * See the schematic in src\sound\discrete.h for full hookup info.
+ *
+ * DISCRETE_555_CC_TO_DISCHARGE_PIN
+ * When the CC source is connected to the discharge pin, it allows the
+ * circuit to charge when the 555 is in charge mode. But when in discharge
+ * mode, the CC source is grounded, disabling it's effect.
+ *
+ * [0]
+ * No resistors. Straight constant current charge of capacitor.
+ * When there is not any charge current, the cap will bleed off.
+ * Once the lower threshold(trigger) is reached, the output will
+ * go high but the cap will continue to discharge due to losses.
+ * .------+---> cap_voltage CHARGING:
+ * | | dv (change in voltage) compared to dt (change in time in seconds).
+ * .---. --- dv = i * dt / C; where i is current in amps and C is capacitance in farads.
+ * | i | --- C cap_voltage = cap_voltage + dv
+ * '---' |
+ * | | DISCHARGING:
+ * gnd gnd instantaneous
+ *
+ * [1]
+ * Same as type 1 but with rDischarge. rDischarge has no effect on the charge rate because
+ * of the constant current source i.
+ * When there is not any charge current, the cap will bleed off.
+ * Once the lower threshold(trigger) is reached, the output will
+ * go high but the cap will continue to discharge due to losses.
+ * .----ZZZ-----+---> cap_voltage CHARGING:
+ * | rDischarge | dv (change in voltage) compared to dt (change in time in seconds).
+ * .---. --- dv = i * dt / C; where i is current in amps and C is capacitance in farads.
+ * | i | --- C cap_voltage = cap_voltage + dv
+ * '---' |
+ * | | DISCHARGING:
+ * gnd gnd through rDischarge
+ *
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * !!!!! IMPORTANT NOTE ABOUT TYPES 3 - 7 !!!!!
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *
+ * From here on in all the circuits have either an rBias or rGnd resistor.
+ * This converts the constant current into a voltage source.
+ * So all the remaining circuit types will be converted to this circuit.
+ * When discharging, rBias is out of the equation because the 555 is grounding the circuit
+ * after that point.
+ *
+ * .------------. Rc Rc is the equivilent circuit resistance.
+ * | v |----ZZZZ---+---> cap_voltage v is the equivilent circuit voltage.
+ * | | |
+ * '------------' --- Then the standard RC charging formula applies.
+ * | --- C
+ * | | NOTE: All the following types are converted to Rc and v values.
+ * gnd gnd
+ *
+ * [2]
+ * When there is not any charge current, the cap will bleed off.
+ * Once the lower threshold(trigger) is reached, the output will
+ * go high but the cap will continue to discharge due to rGnd.
+ * .-------+------+------> cap_voltage CHARGING:
+ * | | | v = vi = i * rGnd
+ * .---. --- Z Rc = rGnd
+ * | i | --- C Z rGnd
+ * '---' | | DISCHARGING:
+ * | | | instantaneous
+ * gnd gnd gnd
+ *
+ * [3]
+ * When there is not any charge current, the cap will bleed off.
+ * Once the lower threshold(trigger) is reached, the output will
+ * go high but the cap will continue to discharge due to rGnd.
+ * .----ZZZ-----+------+------> cap_voltage CHARGING:
+ * | rDischarge | | v = vi = i * rGnd
+ * .---. --- Z Rc = rGnd
+ * | i | --- C Z rGnd
+ * '---' | | DISCHARGING:
+ * | | | through rDischarge || rGnd ( || means in parallel)
+ * gnd gnd gnd
+ *
+ * [4]
+ * .---ZZZ---+------------+-------------> cap_voltage CHARGING:
+ * | rBias | | Rc = rBias
+ * .-------. .---. --- vi = i * rBias
+ * | vBias | | i | --- C v = vBias + vi
+ * '-------' '---' |
+ * | | | DISCHARGING:
+ * gnd gnd gnd instantaneous
+ *
+ * [5]
+ * .---ZZZ---+----ZZZ-----+-------------> cap_voltage CHARGING:
+ * | rBias | rDischarge | Rc = rBias + rDischarge
+ * .-------. .---. --- vi = i * rBias
+ * | vBias | | i | --- C v = vBias + vi
+ * '-------' '---' |
+ * | | | DISCHARGING:
+ * gnd gnd gnd through rDischarge
+ *
+ * [6]
+ * .---ZZZ---+------------+------+------> cap_voltage CHARGING:
+ * | rBias | | | Rc = rBias || rGnd
+ * .-------. .---. --- Z vi = i * Rc
+ * | vBias | | i | --- C Z rGnd v = vBias * (rGnd / (rBias + rGnd)) + vi
+ * '-------' '---' | |
+ * | | | | DISCHARGING:
+ * gnd gnd gnd gnd instantaneous
+ *
+ * [7]
+ * .---ZZZ---+----ZZZ-----+------+------> cap_voltage CHARGING:
+ * | rBias | rDischarge | | Rc = (rBias + rDischarge) || rGnd
+ * .-------. .---. --- Z vi = i * rBias * (rGnd / (rBias + rDischarge + rGnd))
+ * | vBias | | i | --- C Z rGnd v = vBias * (rGnd / (rBias + rDischarge + rGnd)) + vi
+ * '-------' '---' | |
+ * | | | | DISCHARGING:
+ * gnd gnd gnd gnd through rDischarge || rGnd
+ */
+
+ /*
+ * DISCRETE_555_CC_TO_CAP
+ *
+ * When the CC source is connected to the capacitor, it allows the
+ * current to charge the cap while it is in discharge mode, slowing the
+ * discharge. So in charge mode it charges linearly from the constant
+ * current cource. But when in discharge mode it behaves like circuit
+ * type 2 above.
+ * .-------+------+------> cap_voltage CHARGING:
+ * | | | dv = i * dt / C
+ * .---. --- Z cap_voltage = cap_voltage + dv
+ * | i | --- C Z rDischarge
+ * '---' | | DISCHARGING:
+ * | | | v = vi = i * rGnd
+ * gnd gnd discharge Rc = rDischarge
+ */
+}
+
+
+/************************************************************************
+ *
+ * DSD_555_VCO1 - Usage of node_description values
+ *
+ * input[0] - Reset input value
+ * input[1] - Modulation Voltage (Vin1)
+ * input[2] - Control Voltage (Vin2)
+ *
+ * also passed discrete_5555_vco1_desc structure
+ *
+ * Apr 2006, D Renaud.
+ ************************************************************************/
+#define DSD_555_VCO1__RESET DISCRETE_INPUT(0) /* reset active low */
+#define DSD_555_VCO1__VIN1 DISCRETE_INPUT(1)
+#define DSD_555_VCO1__VIN2 DISCRETE_INPUT(2)
+
+DISCRETE_STEP(dsd_555_vco1)
+{
+ DISCRETE_DECLARE_INFO(discrete_555_vco1_desc)
+
+ int count_f = 0;
+ int count_r = 0;
+ double dt; /* change in time */
+ double x_time = 0; /* time since change happened */
+ double v_cap; /* Current voltage on capacitor, before dt */
+ double v_cap_next = 0; /* Voltage on capacitor, after dt */
+
+ double v_out = 0;
+
+ dt = this->sample_time(); /* Change in time */
+ v_cap = m_cap_voltage;
+
+ /* Check: if the Control Voltage node is connected. */
+ if (m_ctrlv_is_node && DSD_555_VCO1__RESET) /* reset active low */
+ {
+ /* If CV is less then .25V, the circuit will oscillate way out of range.
+ * So we will just ignore it when it happens. */
+ if (DSD_555_VCO1__VIN2 < .25) return;
+ /* If it is a node then calculate thresholds based on Control Voltage */
+ m_threshold = DSD_555_VCO1__VIN2;
+ m_trigger = DSD_555_VCO1__VIN2 / 2.0;
+ /* Since the thresholds may have changed we need to update the FF */
+ if (v_cap >= m_threshold)
+ {
+ x_time = dt;
+ m_flip_flop = 0;
+ count_f++;
+ }
+ else
+ if (v_cap <= m_trigger)
+ {
+ x_time = dt;
+ m_flip_flop = 1;
+ count_r++;
+ }
+ }
+
+ /* Keep looping until all toggling in time sample is used up. */
+ do
+ {
+ if (m_flip_flop)
+ {
+ /* if we are in reset then toggle f/f and discharge */
+ if (!DSD_555_VCO1__RESET) /* reset active low */
+ {
+ m_flip_flop = 0;
+ count_f++;
+ }
+ else
+ {
+ /* Charging */
+ /* iC=C*dv/dt works out to dv=iC*dt/C */
+ v_cap_next = v_cap + (m_i_charge * dt / info->c);
+ dt = 0;
+
+ /* has it charged past upper limit? */
+ if (v_cap_next >= m_threshold)
+ {
+ /* calculate the overshoot time */
+ dt = info->c * (v_cap_next - m_threshold) / m_i_charge;
+ v_cap = m_threshold;
+ x_time = dt;
+ m_flip_flop = 0;
+ count_f++;
+ }
+ }
+ }
+ else
+ {
+ /* Discharging */
+ /* iC=C*dv/dt works out to dv=iC*dt/C */
+ v_cap_next = v_cap - (m_i_discharge * dt / info->c);
+
+ /* if we are in reset, then the cap can discharge to 0 */
+ if (!DSD_555_VCO1__RESET) /* reset active low */
+ {
+ if (v_cap_next < 0) v_cap_next = 0;
+ dt = 0;
+ }
+ else
+ {
+ /* if we are out of reset and the cap voltage is less then
+ * the lower threshold, toggle f/f and start charging */
+ if (v_cap <= m_trigger)
+ {
+ if (m_flip_flop == 0)
+ {
+ /* don't need to track x_time here */
+ m_flip_flop = 1;
+ count_r++;
+ }
+ }
+ else
+ {
+ dt = 0;
+ /* has it discharged past lower limit? */
+ if (v_cap_next <= m_trigger)
+ {
+ /* calculate the overshoot time */
+ dt = info->c * (v_cap_next - m_trigger) / m_i_discharge;
+ v_cap = m_trigger;
+ x_time = dt;
+ m_flip_flop = 1;
+ count_r++;
+ }
+ }
+ }
+ }
+ } while(dt);
+
+ m_cap_voltage = v_cap_next;
+
+ /* Convert last switch time to a ratio. No x_time in reset. */
+ x_time = x_time / this->sample_time();
+ if (!DSD_555_VCO1__RESET) x_time = 0;
+
+ switch (m_output_type)
+ {
+ case DISC_555_OUT_SQW:
+ v_out = m_flip_flop * m_v_out_high + m_ac_shift;
+ break;
+ case DISC_555_OUT_CAP:
+ v_out = v_cap_next;
+ /* Fake it to AC if needed */
+ if (m_output_is_ac)
+ v_out -= m_threshold * 3.0 /4.0;
+ break;
+ case DISC_555_OUT_ENERGY:
+ if (x_time == 0) x_time = 1.0;
+ v_out = m_v_out_high * (m_flip_flop ? x_time : (1.0 - x_time));
+ v_out += m_ac_shift;
+ break;
+ case DISC_555_OUT_LOGIC_X:
+ v_out = m_flip_flop + x_time;
+ break;
+ case DISC_555_OUT_COUNT_F_X:
+ v_out = count_f ? count_f + x_time : count_f;
+ break;
+ case DISC_555_OUT_COUNT_R_X:
+ v_out = count_r ? count_r + x_time : count_r;
+ break;
+ case DISC_555_OUT_COUNT_F:
+ v_out = count_f;
+ break;
+ case DISC_555_OUT_COUNT_R:
+ v_out = count_r;
+ break;
+ }
+ set_output(0, v_out);
+}
+
+DISCRETE_RESET(dsd_555_vco1)
+{
+ DISCRETE_DECLARE_INFO(discrete_555_vco1_desc)
+
+ double v_ratio_r3, v_ratio_r4_1, r_in_1;
+
+ m_output_type = info->options & DISC_555_OUT_MASK;
+ m_output_is_ac = info->options & DISC_555_OUT_AC;
+
+ /* Setup op-amp parameters */
+
+ /* The voltage at op-amp +in is always a fixed ratio of the modulation voltage. */
+ v_ratio_r3 = info->r3 / (info->r2 + info->r3); /* +in voltage */
+ /* The voltage at op-amp -in is 1 of 2 fixed ratios of the modulation voltage,
+ * based on the 555 Flip-Flop state. */
+ /* If the FF is 0, then only R1 is connected allowing the full modulation volatge to pass. */
+ /* v_ratio_r4_0 = 1 */
+ /* If the FF is 1, then R1 & R4 make a voltage divider similar to R2 & R3 */
+ v_ratio_r4_1 = info->r4 / (info->r1 + info->r4); /* -in voltage */
+ /* the input resistance to the op amp depends on the FF state */
+ /* r_in_0 = info->r1 when FF = 0 */
+ r_in_1 = 1.0 / (1.0 / info->r1 + 1.0 / info->r4); /* input resistance when r4 switched in */
+
+ /* Now that we know the voltages entering the op amp and the resistance for the
+ * FF states, we can predetermine the ratios for the charge/discharge currents. */
+ m_i_discharge = (1 - v_ratio_r3) / info->r1;
+ m_i_charge = (v_ratio_r3 - v_ratio_r4_1) / r_in_1;
+
+ /* the cap starts off discharged */
+ m_cap_voltage = 0;
+
+ /* Setup 555 parameters */
+
+ /* There is no charge on the cap so the 555 goes high at init. */
+ m_flip_flop = 1;
+ m_ctrlv_is_node = (this->input_is_node() >> 2) & 1;
+ m_v_out_high = (info->v_out_high == DEFAULT_555_HIGH) ? info->v_pos - 1.2 : info->v_out_high;
+
+ /* Calculate 555 thresholds.
+ * If the Control Voltage is a node, then the thresholds will be calculated each step.
+ * If the Control Voltage is a fixed voltage, then the thresholds will be calculated
+ * from that. Otherwise we will use thresholds based on v_pos. */
+ if (!m_ctrlv_is_node && (DSD_555_VCO1__VIN2 != -1))
+ {
+ /* Setup based on supplied Control Voltage static value */
+ m_threshold = DSD_555_VCO1__VIN2;
+ m_trigger = DSD_555_VCO1__VIN2 / 2.0;
+ }
+ else
+ {
+ /* Setup based on v_pos power source */
+ m_threshold = info->v_pos * 2.0 / 3.0;
+ m_trigger = info->v_pos / 3.0;
+ }
+
+ /* Calculate DC shift needed to make squarewave waveform AC */
+ m_ac_shift = m_output_is_ac ? -m_v_out_high / 2.0 : 0;
+}
+
+
+/************************************************************************
+ *
+ * DSD_566 - Usage of node_description values
+ *
+ * Mar 2004, D Renaud. updated Sept 2009
+ *
+ * The data sheets for this are no where near correct.
+ * This simulation is based on the internal schematic and testing of
+ * a real Signetics IC.
+ *
+ * The 566 is a constant current based VCO. If you change R, that affects
+ * the charge/discharge rate. A constant current source will charge the
+ * cap linearly. Of course due to the transistors there will be some
+ * non-linear areas at the ends of the Vmod range. As the Vmod voltage
+ * drops from Vcharge, the frequency generated increases.
+ *
+ * The Triangle (pin 4) output is just a buffered version of the cap
+ * charge. It is about 1.35 higher then the cap voltage.
+ * The Square (pin 3) output starts low as the cap voltages rises.
+ * Once a threshold is reached, the cap starts to discharge, and the
+ * Square output goes high. The Square high output is about 1V less then
+ * B+. Unloaded it is .75V less. With a 4.7k pull-down resistor, it
+ * is 1.06V less. So I will simulate at 1V less. The Square low voltage
+ * is non-linear so I will use a table. The cap toggle thresholds vary
+ * depending on B+, so they will be simulated with a table.
+ *
+ * The data sheets show Vmod should be no less then 3/4*B+. In reality
+ * you can go to close to 1/2*B+ before you lose linearity. Below 1/2,
+ * oscillation stops. When Vmod is 0V to 0.1V less then B+, it also
+ * loses linearity, and stops oscillating when >= B+. This is because
+ * there is no voltage difference to create a current source.
+ *
+ * The current source is dependant on the voltage difference between B+
+ * and Vmod. Due to transistor action, it is not 100%, but this formula
+ * gives a good approximation:
+ * I = ((B+ - Vmod - 0.1) * 0.95) / R
+ * You can test the current VS modulation function by using 10k for R
+ * and replace C with a 10k resistor. Then you can monitor the voltage
+ * on pin 7 to work out the current. I=V/R. It will start to oscillate
+ * when in the cap threshold range.
+ *
+ * When Vmod drops below the stable range, the current source no longer
+ * functions properly. Technically this is out of the range specified
+ * for the IC. Of course old games used this range anyways, so we need
+ * to know how the real IC behaves. When Vmod drops below the stable range,
+ * the charge current is stops dropping instead of increasing, while the
+ * discharge current still functions. This means the frequency generated
+ * starts to drop as the voltage lowers, instead of the normal increase
+ * in frequency.
+ *
+ ************************************************************************/
+#define DSD_566__VMOD DISCRETE_INPUT(0)
+#define DSD_566__R DISCRETE_INPUT(1)
+#define DSD_566__C DISCRETE_INPUT(2)
+#define DSD_566__VPOS DISCRETE_INPUT(3)
+#define DSD_566__VNEG DISCRETE_INPUT(4)
+#define DSD_566__VCHARGE DISCRETE_INPUT(5)
+#define DSD_566__OPTIONS DISCRETE_INPUT(6)
+
+
+static const struct
+{
+ double c_high[6];
+ double c_low[6];
+ double sqr_low[6];
+ double osc_stable[6];
+ double osc_stop[6];
+} ne566 =
+{
+ /* 10 10.5 11 11.5 12 13 14 15 B+ */
+ {3.364, /*3.784,*/ 4.259, /*4.552,*/ 4.888, 5.384, 5.896, 6.416}, /* c_high */
+ {1.940, /*2.100,*/ 2.276, /*2.404,*/ 2.580, 2.880, 3.180, 3.488}, /* c_low */
+ {4.352, /*4.144,*/ 4.080, /*4.260,*/ 4.500, 4.960, 5.456, 5.940}, /* sqr_low */
+ {4.885, /*5.316,*/ 5.772, /*6.075,*/ 6.335, 6.912, 7.492, 7.945}, /* osc_stable */
+ {4.495, /*4.895,*/ 5.343, /*5.703,*/ 5.997, 6.507, 7.016, 7.518} /* osc_stop */
+};
+
+DISCRETE_STEP(dsd_566)
+{
+ double i = 0; /* Charging current created by vIn */
+ double i_rise; /* non-linear rise charge current */
+ double dt; /* change in time */
+ double x_time = 0;
+ double v_cap; /* Current voltage on capacitor, before dt */
+ int count_f = 0, count_r = 0;
+
+ double v_out = 0.0;
+
+ dt = this->sample_time(); /* Change in time */
+ v_cap = m_cap_voltage; /* Set to voltage before change */
+
+ /* Calculate charging current if it is in range */
+ if (EXPECTED(DSD_566__VMOD > m_v_osc_stop))
+ {
+ double v_charge = DSD_566__VCHARGE - DSD_566__VMOD - 0.1;
+ if (v_charge > 0)
+ {
+ i = (v_charge * .95) / DSD_566__R;
+ if (DSD_566__VMOD < m_v_osc_stable)
+ {
+ /* no where near correct calculation of non linear range */
+ i_rise = ((DSD_566__VCHARGE - m_v_osc_stable - 0.1) * .95) / DSD_566__R;
+ i_rise *= 1.0 - (m_v_osc_stable - DSD_566__VMOD) / (m_v_osc_stable - m_v_osc_stop);
+ }
+ else
+ i_rise = i;
+ }
+ else
+ return;
+ }
+ else return;
+
+ /* Keep looping until all toggling in this time sample is used up. */
+ do
+ {
+ if (m_flip_flop)
+ {
+ /* Discharging */
+ v_cap -= i * dt / DSD_566__C;
+ dt = 0;
+
+ /* has it discharged past lower limit? */
+ if (UNEXPECTED(v_cap < m_threshold_low))
+ {
+ /* calculate the overshoot time */
+ dt = DSD_566__C * (m_threshold_low - v_cap) / i;
+ v_cap = m_threshold_low;
+ m_flip_flop = 0;
+ count_f++;
+ x_time = dt;
+ }
+ }
+ else
+ {
+ /* Charging */
+ /* iC=C*dv/dt works out to dv=iC*dt/C */
+ v_cap += i_rise * dt / DSD_566__C;
+ dt = 0;
+ /* Yes, if the cap voltage has reached the max voltage it can,
+ * and the 566 threshold has not been reached, then oscillation stops.
+ * This is the way the actual electronics works.
+ * This is why you never play with the pots after being factory adjusted
+ * to work in the proper range. */
+ if (UNEXPECTED(v_cap > DSD_566__VMOD)) v_cap = DSD_566__VMOD;
+
+ /* has it charged past upper limit? */
+ if (UNEXPECTED(v_cap > m_threshold_high))
+ {
+ /* calculate the overshoot time */
+ dt = DSD_566__C * (v_cap - m_threshold_high) / i;
+ v_cap = m_threshold_high;
+ m_flip_flop = 1;
+ count_r++;
+ x_time = dt;
+ }
+ }
+ } while(dt);
+
+ m_cap_voltage = v_cap;
+
+ /* Convert last switch time to a ratio */
+ x_time /= this->sample_time();
+
+ switch (m_out_type)
+ {
+ case DISC_566_OUT_SQUARE:
+ v_out = m_flip_flop ? m_v_sqr_high : m_v_sqr_low;
+ if (m_fake_ac)
+ v_out += m_ac_shift;
+ break;
+ case DISC_566_OUT_ENERGY:
+ if (x_time == 0) x_time = 1.0;
+ v_out = m_v_sqr_low + m_v_sqr_diff * (m_flip_flop ? x_time : (1.0 - x_time));
+ if (m_fake_ac)
+ v_out += m_ac_shift;
+ break;
+ case DISC_566_OUT_LOGIC:
+ v_out = m_flip_flop;
+ break;
+ case DISC_566_OUT_TRIANGLE:
+ v_out = v_cap;
+ if (m_fake_ac)
+ v_out += m_ac_shift;
+ break;
+ case DISC_566_OUT_COUNT_F_X:
+ v_out = count_f ? count_f + x_time : count_f;
+ break;
+ case DISC_566_OUT_COUNT_R_X:
+ v_out = count_r ? count_r + x_time : count_r;
+ break;
+ case DISC_566_OUT_COUNT_F:
+ v_out = count_f;
+ break;
+ case DISC_566_OUT_COUNT_R:
+ v_out = count_r;
+ break;
+ }
+ set_output(0, v_out);
+}
+
+DISCRETE_RESET(dsd_566)
+{
+ int v_int;
+ double v_float;
+
+ m_out_type = (int)DSD_566__OPTIONS & DISC_566_OUT_MASK;
+ m_fake_ac = (int)DSD_566__OPTIONS & DISC_566_OUT_AC;
+
+ if (DSD_566__VNEG >= DSD_566__VPOS)
+ fatalerror("[v_neg >= v_pos] in NODE_%d!\n", this->index());
+
+ v_float = DSD_566__VPOS - DSD_566__VNEG;
+ v_int = (int)v_float;
+ if ( v_float < 10 || v_float > 15 )
+ fatalerror("v_neg and/or v_pos out of range in NODE_%d\n", this->index());
+ if ( v_float != v_int )
+ /* fatal for now. */
+ fatalerror("Power should be integer in NODE_%d\n", this->index());
+
+ m_flip_flop = 0;
+ m_cap_voltage = 0;
+
+ v_int -= 10;
+ m_threshold_high = ne566.c_high[v_int] + DSD_566__VNEG;
+ m_threshold_low = ne566.c_low[v_int] + DSD_566__VNEG;
+ m_v_sqr_high = DSD_566__VPOS - 1;
+ m_v_sqr_low = ne566.sqr_low[v_int] + DSD_566__VNEG;
+ m_v_sqr_diff = m_v_sqr_high - m_v_sqr_low;
+ m_v_osc_stable = ne566.osc_stable[v_int] + DSD_566__VNEG;
+ m_v_osc_stop = ne566.osc_stop[v_int] + DSD_566__VNEG;
+
+ m_ac_shift = 0;
+ if (m_fake_ac)
+ {
+ if (m_out_type == DISC_566_OUT_TRIANGLE)
+ m_ac_shift = (m_threshold_high - m_threshold_low) / 2 - m_threshold_high;
+ else
+ m_ac_shift = m_v_sqr_diff / 2 - m_v_sqr_high;
+ }
+
+ /* Step the output */
+ this->step();
+}
+
+
+/************************************************************************
+ *
+ * DSD_LS624 - Usage of node_description values
+ *
+ * Dec 2007, Couriersud based on data sheet
+ * Oct 2009, complete re-write based on IC testing
+ ************************************************************************/
+#define DSD_LS624__ENABLE DISCRETE_INPUT(0)
+#define DSD_LS624__VMOD DISCRETE_INPUT(1)
+#define DSD_LS624__VRNG DISCRETE_INPUT(2)
+#define DSD_LS624__C DISCRETE_INPUT(3)
+#define DSD_LS624__R_FREQ_IN DISCRETE_INPUT(4)
+#define DSD_LS624__C_FREQ_IN DISCRETE_INPUT(5)
+#define DSD_LS624__R_RNG_IN DISCRETE_INPUT(6)
+#define DSD_LS624__OUTTYPE DISCRETE_INPUT(7)
+
+#define LS624_R_EXT 600.0 /* as specified in data sheet */
+#define LS624_OUT_HIGH 4.5 /* measured */
+#define LS624_IN_R RES_K(90) /* measured & 70K + 20k per data sheet */
+
+/*
+ * The 74LS624 series are constant current based VCOs. The Freq Control voltage
+ * modulates the current source. The current is created from Rext, which is
+ * internally fixed at 600 ohms for all devices except the 74LS628 which has
+ * external connections. The current source linearly discharges the cap voltage.
+ * The cap starts with 0V charge across it. One side is connected to a fixed voltage
+ * bias circuit. The other side is charged negatively from the current source until
+ * a certain low threshold is reached. Once this threshold is reached, the output
+ * toggles state and the pins on the cap reverse in respect to the charge/bias hookup.
+ * This starts the one side of the cap to be at bias, and the other side of the cap is
+ * now at bias + the charge on the cap which is bias - threshold.
+ * Y = 0; CX1 = bias; CX2 = charge
+ * Y = 1; CX1 = charge; CX2 = bias
+ * The Range voltage adjusts the threshold voltage. The higher the Range voltage,
+ * the lower the threshold voltage, the longer the cap can charge, the lower the frequency.
+ *
+ * In a perfect world it would work like this:
+ * The current is based on the mysterious Rext mentioned in the data sheet.
+ * I = (VfreqControl * 20k/90k) / Rext
+ * where Rext = 600 ohms or external Rext on a 74LS628
+ * The Freq Control has an input impedance of approximately 90k, so any input resistance
+ * connected to the Freq Control pin works as a voltage divider.
+ * I = (VfreqControl * 20k/(90k + RfreqControlIn)) / Rext
+ * That gives us a change in voltage on the cap of
+ * dV = I / sampleRate / C_inFarads
+ *
+ * Unfortunately the chip does not behave linearly do to internal interactions,
+ * so I have just worked out the formula (using zunzun.com) of FreqControl and
+ * range to frequency out for a fixed cap value of 0.1uf. Other cap values can just
+ * scale from that. From the freq, we calculate the time of 1/2 cycle using 1/Freq/2.
+ * Then just use that to toggle a waveform.
+ */
+
+
+DISCRETE_STEP(dsd_ls624)
+{
+ double x_time = 0;
+ double freq, t1;
+ double v_freq_2, v_freq_3, v_freq_4;
+ double t_used = m_t_used;
+ double dt = this->sample_time();;
+ double v_freq = DSD_LS624__VMOD;
+ double v_rng = DSD_LS624__VRNG;
+ int count_f = 0, count_r = 0;
+
+ /* coefficients */
+ const double k1 = 1.9904769024796283E+03;
+ const double k2 = 1.2070059213983407E+03;
+ const double k3 = 1.3266985579561108E+03;
+ const double k4 = -1.5500979825922698E+02;
+ const double k5 = 2.8184536266938172E+00;
+ const double k6 = -2.3503421582744556E+02;
+ const double k7 = -3.3836786704527788E+02;
+ const double k8 = -1.3569136703258670E+02;
+ const double k9 = 2.9914575453819188E+00;
+ const double k10 = 1.6855569086173170E+00;
+
+ if (UNEXPECTED(DSD_LS624__ENABLE == 0))
+ return;
+
+ /* scale due to input resistance */
+ v_freq *= m_v_freq_scale;
+ v_rng *= m_v_rng_scale;
+
+ /* apply cap if needed */
+ if (m_has_freq_in_cap)
+ {
+ m_v_cap_freq_in += (v_freq - m_v_cap_freq_in) * m_exponent;
+ v_freq = m_v_cap_freq_in;
+ }
+
+ /* Polyfunctional3D_model created by zunzun.com using sum of squared absolute error */
+ v_freq_2 = v_freq * v_freq;
+ v_freq_3 = v_freq_2 * v_freq;
+ v_freq_4 = v_freq_3 * v_freq;
+ freq = k1;
+ freq += k2 * v_freq;
+ freq += k3 * v_freq_2;
+ freq += k4 * v_freq_3;
+ freq += k5 * v_freq_4;
+ freq += k6 * v_rng;
+ freq += k7 * v_rng * v_freq;
+ freq += k8 * v_rng * v_freq_2;
+ freq += k9 * v_rng * v_freq_3;
+ freq += k10 * v_rng * v_freq_4;
+
+ freq *= CAP_U(0.1) / DSD_LS624__C;
+
+ t1 = 0.5 / freq ;
+ t_used += this->sample_time();
+ do
+ {
+ dt = 0;
+ if (t_used > t1)
+ {
+ /* calculate the overshoot time */
+ t_used -= t1;
+ m_flip_flop ^= 1;
+ if (m_flip_flop)
+ count_r++;
+ else
+ count_f++;
+ /* fix up any frequency increase change errors */
+ while(t_used > this->sample_time())
+ t_used -= this->sample_time();
+ x_time = t_used;
+ dt = t_used;
+ }
+ }while(dt);
+
+ m_t_used = t_used;
+
+ /* Convert last switch time to a ratio */
+ x_time = x_time / this->sample_time();
+
+ switch (m_out_type)
+ {
+ case DISC_LS624_OUT_LOGIC_X:
+ set_output(0, m_flip_flop + x_time);
+ break;
+ case DISC_LS624_OUT_COUNT_F_X:
+ set_output(0, count_f ? count_f + x_time : count_f);
+ break;
+ case DISC_LS624_OUT_COUNT_R_X:
+ set_output(0, count_r ? count_r + x_time : count_r);
+ break;
+ case DISC_LS624_OUT_COUNT_F:
+ set_output(0, count_f);
+ break;
+ case DISC_LS624_OUT_COUNT_R:
+ set_output(0, count_r);
+ break;
+ case DISC_LS624_OUT_ENERGY:
+ if (x_time == 0) x_time = 1.0;
+ set_output(0, LS624_OUT_HIGH * (m_flip_flop ? x_time : (1.0 - x_time)));
+ break;
+ case DISC_LS624_OUT_LOGIC:
+ set_output(0, m_flip_flop);
+ break;
+ case DISC_LS624_OUT_SQUARE:
+ set_output(0, m_flip_flop ? LS624_OUT_HIGH : 0);
+ break;
+ }
+}
+
+DISCRETE_RESET(dsd_ls624)
+{
+ m_out_type = (int)DSD_LS624__OUTTYPE;
+
+ m_flip_flop = 0;
+ m_t_used = 0;
+ m_v_freq_scale = LS624_IN_R / (DSD_LS624__R_FREQ_IN + LS624_IN_R);
+ m_v_rng_scale = LS624_IN_R / (DSD_LS624__R_RNG_IN + LS624_IN_R);
+ if (DSD_LS624__C_FREQ_IN > 0)
+ {
+ m_has_freq_in_cap = 1;
+ m_exponent = RC_CHARGE_EXP(RES_2_PARALLEL(DSD_LS624__R_FREQ_IN, LS624_IN_R) * DSD_LS624__C_FREQ_IN);
+ m_v_cap_freq_in = 0;
+ }
+ else
+ m_has_freq_in_cap = 0;
+
+ set_output(0, 0);
+}
diff --git a/src/devices/sound/disc_flt.h b/src/devices/sound/disc_flt.h
new file mode 100644
index 00000000000..3c6f2d72607
--- /dev/null
+++ b/src/devices/sound/disc_flt.h
@@ -0,0 +1,180 @@
+// license:BSD-3-Clause
+// copyright-holders:K.Wilkins
+#pragma once
+
+#ifndef __DISC_FLTH__
+#define __DISC_FLT_H__
+
+/***********************************************************************
+ *
+ * MAME - Discrete sound system emulation library
+ *
+ * Written by K.Wilkins (mame@esplexo.co.uk)
+ *
+ * (c) K.Wilkins 2000
+ *
+ * Coding started in November 2000
+ *
+ * Additions/bugfix February 2003 - Derrick Renaud, F.Palazzolo, K.Wilkins
+ * Discrete parallel tasks 2009 - Couriersud
+ * Discrete classes 2010 - Couriersud
+ *
+ ***********************************************************************/
+
+#include "discrete.h"
+
+struct discrete_filter_coeff
+{
+ double x1, x2; /* x[k-1], x[k-2], previous 2 input values */
+ double y1, y2; /* y[k-1], y[k-2], previous 2 output values */
+ double a1, a2; /* digital filter coefficients, denominator */
+ double b0, b1, b2; /* digital filter coefficients, numerator */
+};
+
+
+DISCRETE_CLASS_STEP_RESET(dst_filter1, 1,
+ /* uses x1, y1, a1, b0, b1 */
+ struct discrete_filter_coeff m_fc;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_filter2, 1,
+ struct discrete_filter_coeff m_fc;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_sallen_key, 1,
+ struct discrete_filter_coeff m_fc;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_crfilter, 1,
+ double m_vCap;
+ double m_rc;
+ double m_exponent;
+ UINT8 m_has_rc_nodes;
+ //UINT8 m_is_fast;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_op_amp_filt, 1,
+ int m_type; /* What kind of filter */
+ int m_is_norton; /* 1 = Norton op-amps */
+ double m_vRef;
+ double m_vP;
+ double m_vN;
+ double m_rTotal; /* All input resistance in parallel. */
+ double m_iFixed; /* Current supplied by r3 & r4 if used. */
+ double m_exponentC1;
+ double m_exponentC2;
+ double m_exponentC3;
+ double m_rRatio; /* divide ratio of resistance network */
+ double m_vC1; /* Charge on C1 */
+ double m_vC1b; /* Charge on C1, part of C1 charge if needed */
+ double m_vC2; /* Charge on C2 */
+ double m_vC3; /* Charge on C2 */
+ double m_gain; /* Gain of the filter */
+ struct discrete_filter_coeff m_fc;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_rc_circuit_1, 1,
+ double m_v_cap;
+ double m_v_charge_1_2;
+ double m_v_drop;
+ double m_exp_1;
+ double m_exp_1_2;
+ double m_exp_2;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_rcdisc, 1,
+ int m_state;
+ double m_t; /* time */
+ double m_exponent0;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_rcdisc2, 1,
+ int m_state;
+ double m_v_out;
+ double m_t; /* time */
+ double m_exponent0;
+ double m_exponent1;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_rcdisc3, 1,
+ int m_state;
+ double m_v_out;
+ double m_t; /* time */
+ double m_exponent0;
+ double m_exponent1;
+ double m_v_diode; /* rcdisc3 */
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_rcdisc4, 1,
+ int m_type;
+ double m_max_out;
+ double m_vC1;
+ double m_v[2];
+ double m_exp[2];
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_rcdisc5, 1,
+ int m_state;
+ double m_t; /* time */
+ double m_exponent0;
+ double m_v_cap; /* rcdisc5 */
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_rcintegrate, 1,
+ int m_type;
+ double m_gain_r1_r2;
+ double m_f; /* r2,r3 gain */
+ double m_vCap;
+ double m_vCE;
+ double m_exponent0;
+ double m_exponent1;
+ double m_exp_exponent0;
+ double m_exp_exponent1;
+ double m_c_exp0;
+ double m_c_exp1;
+ double m_EM_IC_0_7;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_rcdisc_mod, 1,
+ double m_v_cap;
+ double m_exp_low[2];
+ double m_exp_high[4];
+ double m_gain[2];
+ double m_vd_gain[4];
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_rcfilter, 1,
+ double m_v_out;
+ double m_vCap;
+ double m_rc;
+ double m_exponent;
+ UINT8 m_has_rc_nodes;
+ UINT8 m_is_fast;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_rcfilter_sw, 1,
+ double m_vCap[4];
+ double m_exp[4];
+ double m_exp0; /* fast case bit 0 */
+ double m_exp1; /* fast case bit 1 */
+ double m_factor; /* fast case */
+ double m_f1[16];
+ double m_f2[16];
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_rcdiscN, 1,
+ double m_x1; /* x[k-1], previous input value */
+ double m_y1; /* y[k-1], previous output value */
+ double m_a1; /* digital filter coefficients, denominator */
+ //double m_b[2]; /* digital filter coefficients, numerator */
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_rcdisc2N, 1,
+ struct discrete_filter_coeff m_fc0;
+ struct discrete_filter_coeff m_fc1;
+ double m_x1;
+ double m_y1;
+);
+
+
+#endif /* __DISC_FLT_H__ */
diff --git a/src/devices/sound/disc_flt.inc b/src/devices/sound/disc_flt.inc
new file mode 100644
index 00000000000..18cb8f41c1c
--- /dev/null
+++ b/src/devices/sound/disc_flt.inc
@@ -0,0 +1,1438 @@
+// license:BSD-3-Clause
+// copyright-holders:K.Wilkins
+/************************************************************************
+ *
+ * MAME - Discrete sound system emulation library
+ *
+ * Written by K.Wilkins (mame@esplexo.co.uk)
+ *
+ * (c) K.Wilkins 2000
+ *
+ ***********************************************************************
+ *
+ * DST_CRFILTER - Simple CR filter & also highpass filter
+ * DST_FILTER1 - Generic 1st order filter
+ * DST_FILTER2 - Generic 2nd order filter
+ * DST_OP_AMP_FILT - Op Amp filter circuits
+ * DST_RC_CIRCUIT_1 - RC charge/discharge circuit
+ * DST_RCDISC - Simple discharging RC
+ * DST_RCDISC2 - Simple charge R1/C, discharge R0/C
+ * DST_RCDISC3 - Simple charge R1/c, discharge R0*R1/(R0+R1)/C
+ * DST_RCDISC4 - Various charge/discharge circuits
+ * DST_RCDISC5 - Diode in series with R//C
+ * DST_RCDISC_MOD - RC triggered by logic and modulated
+ * DST_RCFILTER - Simple RC filter & also lowpass filter
+ * DST_RCFILTER_SW - Usage of node_description values for switchable RC filter
+ * DST_RCINTEGRATE - Two diode inputs, transistor and a R/C charge
+ * discharge network
+ * DST_SALLEN_KEY - Sallen-Key filter circuit
+ *
+ ************************************************************************/
+
+
+/************************************************************************
+ *
+ * DST_CRFILTER - Usage of node_description values for CR filter
+ *
+ * input[0] - Enable input value
+ * input[1] - input value
+ * input[2] - Resistor value (initialization only)
+ * input[3] - Capacitor Value (initialization only)
+ * input[4] - Voltage reference. Usually 0V.
+ *
+ ************************************************************************/
+#define DST_CRFILTER__IN DISCRETE_INPUT(0)
+#define DST_CRFILTER__R DISCRETE_INPUT(1)
+#define DST_CRFILTER__C DISCRETE_INPUT(2)
+#define DST_CRFILTER__VREF DISCRETE_INPUT(3)
+
+DISCRETE_STEP(dst_crfilter)
+{
+ if (UNEXPECTED(m_has_rc_nodes))
+ {
+ double rc = DST_CRFILTER__R * DST_CRFILTER__C;
+ if (rc != m_rc)
+ {
+ m_rc = rc;
+ m_exponent = RC_CHARGE_EXP(rc);
+ }
+ }
+
+ double v_out = DST_CRFILTER__IN - m_vCap;
+ double v_diff = v_out - DST_CRFILTER__VREF;
+ set_output(0, v_out);
+ m_vCap += v_diff * m_exponent;
+}
+
+DISCRETE_RESET(dst_crfilter)
+{
+ m_has_rc_nodes = this->input_is_node() & 0x6;
+ m_rc = DST_CRFILTER__R * DST_CRFILTER__C;
+ m_exponent = RC_CHARGE_EXP(m_rc);
+ m_vCap = 0;
+ set_output(0, DST_CRFILTER__IN);
+}
+
+
+/************************************************************************
+ *
+ * DST_FILTER1 - Generic 1st order filter
+ *
+ * input[0] - Enable input value
+ * input[1] - input value
+ * input[2] - Frequency value (initialization only)
+ * input[3] - Filter type (initialization only)
+ *
+ ************************************************************************/
+#define DST_FILTER1__ENABLE DISCRETE_INPUT(0)
+#define DST_FILTER1__IN DISCRETE_INPUT(1)
+#define DST_FILTER1__FREQ DISCRETE_INPUT(2)
+#define DST_FILTER1__TYPE DISCRETE_INPUT(3)
+
+static void calculate_filter1_coefficients(discrete_base_node *node, double fc, double type,
+ struct discrete_filter_coeff &coeff)
+{
+ double den, w, two_over_T;
+
+ /* calculate digital filter coefficents */
+ /*w = 2.0*M_PI*fc; no pre-warping */
+ w = node->sample_rate()*2.0*tan(M_PI*fc/node->sample_rate()); /* pre-warping */
+ two_over_T = 2.0*node->sample_rate();
+
+ den = w + two_over_T;
+ coeff.a1 = (w - two_over_T)/den;
+ if (type == DISC_FILTER_LOWPASS)
+ {
+ coeff.b0 = coeff.b1 = w/den;
+ }
+ else if (type == DISC_FILTER_HIGHPASS)
+ {
+ coeff.b0 = two_over_T/den;
+ coeff.b1 = -(coeff.b0);
+ }
+ else
+ {
+ /* FIXME: reenable */
+ //node->m_device->discrete_log("calculate_filter1_coefficients() - Invalid filter type for 1st order filter.");
+ }
+}
+
+DISCRETE_STEP(dst_filter1)
+{
+ double gain = 1.0;
+ double v_out;
+
+ if (DST_FILTER1__ENABLE == 0.0)
+ {
+ gain = 0.0;
+ }
+
+ v_out = -m_fc.a1*m_fc.y1 + m_fc.b0*gain*DST_FILTER1__IN + m_fc.b1*m_fc.x1;
+
+ m_fc.x1 = gain*DST_FILTER1__IN;
+ m_fc.y1 = v_out;
+ set_output(0, v_out);
+}
+
+DISCRETE_RESET(dst_filter1)
+{
+ calculate_filter1_coefficients(this, DST_FILTER1__FREQ, DST_FILTER1__TYPE, m_fc);
+ set_output(0, 0);
+}
+
+
+/************************************************************************
+ *
+ * DST_FILTER2 - Generic 2nd order filter
+ *
+ * input[0] - Enable input value
+ * input[1] - input value
+ * input[2] - Frequency value (initialization only)
+ * input[3] - Damping value (initialization only)
+ * input[4] - Filter type (initialization only)
+ *
+ ************************************************************************/
+#define DST_FILTER2__ENABLE DISCRETE_INPUT(0)
+#define DST_FILTER2__IN DISCRETE_INPUT(1)
+#define DST_FILTER2__FREQ DISCRETE_INPUT(2)
+#define DST_FILTER2__DAMP DISCRETE_INPUT(3)
+#define DST_FILTER2__TYPE DISCRETE_INPUT(4)
+
+static void calculate_filter2_coefficients(discrete_base_node *node,
+ double fc, double d, double type,
+ struct discrete_filter_coeff &coeff)
+{
+ double w; /* cutoff freq, in radians/sec */
+ double w_squared;
+ double den; /* temp variable */
+ double two_over_T = 2 * node->sample_rate();
+ double two_over_T_squared = two_over_T * two_over_T;
+
+ /* calculate digital filter coefficents */
+ /*w = 2.0*M_PI*fc; no pre-warping */
+ w = node->sample_rate() * 2.0 * tan(M_PI * fc / node->sample_rate()); /* pre-warping */
+ w_squared = w * w;
+
+ den = two_over_T_squared + d*w*two_over_T + w_squared;
+
+ coeff.a1 = 2.0 * (-two_over_T_squared + w_squared) / den;
+ coeff.a2 = (two_over_T_squared - d * w * two_over_T + w_squared) / den;
+
+ if (type == DISC_FILTER_LOWPASS)
+ {
+ coeff.b0 = coeff.b2 = w_squared/den;
+ coeff.b1 = 2.0 * (coeff.b0);
+ }
+ else if (type == DISC_FILTER_BANDPASS)
+ {
+ coeff.b0 = d * w * two_over_T / den;
+ coeff.b1 = 0.0;
+ coeff.b2 = -(coeff.b0);
+ }
+ else if (type == DISC_FILTER_HIGHPASS)
+ {
+ coeff.b0 = coeff.b2 = two_over_T_squared / den;
+ coeff.b1 = -2.0 * (coeff.b0);
+ }
+ else
+ {
+ /* FIXME: reenable */
+ //node->device->discrete_log("calculate_filter2_coefficients() - Invalid filter type for 2nd order filter.");
+ }
+}
+
+DISCRETE_STEP(dst_filter2)
+{
+ double gain = 1.0;
+ double v_out;
+
+ if (DST_FILTER2__ENABLE == 0.0)
+ {
+ gain = 0.0;
+ }
+
+ v_out = -m_fc.a1 * m_fc.y1 - m_fc.a2 * m_fc.y2 +
+ m_fc.b0 * gain * DST_FILTER2__IN + m_fc.b1 * m_fc.x1 + m_fc.b2 * m_fc.x2;
+
+ m_fc.x2 = m_fc.x1;
+ m_fc.x1 = gain * DST_FILTER2__IN;
+ m_fc.y2 = m_fc.y1;
+ m_fc.y1 = v_out;
+ set_output(0, v_out);
+}
+
+DISCRETE_RESET(dst_filter2)
+{
+ calculate_filter2_coefficients(this, DST_FILTER2__FREQ, DST_FILTER2__DAMP, DST_FILTER2__TYPE,
+ m_fc);
+ set_output(0, 0);
+}
+
+
+/************************************************************************
+ *
+ * DST_OP_AMP_FILT - Op Amp filter circuit RC filter
+ *
+ * input[0] - Enable input value
+ * input[1] - IN0 node
+ * input[2] - IN1 node
+ * input[3] - Filter Type
+ *
+ * also passed discrete_op_amp_filt_info structure
+ *
+ * Mar 2004, D Renaud.
+ ************************************************************************/
+#define DST_OP_AMP_FILT__ENABLE DISCRETE_INPUT(0)
+#define DST_OP_AMP_FILT__INP1 DISCRETE_INPUT(1)
+#define DST_OP_AMP_FILT__INP2 DISCRETE_INPUT(2)
+#define DST_OP_AMP_FILT__TYPE DISCRETE_INPUT(3)
+
+DISCRETE_STEP(dst_op_amp_filt)
+{
+ DISCRETE_DECLARE_INFO(discrete_op_amp_filt_info)
+ double v_out = 0;
+
+ double i, v = 0;
+
+ if (DST_OP_AMP_FILT__ENABLE)
+ {
+ if (m_is_norton)
+ {
+ v = DST_OP_AMP_FILT__INP1 - OP_AMP_NORTON_VBE;
+ if (v < 0) v = 0;
+ }
+ else
+ {
+ /* Millman the input voltages. */
+ i = m_iFixed;
+ switch (m_type)
+ {
+ case DISC_OP_AMP_FILTER_IS_LOW_PASS_1_A:
+ i += (DST_OP_AMP_FILT__INP1 - DST_OP_AMP_FILT__INP2) / info->r1;
+ if (info->r2 != 0)
+ i += (m_vP - DST_OP_AMP_FILT__INP2) / info->r2;
+ if (info->r3 != 0)
+ i += (m_vN - DST_OP_AMP_FILT__INP2) / info->r3;
+ break;
+ default:
+ i += (DST_OP_AMP_FILT__INP1 - m_vRef) / info->r1;
+ if (info->r2 != 0)
+ i += (DST_OP_AMP_FILT__INP2 - m_vRef) / info->r2;
+ break;
+ }
+ v = i * m_rTotal;
+ }
+
+ switch (m_type)
+ {
+ case DISC_OP_AMP_FILTER_IS_LOW_PASS_1:
+ m_vC1 += (v - m_vC1) * m_exponentC1;
+ v_out = m_vC1 * m_gain + info->vRef;
+ break;
+
+ case DISC_OP_AMP_FILTER_IS_LOW_PASS_1_A:
+ m_vC1 += (v - m_vC1) * m_exponentC1;
+ v_out = m_vC1 * m_gain + DST_OP_AMP_FILT__INP2;
+ break;
+
+ case DISC_OP_AMP_FILTER_IS_HIGH_PASS_1:
+ v_out = (v - m_vC1) * m_gain + info->vRef;
+ m_vC1 += (v - m_vC1) * m_exponentC1;
+ break;
+
+ case DISC_OP_AMP_FILTER_IS_BAND_PASS_1:
+ v_out = (v - m_vC2);
+ m_vC2 += (v - m_vC2) * m_exponentC2;
+ m_vC1 += (v_out - m_vC1) * m_exponentC1;
+ v_out = m_vC1 * m_gain + info->vRef;
+ break;
+
+ case DISC_OP_AMP_FILTER_IS_BAND_PASS_0 | DISC_OP_AMP_IS_NORTON:
+ m_vC1 += (v - m_vC1) * m_exponentC1;
+ m_vC2 += (m_vC1 - m_vC2) * m_exponentC2;
+ v = m_vC2;
+ v_out = v - m_vC3;
+ m_vC3 += (v - m_vC3) * m_exponentC3;
+ i = v_out / m_rTotal;
+ v_out = (m_iFixed - i) * info->rF;
+ break;
+
+ case DISC_OP_AMP_FILTER_IS_HIGH_PASS_0 | DISC_OP_AMP_IS_NORTON:
+ v_out = v - m_vC1;
+ m_vC1 += (v - m_vC1) * m_exponentC1;
+ i = v_out / m_rTotal;
+ v_out = (m_iFixed - i) * info->rF;
+ break;
+
+ case DISC_OP_AMP_FILTER_IS_BAND_PASS_1M:
+ case DISC_OP_AMP_FILTER_IS_BAND_PASS_1M | DISC_OP_AMP_IS_NORTON:
+ v_out = -m_fc.a1 * m_fc.y1 - m_fc.a2 * m_fc.y2 +
+ m_fc.b0 * v + m_fc.b1 * m_fc.x1 + m_fc.b2 * m_fc.x2 +
+ m_vRef;
+ m_fc.x2 = m_fc.x1;
+ m_fc.x1 = v;
+ m_fc.y2 = m_fc.y1;
+ break;
+ }
+
+ /* Clip the output to the voltage rails.
+ * This way we get the original distortion in all it's glory.
+ */
+ if (v_out > m_vP) v_out = m_vP;
+ if (v_out < m_vN) v_out = m_vN;
+ m_fc.y1 = v_out - m_vRef;
+ set_output(0, v_out);
+ }
+ else
+ set_output(0, 0);
+
+}
+
+DISCRETE_RESET(dst_op_amp_filt)
+{
+ DISCRETE_DECLARE_INFO(discrete_op_amp_filt_info)
+
+ /* Convert the passed filter type into an int for easy use. */
+ m_type = (int)DST_OP_AMP_FILT__TYPE & DISC_OP_AMP_FILTER_TYPE_MASK;
+ m_is_norton = (int)DST_OP_AMP_FILT__TYPE & DISC_OP_AMP_IS_NORTON;
+
+ if (m_is_norton)
+ {
+ m_vRef = 0;
+ m_rTotal = info->r1;
+ if (m_type == (DISC_OP_AMP_FILTER_IS_BAND_PASS_0 | DISC_OP_AMP_IS_NORTON))
+ m_rTotal += info->r2 + info->r3;
+
+ /* Setup the current to the + input. */
+ m_iFixed = (info->vP - OP_AMP_NORTON_VBE) / info->r4;
+
+ /* Set the output max. */
+ m_vP = info->vP - OP_AMP_NORTON_VBE;
+ m_vN = info->vN;
+ }
+ else
+ {
+ m_vRef = info->vRef;
+ /* Set the output max. */
+ m_vP = info->vP - OP_AMP_VP_RAIL_OFFSET;
+ m_vN = info->vN;
+
+ /* Work out the input resistance. It is all input and bias resistors in parallel. */
+ m_rTotal = 1.0 / info->r1; /* There has to be an R1. Otherwise the table is wrong. */
+ if (info->r2 != 0) m_rTotal += 1.0 / info->r2;
+ if (info->r3 != 0) m_rTotal += 1.0 / info->r3;
+ m_rTotal = 1.0 / m_rTotal;
+
+ m_iFixed = 0;
+
+ m_rRatio = info->rF / (m_rTotal + info->rF);
+ m_gain = -info->rF / m_rTotal;
+ }
+
+ switch (m_type)
+ {
+ case DISC_OP_AMP_FILTER_IS_LOW_PASS_1:
+ case DISC_OP_AMP_FILTER_IS_LOW_PASS_1_A:
+ m_exponentC1 = RC_CHARGE_EXP(info->rF * info->c1);
+ m_exponentC2 = 0;
+ break;
+ case DISC_OP_AMP_FILTER_IS_HIGH_PASS_1:
+ m_exponentC1 = RC_CHARGE_EXP(m_rTotal * info->c1);
+ m_exponentC2 = 0;
+ break;
+ case DISC_OP_AMP_FILTER_IS_BAND_PASS_1:
+ m_exponentC1 = RC_CHARGE_EXP(info->rF * info->c1);
+ m_exponentC2 = RC_CHARGE_EXP(m_rTotal * info->c2);
+ break;
+ case DISC_OP_AMP_FILTER_IS_BAND_PASS_1M | DISC_OP_AMP_IS_NORTON:
+ if (info->r2 == 0)
+ m_rTotal = info->r1;
+ else
+ m_rTotal = RES_2_PARALLEL(info->r1, info->r2);
+ case DISC_OP_AMP_FILTER_IS_BAND_PASS_1M:
+ {
+ double fc = 1.0 / (2 * M_PI * sqrt(m_rTotal * info->rF * info->c1 * info->c2));
+ double d = (info->c1 + info->c2) / sqrt(info->rF / m_rTotal * info->c1 * info->c2);
+ double gain = -info->rF / m_rTotal * info->c2 / (info->c1 + info->c2);
+
+ calculate_filter2_coefficients(this, fc, d, DISC_FILTER_BANDPASS, m_fc);
+ m_fc.b0 *= gain;
+ m_fc.b1 *= gain;
+ m_fc.b2 *= gain;
+
+ if (m_is_norton)
+ m_vRef = (info->vP - OP_AMP_NORTON_VBE) / info->r3 * info->rF;
+ else
+ m_vRef = info->vRef;
+
+ break;
+ }
+ case DISC_OP_AMP_FILTER_IS_BAND_PASS_0 | DISC_OP_AMP_IS_NORTON:
+ m_exponentC1 = RC_CHARGE_EXP(RES_2_PARALLEL(info->r1, info->r2 + info->r3 + info->r4) * info->c1);
+ m_exponentC2 = RC_CHARGE_EXP(RES_2_PARALLEL(info->r1 + info->r2, info->r3 + info->r4) * info->c2);
+ m_exponentC3 = RC_CHARGE_EXP((info->r1 + info->r2 + info->r3 + info->r4) * info->c3);
+ break;
+ case DISC_OP_AMP_FILTER_IS_HIGH_PASS_0 | DISC_OP_AMP_IS_NORTON:
+ m_exponentC1 = RC_CHARGE_EXP(info->r1 * info->c1);
+ break;
+ }
+
+ /* At startup there is no charge on the caps and output is 0V in relation to vRef. */
+ m_vC1 = 0;
+ m_vC1b = 0;
+ m_vC2 = 0;
+ m_vC3 = 0;
+
+ set_output(0, info->vRef);
+}
+
+
+/************************************************************************
+ *
+ * DST_RC_CIRCUIT_1 - RC charge/discharge circuit
+ *
+ ************************************************************************/
+#define DST_RC_CIRCUIT_1__IN0 DISCRETE_INPUT(0)
+#define DST_RC_CIRCUIT_1__IN1 DISCRETE_INPUT(1)
+#define DST_RC_CIRCUIT_1__R DISCRETE_INPUT(2)
+#define DST_RC_CIRCUIT_1__C DISCRETE_INPUT(3)
+
+#define CD4066_R_ON 270
+
+DISCRETE_STEP( dst_rc_circuit_1 )
+{
+ if (DST_RC_CIRCUIT_1__IN0 == 0)
+ if (DST_RC_CIRCUIT_1__IN1 == 0)
+ /* cap is floating and does not change charge */
+ /* output is pulled to ground */
+ set_output(0, 0);
+ else
+ {
+ /* cap is discharged */
+ m_v_cap -= m_v_cap * m_exp_2;
+ set_output(0, m_v_cap * m_v_drop);
+ }
+ else
+ if (DST_RC_CIRCUIT_1__IN1 == 0)
+ {
+ /* cap is charged */
+ m_v_cap += (5.0 - m_v_cap) * m_exp_1;
+ /* output is pulled to ground */
+ set_output(0, 0);
+ }
+ else
+ {
+ /* cap is charged slightly less */
+ m_v_cap += (m_v_charge_1_2 - m_v_cap) * m_exp_1_2;
+ set_output(0, m_v_cap * m_v_drop);
+ }
+}
+
+DISCRETE_RESET( dst_rc_circuit_1 )
+{
+ /* the charging voltage across the cap based on in2*/
+ m_v_drop = RES_VOLTAGE_DIVIDER(CD4066_R_ON, CD4066_R_ON + DST_RC_CIRCUIT_1__R);
+ m_v_charge_1_2 = 5.0 * m_v_drop;
+ m_v_cap = 0;
+
+ /* precalculate charging exponents */
+ /* discharge cap - in1 = 0, in2 = 1*/
+ m_exp_2 = RC_CHARGE_EXP((CD4066_R_ON + DST_RC_CIRCUIT_1__R) * DST_RC_CIRCUIT_1__C);
+ /* charge cap - in1 = 1, in2 = 0 */
+ m_exp_1 = RC_CHARGE_EXP(CD4066_R_ON * DST_RC_CIRCUIT_1__C);
+ /* charge cap - in1 = 1, in2 = 1 */
+ m_exp_1_2 = RC_CHARGE_EXP(RES_2_PARALLEL(CD4066_R_ON, CD4066_R_ON + DST_RC_CIRCUIT_1__R) * DST_RC_CIRCUIT_1__C);
+
+ /* starts at 0 until cap starts charging */
+ set_output(0, 0);
+}
+
+/************************************************************************
+ *
+ * DST_RCDISC - Usage of node_description values for RC discharge
+ * (inverse slope of DST_RCFILTER)
+ *
+ * input[0] - Enable input value
+ * input[1] - input value
+ * input[2] - Resistor value (initialization only)
+ * input[3] - Capacitor Value (initialization only)
+ *
+ ************************************************************************/
+#define DST_RCDISC__ENABLE DISCRETE_INPUT(0)
+#define DST_RCDISC__IN DISCRETE_INPUT(1)
+#define DST_RCDISC__R DISCRETE_INPUT(2)
+#define DST_RCDISC__C DISCRETE_INPUT(3)
+
+DISCRETE_STEP(dst_rcdisc)
+{
+ switch (m_state)
+ {
+ case 0: /* waiting for trigger */
+ if(DST_RCDISC__ENABLE)
+ {
+ m_state = 1;
+ m_t = 0;
+ }
+ set_output(0, 0);
+ break;
+
+ case 1:
+ if (DST_RCDISC__ENABLE)
+ {
+ set_output(0, DST_RCDISC__IN * exp(m_t / m_exponent0));
+ m_t += this->sample_time();
+ } else
+ {
+ m_state = 0;
+ }
+ }
+}
+
+DISCRETE_RESET(dst_rcdisc)
+{
+ set_output(0, 0);
+
+ m_state = 0;
+ m_t = 0;
+ m_exponent0=-1.0 * DST_RCDISC__R * DST_RCDISC__C;
+}
+
+
+/************************************************************************
+ *
+ * DST_RCDISC2 - Usage of node_description values for RC discharge
+ * Has switchable charge resistor/input
+ *
+ * input[0] - Switch input value
+ * input[1] - input[0] value
+ * input[2] - Resistor0 value (initialization only)
+ * input[3] - input[1] value
+ * input[4] - Resistor1 value (initialization only)
+ * input[5] - Capacitor Value (initialization only)
+ *
+ ************************************************************************/
+#define DST_RCDISC2__ENABLE DISCRETE_INPUT(0)
+#define DST_RCDISC2__IN0 DISCRETE_INPUT(1)
+#define DST_RCDISC2__R0 DISCRETE_INPUT(2)
+#define DST_RCDISC2__IN1 DISCRETE_INPUT(3)
+#define DST_RCDISC2__R1 DISCRETE_INPUT(4)
+#define DST_RCDISC2__C DISCRETE_INPUT(5)
+
+DISCRETE_STEP(dst_rcdisc2)
+{
+ double diff;
+
+ /* Works differently to other as we are always on, no enable */
+ /* exponential based in difference between input/output */
+
+ diff = ((DST_RCDISC2__ENABLE == 0) ? DST_RCDISC2__IN0 : DST_RCDISC2__IN1) - m_v_out;
+ diff = diff - (diff * ((DST_RCDISC2__ENABLE == 0) ? m_exponent0 : m_exponent1));
+ m_v_out += diff;
+ set_output(0, m_v_out);
+}
+
+DISCRETE_RESET(dst_rcdisc2)
+{
+ m_v_out = 0;
+
+ m_state = 0;
+ m_t = 0;
+ m_exponent0 = RC_DISCHARGE_EXP(DST_RCDISC2__R0 * DST_RCDISC2__C);
+ m_exponent1 = RC_DISCHARGE_EXP(DST_RCDISC2__R1 * DST_RCDISC2__C);
+}
+
+/************************************************************************
+ *
+ * DST_RCDISC3 - Usage of node_description values for RC discharge
+ *
+ *
+ * input[0] - Enable
+ * input[1] - input value
+ * input[2] - Resistor0 value (initialization only)
+ * input[4] - Resistor1 value (initialization only)
+ * input[5] - Capacitor Value (initialization only)
+ * input[6] - Diode Junction voltage (initialization only)
+ *
+ ************************************************************************/
+#define DST_RCDISC3__ENABLE DISCRETE_INPUT(0)
+#define DST_RCDISC3__IN DISCRETE_INPUT(1)
+#define DST_RCDISC3__R1 DISCRETE_INPUT(2)
+#define DST_RCDISC3__R2 DISCRETE_INPUT(3)
+#define DST_RCDISC3__C DISCRETE_INPUT(4)
+#define DST_RCDISC3__DJV DISCRETE_INPUT(5)
+
+DISCRETE_STEP(dst_rcdisc3)
+{
+ double diff;
+
+ /* Exponential based in difference between input/output */
+
+ if(DST_RCDISC3__ENABLE)
+ {
+ diff = DST_RCDISC3__IN - m_v_out;
+ if (m_v_diode > 0)
+ {
+ if (diff > 0)
+ {
+ diff = diff * m_exponent0;
+ }
+ else if (diff < -m_v_diode)
+ {
+ diff = diff * m_exponent1;
+ }
+ else
+ {
+ diff = diff * m_exponent0;
+ }
+ }
+ else
+ {
+ if (diff < 0)
+ {
+ diff = diff * m_exponent0;
+ }
+ else if (diff > -m_v_diode)
+ {
+ diff = diff * m_exponent1;
+ }
+ else
+ {
+ diff = diff * m_exponent0;
+ }
+ }
+ m_v_out += diff;
+ set_output(0, m_v_out);
+ }
+ else
+ {
+ set_output(0, 0);
+ }
+}
+
+DISCRETE_RESET(dst_rcdisc3)
+{
+ m_v_out = 0;
+
+ m_state = 0;
+ m_t = 0;
+ m_v_diode = DST_RCDISC3__DJV;
+ m_exponent0 = RC_CHARGE_EXP(DST_RCDISC3__R1 * DST_RCDISC3__C);
+ m_exponent1 = RC_CHARGE_EXP(RES_2_PARALLEL(DST_RCDISC3__R1, DST_RCDISC3__R2) * DST_RCDISC3__C);
+}
+
+
+/************************************************************************
+ *
+ * DST_RCDISC4 - Various charge/discharge circuits
+ *
+ * input[0] - Enable input value
+ * input[1] - input value
+ * input[2] - R1 Resistor value (initialization only)
+ * input[2] - R2 Resistor value (initialization only)
+ * input[4] - C1 Capacitor Value (initialization only)
+ * input[4] - vP power source (initialization only)
+ * input[4] - circuit type (initialization only)
+ *
+ ************************************************************************/
+#define DST_RCDISC4__ENABLE DISCRETE_INPUT(0)
+#define DST_RCDISC4__IN DISCRETE_INPUT(1)
+#define DST_RCDISC4__R1 DISCRETE_INPUT(2)
+#define DST_RCDISC4__R2 DISCRETE_INPUT(3)
+#define DST_RCDISC4__R3 DISCRETE_INPUT(4)
+#define DST_RCDISC4__C1 DISCRETE_INPUT(5)
+#define DST_RCDISC4__VP DISCRETE_INPUT(6)
+#define DST_RCDISC4__TYPE DISCRETE_INPUT(7)
+
+DISCRETE_STEP(dst_rcdisc4)
+{
+ int inp1 = (DST_RCDISC4__IN == 0) ? 0 : 1;
+ double v_out = 0;
+
+ if (DST_RCDISC4__ENABLE == 0)
+ {
+ set_output(0, 0);
+ return;
+ }
+
+ switch (m_type)
+ {
+ case 1:
+ case 3:
+ m_vC1 += ((m_v[inp1] - m_vC1) * m_exp[inp1]);
+ v_out = m_vC1;
+ break;
+ }
+
+ /* clip output */
+ if (v_out > m_max_out) v_out = m_max_out;
+ if (v_out < 0) v_out = 0;
+ set_output(0, v_out);
+}
+
+DISCRETE_RESET( dst_rcdisc4)
+{
+ double v, i, r, rT;
+
+ m_type = 0;
+ /* some error checking. */
+ if (DST_RCDISC4__R1 <= 0 || DST_RCDISC4__R2 <= 0 || DST_RCDISC4__C1 <= 0 || (DST_RCDISC4__R3 <= 0 && m_type == 1))
+ {
+ m_device->discrete_log("Invalid component values in NODE_%d.\n", this->index());
+ return;
+ }
+ if (DST_RCDISC4__VP < 3)
+ {
+ m_device->discrete_log("vP must be >= 3V in NODE_%d.\n", this->index());
+ return;
+ }
+ if (DST_RCDISC4__TYPE < 1 || DST_RCDISC4__TYPE > 3)
+ {
+ m_device->discrete_log("Invalid circuit type in NODE_%d.\n", this->index());
+ return;
+ }
+
+ m_vC1 = 0;
+ /* store type as integer */
+ m_type = (int)DST_RCDISC4__TYPE;
+ /* setup the maximum op-amp output. */
+ m_max_out = DST_RCDISC4__VP - OP_AMP_VP_RAIL_OFFSET;
+
+ switch (m_type)
+ {
+ case 1:
+ /* We will simulate this as a voltage divider with 2 states depending
+ * on the input. But we have to take the diodes into account.
+ */
+ v = DST_RCDISC4__VP - .5; /* diode drop */
+
+ /* When the input is 1, both R1 & R3 are basically in parallel. */
+ r = RES_2_PARALLEL(DST_RCDISC4__R1, DST_RCDISC4__R3);
+ rT = DST_RCDISC4__R2 + r;
+ i = v / rT;
+ m_v[1] = i * r + .5;
+ rT = RES_2_PARALLEL(DST_RCDISC4__R2, r);
+ m_exp[1] = RC_CHARGE_EXP(rT * DST_RCDISC4__C1);
+
+ /* When the input is 0, R1 is out of circuit. */
+ rT = DST_RCDISC4__R2 + DST_RCDISC4__R3;
+ i = v / rT;
+ m_v[0] = i * DST_RCDISC4__R3 + .5;
+ rT = RES_2_PARALLEL(DST_RCDISC4__R2, DST_RCDISC4__R3);
+ m_exp[0] = RC_CHARGE_EXP(rT * DST_RCDISC4__C1);
+ break;
+
+ case 3:
+ /* We will simulate this as a voltage divider with 2 states depending
+ * on the input. The 1k pullup is in parallel with the internal TTL
+ * resistance, so we will just use .5k in series with R1.
+ */
+ r = 500.0 + DST_RCDISC4__R1;
+ m_v[1] = RES_VOLTAGE_DIVIDER(r, DST_RCDISC4__R2) * (5.0 - 0.5);
+ rT = RES_2_PARALLEL(r, DST_RCDISC4__R2);
+ m_exp[1] = RC_CHARGE_EXP(rT * DST_RCDISC4__C1);
+
+ /* When the input is 0, R1 is out of circuit. */
+ m_v[0] = 0;
+ m_exp[0] = RC_CHARGE_EXP(DST_RCDISC4__R2 * DST_RCDISC4__C1);
+ break;
+ }
+}
+
+/************************************************************************
+ *
+ * DST_RCDISC5 - Diode in series with R//C
+ *
+ * input[0] - Enable input value
+ * input[1] - input value
+ * input[2] - Resistor value (initialization only)
+ * input[3] - Capacitor Value (initialization only)
+ *
+ ************************************************************************/
+#define DST_RCDISC5__ENABLE DISCRETE_INPUT(0)
+#define DST_RCDISC5__IN DISCRETE_INPUT(1)
+#define DST_RCDISC5__R DISCRETE_INPUT(2)
+#define DST_RCDISC5__C DISCRETE_INPUT(3)
+
+DISCRETE_STEP( dst_rcdisc5)
+{
+ double diff,u;
+
+ /* Exponential based in difference between input/output */
+
+ u = DST_RCDISC5__IN - 0.7; /* Diode drop */
+ if( u < 0)
+ u = 0;
+
+ diff = u - m_v_cap;
+
+ if(DST_RCDISC5__ENABLE)
+ {
+ if(diff < 0)
+ diff = diff * m_exponent0;
+
+ m_v_cap += diff;
+ set_output(0, m_v_cap);
+ }
+ else
+ {
+ if(diff > 0)
+ m_v_cap = u;
+
+ set_output(0, 0);
+ }
+}
+
+DISCRETE_RESET( dst_rcdisc5)
+{
+ set_output(0, 0);
+
+ m_state = 0;
+ m_t = 0;
+ m_v_cap = 0;
+ m_exponent0 = RC_CHARGE_EXP(DST_RCDISC5__R * DST_RCDISC5__C);
+}
+
+
+/************************************************************************
+ *
+ * DST_RCDISC_MOD - RC triggered by logic and modulated
+ *
+ * input[0] - Enable input value
+ * input[1] - input value 1
+ * input[2] - input value 2
+ * input[3] - Resistor 1 value (initialization only)
+ * input[4] - Resistor 2 value (initialization only)
+ * input[5] - Resistor 3 value (initialization only)
+ * input[6] - Resistor 4 value (initialization only)
+ * input[7] - Capacitor Value (initialization only)
+ * input[8] - Voltage Value (initialization only)
+ *
+ ************************************************************************/
+#define DST_RCDISC_MOD__IN1 DISCRETE_INPUT(0)
+#define DST_RCDISC_MOD__IN2 DISCRETE_INPUT(1)
+#define DST_RCDISC_MOD__R1 DISCRETE_INPUT(2)
+#define DST_RCDISC_MOD__R2 DISCRETE_INPUT(3)
+#define DST_RCDISC_MOD__R3 DISCRETE_INPUT(4)
+#define DST_RCDISC_MOD__R4 DISCRETE_INPUT(5)
+#define DST_RCDISC_MOD__C DISCRETE_INPUT(6)
+#define DST_RCDISC_MOD__VP DISCRETE_INPUT(7)
+
+DISCRETE_STEP(dst_rcdisc_mod)
+{
+ double diff, v_cap, u, vD;
+ int mod_state, mod1_state, mod2_state;
+
+ /* Exponential based in difference between input/output */
+ v_cap = m_v_cap;
+
+ mod1_state = DST_RCDISC_MOD__IN1 > 0.5;
+ mod2_state = DST_RCDISC_MOD__IN2 > 0.6;
+ mod_state = (mod2_state << 1) + mod1_state;
+
+ u = mod1_state ? 0 : DST_RCDISC_MOD__VP;
+ /* Clamp */
+ diff = u - v_cap;
+ vD = diff * m_vd_gain[mod_state];
+ if (vD < -0.6)
+ {
+ diff = u + 0.6 - v_cap;
+ diff -= diff * m_exp_low[mod1_state];
+ v_cap += diff;
+ set_output(0, mod2_state ? 0 : -0.6);
+ }
+ else
+ {
+ diff -= diff * m_exp_high[mod_state];
+ v_cap += diff;
+ /* neglecting current through R3 drawn by next8 node */
+ set_output(0, mod2_state ? 0: (u - v_cap) * m_gain[mod1_state]);
+ }
+ m_v_cap = v_cap;
+}
+
+DISCRETE_RESET(dst_rcdisc_mod)
+{
+ double rc[2], rc2[2];
+
+ /* pre-calculate fixed values */
+ /* DST_RCDISC_MOD__IN1 <= 0.5 */
+ rc[0] = DST_RCDISC_MOD__R1 + DST_RCDISC_MOD__R2;
+ if (rc[0] < 1) rc[0] = 1;
+ m_exp_low[0] = RC_DISCHARGE_EXP(DST_RCDISC_MOD__C * rc[0]);
+ m_gain[0] = RES_VOLTAGE_DIVIDER(rc[0], DST_RCDISC_MOD__R4);
+ /* DST_RCDISC_MOD__IN1 > 0.5 */
+ rc[1] = DST_RCDISC_MOD__R2;
+ if (rc[1] < 1) rc[1] = 1;
+ m_exp_low[1] = RC_DISCHARGE_EXP(DST_RCDISC_MOD__C * rc[1]);
+ m_gain[1] = RES_VOLTAGE_DIVIDER(rc[1], DST_RCDISC_MOD__R4);
+ /* DST_RCDISC_MOD__IN2 <= 0.6 */
+ rc2[0] = DST_RCDISC_MOD__R4;
+ /* DST_RCDISC_MOD__IN2 > 0.6 */
+ rc2[1] = RES_2_PARALLEL(DST_RCDISC_MOD__R3, DST_RCDISC_MOD__R4);
+ /* DST_RCDISC_MOD__IN1 <= 0.5 && DST_RCDISC_MOD__IN2 <= 0.6 */
+ m_exp_high[0] = RC_DISCHARGE_EXP(DST_RCDISC_MOD__C * (rc[0] + rc2[0]));
+ m_vd_gain[0] = RES_VOLTAGE_DIVIDER(rc[0], rc2[0]);
+ /* DST_RCDISC_MOD__IN1 > 0.5 && DST_RCDISC_MOD__IN2 <= 0.6 */
+ m_exp_high[1] = RC_DISCHARGE_EXP(DST_RCDISC_MOD__C * (rc[1] + rc2[0]));
+ m_vd_gain[1] = RES_VOLTAGE_DIVIDER(rc[1], rc2[0]);
+ /* DST_RCDISC_MOD__IN1 <= 0.5 && DST_RCDISC_MOD__IN2 > 0.6 */
+ m_exp_high[2] = RC_DISCHARGE_EXP(DST_RCDISC_MOD__C * (rc[0] + rc2[1]));
+ m_vd_gain[2] = RES_VOLTAGE_DIVIDER(rc[0], rc2[1]);
+ /* DST_RCDISC_MOD__IN1 > 0.5 && DST_RCDISC_MOD__IN2 > 0.6 */
+ m_exp_high[3] = RC_DISCHARGE_EXP(DST_RCDISC_MOD__C * (rc[1] + rc2[1]));
+ m_vd_gain[3] = RES_VOLTAGE_DIVIDER(rc[1], rc2[1]);
+
+ m_v_cap = 0;
+ set_output(0, 0);
+}
+
+/************************************************************************
+ *
+ * DST_RCFILTER - Usage of node_description values for RC filter
+ *
+ * input[0] - Enable input value
+ * input[1] - input value
+ * input[2] - Resistor value (initialization only)
+ * input[3] - Capacitor Value (initialization only)
+ * input[4] - Voltage reference. Usually 0V.
+ *
+ ************************************************************************/
+#define DST_RCFILTER__VIN DISCRETE_INPUT(0)
+#define DST_RCFILTER__R DISCRETE_INPUT(1)
+#define DST_RCFILTER__C DISCRETE_INPUT(2)
+#define DST_RCFILTER__VREF DISCRETE_INPUT(3)
+
+DISCRETE_STEP(dst_rcfilter)
+{
+ if (EXPECTED(m_is_fast))
+ m_v_out += ((DST_RCFILTER__VIN - m_v_out) * m_exponent);
+ else
+ {
+ if (UNEXPECTED(m_has_rc_nodes))
+ {
+ double rc = DST_RCFILTER__R * DST_RCFILTER__C;
+ if (rc != m_rc)
+ {
+ m_rc = rc;
+ m_exponent = RC_CHARGE_EXP(rc);
+ }
+ }
+
+ /************************************************************************/
+ /* Next Value = PREV + (INPUT_VALUE - PREV)*(1-(EXP(-TIMEDELTA/RC))) */
+ /************************************************************************/
+
+ m_vCap += ((DST_RCFILTER__VIN - m_v_out) * m_exponent);
+ m_v_out = m_vCap + DST_RCFILTER__VREF;
+ }
+ set_output(0, m_v_out);
+}
+
+
+DISCRETE_RESET(dst_rcfilter)
+{
+ m_has_rc_nodes = this->input_is_node() & 0x6;
+ m_rc = DST_RCFILTER__R * DST_RCFILTER__C;
+ m_exponent = RC_CHARGE_EXP(m_rc);
+ m_vCap = 0;
+ m_v_out = 0;
+ /* FIXME --> we really need another class here */
+ if (!m_has_rc_nodes && DST_RCFILTER__VREF == 0)
+ m_is_fast = 1;
+ else
+ m_is_fast = 0;
+}
+
+/************************************************************************
+ *
+ * DST_RCFILTER_SW - Usage of node_description values for switchable RC filter
+ *
+ * input[0] - Enable input value
+ * input[1] - input value
+ * input[2] - Resistor value (initialization only)
+ * input[3] - Capacitor Value (initialization only)
+ * input[4] - Voltage reference. Usually 0V.
+ *
+ ************************************************************************/
+#define DST_RCFILTER_SW__ENABLE DISCRETE_INPUT(0)
+#define DST_RCFILTER_SW__VIN DISCRETE_INPUT(1)
+#define DST_RCFILTER_SW__SWITCH DISCRETE_INPUT(2)
+#define DST_RCFILTER_SW__R DISCRETE_INPUT(3)
+#define DST_RCFILTER_SW__C(x) DISCRETE_INPUT(4+x)
+
+/* 74HC4066 : 15
+ * 74VHC4066 : 15
+ * UTC4066 : 270 @ 5VCC, 80 @ 15VCC
+ * CD4066BC : 270 (Fairchild)
+ *
+ * The choice below makes scramble sound about "right". For future error reports,
+ * we need the exact type of switch and at which voltage (5, 12?) it is operated.
+ */
+#define CD4066_ON_RES (40)
+
+// FIXME: This needs optimization !
+DISCRETE_STEP(dst_rcfilter_sw)
+{
+ int i;
+ int bits = (int)DST_RCFILTER_SW__SWITCH;
+ double us = 0;
+ double vIn = DST_RCFILTER_SW__VIN;
+ double v_out;
+
+ if (EXPECTED(DST_RCFILTER_SW__ENABLE))
+ {
+ switch (bits)
+ {
+ case 0:
+ v_out = vIn;
+ break;
+ case 1:
+ m_vCap[0] += (vIn - m_vCap[0]) * m_exp0;
+ v_out = m_vCap[0] + (vIn - m_vCap[0]) * m_factor;
+ break;
+ case 2:
+ m_vCap[1] += (vIn - m_vCap[1]) * m_exp1;
+ v_out = m_vCap[1] + (vIn - m_vCap[1]) * m_factor;
+ break;
+ default:
+ for (i = 0; i < 4; i++)
+ {
+ if (( bits & (1 << i)) != 0)
+ us += m_vCap[i];
+ }
+ v_out = m_f1[bits] * vIn + m_f2[bits] * us;
+ for (i = 0; i < 4; i++)
+ {
+ if (( bits & (1 << i)) != 0)
+ m_vCap[i] += (v_out - m_vCap[i]) * m_exp[i];
+ }
+ }
+ set_output(0, v_out);
+ }
+ else
+ {
+ set_output(0, 0);
+ }
+}
+
+DISCRETE_RESET(dst_rcfilter_sw)
+{
+ int i, bits;
+
+ for (i = 0; i < 4; i++)
+ {
+ m_vCap[i] = 0;
+ m_exp[i] = RC_CHARGE_EXP( CD4066_ON_RES * DST_RCFILTER_SW__C(i));
+ }
+
+ for (bits=0; bits < 15; bits++)
+ {
+ double rs = 0;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (( bits & (1 << i)) != 0)
+ rs += DST_RCFILTER_SW__R;
+ }
+ m_f1[bits] = RES_VOLTAGE_DIVIDER(rs, CD4066_ON_RES);
+ m_f2[bits] = DST_RCFILTER_SW__R / (CD4066_ON_RES + rs);
+ }
+
+
+ /* fast cases */
+ m_exp0 = RC_CHARGE_EXP((CD4066_ON_RES + DST_RCFILTER_SW__R) * DST_RCFILTER_SW__C(0));
+ m_exp1 = RC_CHARGE_EXP((CD4066_ON_RES + DST_RCFILTER_SW__R) * DST_RCFILTER_SW__C(1));
+ m_factor = RES_VOLTAGE_DIVIDER(DST_RCFILTER_SW__R, CD4066_ON_RES);
+
+ set_output(0, 0);
+}
+
+
+/************************************************************************
+ *
+ * DST_RCINTEGRATE - Two diode inputs, transistor and a R/C charge
+ * discharge network
+ *
+ * input[0] - Enable input value
+ * input[1] - input value 1
+ * input[2] - input value 2
+ * input[3] - Resistor 1 value (initialization only)
+ * input[4] - Resistor 2 value (initialization only)
+ * input[5] - Capacitor Value (initialization only)
+ *
+ ************************************************************************/
+#define DST_RCINTEGRATE__IN1 DISCRETE_INPUT(0)
+#define DST_RCINTEGRATE__R1 DISCRETE_INPUT(1)
+#define DST_RCINTEGRATE__R2 DISCRETE_INPUT(2)
+#define DST_RCINTEGRATE__R3 DISCRETE_INPUT(3)
+#define DST_RCINTEGRATE__C DISCRETE_INPUT(4)
+#define DST_RCINTEGRATE__VP DISCRETE_INPUT(5)
+#define DST_RCINTEGRATE__TYPE DISCRETE_INPUT(6)
+
+/* Ebers-Moll large signal model
+ * Couriersud:
+ * The implementation avoids all iterative approaches in order not to burn cycles
+ * We will calculate Ic from vBE and use this as an indication where to go.
+ * The implementation may oscillate if you change the weighting factors at the
+ * end.
+ *
+ * This implementation is not perfect, but does it's job in dkong'
+ */
+
+/* reverse saturation current */
+#define IES 7e-15
+#define ALPHAT 0.99
+#define KT 0.026
+#define EM_IC(x) (ALPHAT * IES * exp( (x) / KT - 1.0 ))
+
+DISCRETE_STEP( dst_rcintegrate)
+{
+ double diff, u, iQ, iQc, iC, RG, vE;
+ double vP;
+
+ u = DST_RCINTEGRATE__IN1;
+ vP = DST_RCINTEGRATE__VP;
+
+ if ( u - 0.7 < m_vCap * m_gain_r1_r2)
+ {
+ /* discharge .... */
+ diff = 0.0 - m_vCap;
+ iC = m_c_exp1 * diff; /* iC */
+ diff -= diff * m_exp_exponent1;
+ m_vCap += diff;
+ iQ = 0;
+ vE = m_vCap * m_gain_r1_r2;
+ RG = vE / iC;
+ }
+ else
+ {
+ /* charging */
+ diff = (vP - m_vCE) * m_f - m_vCap;
+ iC = 0.0 - m_c_exp0 * diff; /* iC */
+ diff -= diff * m_exp_exponent0;
+ m_vCap += diff;
+ iQ = iC + (iC * DST_RCINTEGRATE__R1 + m_vCap) / DST_RCINTEGRATE__R2;
+ RG = (vP - m_vCE) / iQ;
+ vE = (RG - DST_RCINTEGRATE__R3) / RG * (vP - m_vCE);
+ }
+
+
+ u = DST_RCINTEGRATE__IN1;
+ if (u > 0.7 + vE)
+ {
+ vE = u - 0.7;
+ //iQc = EM_IC(u - vE);
+ iQc = m_EM_IC_0_7;
+ }
+ else
+ iQc = EM_IC(u - vE);
+
+ m_vCE = MIN(vP - 0.1, vP - RG * iQc);
+
+ /* Avoid oscillations
+ * The method tends to largely overshoot - no wonder without
+ * iterative solution approximation
+ */
+
+ m_vCE = MAX(m_vCE, 0.1 );
+ m_vCE = 0.1 * m_vCE + 0.9 * (vP - vE - iQ * DST_RCINTEGRATE__R3);
+
+ switch (m_type)
+ {
+ case DISC_RC_INTEGRATE_TYPE1:
+ set_output(0, m_vCap);
+ break;
+ case DISC_RC_INTEGRATE_TYPE2:
+ set_output(0, vE);
+ break;
+ case DISC_RC_INTEGRATE_TYPE3:
+ set_output(0, MAX(0, vP - iQ * DST_RCINTEGRATE__R3));
+ break;
+ }
+}
+
+DISCRETE_RESET(dst_rcintegrate)
+{
+ double r;
+ double dt = this->sample_time();
+
+ m_type = DST_RCINTEGRATE__TYPE;
+
+ m_vCap = 0;
+ m_vCE = 0;
+
+ /* pre-calculate fixed values */
+ m_gain_r1_r2 = RES_VOLTAGE_DIVIDER(DST_RCINTEGRATE__R1, DST_RCINTEGRATE__R2);
+
+ r = DST_RCINTEGRATE__R1 / DST_RCINTEGRATE__R2 * DST_RCINTEGRATE__R3 + DST_RCINTEGRATE__R1 + DST_RCINTEGRATE__R3;
+
+ m_f = RES_VOLTAGE_DIVIDER(DST_RCINTEGRATE__R3, DST_RCINTEGRATE__R2);
+ m_exponent0 = -1.0 * r * m_f * DST_RCINTEGRATE__C;
+ m_exponent1 = -1.0 * (DST_RCINTEGRATE__R1 + DST_RCINTEGRATE__R2) * DST_RCINTEGRATE__C;
+ m_exp_exponent0 = exp(dt / m_exponent0);
+ m_exp_exponent1 = exp(dt / m_exponent1);
+ m_c_exp0 = DST_RCINTEGRATE__C / m_exponent0 * m_exp_exponent0;
+ m_c_exp1 = DST_RCINTEGRATE__C / m_exponent1 * m_exp_exponent1;
+
+ m_EM_IC_0_7 = EM_IC(0.7);
+
+ set_output(0, 0);
+}
+
+/************************************************************************
+ *
+ * DST_SALLEN_KEY - Sallen-Key filter circuit
+ *
+ * input[0] - Enable input value
+ * input[1] - IN0 node
+ * input[3] - Filter Type
+ *
+ * also passed discrete_op_amp_filt_info structure
+ *
+ * 2008, couriersud
+ ************************************************************************/
+#define DST_SALLEN_KEY__ENABLE DISCRETE_INPUT(0)
+#define DST_SALLEN_KEY__INP0 DISCRETE_INPUT(1)
+#define DST_SALLEN_KEY__TYPE DISCRETE_INPUT(2)
+
+DISCRETE_STEP(dst_sallen_key)
+{
+ double gain = 1.0;
+ double v_out;
+
+ if (DST_SALLEN_KEY__ENABLE == 0.0)
+ {
+ gain = 0.0;
+ }
+
+ v_out = -m_fc.a1 * m_fc.y1 - m_fc.a2 * m_fc.y2 +
+ m_fc.b0 * gain * DST_SALLEN_KEY__INP0 + m_fc.b1 * m_fc.x1 + m_fc.b2 * m_fc.x2;
+
+ m_fc.x2 = m_fc.x1;
+ m_fc.x1 = gain * DST_SALLEN_KEY__INP0;
+ m_fc.y2 = m_fc.y1;
+ m_fc.y1 = v_out;
+ set_output(0, v_out);
+}
+
+DISCRETE_RESET(dst_sallen_key)
+{
+ DISCRETE_DECLARE_INFO(discrete_op_amp_filt_info)
+
+ double freq, q;
+
+ switch ((int) DST_SALLEN_KEY__TYPE)
+ {
+ case DISC_SALLEN_KEY_LOW_PASS:
+ freq = 1.0 / ( 2.0 * M_PI * sqrt(info->c1 * info->c2 * info->r1 * info->r2));
+ q = sqrt(info->c1 * info->c2 * info->r1 * info->r2) / (info->c2 * (info->r1 + info->r2));
+ break;
+ default:
+ fatalerror("Unknown sallen key filter type\n");
+ }
+
+ calculate_filter2_coefficients(this, freq, 1.0 / q, DISC_FILTER_LOWPASS, m_fc);
+ set_output(0, 0);
+}
+
+
+/* !!!!!!!!!!! NEW FILTERS for testing !!!!!!!!!!!!!!!!!!!!! */
+
+
+/************************************************************************
+ *
+ * DST_RCFILTERN - Usage of node_description values for RC filter
+ *
+ * input[0] - Enable input value
+ * input[1] - input value
+ * input[2] - Resistor value (initialization only)
+ * input[3] - Capacitor Value (initialization only)
+ *
+ ************************************************************************/
+#define DST_RCFILTERN__ENABLE DISCRETE_INPUT(0)
+#define DST_RCFILTERN__IN DISCRETE_INPUT(1)
+#define DST_RCFILTERN__R DISCRETE_INPUT(2)
+#define DST_RCFILTERN__C DISCRETE_INPUT(3)
+
+#if 0
+DISCRETE_RESET(dst_rcfilterN)
+{
+#if 0
+ double f=1.0/(2*M_PI* DST_RCFILTERN__R * DST_RCFILTERN__C);
+
+/* !!!!!!!!!!!!!! CAN'T CHEAT LIKE THIS !!!!!!!!!!!!!!!! */
+/* Put this stuff in a context */
+
+ this->m_input[2] = f;
+ this->m_input[3] = DISC_FILTER_LOWPASS;
+
+ /* Use first order filter */
+ dst_filter1_reset(node);
+#endif
+}
+#endif
+
+/************************************************************************
+ *
+ * DST_RCDISCN - Usage of node_description values for RC discharge
+ * (inverse slope of DST_RCFILTER)
+ *
+ * input[0] - Enable input value
+ * input[1] - input value
+ * input[2] - Resistor value (initialization only)
+ * input[3] - Capacitor Value (initialization only)
+ *
+ ************************************************************************/
+#define DST_RCDISCN__ENABLE DISCRETE_INPUT(0)
+#define DST_RCDISCN__IN DISCRETE_INPUT(1)
+#define DST_RCDISCN__R DISCRETE_INPUT(2)
+#define DST_RCDISCN__C DISCRETE_INPUT(3)
+
+DISCRETE_RESET(dst_rcdiscN)
+{
+#if 0
+ double f = 1.0 / (2 * M_PI * DST_RCDISCN__R * DST_RCDISCN__C);
+
+/* !!!!!!!!!!!!!! CAN'T CHEAT LIKE THIS !!!!!!!!!!!!!!!! */
+/* Put this stuff in a context */
+
+ this->m_input[2] = f;
+ this->m_input[3] = DISC_FILTER_LOWPASS;
+
+ /* Use first order filter */
+ dst_filter1_reset(node);
+#endif
+}
+
+DISCRETE_STEP(dst_rcdiscN)
+{
+ double gain = 1.0;
+ double v_out;
+
+ if (DST_RCDISCN__ENABLE == 0.0)
+ {
+ gain = 0.0;
+ }
+
+ /* A rise in the input signal results in an instant charge, */
+ /* else discharge through the RC to zero */
+ if (gain* DST_RCDISCN__IN > m_x1)
+ v_out = gain* DST_RCDISCN__IN;
+ else
+ v_out = -m_a1*m_y1;
+
+ m_x1 = gain* DST_RCDISCN__IN;
+ m_y1 = v_out;
+ set_output(0, v_out);
+}
+
+
+/************************************************************************
+ *
+ * DST_RCDISC2N - Usage of node_description values for RC discharge
+ * Has switchable charge resistor/input
+ *
+ * input[0] - Switch input value
+ * input[1] - input[0] value
+ * input[2] - Resistor0 value (initialization only)
+ * input[3] - input[1] value
+ * input[4] - Resistor1 value (initialization only)
+ * input[5] - Capacitor Value (initialization only)
+ *
+ ************************************************************************/
+#define DST_RCDISC2N__ENABLE DISCRETE_INPUT(0)
+#define DST_RCDISC2N__IN0 DISCRETE_INPUT(1)
+#define DST_RCDISC2N__R0 DISCRETE_INPUT(2)
+#define DST_RCDISC2N__IN1 DISCRETE_INPUT(3)
+#define DST_RCDISC2N__R1 DISCRETE_INPUT(4)
+#define DST_RCDISC2N__C DISCRETE_INPUT(5)
+
+
+DISCRETE_STEP(dst_rcdisc2N)
+{
+ double inp = ((DST_RCDISC2N__ENABLE == 0) ? DST_RCDISC2N__IN0 : DST_RCDISC2N__IN1);
+ double v_out;
+
+ if (DST_RCDISC2N__ENABLE == 0)
+ v_out = -m_fc0.a1*m_y1 + m_fc0.b0*inp + m_fc0.b1 * m_x1;
+ else
+ v_out = -m_fc1.a1*m_y1 + m_fc1.b0*inp + m_fc1.b1*m_x1;
+
+ m_x1 = inp;
+ m_y1 = v_out;
+ set_output(0, v_out);
+}
+
+DISCRETE_RESET(dst_rcdisc2N)
+{
+ double f1,f2;
+
+ f1 = 1.0 / (2 * M_PI * DST_RCDISC2N__R0 * DST_RCDISC2N__C);
+ f2 = 1.0 / (2 * M_PI * DST_RCDISC2N__R1 * DST_RCDISC2N__C);
+
+ calculate_filter1_coefficients(this, f1, DISC_FILTER_LOWPASS, m_fc0);
+ calculate_filter1_coefficients(this, f2, DISC_FILTER_LOWPASS, m_fc1);
+
+ /* Initialize the object */
+ set_output(0, 0);
+}
diff --git a/src/devices/sound/disc_inp.inc b/src/devices/sound/disc_inp.inc
new file mode 100644
index 00000000000..b9eedb9fdff
--- /dev/null
+++ b/src/devices/sound/disc_inp.inc
@@ -0,0 +1,325 @@
+// license:BSD-3-Clause
+// copyright-holders:K.Wilkins
+/************************************************************************
+ *
+ * MAME - Discrete sound system emulation library
+ *
+ * Written by K.Wilkins (mame@esplexo.co.uk)
+ *
+ * (c) K.Wilkins 2000
+ *
+ ***********************************************************************
+ *
+ * DSS_ADJUSTMENT - UI Mapped adjustable input
+ * DSS_CONSTANT - Node based constant - Do we need this ???
+ * DSS_INPUT_x - Input devices
+ * DSS_INPUT_STREAM - Connects external streams to the discrete system
+ *
+ ************************************************************************/
+
+
+#define DSS_INPUT__GAIN DISCRETE_INPUT(0)
+#define DSS_INPUT__OFFSET DISCRETE_INPUT(1)
+#define DSS_INPUT__INIT DISCRETE_INPUT(2)
+
+/************************************************************************
+ *
+ * DSS_ADJUSTMENT - UI Adjustable constant node to emulate trimmers
+ *
+ * input[0] - Enable
+ * input[1] - Minimum value
+ * input[2] - Maximum value
+ * input[3] - Log/Linear 0=Linear !0=Log
+ * input[4] - Input Port number
+ * input[5] -
+ * input[6] -
+ *
+ ************************************************************************/
+#define DSS_ADJUSTMENT__MIN DISCRETE_INPUT(0)
+#define DSS_ADJUSTMENT__MAX DISCRETE_INPUT(1)
+#define DSS_ADJUSTMENT__LOG DISCRETE_INPUT(2)
+#define DSS_ADJUSTMENT__PORT DISCRETE_INPUT(3)
+#define DSS_ADJUSTMENT__PMIN DISCRETE_INPUT(4)
+#define DSS_ADJUSTMENT__PMAX DISCRETE_INPUT(5)
+
+DISCRETE_STEP(dss_adjustment)
+{
+ INT32 rawportval = m_port->read();
+
+ /* only recompute if the value changed from last time */
+ if (UNEXPECTED(rawportval != m_lastpval))
+ {
+ double portval = (double)(rawportval - m_pmin) * m_pscale;
+ double scaledval = portval * m_scale + m_min;
+
+ m_lastpval = rawportval;
+ if (DSS_ADJUSTMENT__LOG == 0)
+ set_output(0, scaledval);
+ else
+ set_output(0, pow(10, scaledval));
+ }
+}
+
+DISCRETE_RESET(dss_adjustment)
+{
+ double min, max;
+
+ m_port = m_device->machine().root_device().ioport(m_device->siblingtag((const char *)this->custom_data()).c_str());
+ if (m_port == NULL)
+ fatalerror("DISCRETE_ADJUSTMENT - NODE_%d has invalid tag\n", this->index());
+
+ m_lastpval = 0x7fffffff;
+ m_pmin = DSS_ADJUSTMENT__PMIN;
+ m_pscale = 1.0 / (double)(DSS_ADJUSTMENT__PMAX - DSS_ADJUSTMENT__PMIN);
+
+ /* linear scale */
+ if (DSS_ADJUSTMENT__LOG == 0)
+ {
+ m_min = DSS_ADJUSTMENT__MIN;
+ m_scale = DSS_ADJUSTMENT__MAX - DSS_ADJUSTMENT__MIN;
+ }
+
+ /* logarithmic scale */
+ else
+ {
+ /* force minimum and maximum to be > 0 */
+ min = (DSS_ADJUSTMENT__MIN > 0) ? DSS_ADJUSTMENT__MIN : 1;
+ max = (DSS_ADJUSTMENT__MAX > 0) ? DSS_ADJUSTMENT__MAX : 1;
+ m_min = log10(min);
+ m_scale = log10(max) - log10(min);
+ }
+
+ this->step();
+}
+
+
+/************************************************************************
+ *
+ * DSS_CONSTANT - This is a constant.
+ *
+ * input[0] - Constant value
+ *
+ ************************************************************************/
+#define DSS_CONSTANT__INIT DISCRETE_INPUT(0)
+
+DISCRETE_RESET(dss_constant)
+{
+ set_output(0, DSS_CONSTANT__INIT);
+}
+
+
+/************************************************************************
+ *
+ * DSS_INPUT_x - Receives input from discrete_sound_w
+ *
+ * input[0] - Gain value
+ * input[1] - Offset value
+ * input[2] - Starting Position
+ * input[3] - Current data value
+ *
+ ************************************************************************/
+
+DISCRETE_RESET(dss_input_data)
+{
+ m_gain = DSS_INPUT__GAIN;
+ m_offset = DSS_INPUT__OFFSET;
+
+ m_data = DSS_INPUT__INIT;
+ set_output(0, m_data * m_gain + m_offset);
+}
+
+void DISCRETE_CLASS_FUNC(dss_input_data, input_write)(int sub_node, UINT8 data )
+{
+ UINT8 new_data = 0;
+
+ new_data = data;
+
+ if (m_data != new_data)
+ {
+ /* Bring the system up to now */
+ m_device->update_to_current_time();
+
+ m_data = new_data;
+
+ /* Update the node output here so we don't have to do it each step */
+ set_output(0, m_data * m_gain + m_offset);
+ }
+}
+
+DISCRETE_RESET(dss_input_logic)
+{
+ m_gain = DSS_INPUT__GAIN;
+ m_offset = DSS_INPUT__OFFSET;
+
+ m_data = (DSS_INPUT__INIT == 0) ? 0 : 1;
+ set_output(0, m_data * m_gain + m_offset);
+}
+
+void DISCRETE_CLASS_FUNC(dss_input_logic, input_write)(int sub_node, UINT8 data )
+{
+ UINT8 new_data = 0;
+
+ new_data = data ? 1 : 0;
+
+ if (m_data != new_data)
+ {
+ /* Bring the system up to now */
+ m_device->update_to_current_time();
+
+ m_data = new_data;
+
+ /* Update the node output here so we don't have to do it each step */
+ set_output(0, m_data * m_gain + m_offset);
+ }
+}
+
+DISCRETE_RESET(dss_input_not)
+{
+ m_gain = DSS_INPUT__GAIN;
+ m_offset = DSS_INPUT__OFFSET;
+
+ m_data = (DSS_INPUT__INIT == 0) ? 1 : 0;
+ set_output(0, m_data * m_gain + m_offset);
+}
+
+void DISCRETE_CLASS_FUNC(dss_input_not, input_write)(int sub_node, UINT8 data )
+{
+ UINT8 new_data = 0;
+
+ new_data = data ? 0 : 1;
+
+ if (m_data != new_data)
+ {
+ /* Bring the system up to now */
+ m_device->update_to_current_time();
+
+ m_data = new_data;
+
+ /* Update the node output here so we don't have to do it each step */
+ set_output(0, m_data * m_gain + m_offset);
+ }
+}
+
+DISCRETE_STEP(dss_input_pulse)
+{
+ /* Set a valid output */
+ set_output(0, m_data);
+ /* Reset the input to default for the next cycle */
+ /* node order is now important */
+ m_data = DSS_INPUT__INIT;
+}
+
+DISCRETE_RESET(dss_input_pulse)
+{
+ m_data = (DSS_INPUT__INIT == 0) ? 0 : 1;
+ set_output(0, m_data);
+}
+
+void DISCRETE_CLASS_FUNC(dss_input_pulse, input_write)(int sub_node, UINT8 data )
+{
+ UINT8 new_data = 0;
+
+ new_data = data ? 1 : 0;
+
+ if (m_data != new_data)
+ {
+ /* Bring the system up to now */
+ m_device->update_to_current_time();
+ m_data = new_data;
+ }
+}
+
+/************************************************************************
+ *
+ * DSS_INPUT_STREAM - Receives input from a routed stream
+ *
+ * input[0] - Input stream number
+ * input[1] - Gain value
+ * input[2] - Offset value
+ *
+ ************************************************************************/
+#define DSS_INPUT_STREAM__STREAM DISCRETE_INPUT(0)
+#define DSS_INPUT_STREAM__GAIN DISCRETE_INPUT(1)
+#define DSS_INPUT_STREAM__OFFSET DISCRETE_INPUT(2)
+
+void discrete_dss_input_stream_node::stream_generate(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *ptr = outputs[0];
+ int samplenum = samples;
+
+ while (samplenum-- > 0)
+ *(ptr++) = m_data;
+}
+DISCRETE_STEP(dss_input_stream)
+{
+ /* the context pointer is set to point to the current input stream data in discrete_stream_update */
+ if (EXPECTED(m_ptr))
+ {
+ set_output(0, (*m_ptr) * m_gain + m_offset);
+ m_ptr++;
+ }
+ else
+ set_output(0, 0);
+}
+
+DISCRETE_RESET(dss_input_stream)
+{
+ m_ptr = NULL;
+ m_data = 0;
+}
+
+void DISCRETE_CLASS_FUNC(dss_input_stream, input_write)(int sub_node, UINT8 data )
+{
+ UINT8 new_data = 0;
+
+ new_data = data;
+
+ if (m_data != new_data)
+ {
+ if (m_is_buffered)
+ {
+ /* Bring the system up to now */
+ m_buffer_stream->update();
+
+ m_data = new_data;
+ }
+ else
+ {
+ /* Bring the system up to now */
+ m_device->update_to_current_time();
+
+ m_data = new_data;
+
+ /* Update the node output here so we don't have to do it each step */
+ set_output(0, new_data * m_gain + m_offset);
+ }
+ }
+}
+
+DISCRETE_START(dss_input_stream)
+{
+ discrete_base_node::start();
+
+ /* Stream out number is set during start */
+ m_stream_in_number = DSS_INPUT_STREAM__STREAM;
+ m_gain = DSS_INPUT_STREAM__GAIN;
+ m_offset = DSS_INPUT_STREAM__OFFSET;
+ m_ptr = NULL;
+
+ m_is_buffered = is_buffered();
+ m_buffer_stream = NULL;
+}
+
+void DISCRETE_CLASS_NAME(dss_input_stream)::stream_start(void)
+{
+ if (m_is_buffered)
+ {
+ /* stream_buffered input only supported for sound devices */
+ discrete_sound_device *snd_device = downcast<discrete_sound_device *>(m_device);
+ //assert(DSS_INPUT_STREAM__STREAM < snd_device->m_input_stream_list.count());
+
+ m_buffer_stream = m_device->machine().sound().stream_alloc(*snd_device, 0, 1, this->sample_rate(), stream_update_delegate(FUNC(discrete_dss_input_stream_node::stream_generate),this));
+
+ snd_device->get_stream()->set_input(m_stream_in_number, m_buffer_stream);
+ }
+}
diff --git a/src/devices/sound/disc_mth.h b/src/devices/sound/disc_mth.h
new file mode 100644
index 00000000000..a459440a3b6
--- /dev/null
+++ b/src/devices/sound/disc_mth.h
@@ -0,0 +1,240 @@
+// license:BSD-3-Clause
+// copyright-holders:K.Wilkins
+#pragma once
+
+#ifndef __DISC_MTH_H__
+#define __DISC_MTH_H__
+
+/***********************************************************************
+ *
+ * MAME - Discrete sound system emulation library
+ *
+ * Written by K.Wilkins (mame@esplexo.co.uk)
+ *
+ * (c) K.Wilkins 2000
+ *
+ * Coding started in November 2000
+ *
+ * Additions/bugfix February 2003 - Derrick Renaud, F.Palazzolo, K.Wilkins
+ * Discrete parallel tasks 2009 - Couriersud
+ * Discrete classes 2010 - Couriersud
+ *
+ ***********************************************************************/
+
+#include "discrete.h"
+
+DISCRETE_CLASS_STEP(dst_adder, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP(dst_clamp, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP(dst_divide, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP(dst_gain, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP(dst_logic_inv, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP_RESET(dst_bits_decode, 8,
+ int m_count;
+ int m_decode_x_time;
+ int m_from;
+ int m_last_val;
+ int m_last_had_x_time;
+);
+
+DISCRETE_CLASS_STEP(dst_logic_and, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP(dst_logic_nand, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP(dst_logic_or, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP(dst_logic_nor, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP(dst_logic_xor, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP(dst_logic_nxor, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP_RESET(dst_logic_dff, 1,
+ int m_last_clk;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_logic_jkff, 1,
+ double m_v_out;
+ int m_last_clk;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_logic_shift, 1,
+ double m_t_left; /* time unused during last sample in seconds */
+ UINT32 m_shift_data;
+ UINT32 m_bit_mask;
+ UINT8 m_clock_type;
+ UINT8 m_reset_on_high;
+ UINT8 m_shift_r;
+ UINT8 m_last;
+);
+
+DISCRETE_CLASS_STEP(dst_lookup_table, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP_RESET(dst_multiplex, 1,
+ int m_size;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_oneshot, 1,
+ double m_countdown;
+ int m_state;
+ int m_last_trig;
+ int m_type;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_ramp, 1,
+ double m_v_out;
+ double m_step;
+ int m_dir; /* 1 if End is higher then Start */
+ int m_last_en; /* Keep track of the last enable value */
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_samphold, 1,
+ double m_last_input;
+ int m_clocktype;
+);
+
+DISCRETE_CLASS_STEP(dst_switch, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP(dst_aswitch, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP(, 1, /* no context */ );
+class DISCRETE_CLASS_NAME(dst_transform): public discrete_base_node, public discrete_step_interface
+{
+ DISCRETE_CLASS_CONSTRUCTOR(dst_transform, base)
+ DISCRETE_CLASS_DESTRUCTOR(dst_transform)
+public:
+ enum token {
+ TOK_END = 0,
+ TOK_MULT,
+ TOK_DIV,
+ TOK_ADD,
+ TOK_MINUS,
+ TOK_0,
+ TOK_1,
+ TOK_2,
+ TOK_3,
+ TOK_4,
+ TOK_DUP,
+ TOK_ABS, /* absolute value */
+ TOK_NEG, /* * -1 */
+ TOK_NOT, /* Logical NOT of Last Value */
+ TOK_EQUAL, /* Logical = */
+ TOK_GREATER, /* Logical > */
+ TOK_LESS, /* Logical < */
+ TOK_AND, /* Bitwise AND */
+ TOK_OR, /* Bitwise OR */
+ TOK_XOR /* Bitwise XOR */
+ };
+ void step(void);
+ void reset(void);
+protected:
+private:
+ DISCRETE_CLASS_INPUT(I_IN0, 0);
+ DISCRETE_CLASS_INPUT(I_IN1, 1);
+ DISCRETE_CLASS_INPUT(I_IN2, 2);
+ DISCRETE_CLASS_INPUT(I_IN3, 3);
+ DISCRETE_CLASS_INPUT(I_IN4, 4);
+ enum token precomp[32];
+};
+
+/* Component specific */
+
+DISCRETE_CLASS_STEP_RESET(dst_comp_adder, 1,
+ double m_total[256];
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_dac_r1, 1,
+ double m_v_out;
+ double m_exponent;
+ double m_last_v;
+ double m_v_step[256];
+ int m_has_c_filter;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_diode_mix, 1,
+ int m_size;
+ double m_v_junction[8];
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_integrate, 1,
+ double m_v_out;
+ double m_change;
+ double m_v_max_in; /* v1 - norton VBE */
+ double m_v_max_in_d; /* v1 - norton VBE - diode drop */
+ double m_v_max_out;
+);
+
+#define DISC_MIXER_MAX_INPS 8
+DISCRETE_CLASS_STEP_RESET(dst_mixer, 1,
+ int m_type;
+ int m_size;
+ int m_r_node_bit_flag;
+ int m_c_bit_flag;
+ double m_r_total;
+ const double * m_r_node[DISC_MIXER_MAX_INPS]; /* Either pointer to resistance node output OR NULL */
+ double m_r_last[DISC_MIXER_MAX_INPS];
+ double m_exponent_rc[DISC_MIXER_MAX_INPS]; /* For high pass filtering cause by cIn */
+ double m_v_cap[DISC_MIXER_MAX_INPS]; /* cap voltage of each input */
+ double m_exponent_c_f; /* Low pass on mixed inputs */
+ double m_exponent_c_amp; /* Final high pass caused by out cap and amp input impedance */
+ double m_v_cap_f; /* cap voltage of cF */
+ double m_v_cap_amp; /* cap voltage of cAmp */
+ double m_gain; /* used for DISC_MIXER_IS_OP_AMP_WITH_RI */
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_op_amp, 1,
+ UINT8 m_has_cap;
+ UINT8 m_has_r1;
+ UINT8 m_has_r4;
+ double m_v_max;
+ double m_i_fixed;
+ double m_v_cap;
+ double m_exponent;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_op_amp_1sht, 1,
+ double m_v_out;
+ double m_i_fixed;
+ double m_v_max;
+ double m_r34ratio;
+ double m_v_cap1;
+ double m_v_cap2;
+ double m_exponent1c;
+ double m_exponent1d;
+ double m_exponent2;
+);
+
+DISCRETE_CLASS_STEP_RESET(dst_tvca_op_amp, 1,
+ double m_v_out_max; /* Maximum output voltage */
+ double m_v_trig[2]; /* Voltage used to charge cap1 based on function F3 */
+ double m_v_trig2; /* Voltage used to charge cap2 */
+ double m_v_trig3; /* Voltage used to charge cap3 */
+ double m_i_fixed; /* Fixed current going into - input */
+ double m_exponent_c[2]; /* Charge exponents based on function F3 */
+ double m_exponent_d[2]; /* Discharge exponents based on function F3 */
+ double m_exponent2[2]; /* Discharge/charge exponents based on function F4 */
+ double m_exponent3[2]; /* Discharge/charge exponents based on function F5 */
+ double m_exponent4; /* Discharge/charge exponents for c4 */
+ double m_v_cap1; /* charge on cap c1 */
+ double m_v_cap2; /* charge on cap c2 */
+ double m_v_cap3; /* charge on cap c3 */
+ double m_v_cap4; /* charge on cap c4 */
+ double m_r67; /* = r6 + r7 (for easy use later) */
+ UINT8 m_has_c4;
+ UINT8 m_has_r4;
+);
+
+DISCRETE_CLASS_STEP(dst_xtime_buffer, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP(dst_xtime_and, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP(dst_xtime_or, 1, /* no context */ );
+
+DISCRETE_CLASS_STEP(dst_xtime_xor, 1, /* no context */ );
+
+
+#endif /* __DISC_WAV_H__ */
diff --git a/src/devices/sound/disc_mth.inc b/src/devices/sound/disc_mth.inc
new file mode 100644
index 00000000000..45e04654445
--- /dev/null
+++ b/src/devices/sound/disc_mth.inc
@@ -0,0 +1,2773 @@
+// license:BSD-3-Clause
+// copyright-holders:K.Wilkins
+/************************************************************************
+ *
+ * MAME - Discrete sound system emulation library
+ *
+ * Written by K.Wilkins (mame@esplexo.co.uk)
+ *
+ * (c) K.Wilkins 2000
+ * (c) Derrick Renaud 2003-2004
+ *
+ ************************************************************************
+ *
+ * DST_ADDDER - Multichannel adder
+ * DST_BITS_DECODE - Decode Bits from input node
+ * DST_CLAMP - Simple signal clamping circuit
+ * DST_COMP_ADDER - Selectable parallel component circuit
+ * DST_DAC_R1 - R1 Ladder DAC with cap filtering
+ * DST_DIODE_MIX - Diode mixer
+ * DST_DIVIDE - Division function
+ * DST_GAIN - Gain Factor
+ * DST_INTEGRATE - Integration circuits
+ * DST_LOGIC_INV - Logic level invertor
+ * DST_LOGIC_AND - Logic AND gate 4 input
+ * DST_LOGIC_NAND - Logic NAND gate 4 input
+ * DST_LOGIC_OR - Logic OR gate 4 input
+ * DST_LOGIC_NOR - Logic NOR gate 4 input
+ * DST_LOGIC_XOR - Logic XOR gate 2 input
+ * DST_LOGIC_NXOR - Logic NXOR gate 2 input
+ * DST_LOGIC_DFF - Logic D-type flip/flop
+ * DST_LOGIC_JKFF - Logic JK-type flip/flop
+ * DST_LOGIC_SHIFT - Logic Shift Register
+ * DST_LOOKUP_TABLE - Return value from lookup table
+ * DST_MIXER - Final Mixer Stage
+ * DST_MULTIPLEX - 1 of x Multiplexer/switch
+ * DST_ONESHOT - One shot pulse generator
+ * DST_RAMP - Ramp up/down
+ * DST_SAMPHOLD - Sample & Hold Implementation
+ * DST_SWITCH - Switch implementation
+ * DST_ASWITCH - Analog switch
+ * DST_TRANSFORM - Multiple math functions
+ * DST_OP_AMP - Op Amp circuits
+ * DST_OP_AMP_1SHT - Op Amp One Shot
+ * DST_TVCA_OP_AMP - Triggered op amp voltage controlled amplifier
+ * DST_XTIME_BUFFER - Buffer/Invertor gate implementation using X_TIME
+ * DST_XTIME_AND - AND/NAND gate implementation using X_TIME
+ * DST_XTIME_OR - OR/NOR gate implementation using X_TIME
+ * DST_XTIME_XOR - XOR/XNOR gate implementation using X_TIME
+ *
+ ************************************************************************/
+
+#include <float.h>
+
+
+
+/************************************************************************
+ *
+ * DST_ADDER - This is a 4 channel input adder with enable function
+ *
+ * input[0] - Enable input value
+ * input[1] - Channel0 input value
+ * input[2] - Channel1 input value
+ * input[3] - Channel2 input value
+ * input[4] - Channel3 input value
+ *
+ ************************************************************************/
+#define DST_ADDER__ENABLE DISCRETE_INPUT(0)
+#define DST_ADDER__IN0 DISCRETE_INPUT(1)
+#define DST_ADDER__IN1 DISCRETE_INPUT(2)
+#define DST_ADDER__IN2 DISCRETE_INPUT(3)
+#define DST_ADDER__IN3 DISCRETE_INPUT(4)
+
+DISCRETE_STEP(dst_adder)
+{
+ if(DST_ADDER__ENABLE)
+ {
+ set_output(0, DST_ADDER__IN0 + DST_ADDER__IN1 + DST_ADDER__IN2 + DST_ADDER__IN3);
+ }
+ else
+ {
+ set_output(0, 0);
+ }
+}
+
+
+/************************************************************************
+ *
+ * DST_COMP_ADDER - Selectable parallel component adder
+ *
+ * input[0] - Bit Select
+ *
+ * Also passed discrete_comp_adder_table structure
+ *
+ * Mar 2004, D Renaud.
+ ************************************************************************/
+#define DST_COMP_ADDER__SELECT DISCRETE_INPUT(0)
+
+DISCRETE_STEP(dst_comp_adder)
+{
+ int select;
+
+ select = (int)DST_COMP_ADDER__SELECT;
+ assert(select < 256);
+ set_output(0, m_total[select]);
+}
+
+DISCRETE_RESET(dst_comp_adder)
+{
+ DISCRETE_DECLARE_INFO(discrete_comp_adder_table)
+
+ int i, bit;
+ int bit_length = info->length;
+
+ assert(bit_length <= 8);
+
+ /* pre-calculate all possible values to speed up step routine */
+ for(i = 0; i < 256; i++)
+ {
+ switch (info->type)
+ {
+ case DISC_COMP_P_CAPACITOR:
+ m_total[i] = info->cDefault;
+ for(bit = 0; bit < bit_length; bit++)
+ {
+ if (i & (1 << bit))
+ m_total[i] += info->c[bit];
+ }
+ break;
+ case DISC_COMP_P_RESISTOR:
+ m_total[i] = (info->cDefault != 0) ? 1.0 / info->cDefault : 0;
+ for(bit = 0; bit < bit_length; bit++)
+ {
+ if ((i & (1 << bit)) && (info->c[bit] != 0))
+ m_total[i] += 1.0 / info->c[bit];
+ }
+ if (m_total[i] != 0)
+ m_total[i] = 1.0 / m_total[i];
+ break;
+ }
+ }
+ set_output(0, m_total[0]);
+}
+
+/************************************************************************
+ *
+ * DST_CLAMP - Simple signal clamping circuit
+ *
+ * input[0] - Input value
+ * input[1] - Minimum value
+ * input[2] - Maximum value
+ *
+ ************************************************************************/
+#define DST_CLAMP__IN DISCRETE_INPUT(0)
+#define DST_CLAMP__MIN DISCRETE_INPUT(1)
+#define DST_CLAMP__MAX DISCRETE_INPUT(2)
+
+DISCRETE_STEP(dst_clamp)
+{
+ if (DST_CLAMP__IN < DST_CLAMP__MIN)
+ set_output(0, DST_CLAMP__MIN);
+ else if (DST_CLAMP__IN > DST_CLAMP__MAX)
+ set_output(0, DST_CLAMP__MAX);
+ else
+ set_output(0, DST_CLAMP__IN);
+}
+
+
+/************************************************************************
+ *
+ * DST_DAC_R1 - R1 Ladder DAC with cap smoothing
+ *
+ * input[0] - Binary Data Input
+ * input[1] - Data On Voltage (3.4 for TTL)
+ *
+ * also passed discrete_dac_r1_ladder structure
+ *
+ * Mar 2004, D Renaud.
+ * Nov 2010, D Renaud. - optimized for speed
+ ************************************************************************/
+#define DST_DAC_R1__DATA DISCRETE_INPUT(0)
+#define DST_DAC_R1__VON DISCRETE_INPUT(1)
+
+DISCRETE_STEP(dst_dac_r1)
+{
+ int data = (int)DST_DAC_R1__DATA;
+ double v = m_v_step[data];
+ double x_time = DST_DAC_R1__DATA - data;
+ double last_v = m_last_v;
+
+ m_last_v = v;
+
+ if (x_time > 0)
+ v = x_time * (v - last_v) + last_v;
+
+ /* Filter if needed, else just output voltage */
+ if (m_has_c_filter)
+ {
+ double v_diff = v - m_v_out;
+ /* optimization - if charged close enough to voltage */
+ if (fabs(v_diff) < 0.000001)
+ m_v_out = v;
+ else
+ {
+ m_v_out += v_diff * m_exponent;
+ }
+ }
+ else
+ m_v_out = v;
+
+ set_output(0, m_v_out);
+}
+
+DISCRETE_RESET(dst_dac_r1)
+{
+ DISCRETE_DECLARE_INFO(discrete_dac_r1_ladder)
+
+ int bit;
+ int ladderLength = info->ladderLength;
+ int total_steps = 1 << ladderLength;
+ double r_total = 0;
+ double i_bias;
+ double v_on = DST_DAC_R1__VON;
+
+ m_last_v = 0;
+
+ /* Calculate the Millman current of the bias circuit */
+ if (info->rBias > 0)
+ i_bias = info->vBias / info->rBias;
+ else
+ i_bias = 0;
+
+ /*
+ * We will do a small amount of error checking.
+ * But if you are an idiot and pass a bad ladder table
+ * then you deserve a crash.
+ */
+ if (ladderLength < 2 && info->rBias == 0 && info->rGnd == 0)
+ {
+ /* You need at least 2 resistors for a ladder */
+ m_device->discrete_log("dst_dac_r1_reset - Ladder length too small");
+ }
+ if (ladderLength > DISC_LADDER_MAXRES )
+ {
+ m_device->discrete_log("dst_dac_r1_reset - Ladder length exceeds DISC_LADDER_MAXRES");
+ }
+
+ /*
+ * Calculate the total of all resistors in parallel.
+ * This is the combined resistance of the voltage sources.
+ * This is used for the charging curve.
+ */
+ for(bit = 0; bit < ladderLength; bit++)
+ {
+ if (info->r[bit] > 0)
+ r_total += 1.0 / info->r[bit];
+ }
+ if (info->rBias > 0) r_total += 1.0 / info->rBias;
+ if (info->rGnd > 0) r_total += 1.0 / info->rGnd;
+ r_total = 1.0 / r_total;
+
+ m_v_out = 0;
+
+ if (info->cFilter > 0)
+ {
+ m_has_c_filter = 1;
+ /* Setup filter constant */
+ m_exponent = RC_CHARGE_EXP(r_total * info->cFilter);
+ }
+ else
+ m_has_c_filter = 0;
+
+ /* pre-calculate all possible values to speed up step routine */
+ for(int i = 0; i < total_steps; i++)
+ {
+ double i_total = i_bias;
+ for (bit = 0; bit < ladderLength; bit++)
+ {
+ /* Add up currents of ON circuits per Millman. */
+
+ /* ignore if no resistor present */
+ if (EXPECTED(info->r[bit] > 0))
+ {
+ double i_bit;
+ int bit_val = (i >> bit) & 0x01;
+
+ if (bit_val != 0)
+ i_bit = v_on / info->r[bit];
+ else
+ i_bit = 0;
+ i_total += i_bit;
+ }
+ }
+ m_v_step[i] = i_total * r_total;
+ }
+}
+
+
+/************************************************************************
+*
+ * DST_DIODE_MIX - Diode Mixer
+ *
+ * input[0] - Input 0
+ * .....
+ *
+ * Dec 2004, D Renaud.
+ ************************************************************************/
+#define DST_DIODE_MIX_INP_OFFSET 0
+#define DST_DIODE_MIX__INP(addr) DISCRETE_INPUT(DST_DIODE_MIX_INP_OFFSET + addr)
+
+DISCRETE_STEP(dst_diode_mix)
+{
+ double val, max = 0;
+ int addr;
+
+ for (addr = 0; addr < m_size; addr++)
+ {
+ val = DST_DIODE_MIX__INP(addr) - m_v_junction[addr];
+ if (val > max) max = val;
+ }
+ if (max < 0) max = 0;
+ set_output(0, max);
+}
+
+DISCRETE_RESET(dst_diode_mix)
+{
+ DISCRETE_DECLARE_INFO(double)
+
+ int addr;
+
+ m_size = this->active_inputs() - DST_DIODE_MIX_INP_OFFSET;
+ assert(m_size <= 8);
+
+ for (addr = 0; addr < m_size; addr++)
+ {
+ if (info == NULL)
+ {
+ /* setup default junction voltage */
+ m_v_junction[addr] = 0.5;
+ }
+ else
+ {
+ /* use supplied junction voltage */
+ m_v_junction[addr] = *info++;
+ }
+ }
+ this->step();
+}
+
+
+/************************************************************************
+ *
+ * DST_DIVIDE - Programmable divider with enable
+ *
+ * input[0] - Enable input value
+ * input[1] - Channel0 input value
+ * input[2] - Divisor
+ *
+ ************************************************************************/
+#define DST_DIVIDE__ENABLE DISCRETE_INPUT(0)
+#define DST_DIVIDE__IN DISCRETE_INPUT(1)
+#define DST_DIVIDE__DIV DISCRETE_INPUT(2)
+
+DISCRETE_STEP(dst_divide)
+{
+ if(DST_DIVIDE__ENABLE)
+ {
+ if(DST_DIVIDE__DIV == 0)
+ {
+ set_output(0, DBL_MAX); /* Max out but don't break */
+ m_device->discrete_log("dst_divider_step() - Divide by Zero attempted in NODE_%02d.\n",this->index());
+ }
+ else
+ {
+ set_output(0, DST_DIVIDE__IN / DST_DIVIDE__DIV);
+ }
+ }
+ else
+ {
+ set_output(0, 0);
+ }
+}
+
+
+/************************************************************************
+ *
+ * DST_GAIN - This is a programmable gain module with enable function
+ *
+ * input[0] - Channel0 input value
+ * input[1] - Gain value
+ * input[2] - Final addition offset
+ *
+ ************************************************************************/
+#define DST_GAIN__IN DISCRETE_INPUT(0)
+#define DST_GAIN__GAIN DISCRETE_INPUT(1)
+#define DST_GAIN__OFFSET DISCRETE_INPUT(2)
+
+DISCRETE_STEP(dst_gain)
+{
+ set_output(0, DST_GAIN__IN * DST_GAIN__GAIN + DST_GAIN__OFFSET);
+}
+
+
+/************************************************************************
+ *
+ * DST_INTEGRATE - Integration circuits
+ *
+ * input[0] - Trigger 0
+ * input[1] - Trigger 1
+ *
+ * also passed discrete_integrate_info structure
+ *
+ * Mar 2004, D Renaud.
+ ************************************************************************/
+#define DST_INTEGRATE__TRG0 DISCRETE_INPUT(0)
+#define DST_INTEGRATE__TRG1 DISCRETE_INPUT(1)
+
+static int dst_trigger_function(int trig0, int trig1, int trig2, int function)
+{
+ int result = 1;
+ switch (function)
+ {
+ case DISC_OP_AMP_TRIGGER_FUNCTION_TRG0:
+ result = trig0;
+ break;
+ case DISC_OP_AMP_TRIGGER_FUNCTION_TRG0_INV:
+ result = !trig0;
+ break;
+ case DISC_OP_AMP_TRIGGER_FUNCTION_TRG1:
+ result = trig1;
+ break;
+ case DISC_OP_AMP_TRIGGER_FUNCTION_TRG1_INV:
+ result = !trig1;
+ break;
+ case DISC_OP_AMP_TRIGGER_FUNCTION_TRG2:
+ result = trig2;
+ break;
+ case DISC_OP_AMP_TRIGGER_FUNCTION_TRG2_INV:
+ result = !trig2;
+ break;
+ case DISC_OP_AMP_TRIGGER_FUNCTION_TRG01_AND:
+ result = trig0 && trig1;
+ break;
+ case DISC_OP_AMP_TRIGGER_FUNCTION_TRG01_NAND:
+ result = !(trig0 && trig1);
+ break;
+ }
+
+ return (result);
+}
+
+DISCRETE_STEP(dst_integrate)
+{
+ DISCRETE_DECLARE_INFO(discrete_integrate_info)
+
+ int trig0, trig1;
+ double i_neg = 0; /* current into - input */
+ double i_pos = 0; /* current into + input */
+
+ switch (info->type)
+ {
+ case DISC_INTEGRATE_OP_AMP_1:
+ if (DST_INTEGRATE__TRG0 != 0)
+ {
+ /* This forces the cap to completely charge,
+ * and the output to go to it's max value.
+ */
+ m_v_out = m_v_max_out;
+ set_output(0, m_v_out);
+ return;
+ }
+ m_v_out -= m_change;
+ break;
+
+ case DISC_INTEGRATE_OP_AMP_1 | DISC_OP_AMP_IS_NORTON:
+ i_neg = m_v_max_in / info->r1;
+ i_pos = (DST_INTEGRATE__TRG0 - OP_AMP_NORTON_VBE) / info->r2;
+ if (i_pos < 0) i_pos = 0;
+ m_v_out += (i_pos - i_neg) / this->sample_rate() / info->c;
+ break;
+
+ case DISC_INTEGRATE_OP_AMP_2 | DISC_OP_AMP_IS_NORTON:
+ trig0 = (int)DST_INTEGRATE__TRG0;
+ trig1 = (int)DST_INTEGRATE__TRG1;
+ i_neg = dst_trigger_function(trig0, trig1, 0, info->f0) ? m_v_max_in_d / info->r1 : 0;
+ i_pos = dst_trigger_function(trig0, trig1, 0, info->f1) ? m_v_max_in / info->r2 : 0;
+ i_pos += dst_trigger_function(trig0, trig1, 0, info->f2) ? m_v_max_in_d / info->r3 : 0;
+ m_v_out += (i_pos - i_neg) / this->sample_rate() / info->c;
+ break;
+ }
+
+ /* Clip the output. */
+ if (m_v_out < 0) m_v_out = 0;
+ if (m_v_out > m_v_max_out) m_v_out = m_v_max_out;
+
+ set_output(0, m_v_out);
+}
+
+DISCRETE_RESET(dst_integrate)
+{
+ DISCRETE_DECLARE_INFO(discrete_integrate_info)
+
+ double i, v;
+
+ if (info->type & DISC_OP_AMP_IS_NORTON)
+ {
+ m_v_max_out = info->vP - OP_AMP_NORTON_VBE;
+ m_v_max_in = info->v1 - OP_AMP_NORTON_VBE;
+ m_v_max_in_d = m_v_max_in - OP_AMP_NORTON_VBE;
+ }
+ else
+ {
+ m_v_max_out = info->vP - OP_AMP_VP_RAIL_OFFSET;
+
+ v = info->v1 * info->r3 / (info->r2 + info->r3); /* vRef */
+ v = info->v1 - v; /* actual charging voltage */
+ i = v / info->r1;
+ m_change = i / this->sample_rate() / info->c;
+ }
+ m_v_out = 0;
+ set_output(0, m_v_out);
+}
+
+
+/************************************************************************
+ *
+ * DST_LOGIC_INV - Logic invertor gate implementation
+ *
+ * input[0] - Enable
+ * input[1] - input[0] value
+ *
+ ************************************************************************/
+#define DST_LOGIC_INV__IN DISCRETE_INPUT(0)
+
+DISCRETE_STEP(dst_logic_inv)
+{
+ set_output(0, DST_LOGIC_INV__IN ? 0.0 : 1.0);
+}
+
+/************************************************************************
+ *
+ * DST_BITS_DECODE - Decode Bits from input node
+ *
+ ************************************************************************/
+#define DST_BITS_DECODE__IN DISCRETE_INPUT(0)
+#define DST_BITS_DECODE__FROM DISCRETE_INPUT(1)
+#define DST_BITS_DECODE__TO DISCRETE_INPUT(2)
+#define DST_BITS_DECODE__VOUT DISCRETE_INPUT(3)
+
+DISCRETE_STEP(dst_bits_decode)
+{
+ int new_val = DST_BITS_DECODE__IN;
+ int last_val = m_last_val;
+ int last_had_x_time = m_last_had_x_time;
+
+ if (last_val != new_val || last_had_x_time)
+ {
+ int i, new_bit, last_bit, last_bit_had_x_time, bit_changed;
+ double x_time = DST_BITS_DECODE__IN - new_val;
+ int from = m_from;
+ int count = m_count;
+ int decode_x_time = m_decode_x_time;
+ int has_x_time = x_time > 0 ? 1 : 0;
+ double out = 0;
+ double v_out = DST_BITS_DECODE__VOUT;
+
+ for (i = 0; i < count; i++ )
+ {
+ new_bit = (new_val >> (i + from)) & 1;
+ last_bit = (last_val >> (i + from)) & 1;
+ last_bit_had_x_time = (last_had_x_time >> (i + from)) & 1;
+ bit_changed = last_bit != new_bit ? 1 : 0;
+
+ if (!bit_changed && !last_bit_had_x_time)
+ continue;
+
+ if (decode_x_time)
+ {
+ out = new_bit;
+ if (bit_changed)
+ out += x_time;
+ }
+ else
+ {
+ out = v_out;
+ if (has_x_time && bit_changed)
+ {
+ if (new_bit)
+ out *= x_time;
+ else
+ out *= (1.0 - x_time);
+ }
+ else
+ out *= new_bit;
+ }
+ set_output(i, out);
+ if (has_x_time && bit_changed)
+ /* set */
+ m_last_had_x_time |= 1 << (i + from);
+ else
+ /* clear */
+ m_last_had_x_time &= ~(1 << (i + from));
+ }
+ m_last_val = new_val;
+ }
+}
+
+DISCRETE_RESET(dst_bits_decode)
+{
+ m_from = DST_BITS_DECODE__FROM;
+ m_count = DST_BITS_DECODE__TO - m_from + 1;
+ if (DST_BITS_DECODE__VOUT == 0)
+ m_decode_x_time = 1;
+ else
+ m_decode_x_time = 0;
+ m_last_had_x_time = 0;
+
+ this->step();
+}
+
+
+/************************************************************************
+ *
+ * DST_LOGIC_AND - Logic AND gate implementation
+ *
+ * input[0] - input[0] value
+ * input[1] - input[1] value
+ * input[2] - input[2] value
+ * input[3] - input[3] value
+ *
+ ************************************************************************/
+#define DST_LOGIC_AND__IN0 DISCRETE_INPUT(0)
+#define DST_LOGIC_AND__IN1 DISCRETE_INPUT(1)
+#define DST_LOGIC_AND__IN2 DISCRETE_INPUT(2)
+#define DST_LOGIC_AND__IN3 DISCRETE_INPUT(3)
+
+DISCRETE_STEP(dst_logic_and)
+{
+ set_output(0, (DST_LOGIC_AND__IN0 && DST_LOGIC_AND__IN1 && DST_LOGIC_AND__IN2 && DST_LOGIC_AND__IN3)? 1.0 : 0.0);
+}
+
+/************************************************************************
+ *
+ * DST_LOGIC_NAND - Logic NAND gate implementation
+ *
+ * input[0] - input[0] value
+ * input[1] - input[1] value
+ * input[2] - input[2] value
+ * input[3] - input[3] value
+ *
+ ************************************************************************/
+#define DST_LOGIC_NAND__IN0 DISCRETE_INPUT(0)
+#define DST_LOGIC_NAND__IN1 DISCRETE_INPUT(1)
+#define DST_LOGIC_NAND__IN2 DISCRETE_INPUT(2)
+#define DST_LOGIC_NAND__IN3 DISCRETE_INPUT(3)
+
+DISCRETE_STEP(dst_logic_nand)
+{
+ set_output(0, (DST_LOGIC_NAND__IN0 && DST_LOGIC_NAND__IN1 && DST_LOGIC_NAND__IN2 && DST_LOGIC_NAND__IN3)? 0.0 : 1.0);
+}
+
+/************************************************************************
+ *
+ * DST_LOGIC_OR - Logic OR gate implementation
+ *
+ * input[0] - input[0] value
+ * input[1] - input[1] value
+ * input[2] - input[2] value
+ * input[3] - input[3] value
+ *
+ ************************************************************************/
+#define DST_LOGIC_OR__IN0 DISCRETE_INPUT(0)
+#define DST_LOGIC_OR__IN1 DISCRETE_INPUT(1)
+#define DST_LOGIC_OR__IN2 DISCRETE_INPUT(2)
+#define DST_LOGIC_OR__IN3 DISCRETE_INPUT(3)
+
+DISCRETE_STEP(dst_logic_or)
+{
+ set_output(0, (DST_LOGIC_OR__IN0 || DST_LOGIC_OR__IN1 || DST_LOGIC_OR__IN2 || DST_LOGIC_OR__IN3) ? 1.0 : 0.0);
+}
+
+/************************************************************************
+ *
+ * DST_LOGIC_NOR - Logic NOR gate implementation
+ *
+ * input[0] - input[0] value
+ * input[1] - input[1] value
+ * input[2] - input[2] value
+ * input[3] - input[3] value
+ *
+ ************************************************************************/
+#define DST_LOGIC_NOR__IN0 DISCRETE_INPUT(0)
+#define DST_LOGIC_NOR__IN1 DISCRETE_INPUT(1)
+#define DST_LOGIC_NOR__IN2 DISCRETE_INPUT(2)
+#define DST_LOGIC_NOR__IN3 DISCRETE_INPUT(3)
+
+DISCRETE_STEP(dst_logic_nor)
+{
+ set_output(0, (DST_LOGIC_NOR__IN0 || DST_LOGIC_NOR__IN1 || DST_LOGIC_NOR__IN2 || DST_LOGIC_NOR__IN3) ? 0.0 : 1.0);
+}
+
+/************************************************************************
+ *
+ * DST_LOGIC_XOR - Logic XOR gate implementation
+ *
+ * input[0] - input[0] value
+ * input[1] - input[1] value
+ *
+ ************************************************************************/
+#define DST_LOGIC_XOR__IN0 DISCRETE_INPUT(0)
+#define DST_LOGIC_XOR__IN1 DISCRETE_INPUT(1)
+
+DISCRETE_STEP(dst_logic_xor)
+{
+ set_output(0, ((DST_LOGIC_XOR__IN0 && !DST_LOGIC_XOR__IN1) || (!DST_LOGIC_XOR__IN0 && DST_LOGIC_XOR__IN1)) ? 1.0 : 0.0);
+}
+
+/************************************************************************
+ *
+ * DST_LOGIC_NXOR - Logic NXOR gate implementation
+ *
+ * input[0] - input[0] value
+ * input[1] - input[1] value
+ *
+ ************************************************************************/
+#define DST_LOGIC_XNOR__IN0 DISCRETE_INPUT(0)
+#define DST_LOGIC_XNOR__IN1 DISCRETE_INPUT(1)
+
+DISCRETE_STEP(dst_logic_nxor)
+{
+ set_output(0, ((DST_LOGIC_XNOR__IN0 && !DST_LOGIC_XNOR__IN1) || (!DST_LOGIC_XNOR__IN0 && DST_LOGIC_XNOR__IN1)) ? 0.0 : 1.0);
+}
+
+
+/************************************************************************
+ *
+ * DST_LOGIC_DFF - Standard D-type flip-flop implementation
+ *
+ * input[0] - /Reset
+ * input[1] - /Set
+ * input[2] - clock
+ * input[3] - data
+ *
+ ************************************************************************/
+#define DST_LOGIC_DFF__RESET !DISCRETE_INPUT(0)
+#define DST_LOGIC_DFF__SET !DISCRETE_INPUT(1)
+#define DST_LOGIC_DFF__CLOCK DISCRETE_INPUT(2)
+#define DST_LOGIC_DFF__DATA DISCRETE_INPUT(3)
+
+DISCRETE_STEP(dst_logic_dff)
+{
+ int clk = (int)DST_LOGIC_DFF__CLOCK;
+
+ if (DST_LOGIC_DFF__RESET)
+ set_output(0, 0);
+ else if (DST_LOGIC_DFF__SET)
+ set_output(0, 1);
+ else if (!m_last_clk && clk) /* low to high */
+ set_output(0, DST_LOGIC_DFF__DATA);
+ m_last_clk = clk;
+}
+
+DISCRETE_RESET(dst_logic_dff)
+{
+ m_last_clk = 0;
+ set_output(0, 0);
+}
+
+
+/************************************************************************
+ *
+ * DST_LOGIC_JKFF - Standard JK-type flip-flop implementation
+ *
+ * input[0] - /Reset
+ * input[1] - /Set
+ * input[2] - clock
+ * input[3] - J
+ * input[4] - K
+ *
+ ************************************************************************/
+#define DST_LOGIC_JKFF__RESET !DISCRETE_INPUT(0)
+#define DST_LOGIC_JKFF__SET !DISCRETE_INPUT(1)
+#define DST_LOGIC_JKFF__CLOCK DISCRETE_INPUT(2)
+#define DST_LOGIC_JKFF__J DISCRETE_INPUT(3)
+#define DST_LOGIC_JKFF__K DISCRETE_INPUT(4)
+
+DISCRETE_STEP(dst_logic_jkff)
+{
+ int clk = (int)DST_LOGIC_JKFF__CLOCK;
+ int j = (int)DST_LOGIC_JKFF__J;
+ int k = (int)DST_LOGIC_JKFF__K;
+
+ if (DST_LOGIC_JKFF__RESET)
+ m_v_out = 0;
+ else if (DST_LOGIC_JKFF__SET)
+ m_v_out = 1;
+ else if (m_last_clk && !clk) /* high to low */
+ {
+ if (!j)
+ {
+ /* J=0, K=0 - Hold */
+ if (k)
+ /* J=0, K=1 - Reset */
+ m_v_out = 0;
+ }
+ else
+ {
+ if (!k)
+ /* J=1, K=0 - Set */
+ m_v_out = 1;
+ else
+ /* J=1, K=1 - Toggle */
+ m_v_out = !(int)m_v_out;
+ }
+ }
+ m_last_clk = clk;
+ set_output(0, m_v_out);
+}
+
+DISCRETE_RESET(dst_logic_jkff)
+{
+ m_last_clk = 0;
+ m_v_out = 0;
+ set_output(0, m_v_out);
+}
+
+/************************************************************************
+ *
+ * DST_LOGIC_SHIFT - Shift Register implementation
+ *
+ ************************************************************************/
+#define DST_LOGIC_SHIFT__IN DISCRETE_INPUT(0)
+#define DST_LOGIC_SHIFT__RESET DISCRETE_INPUT(1)
+#define DST_LOGIC_SHIFT__CLK DISCRETE_INPUT(2)
+#define DST_LOGIC_SHIFT__SIZE DISCRETE_INPUT(3)
+#define DST_LOGIC_SHIFT__OPTIONS DISCRETE_INPUT(4)
+
+DISCRETE_STEP(dst_logic_shift)
+{
+ double cycles;
+ double ds_clock;
+ int clock = 0, inc = 0;
+
+ int input_bit = (DST_LOGIC_SHIFT__IN != 0) ? 1 : 0;
+ ds_clock = DST_LOGIC_SHIFT__CLK;
+ if (m_clock_type == DISC_CLK_IS_FREQ)
+ {
+ /* We need to keep clocking the internal clock even if in reset. */
+ cycles = (m_t_left + this->sample_time()) * ds_clock;
+ inc = (int)cycles;
+ m_t_left = (cycles - inc) / ds_clock;
+ }
+ else
+ {
+ clock = (int)ds_clock;
+ }
+
+ /* If reset enabled then set output to the reset value. No x_time in reset. */
+ if(((DST_LOGIC_SHIFT__RESET == 0) ? 0 : 1) == m_reset_on_high)
+ {
+ m_shift_data = 0;
+ set_output(0, 0);
+ return;
+ }
+
+ /* increment clock */
+ switch (m_clock_type)
+ {
+ case DISC_CLK_ON_F_EDGE:
+ case DISC_CLK_ON_R_EDGE:
+ /* See if the clock has toggled to the proper edge */
+ clock = (clock != 0);
+ if (m_last != clock)
+ {
+ m_last = clock;
+ if (m_clock_type == clock)
+ {
+ /* Toggled */
+ inc = 1;
+ }
+ }
+ break;
+
+ case DISC_CLK_BY_COUNT:
+ /* Clock number of times specified. */
+ inc = clock;
+ break;
+ }
+
+ if (inc > 0)
+ {
+ if (m_shift_r)
+ {
+ m_shift_data >>= 1;
+ m_shift_data |= input_bit << ((int)DST_LOGIC_SHIFT__SIZE - 1);
+ inc--;
+ m_shift_data >>= inc;
+ }
+ else
+ {
+ m_shift_data <<= 1;
+ m_shift_data |= input_bit;
+ inc--;
+ m_shift_data <<= inc;
+ }
+ m_shift_data &= m_bit_mask;
+ }
+
+ set_output(0, m_shift_data);
+}
+
+DISCRETE_RESET(dst_logic_shift)
+{
+ m_bit_mask = (1 << (int)DST_LOGIC_SHIFT__SIZE) - 1;
+ m_clock_type = (int)DST_LOGIC_SHIFT__OPTIONS & DISC_CLK_MASK;
+ m_reset_on_high = ((int)DST_LOGIC_SHIFT__OPTIONS & DISC_LOGIC_SHIFT__RESET_H) ? 1 : 0;
+ m_shift_r = ((int)DST_LOGIC_SHIFT__OPTIONS & DISC_LOGIC_SHIFT__RIGHT) ? 1 : 0;
+
+ m_t_left = 0;
+ m_last = 0;
+ m_shift_data = 0;
+ set_output(0, 0);
+}
+
+/************************************************************************
+ *
+ * DST_LOOKUP_TABLE - Return value from lookup table
+ *
+ * input[0] - Input 1
+ * input[1] - Table size
+ *
+ * Also passed address of the lookup table
+ *
+ * Feb 2007, D Renaud.
+ ************************************************************************/
+#define DST_LOOKUP_TABLE__IN DISCRETE_INPUT(0)
+#define DST_LOOKUP_TABLE__SIZE DISCRETE_INPUT(1)
+
+DISCRETE_STEP(dst_lookup_table)
+{
+ DISCRETE_DECLARE_INFO(double)
+
+ int addr = DST_LOOKUP_TABLE__IN;
+
+ if (addr < 0 || addr >= DST_LOOKUP_TABLE__SIZE)
+ set_output(0, 0);
+ else
+ set_output(0, info[addr]);
+}
+
+
+/************************************************************************
+ *
+ * DST_MIXER - Mixer/Gain stage
+ *
+ * input[0] - Enable input value
+ * input[1] - Input 1
+ * input[2] - Input 2
+ * input[3] - Input 3
+ * input[4] - Input 4
+ * input[5] - Input 5
+ * input[6] - Input 6
+ * input[7] - Input 7
+ * input[8] - Input 8
+ *
+ * Also passed discrete_mixer_info structure
+ *
+ * Mar 2004, D Renaud.
+ ************************************************************************/
+/*
+ * The input resistors can be a combination of static values and nodes.
+ * If a node is used then its value is in series with the static value.
+ * Also if a node is used and its value is 0, then that means the
+ * input is disconnected from the circuit.
+ *
+ * There are 3 basic types of mixers, defined by the 2 types. The
+ * op amp mixer is further defined by the prescence of rI. This is a
+ * brief explanation.
+ *
+ * DISC_MIXER_IS_RESISTOR
+ * The inputs are high pass filtered if needed, using (rX || rF) * cX.
+ * Then Millman is used for the voltages.
+ * r = (1/rF + 1/r1 + 1/r2...)
+ * i = (v1/r1 + v2/r2...)
+ * v = i * r
+ *
+ * DISC_MIXER_IS_OP_AMP - no rI
+ * This is just a summing circuit.
+ * The inputs are high pass filtered if needed, using rX * cX.
+ * Then a modified Millman is used for the voltages.
+ * i = ((vRef - v1)/r1 + (vRef - v2)/r2...)
+ * v = i * rF
+ *
+ * DISC_MIXER_IS_OP_AMP_WITH_RI
+ * The inputs are high pass filtered if needed, using (rX + rI) * cX.
+ * Then Millman is used for the voltages including vRef/rI.
+ * r = (1/rI + 1/r1 + 1/r2...)
+ * i = (vRef/rI + v1/r1 + v2/r2...)
+ * The voltage is then modified by an inverting amp formula.
+ * v = vRef + (rF/rI) * (vRef - (i * r))
+ */
+#define DST_MIXER__ENABLE DISCRETE_INPUT(0)
+#define DST_MIXER__IN(bit) DISCRETE_INPUT(bit + 1)
+
+DISCRETE_STEP(dst_mixer)
+{
+ DISCRETE_DECLARE_INFO(discrete_mixer_desc)
+
+ double v, vTemp, r_total, rTemp, rTemp2 = 0;
+ double i = 0; /* total current of inputs */
+ int bit, connected;
+
+ /* put commonly used stuff in local variables for speed */
+ int r_node_bit_flag = m_r_node_bit_flag;
+ int c_bit_flag = m_c_bit_flag;
+ int bit_mask = 1;
+ int has_rF = (info->rF != 0);
+ int type = m_type;
+ double v_ref = info->vRef;
+ double rI = info->rI;
+
+ if (EXPECTED(DST_MIXER__ENABLE))
+ {
+ r_total = m_r_total;
+
+ if (UNEXPECTED(m_r_node_bit_flag != 0))
+ {
+ /* loop and do any high pass filtering for connected caps */
+ /* but first see if there is an r_node for the current path */
+ /* if so, then the exponents need to be re-calculated */
+ for (bit = 0; bit < m_size; bit++)
+ {
+ rTemp = info->r[bit];
+ connected = 1;
+ vTemp = DST_MIXER__IN(bit);
+
+ /* is there a resistor? */
+ if (r_node_bit_flag & bit_mask)
+ {
+ /* a node has the possibility of being disconnected from the circuit. */
+ if (*m_r_node[bit] == 0)
+ connected = 0;
+ else
+ {
+ /* value currently holds resistance */
+ rTemp += *m_r_node[bit];
+ r_total += 1.0 / rTemp;
+ /* is there a capacitor? */
+ if (c_bit_flag & bit_mask)
+ {
+ switch (type)
+ {
+ case DISC_MIXER_IS_RESISTOR:
+ /* is there an rF? */
+ if (has_rF)
+ {
+ rTemp2 = RES_2_PARALLEL(rTemp, info->rF);
+ break;
+ }
+ /* else, fall through and just use the resistor value */
+ case DISC_MIXER_IS_OP_AMP:
+ rTemp2 = rTemp;
+ break;
+ case DISC_MIXER_IS_OP_AMP_WITH_RI:
+ rTemp2 = rTemp + rI;
+ break;
+ }
+ /* Re-calculate exponent if resistor is a node and has changed value */
+ if (*m_r_node[bit] != m_r_last[bit])
+ {
+ m_exponent_rc[bit] = RC_CHARGE_EXP(rTemp2 * info->c[bit]);
+ m_r_last[bit] = *m_r_node[bit];
+ }
+ }
+ }
+ }
+
+ if (connected)
+ {
+ /* is there a capacitor? */
+ if (c_bit_flag & bit_mask)
+ {
+ /* do input high pass filtering if needed. */
+ m_v_cap[bit] += (vTemp - v_ref - m_v_cap[bit]) * m_exponent_rc[bit];
+ vTemp -= m_v_cap[bit];
+ }
+ i += ((type == DISC_MIXER_IS_OP_AMP) ? v_ref - vTemp : vTemp) / rTemp;
+ }
+ bit_mask = bit_mask << 1;
+ }
+ }
+ else if (UNEXPECTED(c_bit_flag != 0))
+ {
+ /* no r_nodes, so just do high pass filtering */
+ for (bit = 0; bit < m_size; bit++)
+ {
+ vTemp = DST_MIXER__IN(bit);
+
+ if (c_bit_flag & (1 << bit))
+ {
+ /* do input high pass filtering if needed. */
+ m_v_cap[bit] += (vTemp - v_ref - m_v_cap[bit]) * m_exponent_rc[bit];
+ vTemp -= m_v_cap[bit];
+ }
+ i += ((type == DISC_MIXER_IS_OP_AMP) ? v_ref - vTemp : vTemp) / info->r[bit];
+ }
+ }
+ else
+ {
+ /* no r_nodes or c_nodes, mixing only */
+ if (UNEXPECTED(type == DISC_MIXER_IS_OP_AMP))
+ {
+ for (bit = 0; bit < m_size; bit++)
+ i += ( v_ref - DST_MIXER__IN(bit) ) / info->r[bit];
+ }
+ else
+ {
+ for (bit = 0; bit < m_size; bit++)
+ i += DST_MIXER__IN(bit) / info->r[bit];
+ }
+ }
+
+ if (UNEXPECTED(type == DISC_MIXER_IS_OP_AMP_WITH_RI))
+ i += v_ref / rI;
+
+ r_total = 1.0 / r_total;
+
+ /* If resistor network or has rI then Millman is used.
+ * If op-amp then summing formula is used. */
+ v = i * ((type == DISC_MIXER_IS_OP_AMP) ? info->rF : r_total);
+
+ if (UNEXPECTED(type == DISC_MIXER_IS_OP_AMP_WITH_RI))
+ v = v_ref + (m_gain * (v_ref - v));
+
+ /* Do the low pass filtering for cF */
+ if (EXPECTED(info->cF != 0))
+ {
+ if (UNEXPECTED(r_node_bit_flag != 0))
+ {
+ /* Re-calculate exponent if resistor nodes are used */
+ m_exponent_c_f = RC_CHARGE_EXP(r_total * info->cF);
+ }
+ m_v_cap_f += (v - v_ref - m_v_cap_f) * m_exponent_c_f;
+ v = m_v_cap_f;
+ }
+
+ /* Do the high pass filtering for cAmp */
+ if (EXPECTED(info->cAmp != 0))
+ {
+ m_v_cap_amp += (v - m_v_cap_amp) * m_exponent_c_amp;
+ v -= m_v_cap_amp;
+ }
+ set_output(0, v * info->gain);
+ }
+ else
+ {
+ set_output(0, 0);
+ }
+}
+
+
+DISCRETE_RESET(dst_mixer)
+{
+ DISCRETE_DECLARE_INFO(discrete_mixer_desc)
+
+ int bit;
+ double rTemp = 0;
+
+ /* link to r_node outputs */
+ m_r_node_bit_flag = 0;
+ for (bit = 0; bit < 8; bit++)
+ {
+ m_r_node[bit] = m_device->node_output_ptr(info->r_node[bit]);
+ if (m_r_node[bit] != NULL)
+ {
+ m_r_node_bit_flag |= 1 << bit;
+ }
+
+ /* flag any caps */
+ if (info->c[bit] != 0)
+ m_c_bit_flag |= 1 << bit;
+ }
+
+ m_size = this->active_inputs() - 1;
+
+ /*
+ * THERE IS NO ERROR CHECKING!!!!!!!!!
+ * If you pass a bad ladder table
+ * then you deserve a crash.
+ */
+
+ m_type = info->type;
+ if ((info->type == DISC_MIXER_IS_OP_AMP) && (info->rI != 0))
+ m_type = DISC_MIXER_IS_OP_AMP_WITH_RI;
+
+ /*
+ * Calculate the total of all resistors in parallel.
+ * This is the combined resistance of the voltage sources.
+ * Also calculate the exponents while we are here.
+ */
+ m_r_total = 0;
+ for(bit = 0; bit < m_size; bit++)
+ {
+ if ((info->r[bit] != 0) && !info->r_node[bit] )
+ {
+ m_r_total += 1.0 / info->r[bit];
+ }
+
+ m_v_cap[bit] = 0;
+ m_exponent_rc[bit] = 0;
+ if ((info->c[bit] != 0) && !info->r_node[bit])
+ {
+ switch (m_type)
+ {
+ case DISC_MIXER_IS_RESISTOR:
+ /* is there an rF? */
+ if (info->rF != 0)
+ {
+ rTemp = 1.0 / ((1.0 / info->r[bit]) + (1.0 / info->rF));
+ break;
+ }
+ /* else, fall through and just use the resistor value */
+ case DISC_MIXER_IS_OP_AMP:
+ rTemp = info->r[bit];
+ break;
+ case DISC_MIXER_IS_OP_AMP_WITH_RI:
+ rTemp = info->r[bit] + info->rI;
+ break;
+ }
+ /* Setup filter constants */
+ m_exponent_rc[bit] = RC_CHARGE_EXP(rTemp * info->c[bit]);
+ }
+ }
+
+ if (info->rF != 0)
+ {
+ if (m_type == DISC_MIXER_IS_RESISTOR) m_r_total += 1.0 / info->rF;
+ }
+ if (m_type == DISC_MIXER_IS_OP_AMP_WITH_RI) m_r_total += 1.0 / info->rI;
+
+ m_v_cap_f = 0;
+ m_exponent_c_f = 0;
+ if (info->cF != 0)
+ {
+ /* Setup filter constants */
+ m_exponent_c_f = RC_CHARGE_EXP(((info->type == DISC_MIXER_IS_OP_AMP) ? info->rF : (1.0 / m_r_total)) * info->cF);
+ }
+
+ m_v_cap_amp = 0;
+ m_exponent_c_amp = 0;
+ if (info->cAmp != 0)
+ {
+ /* Setup filter constants */
+ /* We will use 100k ohms as an average final stage impedance. */
+ /* Your amp/speaker system will have more effect on incorrect filtering then any value used here. */
+ m_exponent_c_amp = RC_CHARGE_EXP(RES_K(100) * info->cAmp);
+ }
+
+ if (m_type == DISC_MIXER_IS_OP_AMP_WITH_RI) m_gain = info->rF / info->rI;
+
+ set_output(0, 0);
+}
+
+
+/************************************************************************
+ *
+ * DST_MULTIPLEX - 1 of x multiplexer/switch
+ *
+ * input[0] - switch position
+ * input[1] - input[0]
+ * input[2] - input[1]
+ * .....
+ *
+ * Dec 2004, D Renaud.
+ ************************************************************************/
+#define DST_MULTIPLEX__ADDR DISCRETE_INPUT(0)
+#define DST_MULTIPLEX__INP(addr) DISCRETE_INPUT(1 + addr)
+
+DISCRETE_STEP(dst_multiplex)
+{
+ int addr;
+
+ addr = DST_MULTIPLEX__ADDR; /* FP to INT */
+ if ((addr >= 0) && (addr < m_size))
+ {
+ set_output(0, DST_MULTIPLEX__INP(addr));
+ }
+ else
+ {
+ /* Bad address. We will leave the output alone. */
+ m_device->discrete_log("NODE_%02d - Address = %d. Out of bounds\n", this->index(), addr);
+ }
+}
+
+DISCRETE_RESET(dst_multiplex)
+{
+ m_size = this->active_inputs() - 1;
+
+ this->step();
+}
+
+
+/************************************************************************
+ *
+ * DST_ONESHOT - Usage of node_description values for one shot pulse
+ *
+ * input[0] - Reset value
+ * input[1] - Trigger value
+ * input[2] - Amplitude value
+ * input[3] - Width of oneshot pulse
+ * input[4] - type R/F edge, Retriggerable?
+ *
+ * Complete re-write Jan 2004, D Renaud.
+ ************************************************************************/
+#define DST_ONESHOT__RESET DISCRETE_INPUT(0)
+#define DST_ONESHOT__TRIG DISCRETE_INPUT(1)
+#define DST_ONESHOT__AMP DISCRETE_INPUT(2)
+#define DST_ONESHOT__WIDTH DISCRETE_INPUT(3)
+#define DST_ONESHOT__TYPE (int)DISCRETE_INPUT(4)
+
+DISCRETE_STEP(dst_oneshot)
+{
+ int trigger = (DST_ONESHOT__TRIG != 0);
+
+ /* If the state is triggered we will need to countdown later */
+ int do_count = m_state;
+
+ if (UNEXPECTED(DST_ONESHOT__RESET))
+ {
+ /* Hold in Reset */
+ set_output(0, 0);
+ m_state = 0;
+ }
+ else
+ {
+ /* are we at an edge? */
+ if (UNEXPECTED(trigger != m_last_trig))
+ {
+ /* There has been a trigger edge */
+ m_last_trig = trigger;
+
+ /* Is it the proper edge trigger */
+ if ((m_type & DISC_ONESHOT_REDGE) ? trigger : !trigger)
+ {
+ if (!m_state)
+ {
+ /* We have first trigger */
+ m_state = 1;
+ set_output(0, (m_type & DISC_OUT_ACTIVE_LOW) ? 0 : DST_ONESHOT__AMP);
+ m_countdown = DST_ONESHOT__WIDTH;
+ }
+ else
+ {
+ /* See if we retrigger */
+ if (m_type & DISC_ONESHOT_RETRIG)
+ {
+ /* Retrigger */
+ m_countdown = DST_ONESHOT__WIDTH;
+ do_count = 0;
+ }
+ }
+ }
+ }
+
+ if (UNEXPECTED(do_count))
+ {
+ m_countdown -= this->sample_time();
+ if(m_countdown <= 0.0)
+ {
+ set_output(0, (m_type & DISC_OUT_ACTIVE_LOW) ? DST_ONESHOT__AMP : 0);
+ m_countdown = 0;
+ m_state = 0;
+ }
+ }
+ }
+}
+
+
+DISCRETE_RESET(dst_oneshot)
+{
+ m_countdown = 0;
+ m_state = 0;
+
+ m_last_trig = 0;
+ m_type = DST_ONESHOT__TYPE;
+
+ set_output(0, (m_type & DISC_OUT_ACTIVE_LOW) ? DST_ONESHOT__AMP : 0);
+}
+
+
+/************************************************************************
+ *
+ * DST_RAMP - Ramp up/down model usage
+ *
+ * input[0] - Enable ramp
+ * input[1] - Ramp Reverse/Forward switch
+ * input[2] - Gradient, change/sec
+ * input[3] - Start value
+ * input[4] - End value
+ * input[5] - Clamp value when disabled
+ *
+ ************************************************************************/
+#define DST_RAMP__ENABLE DISCRETE_INPUT(0)
+#define DST_RAMP__DIR DISCRETE_INPUT(1)
+#define DST_RAMP__GRAD DISCRETE_INPUT(2)
+#define DST_RAMP__START DISCRETE_INPUT(3)
+#define DST_RAMP__END DISCRETE_INPUT(4)
+#define DST_RAMP__CLAMP DISCRETE_INPUT(5)
+
+DISCRETE_STEP(dst_ramp)
+{
+ if(DST_RAMP__ENABLE)
+ {
+ if (!m_last_en)
+ {
+ m_last_en = 1;
+ m_v_out = DST_RAMP__START;
+ }
+ if(m_dir ? DST_RAMP__DIR : !DST_RAMP__DIR) m_v_out += m_step;
+ else m_v_out -= m_step;
+ /* Clamp to min/max */
+ if(m_dir ? (m_v_out < DST_RAMP__START)
+ : (m_v_out > DST_RAMP__START)) m_v_out = DST_RAMP__START;
+ if(m_dir ? (m_v_out > DST_RAMP__END)
+ : (m_v_out < DST_RAMP__END)) m_v_out = DST_RAMP__END;
+ }
+ else
+ {
+ m_last_en = 0;
+ /* Disabled so clamp to output */
+ m_v_out = DST_RAMP__CLAMP;
+ }
+
+ set_output(0, m_v_out);
+}
+
+DISCRETE_RESET(dst_ramp)
+{
+ m_v_out = DST_RAMP__CLAMP;
+ m_step = DST_RAMP__GRAD / this->sample_rate();
+ m_dir = ((DST_RAMP__END - DST_RAMP__START) == fabs(DST_RAMP__END - DST_RAMP__START));
+ m_last_en = 0;
+}
+
+
+/************************************************************************
+ *
+ * DST_SAMPHOLD - Sample & Hold Implementation
+ *
+ * input[0] - input[0] value
+ * input[1] - clock node
+ * input[2] - clock type
+ *
+ ************************************************************************/
+#define DST_SAMPHOLD__IN0 DISCRETE_INPUT(0)
+#define DST_SAMPHOLD__CLOCK DISCRETE_INPUT(1)
+#define DST_SAMPHOLD__TYPE DISCRETE_INPUT(2)
+
+DISCRETE_STEP(dst_samphold)
+{
+ switch(m_clocktype)
+ {
+ case DISC_SAMPHOLD_REDGE:
+ /* Clock the whole time the input is rising */
+ if (DST_SAMPHOLD__CLOCK > m_last_input) set_output(0, DST_SAMPHOLD__IN0);
+ break;
+ case DISC_SAMPHOLD_FEDGE:
+ /* Clock the whole time the input is falling */
+ if(DST_SAMPHOLD__CLOCK < m_last_input) set_output(0, DST_SAMPHOLD__IN0);
+ break;
+ case DISC_SAMPHOLD_HLATCH:
+ /* Output follows input if clock != 0 */
+ if( DST_SAMPHOLD__CLOCK) set_output(0, DST_SAMPHOLD__IN0);
+ break;
+ case DISC_SAMPHOLD_LLATCH:
+ /* Output follows input if clock == 0 */
+ if (DST_SAMPHOLD__CLOCK == 0) set_output(0, DST_SAMPHOLD__IN0);
+ break;
+ default:
+ m_device->discrete_log("dst_samphold_step - Invalid clocktype passed");
+ break;
+ }
+ /* Save the last value */
+ m_last_input = DST_SAMPHOLD__CLOCK;
+}
+
+DISCRETE_RESET(dst_samphold)
+{
+ set_output(0, 0);
+ m_last_input = -1;
+ /* Only stored in here to speed up and save casting in the step function */
+ m_clocktype = (int)DST_SAMPHOLD__TYPE;
+ this->step();
+}
+
+
+/************************************************************************
+ *
+ * DST_SWITCH - Programmable 2 pole switch module with enable function
+ *
+ * input[0] - Enable input value
+ * input[1] - switch position
+ * input[2] - input[0]
+ * input[3] - input[1]
+ *
+ ************************************************************************/
+#define DST_SWITCH__ENABLE DISCRETE_INPUT(0)
+#define DST_SWITCH__SWITCH DISCRETE_INPUT(1)
+#define DST_SWITCH__IN0 DISCRETE_INPUT(2)
+#define DST_SWITCH__IN1 DISCRETE_INPUT(3)
+
+DISCRETE_STEP(dst_switch)
+{
+ if(DST_SWITCH__ENABLE)
+ {
+ set_output(0, DST_SWITCH__SWITCH ? DST_SWITCH__IN1 : DST_SWITCH__IN0);
+ }
+ else
+ {
+ set_output(0, 0);
+ }
+}
+
+/************************************************************************
+ *
+ * DST_ASWITCH - Analog switch
+ *
+ * input[1] - Control
+ * input[2] - Input
+ * input[3] - Threshold for enable
+ *
+ ************************************************************************/
+#define DST_ASWITCH__CTRL DISCRETE_INPUT(0)
+#define DST_ASWITCH__IN DISCRETE_INPUT(1)
+#define DST_ASWITCH__THRESHOLD DISCRETE_INPUT(2)
+
+
+DISCRETE_STEP(dst_aswitch)
+{
+ set_output(0, DST_ASWITCH__CTRL > DST_ASWITCH__THRESHOLD ? DST_ASWITCH__IN : 0);
+}
+
+/************************************************************************
+ *
+ * DST_TRANSFORM - Programmable math module
+ *
+ * input[0] - Channel0 input value
+ * input[1] - Channel1 input value
+ * input[2] - Channel2 input value
+ * input[3] - Channel3 input value
+ * input[4] - Channel4 input value
+ *
+ ************************************************************************/
+#define MAX_TRANS_STACK 16
+
+struct double_stack {
+public:
+ double_stack() : p(&stk[0]) { }
+ inline void push(double v)
+ {
+ //Store THEN increment
+ assert(p <= &stk[MAX_TRANS_STACK-1]);
+ *p++ = v;
+ }
+ inline double pop(void)
+ {
+ //decrement THEN read
+ assert(p > &stk[0]);
+ p--;
+ return *p;
+ }
+private:
+ double stk[MAX_TRANS_STACK];
+ double *p;
+};
+
+DISCRETE_STEP(dst_transform)
+{
+ double_stack stack;
+ double top;
+
+ enum token *fPTR = &precomp[0];
+
+ top = HUGE_VAL;
+
+ while(*fPTR != TOK_END)
+ {
+ switch (*fPTR++)
+ {
+ case TOK_MULT: top = stack.pop() * top; break;
+ case TOK_DIV: top = stack.pop() / top; break;
+ case TOK_ADD: top = stack.pop() + top; break;
+ case TOK_MINUS: top = stack.pop() - top; break;
+ case TOK_0: stack.push(top); top = I_IN0(); break;
+ case TOK_1: stack.push(top); top = I_IN1(); break;
+ case TOK_2: stack.push(top); top = I_IN2(); break;
+ case TOK_3: stack.push(top); top = I_IN3(); break;
+ case TOK_4: stack.push(top); top = I_IN4(); break;
+ case TOK_DUP: stack.push(top); break;
+ case TOK_ABS: top = fabs(top); break; /* absolute value */
+ case TOK_NEG: top = -top; break; /* * -1 */
+ case TOK_NOT: top = !top; break; /* Logical NOT of Last Value */
+ case TOK_EQUAL: top = (int)stack.pop() == (int)top; break; /* Logical = */
+ case TOK_GREATER: top = (stack.pop() > top); break; /* Logical > */
+ case TOK_LESS: top = (stack.pop() < top); break; /* Logical < */
+ case TOK_AND: top = (int)stack.pop() & (int)top; break; /* Bitwise AND */
+ case TOK_OR: top = (int)stack.pop() | (int)top; break; /* Bitwise OR */
+ case TOK_XOR: top = (int)stack.pop() ^ (int)top; break; /* Bitwise XOR */
+ case TOK_END: break; /* please compiler */
+ }
+ }
+ set_output(0, top);
+}
+
+DISCRETE_RESET(dst_transform)
+{
+ const char *fPTR = (const char *)this->custom_data();
+ enum token *p = &precomp[0];
+
+ while(*fPTR != 0)
+ {
+ switch (*fPTR++)
+ {
+ case '*': *p = TOK_MULT; break;
+ case '/': *p = TOK_DIV; break;
+ case '+': *p = TOK_ADD; break;
+ case '-': *p = TOK_MINUS; break;
+ case '0': *p = TOK_0; break;
+ case '1': *p = TOK_1; break;
+ case '2': *p = TOK_2; break;
+ case '3': *p = TOK_3; break;
+ case '4': *p = TOK_4; break;
+ case 'P': *p = TOK_DUP; break;
+ case 'a': *p = TOK_ABS; break; /* absolute value */
+ case 'i': *p = TOK_NEG; break; /* * -1 */
+ case '!': *p = TOK_NOT; break; /* Logical NOT of Last Value */
+ case '=': *p = TOK_EQUAL; break; /* Logical = */
+ case '>': *p = TOK_GREATER; break; /* Logical > */
+ case '<': *p = TOK_LESS; break; /* Logical < */
+ case '&': *p = TOK_AND; break; /* Bitwise AND */
+ case '|': *p = TOK_OR; break; /* Bitwise OR */
+ case '^': *p = TOK_XOR; break; /* Bitwise XOR */
+ default:
+ m_device->discrete_log("dst_transform_step - Invalid function type/variable passed: %s",(const char *)this->custom_data());
+ /* that is enough to fatalerror */
+ fatalerror("dst_transform_step - Invalid function type/variable passed: %s\n", (const char *)this->custom_data());
+ break;
+ }
+ p++;
+ }
+ *p = TOK_END;
+}
+
+/************************************************************************
+ *
+ * DST_OP_AMP - op amp circuits
+ *
+ * input[0] - Enable
+ * input[1] - Input 0
+ * input[2] - Input 1
+ *
+ * also passed discrete_op_amp_info structure
+ *
+ * Mar 2007, D Renaud.
+ ************************************************************************/
+#define DST_OP_AMP__ENABLE DISCRETE_INPUT(0)
+#define DST_OP_AMP__INP0 DISCRETE_INPUT(1)
+#define DST_OP_AMP__INP1 DISCRETE_INPUT(2)
+
+DISCRETE_STEP(dst_op_amp)
+{
+ DISCRETE_DECLARE_INFO(discrete_op_amp_info)
+
+ double i_pos = 0;
+ double i_neg = 0;
+ double i = 0;
+ double v_out;
+
+ if (DST_OP_AMP__ENABLE)
+ {
+ switch (info->type)
+ {
+ case DISC_OP_AMP_IS_NORTON:
+ /* work out neg pin current */
+ if (m_has_r1)
+ {
+ i_neg = (DST_OP_AMP__INP0 - OP_AMP_NORTON_VBE) / info->r1;
+ if (i_neg < 0) i_neg = 0;
+ }
+ i_neg += m_i_fixed;
+
+ /* work out neg pin current */
+ i_pos = (DST_OP_AMP__INP1 - OP_AMP_NORTON_VBE) / info->r2;
+ if (i_pos < 0) i_pos = 0;
+
+ /* work out current across r4 */
+ i = i_pos - i_neg;
+
+ if (m_has_cap)
+ {
+ if (m_has_r4)
+ {
+ /* voltage across r4 charging cap */
+ i *= info->r4;
+ /* exponential charge */
+ m_v_cap += (i - m_v_cap) * m_exponent;
+ }
+ else
+ /* linear charge */
+ m_v_cap += i / m_exponent;
+ v_out = m_v_cap;
+ }
+ else
+ if (m_has_r4)
+ v_out = i * info->r4;
+ else
+ /* output just swings to rail when there is no r4 */
+ if (i > 0)
+ v_out = m_v_max;
+ else
+ v_out = 0;
+
+ /* clamp output */
+ if (v_out > m_v_max) v_out = m_v_max;
+ else if (v_out < info->vN) v_out = info->vN;
+ m_v_cap = v_out;
+
+ set_output(0, v_out);
+ break;
+
+ default:
+ set_output(0, 0);
+ }
+ }
+ else
+ set_output(0, 0);
+}
+
+DISCRETE_RESET(dst_op_amp)
+{
+ DISCRETE_DECLARE_INFO(discrete_op_amp_info)
+
+ m_has_r1 = info->r1 > 0;
+ m_has_r4 = info->r4 > 0;
+
+ m_v_max = info->vP - OP_AMP_NORTON_VBE;
+
+ m_v_cap = 0;
+ if (info->c > 0)
+ {
+ m_has_cap = 1;
+ /* Setup filter constants */
+ if (m_has_r4)
+ {
+ /* exponential charge */
+ m_exponent = RC_CHARGE_EXP(info->r4 * info->c);
+ }
+ else
+ /* linear charge */
+ m_exponent = this->sample_rate() * info->c;
+ }
+
+ if (info->r3 > 0)
+ m_i_fixed = (info->vP - OP_AMP_NORTON_VBE) / info->r3;
+ else
+ m_i_fixed = 0;
+}
+
+
+/************************************************************************
+ *
+ * DST_OP_AMP_1SHT - op amp one shot circuits
+ *
+ * input[0] - Trigger
+ *
+ * also passed discrete_op_amp_1sht_info structure
+ *
+ * Mar 2007, D Renaud.
+ ************************************************************************/
+#define DST_OP_AMP_1SHT__TRIGGER DISCRETE_INPUT(0)
+
+DISCRETE_STEP(dst_op_amp_1sht)
+{
+ DISCRETE_DECLARE_INFO(discrete_op_amp_1sht_info)
+
+ double i_pos;
+ double i_neg;
+ double v;
+
+ /* update trigger circuit */
+ i_pos = (DST_OP_AMP_1SHT__TRIGGER - m_v_cap2) / info->r2;
+ i_pos += m_v_out / info->r5;
+ m_v_cap2 += (DST_OP_AMP_1SHT__TRIGGER - m_v_cap2) * m_exponent2;
+
+ /* calculate currents and output */
+ i_neg = (m_v_cap1 - OP_AMP_NORTON_VBE) / info->r3;
+ if (i_neg < 0) i_neg = 0;
+ i_neg += m_i_fixed;
+
+ if (i_pos > i_neg) m_v_out = m_v_max;
+ else m_v_out = info->vN;
+
+ /* update c1 */
+ /* rough value of voltage at anode of diode if discharging */
+ v = m_v_out + 0.6;
+ if (m_v_cap1 > m_v_out)
+ {
+ /* discharge */
+ if (m_v_cap1 > v)
+ /* immediate discharge through diode */
+ m_v_cap1 = v;
+ else
+ /* discharge through r4 */
+ m_v_cap1 += (m_v_out - m_v_cap1) * m_exponent1d;
+ }
+ else
+ /* charge */
+ m_v_cap1 += ((m_v_out - OP_AMP_NORTON_VBE) * m_r34ratio + OP_AMP_NORTON_VBE - m_v_cap1) * m_exponent1c;
+
+ set_output(0, m_v_out);
+}
+
+DISCRETE_RESET(dst_op_amp_1sht)
+{
+ DISCRETE_DECLARE_INFO(discrete_op_amp_1sht_info)
+
+ m_exponent1c = RC_CHARGE_EXP(RES_2_PARALLEL(info->r3, info->r4) * info->c1);
+ m_exponent1d = RC_CHARGE_EXP(info->r4 * info->c1);
+ m_exponent2 = RC_CHARGE_EXP(info->r2 * info->c2);
+ m_i_fixed = (info->vP - OP_AMP_NORTON_VBE) / info->r1;
+ m_v_cap1 = m_v_cap2 = 0;
+ m_v_max = info->vP - OP_AMP_NORTON_VBE;
+ m_r34ratio = info->r3 / (info->r3 + info->r4);
+}
+
+
+/************************************************************************
+ *
+ * DST_TVCA_OP_AMP - trigged op-amp VCA
+ *
+ * input[0] - Trigger 0
+ * input[1] - Trigger 1
+ * input[2] - Trigger 2
+ * input[3] - Input 0
+ * input[4] - Input 1
+ *
+ * also passed discrete_op_amp_tvca_info structure
+ *
+ * Mar 2004, D Renaud.
+ ************************************************************************/
+#define DST_TVCA_OP_AMP__TRG0 DISCRETE_INPUT(0)
+#define DST_TVCA_OP_AMP__TRG1 DISCRETE_INPUT(1)
+#define DST_TVCA_OP_AMP__TRG2 DISCRETE_INPUT(2)
+#define DST_TVCA_OP_AMP__INP0 DISCRETE_INPUT(3)
+#define DST_TVCA_OP_AMP__INP1 DISCRETE_INPUT(4)
+
+DISCRETE_STEP(dst_tvca_op_amp)
+{
+ DISCRETE_DECLARE_INFO(discrete_op_amp_tvca_info)
+
+ int trig0, trig1, trig2, f3;
+ double i2 = 0; /* current through r2 */
+ double i3 = 0; /* current through r3 */
+ double i_neg = 0; /* current into - input */
+ double i_pos = 0; /* current into + input */
+ double i_out = 0; /* current at output */
+
+ double v_out;
+
+ trig0 = (int)DST_TVCA_OP_AMP__TRG0;
+ trig1 = (int)DST_TVCA_OP_AMP__TRG1;
+ trig2 = (int)DST_TVCA_OP_AMP__TRG2;
+ f3 = dst_trigger_function(trig0, trig1, trig2, info->f3);
+
+ if ((info->r2 != 0) && dst_trigger_function(trig0, trig1, trig2, info->f0))
+ {
+ /* r2 is present, so we assume Input 0 is connected and valid. */
+ i2 = (DST_TVCA_OP_AMP__INP0 - OP_AMP_NORTON_VBE) / info->r2;
+ if ( i2 < 0) i2 = 0;
+ }
+
+ if ((info->r3 != 0) && dst_trigger_function(trig0, trig1, trig2, info->f1))
+ {
+ /* r2 is present, so we assume Input 1 is connected and valid. */
+ /* Function F1 is not grounding the circuit. */
+ i3 = (DST_TVCA_OP_AMP__INP1 - OP_AMP_NORTON_VBE) / info->r3;
+ if ( i3 < 0) i3 = 0;
+ }
+
+ /* Calculate current going in to - input. */
+ i_neg = m_i_fixed + i2 + i3;
+
+ /* Update the c1 cap voltage. */
+ if (dst_trigger_function(trig0, trig1, trig2, info->f2))
+ {
+ /* F2 is not grounding the circuit so we charge the cap. */
+ m_v_cap1 += (m_v_trig[f3] - m_v_cap1) * m_exponent_c[f3];
+ }
+ else
+ {
+ /* F2 is at ground. The diode blocks this so F2 and r5 are out of circuit.
+ * So now the discharge rate is dependent upon F3.
+ * If F3 is at ground then we discharge to 0V through r6.
+ * If F3 is out of circuit then we discharge to OP_AMP_NORTON_VBE through r6+r7. */
+ m_v_cap1 += ((f3 ? OP_AMP_NORTON_VBE : 0.0) - m_v_cap1) * m_exponent_d[f3];
+ }
+
+ /* Calculate c1 current going in to + input. */
+ i_pos = (m_v_cap1 - OP_AMP_NORTON_VBE) / m_r67;
+ if ((i_pos < 0) || !f3) i_pos = 0;
+
+ /* Update the c2 cap voltage and current. */
+ if (info->r9 != 0)
+ {
+ f3 = dst_trigger_function(trig0, trig1, trig2, info->f4);
+ m_v_cap2 += ((f3 ? m_v_trig2 : 0) - m_v_cap2) * m_exponent2[f3];
+ i_pos += m_v_cap2 / info->r9;
+ }
+
+ /* Update the c3 cap voltage and current. */
+ if (info->r11 != 0)
+ {
+ f3 = dst_trigger_function(trig0, trig1, trig2, info->f5);
+ m_v_cap3 += ((f3 ? m_v_trig3 : 0) - m_v_cap3) * m_exponent3[f3];
+ i_pos += m_v_cap3 / info->r11;
+ }
+
+ /* Calculate output current. */
+ i_out = i_pos - i_neg;
+ if (i_out < 0) i_out = 0;
+
+ /* Convert to voltage for final output. */
+ if (m_has_c4)
+ {
+ if (m_has_r4)
+ {
+ /* voltage across r4 charging cap */
+ i_out *= info->r4;
+ /* exponential charge */
+ m_v_cap4 += (i_out - m_v_cap4) * m_exponent4;
+ }
+ else
+ /* linear charge */
+ m_v_cap4 += i_out / m_exponent4;
+ if (m_v_cap4 < 0)
+ m_v_cap4 = 0;
+ v_out = m_v_cap4;
+ }
+ else
+ v_out = i_out * info->r4;
+
+
+
+ /* Clip the output if needed. */
+ if (v_out > m_v_out_max) v_out = m_v_out_max;
+
+ set_output(0, v_out);
+}
+
+DISCRETE_RESET(dst_tvca_op_amp)
+{
+ DISCRETE_DECLARE_INFO(discrete_op_amp_tvca_info)
+
+ m_r67 = info->r6 + info->r7;
+
+ m_v_out_max = info->vP - OP_AMP_NORTON_VBE;
+ /* This is probably overkill because R5 is usually much lower then r6 or r7,
+ * but it is better to play it safe. */
+ m_v_trig[0] = (info->v1 - 0.6) * RES_VOLTAGE_DIVIDER(info->r5, info->r6);
+ m_v_trig[1] = (info->v1 - 0.6 - OP_AMP_NORTON_VBE) * RES_VOLTAGE_DIVIDER(info->r5, m_r67) + OP_AMP_NORTON_VBE;
+ m_i_fixed = m_v_out_max / info->r1;
+
+ m_v_cap1 = 0;
+ /* Charge rate through r5 */
+ /* There can be a different charge rates depending on function F3. */
+ m_exponent_c[0] = RC_CHARGE_EXP(RES_2_PARALLEL(info->r5, info->r6) * info->c1);
+ m_exponent_c[1] = RC_CHARGE_EXP(RES_2_PARALLEL(info->r5, m_r67) * info->c1);
+ /* Discharge rate through r6 + r7 */
+ m_exponent_d[1] = RC_CHARGE_EXP(m_r67 * info->c1);
+ /* Discharge rate through r6 */
+ if (info->r6 != 0)
+ {
+ m_exponent_d[0] = RC_CHARGE_EXP(info->r6 * info->c1);
+ }
+ m_v_cap2 = 0;
+ m_v_trig2 = (info->v2 - 0.6 - OP_AMP_NORTON_VBE) * RES_VOLTAGE_DIVIDER(info->r8, info->r9);
+ m_exponent2[0] = RC_CHARGE_EXP(info->r9 * info->c2);
+ m_exponent2[1] = RC_CHARGE_EXP(RES_2_PARALLEL(info->r8, info->r9) * info->c2);
+ m_v_cap3 = 0;
+ m_v_trig3 = (info->v3 - 0.6 - OP_AMP_NORTON_VBE) * RES_VOLTAGE_DIVIDER(info->r10, info->r11);
+ m_exponent3[0] = RC_CHARGE_EXP(info->r11 * info->c3);
+ m_exponent3[1] = RC_CHARGE_EXP(RES_2_PARALLEL(info->r10, info->r11) * info->c3);
+ m_v_cap4 = 0;
+ if (info->r4 != 0) m_has_r4 = 1;
+ if (info->c4 != 0) m_has_c4 = 1;
+ if (m_has_r4 && m_has_c4)
+ m_exponent4 = RC_CHARGE_EXP(info->r4 * info->c4);
+
+ this->step();
+}
+
+
+/* the different logic and xtime states */
+enum
+{
+ XTIME__IN0_0__IN1_0__IN0_NOX__IN1_NOX = 0,
+ XTIME__IN0_0__IN1_0__IN0_NOX__IN1_X,
+ XTIME__IN0_0__IN1_0__IN0_X__IN1_NOX,
+ XTIME__IN0_0__IN1_0__IN0_X__IN1_X,
+ XTIME__IN0_0__IN1_1__IN0_NOX__IN1_NOX,
+ XTIME__IN0_0__IN1_1__IN0_NOX__IN1_X,
+ XTIME__IN0_0__IN1_1__IN0_X__IN1_NOX,
+ XTIME__IN0_0__IN1_1__IN0_X__IN1_X,
+ XTIME__IN0_1__IN1_0__IN0_NOX__IN1_NOX,
+ XTIME__IN0_1__IN1_0__IN0_NOX__IN1_X,
+ XTIME__IN0_1__IN1_0__IN0_X__IN1_NOX,
+ XTIME__IN0_1__IN1_0__IN0_X__IN1_X,
+ XTIME__IN0_1__IN1_1__IN0_NOX__IN1_NOX,
+ XTIME__IN0_1__IN1_1__IN0_NOX__IN1_X,
+ XTIME__IN0_1__IN1_1__IN0_X__IN1_NOX,
+ XTIME__IN0_1__IN1_1__IN0_X__IN1_X
+};
+
+
+/************************************************************************
+ *
+ * DST_XTIME_BUFFER - Buffer/Invertor gate implementation using X_TIME
+ *
+ * If OUT_LOW and OUT_HIGH are defined then the output will be energy.
+ * If they are both 0, then the output will be X_TIME logic.
+ *
+ ************************************************************************/
+#define DST_XTIME_BUFFER__IN DISCRETE_INPUT(0)
+#define DST_XTIME_BUFFER_OUT_LOW DISCRETE_INPUT(1)
+#define DST_XTIME_BUFFER_OUT_HIGH DISCRETE_INPUT(2)
+#define DST_XTIME_BUFFER_INVERT DISCRETE_INPUT(3)
+
+DISCRETE_STEP(dst_xtime_buffer)
+{
+ int in0 = (int)DST_XTIME_BUFFER__IN;
+ int out = in0;
+ int out_is_energy = 1;
+
+ double x_time = DST_XTIME_BUFFER__IN - in0;
+
+ double out_low = DST_XTIME_BUFFER_OUT_LOW;
+ double out_high = DST_XTIME_BUFFER_OUT_HIGH;
+
+ if (out_low ==0 && out_high == 0)
+ out_is_energy = 0;
+
+ if (DST_XTIME_BUFFER_INVERT != 0)
+ out ^= 1;
+
+ if (out_is_energy)
+ {
+ if (x_time > 0)
+ {
+ double diff = out_high - out_low;
+ diff = out ? diff * x_time : diff * (1.0 - x_time);
+ set_output(0, out_low + diff);
+ }
+ else
+ set_output(0, out ? out_high : out_low);
+ }
+ else
+ set_output(0, out + x_time);
+}
+
+
+/************************************************************************
+ *
+ * DST_XTIME_AND - AND/NAND gate implementation using X_TIME
+ *
+ * If OUT_LOW and OUT_HIGH are defined then the output will be energy.
+ * If they are both 0, then the output will be X_TIME logic.
+ *
+ ************************************************************************/
+#define DST_XTIME_AND__IN0 DISCRETE_INPUT(0)
+#define DST_XTIME_AND__IN1 DISCRETE_INPUT(1)
+#define DST_XTIME_AND_OUT_LOW DISCRETE_INPUT(2)
+#define DST_XTIME_AND_OUT_HIGH DISCRETE_INPUT(3)
+#define DST_XTIME_AND_INVERT DISCRETE_INPUT(4)
+
+DISCRETE_STEP(dst_xtime_and)
+{
+ int in0 = (int)DST_XTIME_AND__IN0;
+ int in1 = (int)DST_XTIME_AND__IN1;
+ int out = 0;
+ int out_is_energy = 1;
+
+ double x_time = 0;
+ double x_time0 = DST_XTIME_AND__IN0 - in0;
+ double x_time1 = DST_XTIME_AND__IN1 - in1;
+
+ int in0_has_xtime = x_time0 > 0 ? 1 : 0;
+ int in1_has_xtime = x_time1 > 0 ? 1 : 0;
+
+ double out_low = DST_XTIME_AND_OUT_LOW;
+ double out_high = DST_XTIME_AND_OUT_HIGH;
+
+ if (out_low ==0 && out_high == 0)
+ out_is_energy = 0;
+
+ switch ((in0 << 3) | (in1 << 2) | (in0_has_xtime < 1) | in1_has_xtime)
+ {
+ // these are all 0
+ //case XTIME__IN0_0__IN1_0__IN0_NOX__IN1_NOX:
+ //case XTIME__IN0_0__IN1_1__IN0_NOX__IN1_NOX:
+ //case XTIME__IN0_1__IN1_0__IN0_NOX__IN1_NOX:
+ //case XTIME__IN0_0__IN1_0__IN0_NOX__IN1_X:
+ //case XTIME__IN0_0__IN1_0__IN0_X__IN1_NOX:
+ //case XTIME__IN0_0__IN1_1__IN0_NOX__IN1_X:
+ //case XTIME__IN0_1__IN1_0__IN0_X__IN1_NOX:
+ // break;
+
+ case XTIME__IN0_1__IN1_1__IN0_NOX__IN1_NOX:
+ out = 1;
+ break;
+
+ case XTIME__IN0_0__IN1_1__IN0_X__IN1_NOX:
+ /*
+ * in0 1 ------
+ * 0 -------
+ * ...^....^...
+ *
+ * in1 1 -------------
+ * 0
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ x_time = x_time0;
+ break;
+
+ case XTIME__IN0_1__IN1_0__IN0_NOX__IN1_X:
+ /*
+ * in0 1 -------------
+ * 0
+ * ...^....^...
+ *
+ * in1 1 ------
+ * 0 -------
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ x_time = x_time1;
+ break;
+
+ case XTIME__IN0_0__IN1_0__IN0_X__IN1_X:
+ /*
+ * in0 1 ----- -------
+ * 0 -------- ------
+ * ...^....^... ...^....^...
+ *
+ * in1 1 ------- -----
+ * 0 ------ --------
+ * ...^....^... ...^....^...
+ *
+ * out 1 ----- -----
+ * 0 ------- -------
+ * ...^....^... ...^....^...
+ */
+ // use x_time of input that went to 0 first/longer
+ if (x_time0 >= x_time1)
+ x_time = x_time0;
+ else
+ x_time = x_time1;
+ break;
+
+ case XTIME__IN0_0__IN1_1__IN0_X__IN1_X:
+ /*
+ * in0 1 ------- -----
+ * 0 ----- -------
+ * ...^....^... ...^....^...
+ *
+ * in1 1 ------- -----
+ * 0 ----- -------
+ * ...^....^... ...^....^...
+ *
+ * out 1 --
+ * 0 ----- ----- ------------
+ * ...^....^... ...^....^...
+ */
+ // may have went high for a bit in this cycle
+ //if (x_time0 < x_time1)
+ // x_time = time1 - x_time0;
+ break;
+
+ case XTIME__IN0_1__IN1_0__IN0_X__IN1_X:
+ /*
+ * in0 1 ------- -----
+ * 0 ----- -------
+ * ...^....^... ...^....^...
+ *
+ * in1 1 ------- -----
+ * 0 ----- -------
+ * ...^....^... ...^....^...
+ *
+ * out 1 --
+ * 0 ----- ----- ------------
+ * ...^....^... ...^....^...
+ */
+ // may have went high for a bit in this cycle
+ //if (x_time0 > x_time1)
+ // x_time = x_time0 - x_time1;
+ break;
+
+ case XTIME__IN0_1__IN1_1__IN0_NOX__IN1_X:
+ /*
+ * in0 1 ------------
+ * 0
+ * ...^....^...
+ *
+ * in1 1 ------
+ * 0 ------
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ out = 1;
+ x_time = x_time1;
+ break;
+
+ case XTIME__IN0_1__IN1_1__IN0_X__IN1_NOX:
+ /*
+ * in1 0 ------
+ * 0 ------
+ * ...^....^...
+ *
+ * in1 1 ------------
+ * 0
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ out = 1;
+ x_time = x_time0;
+ break;
+
+ case XTIME__IN0_1__IN1_1__IN0_X__IN1_X:
+ /*
+ * in0 1 ------ --------
+ * 0 ------ ----
+ * ...^....^... ...^....^...
+ *
+ * in1 1 -------- ------
+ * 0 ---- ------
+ * ...^....^... ...^....^...
+ *
+ * out 1 ------ ------
+ * 0 ------ ------
+ * ...^....^... ...^....^...
+ */
+ out = 1;
+ if (x_time0 < x_time1)
+ x_time = x_time0;
+ else
+ x_time = x_time1;
+ break;
+ }
+
+ if (DST_XTIME_AND_INVERT != 0)
+ out ^= 1;
+
+ if (out_is_energy)
+ {
+ if (x_time > 0)
+ {
+ double diff = out_high - out_low;
+ diff = out ? diff * x_time : diff * (1.0 - x_time);
+ set_output(0, out_low + diff);
+ }
+ else
+ set_output(0, out ? out_high : out_low);
+ }
+ else
+ set_output(0, out + x_time);
+}
+
+
+/************************************************************************
+ *
+ * DST_XTIME_OR - OR/NOR gate implementation using X_TIME
+ *
+ * If OUT_LOW and OUT_HIGH are defined then the output will be energy.
+ * If they are both 0, then the output will be X_TIME logic.
+ *
+ ************************************************************************/
+#define DST_XTIME_OR__IN0 DISCRETE_INPUT(0)
+#define DST_XTIME_OR__IN1 DISCRETE_INPUT(1)
+#define DST_XTIME_OR_OUT_LOW DISCRETE_INPUT(2)
+#define DST_XTIME_OR_OUT_HIGH DISCRETE_INPUT(3)
+#define DST_XTIME_OR_INVERT DISCRETE_INPUT(4)
+
+DISCRETE_STEP(dst_xtime_or)
+{
+ int in0 = (int)DST_XTIME_OR__IN0;
+ int in1 = (int)DST_XTIME_OR__IN1;
+ int out = 1;
+ int out_is_energy = 1;
+
+ double x_time = 0;
+ double x_time0 = DST_XTIME_OR__IN0 - in0;
+ double x_time1 = DST_XTIME_OR__IN1 - in1;
+
+ int in0_has_xtime = x_time0 > 0 ? 1 : 0;
+ int in1_has_xtime = x_time1 > 0 ? 1 : 0;
+
+ double out_low = DST_XTIME_OR_OUT_LOW;
+ double out_high = DST_XTIME_OR_OUT_HIGH;
+
+ if (out_low ==0 && out_high == 0)
+ out_is_energy = 0;
+
+ switch ((in0 << 3) | (in1 << 2) | (in0_has_xtime < 1) | in1_has_xtime)
+ {
+ // these are all 1
+ //case XTIME__IN0_1__IN1_1__IN0_NOX__IN1_NOX:
+ //case XTIME__IN0_0__IN1_1__IN0_NOX__IN1_NOX:
+ //case XTIME__IN0_1__IN1_0__IN0_NOX__IN1_NOX:
+ //case XTIME__IN0_1__IN1_0__IN0_NOX__IN1_X:
+ //case XTIME__IN0_0__IN1_1__IN0_X__IN1_NOX:
+ //case XTIME__IN0_1__IN1_1__IN0_NOX__IN1_X:
+ //case XTIME__IN0_1__IN1_1__IN0_X__IN1_NOX:
+ // break;
+
+ case XTIME__IN0_0__IN1_0__IN0_NOX__IN1_NOX:
+ out = 0;
+ break;
+
+ case XTIME__IN0_0__IN1_0__IN0_NOX__IN1_X:
+ /*
+ * in0 1
+ * 0 -------------
+ * ...^....^...
+ *
+ * in1 1 ------
+ * 0 -------
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ out = 0;
+ x_time = x_time1;
+ break;
+
+ case XTIME__IN0_0__IN1_0__IN0_X__IN1_NOX:
+ /*
+ * in0 1 ------
+ * 0 -------
+ * ...^....^...
+ *
+ * in1 1
+ * 0 -------------
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ out = 0;
+ x_time = x_time0;
+ break;
+
+ case XTIME__IN0_0__IN1_0__IN0_X__IN1_X:
+ /*
+ * in0 1 ----- -------
+ * 0 -------- ------
+ * ...^....^... ...^....^...
+ *
+ * in1 1 ------- -----
+ * 0 ------ --------
+ * ...^....^... ...^....^...
+ *
+ * out 1 ------- -------
+ * 0 ----- -----
+ * ...^....^... ...^....^...
+ */
+ out = 0;
+ // use x_time of input that was 1 last/longer
+ // this means at 0 for less x_time
+ if (x_time0 > x_time1)
+ x_time = x_time1;
+ else
+ x_time = x_time0;
+ break;
+
+ case XTIME__IN0_0__IN1_1__IN0_NOX__IN1_X:
+ /*
+ * in0 1
+ * 0 ------------
+ * ...^....^...
+ *
+ * in1 1 ------
+ * 0 ------
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ x_time = x_time1;
+ break;
+
+ case XTIME__IN0_1__IN1_0__IN0_X__IN1_NOX:
+ /*
+ * in0 1 ------
+ * 0 ------
+ * ...^....^...
+ *
+ * in1 1
+ * 0 ------------
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ x_time = x_time0;
+ break;
+
+ case XTIME__IN0_0__IN1_1__IN0_X__IN1_X:
+ /*
+ * in0 1 ------- -----
+ * 0 ----- -------
+ * ...^....^... ...^....^...
+ *
+ * in1 1 ------- -----
+ * 0 ----- -------
+ * ...^....^... ...^....^...
+ *
+ * out 1 ------------ ----- -----
+ * 0 --
+ * ...^....^... ...^....^...
+ */
+ // if (x_time0 > x_time1)
+ /* Not sure if it is better to use 1
+ * or the total energy which would smear the switch points together.
+ * Let's try just using 1 */
+ //x_time = xtime_0 - xtime_1;
+ break;
+
+ case XTIME__IN0_1__IN1_0__IN0_X__IN1_X:
+ /*
+ * in0 1 ------- -----
+ * 0 ----- -------
+ * ...^....^... ...^....^...
+ *
+ * in1 1 ------- -----
+ * 0 ----- -------
+ * ...^....^... ...^....^...
+ *
+ * out 1 ------------ ----- -----
+ * 0 --
+ * ...^....^... ...^....^...
+ */
+ //if (x_time0 < x_time1)
+ /* Not sure if it is better to use 1
+ * or the total energy which would smear the switch points together.
+ * Let's try just using 1 */
+ //x_time = xtime_1 - xtime_0;
+ break;
+
+ case XTIME__IN0_1__IN1_1__IN0_X__IN1_X:
+ /*
+ * in0 1 ------ --------
+ * 0 ------ ----
+ * ...^....^... ...^....^...
+ *
+ * in1 1 -------- ------
+ * 0 ---- ------
+ * ...^....^... ...^....^...
+ *
+ * out 1 -------- --------
+ * 0 ---- ----
+ * ...^....^... ...^....^...
+ */
+ if (x_time0 > x_time1)
+ x_time = x_time0;
+ else
+ x_time = x_time1;
+ break;
+ }
+
+ if (DST_XTIME_OR_INVERT != 0)
+ out ^= 1;
+
+ if (out_is_energy)
+ {
+ if (x_time > 0)
+ {
+ double diff = out_high - out_low;
+ diff = out ? diff * x_time : diff * (1.0 - x_time);
+ set_output(0, out_low + diff);
+ }
+ else
+ set_output(0, out ? out_high : out_low);
+ }
+ else
+ set_output(0, out + x_time);
+}
+
+
+/************************************************************************
+ *
+ * DST_XTIME_XOR - XOR/XNOR gate implementation using X_TIME
+ *
+ * If OUT_LOW and OUT_HIGH are defined then the output will be energy.
+ * If they are both 0, then the output will be X_TIME logic.
+ *
+ ************************************************************************/
+#define DST_XTIME_XOR__IN0 DISCRETE_INPUT(0)
+#define DST_XTIME_XOR__IN1 DISCRETE_INPUT(1)
+#define DST_XTIME_XOR_OUT_LOW DISCRETE_INPUT(2)
+#define DST_XTIME_XOR_OUT_HIGH DISCRETE_INPUT(3)
+#define DST_XTIME_XOR_INVERT DISCRETE_INPUT(4)
+
+DISCRETE_STEP(dst_xtime_xor)
+{
+ int in0 = (int)DST_XTIME_XOR__IN0;
+ int in1 = (int)DST_XTIME_XOR__IN1;
+ int out = 1;
+ int out_is_energy = 1;
+
+ double x_time = 0;
+ double x_time0 = DST_XTIME_XOR__IN0 - in0;
+ double x_time1 = DST_XTIME_XOR__IN1 - in1;
+
+ int in0_has_xtime = x_time0 > 0 ? 1 : 0;
+ int in1_has_xtime = x_time1 > 0 ? 1 : 0;
+
+ double out_low = DST_XTIME_XOR_OUT_LOW;
+ double out_high = DST_XTIME_XOR_OUT_HIGH;
+
+ if (out_low ==0 && out_high == 0)
+ out_is_energy = 0;
+
+ switch ((in0 << 3) | (in1 << 2) | (in0_has_xtime < 1) | in1_has_xtime)
+ {
+ // these are all 1
+ //case XTIME__IN0_0__IN1_1__IN0_NOX__IN1_NOX:
+ //case XTIME__IN0_1__IN1_0__IN0_NOX__IN1_NOX:
+ // break;
+
+ case XTIME__IN0_1__IN1_1__IN0_NOX__IN1_NOX:
+ case XTIME__IN0_0__IN1_0__IN0_NOX__IN1_NOX:
+ out = 0;
+ break;
+
+ case XTIME__IN0_1__IN1_0__IN0_X__IN1_NOX:
+ /*
+ * in0 1 ------
+ * 0 ------
+ * ...^....^...
+ *
+ * in1 1
+ * 0 ------------
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ case XTIME__IN0_0__IN1_1__IN0_X__IN1_NOX:
+ /*
+ * in0 1 ------
+ * 0 -------
+ * ...^....^...
+ *
+ * in1 1 -------------
+ * 0
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ x_time = x_time0;
+ break;
+
+ case XTIME__IN0_0__IN1_1__IN0_NOX__IN1_X:
+ /*
+ * in0 1
+ * 0 ------------
+ * ...^....^...
+ *
+ * in1 1 ------
+ * 0 ------
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ case XTIME__IN0_1__IN1_0__IN0_NOX__IN1_X:
+ /*
+ * in0 1 -------------
+ * 0
+ * ...^....^...
+ *
+ * in1 1 ------
+ * 0 -------
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ x_time = x_time1;
+ break;
+
+ case XTIME__IN0_0__IN1_0__IN0_X__IN1_NOX:
+ /*
+ * in0 1 ------
+ * 0 ------
+ * ...^....^...
+ *
+ * in1 1
+ * 0 ------------
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ case XTIME__IN0_1__IN1_1__IN0_X__IN1_NOX:
+ /*
+ * in1 0 ------
+ * 0 ------
+ * ...^....^...
+ *
+ * in1 1 ------------
+ * 0
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ out = 0;
+ x_time = x_time0;
+ break;
+
+ case XTIME__IN0_0__IN1_0__IN0_NOX__IN1_X:
+ /*
+ * in0 1
+ * 0 ------------
+ * ...^....^...
+ *
+ * in1 1 ------
+ * 0 ------
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ case XTIME__IN0_1__IN1_1__IN0_NOX__IN1_X:
+ /*
+ * in0 1 ------------
+ * 0
+ * ...^....^...
+ *
+ * in1 1 ------
+ * 0 ------
+ * ...^....^...
+ *
+ * out 1 ------
+ * 0 ------
+ * ...^....^...
+ */
+ out = 0;
+ x_time = x_time1;
+ break;
+
+ case XTIME__IN0_0__IN1_0__IN0_X__IN1_X:
+ /*
+ * in0 1 ----- -------
+ * 0 ------- -----
+ * ...^....^... ...^....^...
+ *
+ * in1 1 ------- -----
+ * 0 ----- -------
+ * ...^....^... ...^....^...
+ *
+ * out 1 -- --
+ * 0 ----- ----- ----- -----
+ * ...^....^... ...^....^...
+ */
+ case XTIME__IN0_1__IN1_1__IN0_X__IN1_X:
+ /*
+ * in0 1 ------ --------
+ * 0 ------ ----
+ * ...^....^... ...^....^...
+ *
+ * in1 1 -------- ------
+ * 0 ---- ------
+ * ...^....^... ...^....^...
+ *
+ * out 1 -- --
+ * 0 ---- ------ ---- ------
+ * ...^....^... ...^....^...
+ */
+ out = 0;
+ /* Not sure if it is better to use 0
+ * or the total energy which would smear the switch points together.
+ * Let's try just using 0 */
+ // x_time = abs(x_time0 - x_time1);
+ break;
+
+ case XTIME__IN0_0__IN1_1__IN0_X__IN1_X:
+ /*
+ * in0 1 ------- -----
+ * 0 ----- -------
+ * ...^....^... ...^....^...
+ *
+ * in1 1 ------- -----
+ * 0 ----- -------
+ * ...^....^... ...^....^...
+ *
+ * out 1 ----- ----- ----- -----
+ * 0 -- --
+ * ...^....^... ...^....^...
+ */
+ case XTIME__IN0_1__IN1_0__IN0_X__IN1_X:
+ /*
+ * in0 1 ------- -----
+ * 0 ----- -------
+ * ...^....^... ...^....^...
+ *
+ * in1 1 ------- -----
+ * 0 ----- -------
+ * ...^....^... ...^....^...
+ *
+ * out 1 ----- ----- ----- -----
+ * 0 -- --
+ * ...^....^... ...^....^...
+ */
+ /* Not sure if it is better to use 1
+ * or the total energy which would smear the switch points together.
+ * Let's try just using 1 */
+ // x_time = 1.0 - abs(x_time0 - x_time1);
+ break;
+}
+
+ if (DST_XTIME_XOR_INVERT != 0)
+ out ^= 1;
+
+ if (out_is_energy)
+ {
+ if (x_time > 0)
+ {
+ double diff = out_high - out_low;
+ diff = out ? diff * x_time : diff * (1.0 - x_time);
+ set_output(0, out_low + diff);
+ }
+ else
+ set_output(0, out ? out_high : out_low);
+ }
+ else
+ set_output(0, out + x_time);
+}
diff --git a/src/devices/sound/disc_sys.inc b/src/devices/sound/disc_sys.inc
new file mode 100644
index 00000000000..4192e832d93
--- /dev/null
+++ b/src/devices/sound/disc_sys.inc
@@ -0,0 +1,123 @@
+// license:BSD-3-Clause
+// copyright-holders:K.Wilkins
+/************************************************************************
+ *
+ * MAME - Discrete sound system emulation library
+ *
+ * Written by K.Wilkins (mame@esplexo.co.uk)
+ *
+ * (c) K.Wilkins 2000
+ * (c) Derrick Renaud 2003-2004
+ *
+ ************************************************************************
+ *
+ * DSO_OUTPUT - Output node
+ * DSO_TASK - Task node
+ *
+ * Task and list routines
+ *
+ ************************************************************************/
+
+
+
+
+/*************************************
+ *
+ * Task node (main task execution)
+ *
+ *************************************/
+
+DISCRETE_START( dso_csvlog )
+{
+ int log_num, node_num;
+
+ log_num = m_device->same_module_index(*this);
+ m_sample_num = 0;
+
+ sprintf(m_name, "discrete_%s_%d.csv", m_device->tag(), log_num);
+ m_csv_file = fopen(m_name, "w");
+ /* Output some header info */
+ fprintf(m_csv_file, "\"MAME Discrete System Node Log\"\n");
+ fprintf(m_csv_file, "\"Log Version\", 1.0\n");
+ fprintf(m_csv_file, "\"Sample Rate\", %d\n", this->sample_rate());
+ fprintf(m_csv_file, "\n");
+ fprintf(m_csv_file, "\"Sample\"");
+ for (node_num = 0; node_num < this->active_inputs(); node_num++)
+ {
+ fprintf(m_csv_file, ", \"NODE_%2d\"", NODE_INDEX(this->input_node(node_num)));
+ }
+ fprintf(m_csv_file, "\n");
+}
+
+DISCRETE_STOP( dso_csvlog )
+{
+ /* close any csv files */
+ if (m_csv_file)
+ fclose(m_csv_file);
+}
+
+DISCRETE_STEP( dso_csvlog )
+{
+ int nodenum;
+
+ /* Dump any csv logs */
+ fprintf(m_csv_file, "%" I64FMT "d", ++m_sample_num);
+ for (nodenum = 0; nodenum < this->active_inputs(); nodenum++)
+ {
+ fprintf(m_csv_file, ", %f", *this->m_input[nodenum]);
+ }
+ fprintf(m_csv_file, "\n");
+}
+
+DISCRETE_RESET( dso_csvlog )
+{
+ this->step();
+}
+
+
+DISCRETE_START( dso_wavlog )
+{
+ int log_num;
+
+ log_num = m_device->same_module_index(*this);
+ sprintf(m_name, "discrete_%s_%d.wav", m_device->tag(), log_num);
+ m_wavfile = wav_open(m_name, sample_rate(), active_inputs()/2);
+}
+
+DISCRETE_STOP( dso_wavlog )
+{
+ /* close any wave files */
+ if (m_wavfile)
+ wav_close(m_wavfile);
+}
+
+DISCRETE_STEP( dso_wavlog )
+{
+ double val;
+ INT16 wave_data_l, wave_data_r;
+
+ /* Dump any wave logs */
+ /* get nodes to be logged and apply gain, then clip to 16 bit */
+ val = DISCRETE_INPUT(0) * DISCRETE_INPUT(1);
+ val = (val < -32768) ? -32768 : (val > 32767) ? 32767 : val;
+ wave_data_l = (INT16)val;
+ if (this->active_inputs() == 2)
+ {
+ /* DISCRETE_WAVLOG1 */
+ wav_add_data_16(m_wavfile, &wave_data_l, 1);
+ }
+ else
+ {
+ /* DISCRETE_WAVLOG2 */
+ val = DISCRETE_INPUT(2) * DISCRETE_INPUT(3);
+ val = (val < -32768) ? -32768 : (val > 32767) ? 32767 : val;
+ wave_data_r = (INT16)val;
+
+ wav_add_data_16lr(m_wavfile, &wave_data_l, &wave_data_r, 1);
+ }
+}
+
+DISCRETE_RESET( dso_wavlog )
+{
+ this->step();
+}
diff --git a/src/devices/sound/disc_wav.h b/src/devices/sound/disc_wav.h
new file mode 100644
index 00000000000..4775dd8fe68
--- /dev/null
+++ b/src/devices/sound/disc_wav.h
@@ -0,0 +1,193 @@
+// license:BSD-3-Clause
+// copyright-holders:K.Wilkins
+#pragma once
+
+#ifndef __DISC_WAV_H__
+#define __DISC_WAV_H__
+
+/***********************************************************************
+ *
+ * MAME - Discrete sound system emulation library
+ *
+ * Written by K.Wilkins (mame@esplexo.co.uk)
+ *
+ * (c) K.Wilkins 2000
+ *
+ * Coding started in November 2000
+ *
+ * Additions/bugfix February 2003 - Derrick Renaud, F.Palazzolo, K.Wilkins
+ * Discrete parallel tasks 2009 - Couriersud
+ * Discrete classes 2010 - Couriersud
+ *
+ ***********************************************************************/
+
+#include "discrete.h"
+
+DISCRETE_CLASS_STEP_RESET(dss_counter, 1,
+ int m_clock_type;
+ int m_out_type;
+ int m_is_7492;
+ int m_last_clock;
+ UINT32 m_last_count;
+ //UINT32 m_last; /* Last clock state */
+ UINT32 m_min;
+ UINT32 m_max;
+ UINT32 m_diff;
+ double m_t_left; /* time unused during last sample in seconds */
+);
+
+DISCRETE_CLASS_STEP_RESET(dss_lfsr_noise, 2,
+ unsigned int m_lfsr_reg;
+ int m_last; /* Last clock state */
+ double m_t_clock; /* fixed counter clock in seconds */
+ double m_t_left; /* time unused during last sample in seconds */
+ //double m_sample_step;
+ //double m_t;
+ UINT8 m_reset_on_high;
+ UINT8 m_invert_output;
+ UINT8 m_out_is_f0;
+ UINT8 m_out_lfsr_reg;
+);
+
+DISCRETE_CLASS_STEP_RESET(dss_noise, 2,
+ double m_phase;
+);
+
+DISCRETE_CLASS_STEP_RESET(dss_note, 1,
+ int m_clock_type;
+ int m_out_type;
+ int m_last; /* Last clock state */
+ double m_t_clock; /* fixed counter clock in seconds */
+ double m_t_left; /* time unused during last sample in seconds */
+ int m_max1; /* Max 1 Count stored as int for easy use. */
+ int m_max2; /* Max 2 Count stored as int for easy use. */
+ int m_count1; /* current count1 */
+ int m_count2; /* current count2 */
+);
+
+DISCRETE_CLASS_STEP_RESET(dss_sawtoothwave, 1,
+ double m_phase;
+ int m_type;
+);
+
+DISCRETE_CLASS_STEP_RESET(dss_sinewave, 1,
+ double m_phase;
+);
+
+DISCRETE_CLASS_STEP_RESET(dss_squarewave, 1,
+ double m_phase;
+ double m_trigger;
+);
+DISCRETE_CLASS_STEP_RESET(dss_squarewfix, 1,
+ int m_flip_flop;
+ double m_sample_step;
+ double m_t_left;
+ double m_t_off;
+ double m_t_on;
+);
+
+DISCRETE_CLASS_STEP_RESET(dss_squarewave2, 1,
+ double m_phase;
+ double m_trigger;
+);
+
+DISCRETE_CLASS_STEP_RESET(dss_trianglewave, 1,
+ double m_phase;
+);
+
+/* Component specific modules */
+
+#define DSS_INV_TAB_SIZE 500
+#define DEFAULT_CD40XX_VALUES(_vB) (_vB),(_vB)*0.02,(_vB)*0.98,(_vB)/5.0*1.5,(_vB)/5.0*3.5, 0.1
+
+class DISCRETE_CLASS_NAME(dss_inverter_osc): public discrete_base_node, public discrete_step_interface
+{
+ DISCRETE_CLASS_CONSTRUCTOR(dss_inverter_osc, base)
+ DISCRETE_CLASS_DESTRUCTOR(dss_inverter_osc)
+public:
+ struct description
+ {
+ double vB;
+ double vOutLow;
+ double vOutHigh;
+ double vInFall; // voltage that triggers the gate input to go low (0V) on fall
+ double vInRise; // voltage that triggers the gate input to go high (vGate) on rise
+ double clamp; // voltage is clamped to -clamp ... vb+clamp if clamp>= 0;
+ int options; // bitmaped options
+ };
+ enum {
+ IS_TYPE1 = 0x00,
+ IS_TYPE2 = 0x01,
+ IS_TYPE3 = 0x02,
+ IS_TYPE4 = 0x03,
+ IS_TYPE5 = 0x04,
+ TYPE_MASK = 0x0f,
+ OUT_IS_LOGIC = 0x10
+ };
+ void step(void);
+ void reset(void);
+protected:
+ inline double tftab(double x);
+ inline double tf(double x);
+private:
+ DISCRETE_CLASS_INPUT(I_ENABLE, 0);
+ DISCRETE_CLASS_INPUT(I_MOD, 1);
+ DISCRETE_CLASS_INPUT(I_RC, 2);
+ DISCRETE_CLASS_INPUT(I_RP, 3);
+ DISCRETE_CLASS_INPUT(I_C, 4);
+ DISCRETE_CLASS_INPUT(I_R2, 5);
+
+ double mc_v_cap;
+ double mc_v_g2_old;
+ double mc_w;
+ double mc_wc;
+ double mc_rp;
+ double mc_r1;
+ double mc_r2;
+ double mc_c;
+ double mc_tf_a;
+ double mc_tf_b;
+ double mc_tf_tab[DSS_INV_TAB_SIZE];
+};
+
+DISCRETE_CLASS_STEP_RESET(dss_op_amp_osc, 1,
+ const double * m_r[8]; /* pointers to resistor values */
+ int m_type;
+ UINT8 m_flip_flop; /* flip/flop output state */
+ UINT8 m_flip_flop_xor; /* flip_flop ^ flip_flop_xor, 0 = discharge, 1 = charge */
+ UINT8 m_output_type;
+ UINT8 m_has_enable;
+ double m_v_out_high;
+ double m_threshold_low; /* falling threshold */
+ double m_threshold_high; /* rising threshold */
+ double m_v_cap; /* current capacitor voltage */
+ double m_r_total; /* all input resistors in parallel */
+ double m_i_fixed; /* fixed current at the input */
+ double m_i_enable; /* fixed current at the input if enabled */
+ double m_temp1; /* Multi purpose */
+ double m_temp2; /* Multi purpose */
+ double m_temp3; /* Multi purpose */
+ double m_is_linear_charge;
+ double m_charge_rc[2];
+ double m_charge_exp[2];
+ double m_charge_v[2];
+);
+
+DISCRETE_CLASS_STEP_RESET(dss_schmitt_osc, 1,
+ double m_ration_in; /* ratio of total charging voltage that comes from the input */
+ double m_ratio_feedback; /* ratio of total charging voltage that comes from the feedback */
+ double m_v_cap; /* current capacitor voltage */
+ double m_rc; /* r*c */
+ double m_exponent;
+ int m_state; /* state of the output */
+ int m_enable_type;
+ UINT8 m_input_is_voltage;
+);
+
+/* Not yet implemented */
+DISCRETE_CLASS_STEP_RESET(dss_adsrenv, 1,
+ //double m_phase;
+);
+
+
+#endif /* __DISC_WAV_H__ */
diff --git a/src/devices/sound/disc_wav.inc b/src/devices/sound/disc_wav.inc
new file mode 100644
index 00000000000..57a90ae5a5b
--- /dev/null
+++ b/src/devices/sound/disc_wav.inc
@@ -0,0 +1,1787 @@
+// license:BSD-3-Clause
+// copyright-holders:K.Wilkins
+/************************************************************************
+ *
+ * MAME - Discrete sound system emulation library
+ * Written by K.Wilkins (mame@esplexo.co.uk)
+ *
+ * (c) K.Wilkins 2000
+ *
+ ************************************************************************
+ *
+ * DSS_COUNTER - External clock Binary Counter
+ * DSS_LFSR_NOISE - Linear Feedback Shift Register Noise
+ * DSS_NOISE - Noise Source - Random source
+ * DSS_NOTE - Note/tone generator
+ * DSS_OP_AMP_OSC - Op Amp oscillator circuits
+ * DSS_SAWTOOTHWAVE - Sawtooth waveform generator
+ * DSS_SCHMITT_OSC - Schmitt Feedback Oscillator
+ * DSS_SINEWAVE - Sinewave generator source code
+ * DSS_SQUAREWAVE - Squarewave generator source code
+ * DSS_SQUAREWFIX - Squarewave generator - fixed frequency
+ * DSS_SQUAREWAVE2 - Squarewave generator - by t_on/t_off
+ * DSS_TRIANGLEWAVE - Triangle waveform generator
+ *
+ ************************************************************************/
+
+
+
+
+
+
+
+/************************************************************************
+ *
+ * DSS_COUNTER - External clock Binary Counter
+ *
+ * input0 - Enable input value
+ * input1 - Reset input (active high)
+ * input2 - Clock Input
+ * input3 - Max count
+ * input4 - Direction - 0=down, 1=up
+ * input5 - Reset Value
+ * input6 - Clock type
+ *
+ * Jan 2004, D Renaud.
+ ************************************************************************/
+#define DSS_COUNTER__ENABLE DISCRETE_INPUT(0)
+#define DSS_COUNTER__RESET DISCRETE_INPUT(1)
+#define DSS_COUNTER__CLOCK DISCRETE_INPUT(2)
+#define DSS_COUNTER__MIN DISCRETE_INPUT(3)
+#define DSS_COUNTER__MAX DISCRETE_INPUT(4)
+#define DSS_COUNTER__DIR DISCRETE_INPUT(5)
+#define DSS_COUNTER__INIT DISCRETE_INPUT(6)
+#define DSS_COUNTER__CLOCK_TYPE DISCRETE_INPUT(7)
+#define DSS_7492__CLOCK_TYPE DSS_COUNTER__MIN
+
+static const int disc_7492_count[6] = {0x00, 0x01, 0x02, 0x04, 0x05, 0x06};
+
+DISCRETE_STEP(dss_counter)
+{
+ double cycles;
+ double ds_clock;
+ int clock = 0, inc = 0;
+ UINT32 last_count = m_last_count; /* it is different then output in 7492 */
+ double x_time = 0;
+ UINT32 count = last_count;
+
+ ds_clock = DSS_COUNTER__CLOCK;
+ if (UNEXPECTED(m_clock_type == DISC_CLK_IS_FREQ))
+ {
+ /* We need to keep clocking the internal clock even if disabled. */
+ cycles = (m_t_left + this->sample_time()) * ds_clock;
+ inc = (int)cycles;
+ m_t_left = (cycles - inc) / ds_clock;
+ if (inc) x_time = m_t_left / this->sample_time();
+ }
+ else
+ {
+ clock = (int)ds_clock;
+ /* x_time from input clock */
+ x_time = ds_clock - clock;
+ }
+
+
+ /* If reset enabled then set output to the reset value. No x_time in reset. */
+ if (UNEXPECTED(DSS_COUNTER__RESET))
+ {
+ m_last_count = (int)DSS_COUNTER__INIT;
+ set_output(0, (int)DSS_COUNTER__INIT);
+ return;
+ }
+
+ /*
+ * Only count if module is enabled.
+ * This has the effect of holding the output at it's current value.
+ */
+ if (EXPECTED(DSS_COUNTER__ENABLE))
+ {
+ double v_out;
+
+ switch (m_clock_type)
+ {
+ case DISC_CLK_ON_F_EDGE:
+ case DISC_CLK_ON_R_EDGE:
+ /* See if the clock has toggled to the proper edge */
+ clock = (clock != 0);
+ if (m_last_clock != clock)
+ {
+ m_last_clock = clock;
+ if (m_clock_type == clock)
+ {
+ /* Toggled */
+ inc = 1;
+ }
+ }
+ break;
+
+ case DISC_CLK_BY_COUNT:
+ /* Clock number of times specified. */
+ inc = clock;
+ break;
+ }
+
+ /* use loops because init is not always min or max */
+ if (DSS_COUNTER__DIR)
+ {
+ count += inc;
+ while (count > m_max)
+ {
+ count -= m_diff;
+ }
+ }
+ else
+ {
+ count -= inc;
+ while (count < m_min || count > (0xffffffff - inc))
+ {
+ count += m_diff;
+ }
+ }
+
+ m_last_count = count;
+ v_out = m_is_7492 ? disc_7492_count[count] : count;
+
+ if (UNEXPECTED(count != last_count))
+ {
+ /* the x_time is only output if the output changed. */
+ switch (m_out_type)
+ {
+ case DISC_OUT_HAS_XTIME:
+ v_out += x_time;
+ break;
+ case DISC_OUT_IS_ENERGY:
+ if (x_time == 0) x_time = 1.0;
+ v_out = last_count;
+ if (count > last_count)
+ v_out += (count - last_count) * x_time;
+ else
+ v_out -= (last_count - count) * x_time;
+ break;
+ }
+ }
+ set_output(0, v_out);
+ }
+}
+
+DISCRETE_RESET(dss_counter)
+{
+ if ((int)DSS_COUNTER__CLOCK_TYPE & DISC_COUNTER_IS_7492)
+ {
+ m_is_7492 = 1;
+ m_clock_type = DSS_7492__CLOCK_TYPE;
+ m_max = 5;
+ m_min = 0;
+ m_diff = 6;
+ }
+ else
+ {
+ m_is_7492 = 0;
+ m_clock_type = DSS_COUNTER__CLOCK_TYPE;
+ m_max = DSS_COUNTER__MAX;
+ m_min = DSS_COUNTER__MIN;
+ m_diff = m_max - m_min + 1;
+ }
+
+
+ if (!m_is_7492 && (DSS_COUNTER__MAX < DSS_COUNTER__MIN))
+ fatalerror("MAX < MIN in NODE_%02d\n", this->index());
+
+ m_out_type = m_clock_type & DISC_OUT_MASK;
+ m_clock_type &= DISC_CLK_MASK;
+
+ m_t_left = 0;
+ m_last_count = 0;
+ m_last_clock = 0;
+ set_output(0, DSS_COUNTER__INIT); /* count starts at reset value */
+}
+
+
+/************************************************************************
+ *
+ * DSS_LFSR_NOISE - Usage of node_description values for LFSR noise gen
+ *
+ * input0 - Enable input value
+ * input1 - Register reset
+ * input2 - Clock Input
+ * input3 - Amplitude input value
+ * input4 - Input feed bit
+ * input5 - Bias
+ *
+ * also passed dss_lfsr_context structure
+ *
+ ************************************************************************/
+#define DSS_LFSR_NOISE__ENABLE DISCRETE_INPUT(0)
+#define DSS_LFSR_NOISE__RESET DISCRETE_INPUT(1)
+#define DSS_LFSR_NOISE__CLOCK DISCRETE_INPUT(2)
+#define DSS_LFSR_NOISE__AMP DISCRETE_INPUT(3)
+#define DSS_LFSR_NOISE__FEED DISCRETE_INPUT(4)
+#define DSS_LFSR_NOISE__BIAS DISCRETE_INPUT(5)
+
+INLINE int dss_lfsr_function(discrete_device *dev, int myfunc, int in0, int in1, int bitmask)
+{
+ int retval;
+
+ in0 &= bitmask;
+ in1 &= bitmask;
+
+ switch(myfunc)
+ {
+ case DISC_LFSR_XOR:
+ retval = in0 ^ in1;
+ break;
+ case DISC_LFSR_OR:
+ retval = in0 | in1;
+ break;
+ case DISC_LFSR_AND:
+ retval = in0 & in1;
+ break;
+ case DISC_LFSR_XNOR:
+ retval = in0 ^ in1;
+ retval = retval ^ bitmask; /* Invert output */
+ break;
+ case DISC_LFSR_NOR:
+ retval = in0 | in1;
+ retval = retval ^ bitmask; /* Invert output */
+ break;
+ case DISC_LFSR_NAND:
+ retval = in0 & in1;
+ retval = retval ^ bitmask; /* Invert output */
+ break;
+ case DISC_LFSR_IN0:
+ retval = in0;
+ break;
+ case DISC_LFSR_IN1:
+ retval = in1;
+ break;
+ case DISC_LFSR_NOT_IN0:
+ retval = in0 ^ bitmask;
+ break;
+ case DISC_LFSR_NOT_IN1:
+ retval = in1 ^ bitmask;
+ break;
+ case DISC_LFSR_REPLACE:
+ retval = in0 & ~in1;
+ retval = retval | in1;
+ break;
+ case DISC_LFSR_XOR_INV_IN0:
+ retval = in0 ^ bitmask; /* invert in0 */
+ retval = retval ^ in1; /* xor in1 */
+ break;
+ case DISC_LFSR_XOR_INV_IN1:
+ retval = in1 ^ bitmask; /* invert in1 */
+ retval = retval ^ in0; /* xor in0 */
+ break;
+ default:
+ dev->discrete_log("dss_lfsr_function - Invalid function type passed");
+ retval=0;
+ break;
+ }
+ return retval;
+}
+
+
+DISCRETE_STEP(dss_lfsr_noise)
+{
+ DISCRETE_DECLARE_INFO(discrete_lfsr_desc)
+
+ double cycles;
+ int clock, inc = 0;
+ int fb0, fb1, fbresult = 0, noise_feed;
+
+ if (info->clock_type == DISC_CLK_IS_FREQ)
+ {
+ /* We need to keep clocking the internal clock even if disabled. */
+ cycles = (m_t_left + this->sample_time()) / m_t_clock;
+ inc = (int)cycles;
+ m_t_left = (cycles - inc) * m_t_clock;
+ }
+
+ /* Reset everything if necessary */
+ if(((DSS_LFSR_NOISE__RESET == 0) ? 0 : 1) == m_reset_on_high)
+ {
+ this->reset();
+ return;
+ }
+
+ switch (info->clock_type)
+ {
+ case DISC_CLK_ON_F_EDGE:
+ case DISC_CLK_ON_R_EDGE:
+ /* See if the clock has toggled to the proper edge */
+ clock = (DSS_LFSR_NOISE__CLOCK != 0);
+ if (m_last != clock)
+ {
+ m_last = clock;
+ if (info->clock_type == clock)
+ {
+ /* Toggled */
+ inc = 1;
+ }
+ }
+ break;
+
+ case DISC_CLK_BY_COUNT:
+ /* Clock number of times specified. */
+ inc = (int)DSS_LFSR_NOISE__CLOCK;
+ break;
+ }
+
+ if (inc > 0)
+ {
+ double v_out;
+
+ noise_feed = (DSS_LFSR_NOISE__FEED ? 0x01 : 0x00);
+ for (clock = 0; clock < inc; clock++)
+ {
+ /* Fetch the last feedback result */
+ fbresult = (m_lfsr_reg >> info->bitlength) & 0x01;
+
+ /* Stage 2 feedback combine fbresultNew with infeed bit */
+ fbresult = dss_lfsr_function(m_device, info->feedback_function1, fbresult, noise_feed, 0x01);
+
+ /* Stage 3 first we setup where the bit is going to be shifted into */
+ fbresult = fbresult * info->feedback_function2_mask;
+ /* Then we left shift the register, */
+ m_lfsr_reg = m_lfsr_reg << 1;
+ /* Now move the fbresult into the shift register and mask it to the bitlength */
+ m_lfsr_reg = dss_lfsr_function(m_device, info->feedback_function2, fbresult, m_lfsr_reg, (1 << info->bitlength) - 1 );
+
+ /* Now get and store the new feedback result */
+ /* Fetch the feedback bits */
+ fb0 = (m_lfsr_reg >> info->feedback_bitsel0) & 0x01;
+ fb1 = (m_lfsr_reg >> info->feedback_bitsel1) & 0x01;
+ /* Now do the combo on them */
+ fbresult = dss_lfsr_function(m_device, info->feedback_function0, fb0, fb1, 0x01);
+ m_lfsr_reg = dss_lfsr_function(m_device, DISC_LFSR_REPLACE, m_lfsr_reg, fbresult << info->bitlength, (2 << info->bitlength) - 1);
+
+ }
+ /* Now select the output bit */
+ if (m_out_is_f0)
+ v_out = fbresult & 0x01;
+ else
+ v_out = (m_lfsr_reg >> info->output_bit) & 0x01;
+
+ /* Final inversion if required */
+ if (m_invert_output) v_out = v_out ? 0 : 1;
+
+ /* Gain stage */
+ v_out = v_out ? DSS_LFSR_NOISE__AMP / 2 : -DSS_LFSR_NOISE__AMP / 2;
+ /* Bias input as required */
+ v_out = v_out + DSS_LFSR_NOISE__BIAS;
+
+ set_output(0, v_out);
+
+ /* output the lfsr reg ?*/
+ if (m_out_lfsr_reg)
+ set_output(1, (double) m_lfsr_reg);
+
+ }
+ if(!DSS_LFSR_NOISE__ENABLE)
+ {
+ set_output(0, 0);
+ }
+}
+
+DISCRETE_RESET(dss_lfsr_noise)
+{
+ DISCRETE_DECLARE_INFO(discrete_lfsr_desc)
+
+ int fb0 , fb1, fbresult;
+ double v_out;
+
+ m_reset_on_high = (info->flags & DISC_LFSR_FLAG_RESET_TYPE_H) ? 1 : 0;
+ m_invert_output = info->flags & DISC_LFSR_FLAG_OUT_INVERT;
+ m_out_is_f0 = (info->flags & DISC_LFSR_FLAG_OUTPUT_F0) ? 1 : 0;
+ m_out_lfsr_reg = (info->flags & DISC_LFSR_FLAG_OUTPUT_SR_SN1) ? 1 : 0;
+
+ if ((info->clock_type < DISC_CLK_ON_F_EDGE) || (info->clock_type > DISC_CLK_IS_FREQ))
+ m_device->discrete_log("Invalid clock type passed in NODE_%d\n", this->index());
+
+ m_last = (DSS_COUNTER__CLOCK != 0);
+ if (info->clock_type == DISC_CLK_IS_FREQ) m_t_clock = 1.0 / DSS_LFSR_NOISE__CLOCK;
+ m_t_left = 0;
+
+ m_lfsr_reg = info->reset_value;
+
+ /* Now get and store the new feedback result */
+ /* Fetch the feedback bits */
+ fb0 = (m_lfsr_reg >> info->feedback_bitsel0) & 0x01;
+ fb1=(m_lfsr_reg >> info->feedback_bitsel1) & 0x01;
+ /* Now do the combo on them */
+ fbresult = dss_lfsr_function(m_device, info->feedback_function0, fb0, fb1, 0x01);
+ m_lfsr_reg=dss_lfsr_function(m_device, DISC_LFSR_REPLACE, m_lfsr_reg, fbresult << info->bitlength, (2<< info->bitlength ) - 1);
+
+ /* Now select and setup the output bit */
+ v_out = (m_lfsr_reg >> info->output_bit) & 0x01;
+
+ /* Final inversion if required */
+ if(info->flags & DISC_LFSR_FLAG_OUT_INVERT) v_out = v_out ? 0 : 1;
+
+ /* Gain stage */
+ v_out = v_out ? DSS_LFSR_NOISE__AMP / 2 : -DSS_LFSR_NOISE__AMP / 2;
+ /* Bias input as required */
+ v_out += DSS_LFSR_NOISE__BIAS;
+
+ set_output(0, v_out);
+ set_output(1, 0);
+}
+
+
+/************************************************************************
+ *
+ * DSS_NOISE - Usage of node_description values for white nose generator
+ *
+ * input0 - Enable input value
+ * input1 - Noise sample frequency
+ * input2 - Amplitude input value
+ * input3 - DC Bias value
+ *
+ ************************************************************************/
+#define DSS_NOISE__ENABLE DISCRETE_INPUT(0)
+#define DSS_NOISE__FREQ DISCRETE_INPUT(1)
+#define DSS_NOISE__AMP DISCRETE_INPUT(2)
+#define DSS_NOISE__BIAS DISCRETE_INPUT(3)
+
+DISCRETE_STEP(dss_noise)
+{
+ double v_out;
+
+ if(DSS_NOISE__ENABLE)
+ {
+ /* Only sample noise on rollover to next cycle */
+ if(m_phase > (2.0 * M_PI))
+ {
+ /* GCC's rand returns a RAND_MAX value of 0x7fff */
+ int newval = (m_device->machine().rand() & 0x7fff) - 16384;
+
+ /* make sure the peak to peak values are the amplitude */
+ v_out = DSS_NOISE__AMP / 2;
+ if (newval > 0)
+ v_out *= ((double)newval / 16383);
+ else
+ v_out *= ((double)newval / 16384);
+
+ /* Add DC Bias component */
+ v_out += DSS_NOISE__BIAS;
+ set_output(0, v_out);
+ }
+ }
+ else
+ {
+ set_output(0, 0);
+ }
+
+ /* Keep the new phasor in the 2Pi range.*/
+ m_phase = fmod(m_phase, 2.0 * M_PI);
+
+ /* The enable input only curtails output, phase rotation still occurs. */
+ /* We allow the phase to exceed 2Pi here, so we can tell when to sample the noise. */
+ m_phase += ((2.0 * M_PI * DSS_NOISE__FREQ) / this->sample_rate());
+}
+
+
+DISCRETE_RESET(dss_noise)
+{
+ m_phase=0;
+ this->step();
+}
+
+
+/************************************************************************
+ *
+ * DSS_NOTE - Note/tone generator
+ *
+ * input0 - Enable input value
+ * input1 - Clock Input
+ * input2 - data value
+ * input3 - Max count 1
+ * input4 - Max count 2
+ * input5 - Clock type
+ *
+ * Mar 2004, D Renaud.
+ ************************************************************************/
+ #define DSS_NOTE__ENABLE DISCRETE_INPUT(0)
+ #define DSS_NOTE__CLOCK DISCRETE_INPUT(1)
+ #define DSS_NOTE__DATA DISCRETE_INPUT(2)
+ #define DSS_NOTE__MAX1 DISCRETE_INPUT(3)
+ #define DSS_NOTE__MAX2 DISCRETE_INPUT(4)
+ #define DSS_NOTE__CLOCK_TYPE DISCRETE_INPUT(5)
+
+DISCRETE_STEP(dss_note)
+{
+ double cycles;
+ int clock = 0, last_count2, inc = 0;
+ double x_time = 0;
+ double v_out;
+
+ if (m_clock_type == DISC_CLK_IS_FREQ)
+ {
+ /* We need to keep clocking the internal clock even if disabled. */
+ cycles = (m_t_left + this->sample_time()) / m_t_clock;
+ inc = (int)cycles;
+ m_t_left = (cycles - inc) * m_t_clock;
+ if (inc) x_time = m_t_left / this->sample_time();
+ }
+ else
+ {
+ /* separate clock info from x_time info. */
+ clock = (int)DSS_NOTE__CLOCK;
+ x_time = DSS_NOTE__CLOCK - clock;
+ }
+
+ if (DSS_NOTE__ENABLE)
+ {
+ last_count2 = m_count2;
+
+ switch (m_clock_type)
+ {
+ case DISC_CLK_ON_F_EDGE:
+ case DISC_CLK_ON_R_EDGE:
+ /* See if the clock has toggled to the proper edge */
+ clock = (clock != 0);
+ if (m_last != clock)
+ {
+ m_last = clock;
+ if (m_clock_type == clock)
+ {
+ /* Toggled */
+ inc = 1;
+ }
+ }
+ break;
+
+ case DISC_CLK_BY_COUNT:
+ /* Clock number of times specified. */
+ inc = clock;
+ break;
+ }
+
+ /* Count output as long as the data loaded is not already equal to max 1 count. */
+ if (DSS_NOTE__DATA != DSS_NOTE__MAX1)
+ {
+ for (clock = 0; clock < inc; clock++)
+ {
+ m_count1++;
+ if (m_count1 > m_max1)
+ {
+ /* Max 1 count reached. Load Data into counter. */
+ m_count1 = (int)DSS_NOTE__DATA;
+ m_count2 += 1;
+ if (m_count2 > m_max2) m_count2 = 0;
+ }
+ }
+ }
+
+ v_out = m_count2;
+ if (m_count2 != last_count2)
+ {
+ /* the x_time is only output if the output changed. */
+ switch (m_out_type)
+ {
+ case DISC_OUT_IS_ENERGY:
+ if (x_time == 0) x_time = 1.0;
+ v_out = last_count2;
+ if (m_count2 > last_count2)
+ v_out += (m_count2 - last_count2) * x_time;
+ else
+ v_out -= (last_count2 - m_count2) * x_time;
+ break;
+ case DISC_OUT_HAS_XTIME:
+ v_out += x_time;
+ break;
+ }
+ }
+ set_output(0, v_out);
+ }
+ else
+ set_output(0, 0);
+}
+
+DISCRETE_RESET(dss_note)
+{
+ m_clock_type = (int)DSS_NOTE__CLOCK_TYPE & DISC_CLK_MASK;
+ m_out_type = (int)DSS_NOTE__CLOCK_TYPE & DISC_OUT_MASK;
+
+ m_last = (DSS_NOTE__CLOCK != 0);
+ m_t_left = 0;
+ m_t_clock = 1.0 / DSS_NOTE__CLOCK;
+
+ m_count1 = (int)DSS_NOTE__DATA;
+ m_count2 = 0;
+ m_max1 = (int)DSS_NOTE__MAX1;
+ m_max2 = (int)DSS_NOTE__MAX2;
+ set_output(0, 0);
+}
+
+/************************************************************************
+ *
+ * DSS_OP_AMP_OSC - Op Amp Oscillators
+ *
+ * input0 - Enable input value
+ * input1 - vMod1 (if needed)
+ * input2 - vMod2 (if needed)
+ *
+ * also passed discrete_op_amp_osc_info structure
+ *
+ * Mar 2004, D Renaud.
+ ************************************************************************/
+#define DSS_OP_AMP_OSC__ENABLE DISCRETE_INPUT(0)
+#define DSS_OP_AMP_OSC__VMOD1 DISCRETE_INPUT(1)
+#define DSS_OP_AMP_OSC__VMOD2 DISCRETE_INPUT(2)
+
+/* The inputs on a norton op-amp are (info->vP - OP_AMP_NORTON_VBE) */
+/* which is the same as the output high voltage. We will define them */
+/* the same to save a calculation step */
+#define DSS_OP_AMP_OSC_NORTON_VP_IN m_v_out_high
+
+DISCRETE_STEP(dss_op_amp_osc)
+{
+ DISCRETE_DECLARE_INFO(discrete_op_amp_osc_info)
+
+ double i = 0; /* Charging current created by vIn */
+ double v = 0; /* all input voltages mixed */
+ double dt; /* change in time */
+ double v_cap; /* Current voltage on capacitor, before dt */
+ double v_cap_next = 0; /* Voltage on capacitor, after dt */
+ double charge[2] = {0};
+ double x_time = 0; /* time since change happened */
+ double exponent;
+ UINT8 force_charge = 0;
+ UINT8 enable = DSS_OP_AMP_OSC__ENABLE;
+ UINT8 update_exponent = 0;
+ UINT8 flip_flop = m_flip_flop;
+ int count_f = 0;
+ int count_r = 0;
+
+ double v_out = 0;
+
+ dt = this->sample_time(); /* Change in time */
+ v_cap = m_v_cap; /* Set to voltage before change */
+
+ /* work out the charge currents/voltages. */
+ switch (m_type)
+ {
+ case DISC_OP_AMP_OSCILLATOR_VCO_1:
+ /* Work out the charge rates. */
+ /* i is not a current. It is being used as a temp variable. */
+ i = DSS_OP_AMP_OSC__VMOD1 * m_temp1;
+ charge[0] = (DSS_OP_AMP_OSC__VMOD1 - i) / info->r1;
+ charge[1] = (i - (DSS_OP_AMP_OSC__VMOD1 * m_temp2)) / m_temp3;
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_1 | DISC_OP_AMP_IS_NORTON:
+ {
+ /* resistors can be nodes, so everything needs updating */
+ double i1, i2;
+ /* add in enable current if using real enable */
+ if (m_has_enable)
+ {
+ if (enable)
+ i = m_i_enable;
+ enable = 1;
+ }
+ /* Work out the charge rates. */
+ charge[0] = DSS_OP_AMP_OSC_NORTON_VP_IN / *m_r[1-1] - i;
+ charge[1] = (m_v_out_high - OP_AMP_NORTON_VBE) / *m_r[2-1] - charge[0];
+ /* Work out the Inverting Schmitt thresholds. */
+ i1 = DSS_OP_AMP_OSC_NORTON_VP_IN / *m_r[5-1];
+ i2 = (0.0 - OP_AMP_NORTON_VBE) / *m_r[4-1];
+ m_threshold_low = (i1 + i2) * *m_r[3-1] + OP_AMP_NORTON_VBE;
+ i2 = (m_v_out_high - OP_AMP_NORTON_VBE) / *m_r[4-1];
+ m_threshold_high = (i1 + i2) * *m_r[3-1] + OP_AMP_NORTON_VBE;
+ break;
+ }
+
+ case DISC_OP_AMP_OSCILLATOR_VCO_1 | DISC_OP_AMP_IS_NORTON:
+ /* Millman the input voltages. */
+ if (info->r7 == 0)
+ {
+ /* No r7 means that the modulation circuit is fed directly into the circuit. */
+ v = DSS_OP_AMP_OSC__VMOD1;
+ }
+ else
+ {
+ /* we need to mix any bias and all modulation voltages together. */
+ i = m_i_fixed;
+ i += DSS_OP_AMP_OSC__VMOD1 / info->r7;
+ if (info->r8 != 0)
+ i += DSS_OP_AMP_OSC__VMOD2 / info->r8;
+ v = i * m_r_total;
+ }
+
+ /* Work out the charge rates. */
+ v -= OP_AMP_NORTON_VBE;
+ charge[0] = v / info->r1;
+ charge[1] = v / info->r2 - charge[0];
+
+ /* use the real enable circuit */
+ force_charge = !enable;
+ enable = 1;
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_VCO_2 | DISC_OP_AMP_IS_NORTON:
+ /* Work out the charge rates. */
+ i = DSS_OP_AMP_OSC__VMOD1 / info->r1;
+ charge[0] = i - m_temp1;
+ charge[1] = m_temp2 - i;
+ /* if the negative pin current is less then the positive pin current, */
+ /* then the osc is disabled and the cap keeps charging */
+ if (charge[0] < 0)
+ {
+ force_charge = 1;
+ charge[0] *= -1;
+ }
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_VCO_3 | DISC_OP_AMP_IS_NORTON:
+ /* start with fixed bias */
+ charge[0] = m_i_fixed;
+ /* add in enable current if using real enable */
+ if (m_has_enable)
+ {
+ if (enable)
+ charge[0] -= m_i_enable;
+ enable = 1;
+ }
+ /* we need to mix any bias and all modulation voltages together. */
+ v = DSS_OP_AMP_OSC__VMOD1 - OP_AMP_NORTON_VBE;
+ if (v < 0) v = 0;
+ charge[0] += v / info->r1;
+ if (info->r6 != 0)
+ {
+ v = DSS_OP_AMP_OSC__VMOD2 - OP_AMP_NORTON_VBE;
+ charge[0] += v / info->r6;
+ }
+ charge[1] = m_temp1 - charge[0];
+ break;
+ }
+
+ if (!enable)
+ {
+ /* we will just output 0 for oscillators that have no real enable. */
+ set_output(0, 0);
+ return;
+ }
+
+ /* Keep looping until all toggling in time sample is used up. */
+ do
+ {
+ if (m_is_linear_charge)
+ {
+ if ((flip_flop ^ m_flip_flop_xor) || force_charge)
+ {
+ /* Charging */
+ /* iC=C*dv/dt works out to dv=iC*dt/C */
+ v_cap_next = v_cap + (charge[1] * dt / info->c);
+ dt = 0;
+
+ /* has it charged past upper limit? */
+ if (v_cap_next > m_threshold_high)
+ {
+ flip_flop = m_flip_flop_xor;
+ if (flip_flop)
+ count_r++;
+ else
+ count_f++;
+ if (force_charge)
+ {
+ /* we need to keep charging the cap to the max thereby disabling the circuit */
+ if (v_cap_next > m_v_out_high)
+ v_cap_next = m_v_out_high;
+ }
+ else
+ {
+ /* calculate the overshoot time */
+ dt = info->c * (v_cap_next - m_threshold_high) / charge[1];
+ x_time = dt;
+ v_cap_next = m_threshold_high;
+ }
+ }
+ }
+ else
+ {
+ /* Discharging */
+ v_cap_next = v_cap - (charge[0] * dt / info->c);
+ dt = 0;
+
+ /* has it discharged past lower limit? */
+ if (v_cap_next < m_threshold_low)
+ {
+ flip_flop = !m_flip_flop_xor;
+ if (flip_flop)
+ count_r++;
+ else
+ count_f++;
+ /* calculate the overshoot time */
+ dt = info->c * (m_threshold_low - v_cap_next) / charge[0];
+ x_time = dt;
+ v_cap_next = m_threshold_low;
+ }
+ }
+ }
+ else /* non-linear charge */
+ {
+ if (update_exponent)
+ exponent = RC_CHARGE_EXP_DT(m_charge_rc[flip_flop], dt);
+ else
+ exponent = m_charge_exp[flip_flop];
+
+ v_cap_next = v_cap + ((m_charge_v[flip_flop] - v_cap) * exponent);
+ dt = 0;
+
+ if (flip_flop)
+ {
+ /* Has it charged past upper limit? */
+ if (v_cap_next > m_threshold_high)
+ {
+ dt = m_charge_rc[1] * log(1.0 / (1.0 - ((v_cap_next - m_threshold_high) / (m_v_out_high - v_cap))));
+ x_time = dt;
+ v_cap_next = m_threshold_high;
+ flip_flop = 0;
+ count_f++;
+ update_exponent = 1;
+ }
+ }
+ else
+ {
+ /* has it discharged past lower limit? */
+ if (v_cap_next < m_threshold_low)
+ {
+ dt = m_charge_rc[0] * log(1.0 / (1.0 - ((m_threshold_low - v_cap_next) / v_cap)));
+ x_time = dt;
+ v_cap_next = m_threshold_low;
+ flip_flop = 1;
+ count_r++;
+ update_exponent = 1;
+ }
+ }
+ }
+ v_cap = v_cap_next;
+ } while(dt);
+ if (v_cap > m_v_out_high)
+ v_cap = m_v_out_high;
+ if (v_cap < 0)
+ v_cap = 0;
+ m_v_cap = v_cap;
+
+ x_time = dt / this->sample_time();
+
+ switch (m_output_type)
+ {
+ case DISC_OP_AMP_OSCILLATOR_OUT_CAP:
+ v_out = v_cap;
+ break;
+ case DISC_OP_AMP_OSCILLATOR_OUT_ENERGY:
+ if (x_time == 0) x_time = 1.0;
+ v_out = m_v_out_high * (flip_flop ? x_time : (1.0 - x_time));
+ break;
+ case DISC_OP_AMP_OSCILLATOR_OUT_SQW:
+ if (count_f + count_r >= 2)
+ /* force at least 1 toggle */
+ v_out = m_flip_flop ? 0 : m_v_out_high;
+ else
+ v_out = flip_flop * m_v_out_high;
+ break;
+ case DISC_OP_AMP_OSCILLATOR_OUT_COUNT_F_X:
+ v_out = count_f ? count_f + x_time : count_f;
+ break;
+ case DISC_OP_AMP_OSCILLATOR_OUT_COUNT_R_X:
+ v_out = count_r ? count_r + x_time : count_r;
+ break;
+ case DISC_OP_AMP_OSCILLATOR_OUT_LOGIC_X:
+ v_out = m_flip_flop + x_time;
+ break;
+ }
+ set_output(0, v_out);
+ m_flip_flop = flip_flop;
+}
+
+#define DIODE_DROP 0.7
+
+DISCRETE_RESET(dss_op_amp_osc)
+{
+ DISCRETE_DECLARE_INFO(discrete_op_amp_osc_info)
+
+ const double *r_info_ptr;
+ int loop;
+
+ double i1 = 0; /* inverting input current */
+ double i2 = 0; /* non-inverting input current */
+
+ /* link to resistor static or node values */
+ r_info_ptr = &info->r1;
+ for (loop = 0; loop < 8; loop ++)
+ {
+ m_r[loop] = m_device->node_output_ptr(*r_info_ptr);
+ if (m_r[loop] == NULL)
+ m_r[loop] = r_info_ptr;
+ r_info_ptr++;
+ }
+
+ m_is_linear_charge = 1;
+ m_output_type = info->type & DISC_OP_AMP_OSCILLATOR_OUT_MASK;
+ m_type = info->type & DISC_OP_AMP_OSCILLATOR_TYPE_MASK;
+ m_charge_rc[0] = 0;
+ m_charge_rc[1] = 0;
+ m_charge_v[0] = 0;
+ m_charge_v[1] = 0;
+ m_i_fixed = 0;
+ m_has_enable = 0;
+
+ switch (m_type)
+ {
+ case DISC_OP_AMP_OSCILLATOR_VCO_1:
+ /* The charge rates vary depending on vMod so they are not precalculated. */
+ /* Charges while FlipFlop High */
+ m_flip_flop_xor = 0;
+ /* Work out the Non-inverting Schmitt thresholds. */
+ m_temp1 = (info->vP / 2) / info->r4;
+ m_temp2 = (info->vP - OP_AMP_VP_RAIL_OFFSET) / info->r3;
+ m_temp3 = 1.0 / (1.0 / info->r3 + 1.0 / info->r4);
+ m_threshold_low = m_temp1 * m_temp3;
+ m_threshold_high = (m_temp1 + m_temp2) * m_temp3;
+ /* There is no charge on the cap so the schmitt goes high at init. */
+ m_flip_flop = 1;
+ /* Setup some commonly used stuff */
+ m_temp1 = info->r5 / (info->r2 + info->r5); /* voltage ratio across r5 */
+ m_temp2 = info->r6 / (info->r1 + info->r6); /* voltage ratio across r6 */
+ m_temp3 = 1.0 / (1.0 / info->r1 + 1.0 / info->r6); /* input resistance when r6 switched in */
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_1 | DISC_OP_AMP_IS_NORTON:
+ /* Charges while FlipFlop High */
+ m_flip_flop_xor = 0;
+ /* There is no charge on the cap so the schmitt inverter goes high at init. */
+ m_flip_flop = 1;
+ /* setup current if using real enable */
+ if (info->r6 > 0)
+ {
+ m_has_enable = 1;
+ m_i_enable = (info->vP - OP_AMP_NORTON_VBE) / (info->r6 + RES_K(1));
+ }
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_2 | DISC_OP_AMP_IS_NORTON:
+ m_is_linear_charge = 0;
+ /* First calculate the parallel charge resistors and volatges. */
+ /* We can cheat and just calcuate the charges in the working area. */
+ /* The thresholds are well past the effect of the voltage drop */
+ /* and the component tolerances far exceed the .5V charge difference */
+ if (info->r1 != 0)
+ {
+ m_charge_rc[0] = 1.0 / info->r1;
+ m_charge_rc[1] = 1.0 / info->r1;
+ m_charge_v[1] = (info->vP - OP_AMP_NORTON_VBE) / info->r1;
+ }
+ if (info->r5 != 0)
+ {
+ m_charge_rc[0] += 1.0 / info->r5;
+ m_charge_v[0] = DIODE_DROP / info->r5;
+ }
+ if (info->r6 != 0)
+ {
+ m_charge_rc[1] += 1.0 / info->r6;
+ m_charge_v[1] += (info->vP - OP_AMP_NORTON_VBE - DIODE_DROP) / info->r6;
+ }
+ m_charge_rc[0] += 1.0 / info->r2;
+ m_charge_rc[0] = 1.0 / m_charge_rc[0];
+ m_charge_v[0] += OP_AMP_NORTON_VBE / info->r2;
+ m_charge_v[0] *= m_charge_rc[0];
+ m_charge_rc[1] += 1.0 / info->r2;
+ m_charge_rc[1] = 1.0 / m_charge_rc[1];
+ m_charge_v[1] += OP_AMP_NORTON_VBE / info->r2;
+ m_charge_v[1] *= m_charge_rc[1];
+
+ m_charge_rc[0] *= info->c;
+ m_charge_rc[1] *= info->c;
+ m_charge_exp[0] = RC_CHARGE_EXP(m_charge_rc[0]);
+ m_charge_exp[1] = RC_CHARGE_EXP(m_charge_rc[1]);
+ m_threshold_low = (info->vP - OP_AMP_NORTON_VBE) / info->r4;
+ m_threshold_high = m_threshold_low + (info->vP - 2 * OP_AMP_NORTON_VBE) / info->r3;;
+ m_threshold_low = m_threshold_low * info->r2 + OP_AMP_NORTON_VBE;
+ m_threshold_high = m_threshold_high * info->r2 + OP_AMP_NORTON_VBE;
+
+ /* There is no charge on the cap so the schmitt inverter goes high at init. */
+ m_flip_flop = 1;
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_VCO_1 | DISC_OP_AMP_IS_NORTON:
+ /* Charges while FlipFlop Low */
+ m_flip_flop_xor = 1;
+ /* There is no charge on the cap so the schmitt goes low at init. */
+ m_flip_flop = 0;
+ /* The charge rates vary depending on vMod so they are not precalculated. */
+ /* But we can precalculate the fixed currents. */
+ if (info->r6 != 0) m_i_fixed += info->vP / info->r6;
+ m_i_fixed += OP_AMP_NORTON_VBE / info->r1;
+ m_i_fixed += OP_AMP_NORTON_VBE / info->r2;
+ /* Work out the input resistance to be used later to calculate the Millman voltage. */
+ m_r_total = 1.0 / info->r1 + 1.0 / info->r2 + 1.0 / info->r7;
+ if (info->r6) m_r_total += 1.0 / info->r6;
+ if (info->r8) m_r_total += 1.0 / info->r8;
+ m_r_total = 1.0 / m_r_total;
+ /* Work out the Non-inverting Schmitt thresholds. */
+ i1 = (info->vP - OP_AMP_NORTON_VBE) / info->r5;
+ i2 = (info->vP - OP_AMP_NORTON_VBE - OP_AMP_NORTON_VBE) / info->r4;
+ m_threshold_low = (i1 - i2) * info->r3 + OP_AMP_NORTON_VBE;
+ i2 = (0.0 - OP_AMP_NORTON_VBE) / info->r4;
+ m_threshold_high = (i1 - i2) * info->r3 + OP_AMP_NORTON_VBE;
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_VCO_2 | DISC_OP_AMP_IS_NORTON:
+ /* Charges while FlipFlop High */
+ m_flip_flop_xor = 0;
+ /* There is no charge on the cap so the schmitt inverter goes high at init. */
+ m_flip_flop = 1;
+ /* Work out the charge rates. */
+ m_temp1 = (info->vP - OP_AMP_NORTON_VBE) / info->r2;
+ m_temp2 = (info->vP - OP_AMP_NORTON_VBE) * (1.0 / info->r2 + 1.0 / info->r6);
+ /* Work out the Inverting Schmitt thresholds. */
+ i1 = (info->vP - OP_AMP_NORTON_VBE) / info->r5;
+ i2 = (0.0 - OP_AMP_NORTON_VBE) / info->r4;
+ m_threshold_low = (i1 + i2) * info->r3 + OP_AMP_NORTON_VBE;
+ i2 = (info->vP - OP_AMP_NORTON_VBE - OP_AMP_NORTON_VBE) / info->r4;
+ m_threshold_high = (i1 + i2) * info->r3 + OP_AMP_NORTON_VBE;
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_VCO_3 | DISC_OP_AMP_IS_NORTON:
+ /* Charges while FlipFlop High */
+ m_flip_flop_xor = 0;
+ /* There is no charge on the cap so the schmitt inverter goes high at init. */
+ m_flip_flop = 1;
+ /* setup current if using real enable */
+ if (info->r8 > 0)
+ {
+ m_has_enable = 1;
+ m_i_enable = (info->vP - OP_AMP_NORTON_VBE) / (info->r8 + RES_K(1));
+ }
+ /* Work out the charge rates. */
+ /* The charge rates vary depending on vMod so they are not precalculated. */
+ /* But we can precalculate the fixed currents. */
+ if (info->r7 != 0) m_i_fixed = (info->vP - OP_AMP_NORTON_VBE) / info->r7;
+ m_temp1 = (info->vP - OP_AMP_NORTON_VBE - OP_AMP_NORTON_VBE) / info->r2;
+ /* Work out the Inverting Schmitt thresholds. */
+ i1 = (info->vP - OP_AMP_NORTON_VBE) / info->r5;
+ i2 = (0.0 - OP_AMP_NORTON_VBE) / info->r4;
+ m_threshold_low = (i1 + i2) * info->r3 + OP_AMP_NORTON_VBE;
+ i2 = (info->vP - OP_AMP_NORTON_VBE - OP_AMP_NORTON_VBE) / info->r4;
+ m_threshold_high = (i1 + i2) * info->r3 + OP_AMP_NORTON_VBE;
+ break;
+ }
+
+ m_v_out_high = info->vP - ((m_type & DISC_OP_AMP_IS_NORTON) ? OP_AMP_NORTON_VBE : OP_AMP_VP_RAIL_OFFSET);
+ m_v_cap = 0;
+
+ this->step();
+}
+
+
+/************************************************************************
+ *
+ * DSS_SAWTOOTHWAVE - Usage of node_description values for step function
+ *
+ * input0 - Enable input value
+ * input1 - Frequency input value
+ * input2 - Amplitde input value
+ * input3 - DC Bias Value
+ * input4 - Gradient
+ * input5 - Initial Phase
+ *
+ ************************************************************************/
+#define DSS_SAWTOOTHWAVE__ENABLE DISCRETE_INPUT(0)
+#define DSS_SAWTOOTHWAVE__FREQ DISCRETE_INPUT(1)
+#define DSS_SAWTOOTHWAVE__AMP DISCRETE_INPUT(2)
+#define DSS_SAWTOOTHWAVE__BIAS DISCRETE_INPUT(3)
+#define DSS_SAWTOOTHWAVE__GRAD DISCRETE_INPUT(4)
+#define DSS_SAWTOOTHWAVE__PHASE DISCRETE_INPUT(5)
+
+DISCRETE_STEP(dss_sawtoothwave)
+{
+ double v_out;
+
+ if(DSS_SAWTOOTHWAVE__ENABLE)
+ {
+ v_out = (m_type == 0) ? m_phase * (DSS_SAWTOOTHWAVE__AMP / (2.0 * M_PI)) : DSS_SAWTOOTHWAVE__AMP - (m_phase * (DSS_SAWTOOTHWAVE__AMP / (2.0 * M_PI)));
+ v_out -= DSS_SAWTOOTHWAVE__AMP / 2.0;
+ /* Add DC Bias component */
+ v_out = v_out + DSS_SAWTOOTHWAVE__BIAS;
+ }
+ else
+ {
+ v_out = 0;
+ }
+ set_output(0, v_out);
+
+ /* Work out the phase step based on phase/freq & sample rate */
+ /* The enable input only curtails output, phase rotation */
+ /* still occurs */
+ /* phase step = 2Pi/(output period/sample period) */
+ /* boils out to */
+ /* phase step = (2Pi*output freq)/sample freq) */
+ /* Also keep the new phasor in the 2Pi range. */
+ m_phase = fmod((m_phase + ((2.0 * M_PI * DSS_SAWTOOTHWAVE__FREQ) / this->sample_rate())), 2.0 * M_PI);
+}
+
+DISCRETE_RESET(dss_sawtoothwave)
+{
+ double start;
+
+ /* Establish starting phase, convert from degrees to radians */
+ start = (DSS_SAWTOOTHWAVE__PHASE / 360.0) * (2.0 * M_PI);
+ /* Make sure its always mod 2Pi */
+ m_phase = fmod(start, 2.0 * M_PI);
+
+ /* Invert gradient depending on sawtooth type /|/|/|/|/| or |\|\|\|\|\ */
+ m_type = (DSS_SAWTOOTHWAVE__GRAD) ? 1 : 0;
+
+ /* Step the node to set the output */
+ this->step();
+}
+
+
+/************************************************************************
+ *
+ * DSS_SCHMITT_OSC - Schmitt feedback oscillator
+ *
+ * input0 - Enable input value
+ * input1 - Vin
+ * input2 - Amplitude
+ *
+ * also passed discrete_schmitt_osc_disc structure
+ *
+ * Mar 2004, D Renaud.
+ ************************************************************************/
+#define DSS_SCHMITT_OSC__ENABLE (int)DISCRETE_INPUT(0)
+#define DSS_SCHMITT_OSC__VIN DISCRETE_INPUT(1)
+#define DSS_SCHMITT_OSC__AMP DISCRETE_INPUT(2)
+
+DISCRETE_STEP(dss_schmitt_osc)
+{
+ DISCRETE_DECLARE_INFO(discrete_schmitt_osc_desc)
+
+ double supply, v_cap, new_vCap, t, exponent;
+ double v_out = 0;
+
+ /* We will always oscillate. The enable just affects the output. */
+ v_cap = m_v_cap;
+ exponent = m_exponent;
+
+ /* Keep looping until all toggling in time sample is used up. */
+ do
+ {
+ t = 0;
+ /* The charging voltage to the cap is the sum of the input voltage and the gate
+ * output voltage in the ratios determined by their resistors in a divider network.
+ * The input voltage is selectable as straight voltage in or logic level that will
+ * use vGate as its voltage. Note that ration_in is just the ratio of the total
+ * voltage and needs to be multipled by the input voltage. ratio_feedback has
+ * already been multiplied by vGate to save time because that voltage never changes. */
+ supply = m_input_is_voltage ? m_ration_in * DSS_SCHMITT_OSC__VIN : (DSS_SCHMITT_OSC__VIN ? m_ration_in * info->vGate : 0);
+ supply += (m_state ? m_ratio_feedback : 0);
+ new_vCap = v_cap + ((supply - v_cap) * exponent);
+ if (m_state)
+ {
+ /* Charging */
+ /* has it charged past upper limit? */
+ if (new_vCap > info->trshRise)
+ {
+ /* calculate the overshoot time */
+ t = m_rc * log(1.0 / (1.0 - ((new_vCap - info->trshRise) / (info->vGate - v_cap))));
+ /* calculate new exponent because of reduced time */
+ exponent = RC_CHARGE_EXP_DT(m_rc, t);
+ v_cap = new_vCap = info->trshRise;
+ m_state = 0;
+ }
+ }
+ else
+ {
+ /* Discharging */
+ /* has it discharged past lower limit? */
+ if (new_vCap < info->trshFall)
+ {
+ /* calculate the overshoot time */
+ t = m_rc * log(1.0 / (1.0 - ((info->trshFall - new_vCap) / v_cap)));
+ /* calculate new exponent because of reduced time */
+ exponent = RC_CHARGE_EXP_DT(m_rc, t);
+ v_cap = new_vCap = info->trshFall;
+ m_state = 1;
+ }
+ }
+ } while(t);
+
+ m_v_cap = new_vCap;
+
+ switch (m_enable_type)
+ {
+ case DISC_SCHMITT_OSC_ENAB_IS_AND:
+ v_out = DSS_SCHMITT_OSC__ENABLE && m_state;
+ break;
+ case DISC_SCHMITT_OSC_ENAB_IS_NAND:
+ v_out = !(DSS_SCHMITT_OSC__ENABLE && m_state);
+ break;
+ case DISC_SCHMITT_OSC_ENAB_IS_OR:
+ v_out = DSS_SCHMITT_OSC__ENABLE || m_state;
+ break;
+ case DISC_SCHMITT_OSC_ENAB_IS_NOR:
+ v_out = !(DSS_SCHMITT_OSC__ENABLE || m_state);
+ break;
+ }
+ v_out *= DSS_SCHMITT_OSC__AMP;
+ set_output(0, v_out);
+}
+
+DISCRETE_RESET(dss_schmitt_osc)
+{
+ DISCRETE_DECLARE_INFO(discrete_schmitt_osc_desc)
+
+ double rSource;
+
+ m_enable_type = info->options & DISC_SCHMITT_OSC_ENAB_MASK;
+ m_input_is_voltage = (info->options & DISC_SCHMITT_OSC_IN_IS_VOLTAGE) ? 1 : 0;
+
+ /* The 2 resistors make a voltage divider, so their ratios add together
+ * to make the charging voltage. */
+ m_ration_in = info->rFeedback / (info->rIn + info->rFeedback);
+ m_ratio_feedback = info->rIn / (info->rIn + info->rFeedback) * info->vGate;
+
+ /* The voltage source resistance works out to the 2 resistors in parallel.
+ * So use this for the RC charge constant. */
+ rSource = 1.0 / ((1.0 / info->rIn) + (1.0 / info->rFeedback));
+ m_rc = rSource * info->c;
+ m_exponent = RC_CHARGE_EXP(m_rc);
+
+ /* Cap is at 0V on power up. Causing output to be high. */
+ m_v_cap = 0;
+ m_state = 1;
+
+ set_output(0, info->options ? 0 : DSS_SCHMITT_OSC__AMP);
+}
+
+
+/************************************************************************
+ *
+ * DSS_SINEWAVE - Usage of node_description values for step function
+ *
+ * input0 - Enable input value
+ * input1 - Frequency input value
+ * input2 - Amplitude input value
+ * input3 - DC Bias
+ * input4 - Starting phase
+ *
+ ************************************************************************/
+#define DSS_SINEWAVE__ENABLE DISCRETE_INPUT(0)
+#define DSS_SINEWAVE__FREQ DISCRETE_INPUT(1)
+#define DSS_SINEWAVE__AMPL DISCRETE_INPUT(2)
+#define DSS_SINEWAVE__BIAS DISCRETE_INPUT(3)
+#define DSS_SINEWAVE__PHASE DISCRETE_INPUT(4)
+
+DISCRETE_STEP(dss_sinewave)
+{
+ /* Set the output */
+ if(DSS_SINEWAVE__ENABLE)
+ {
+ set_output(0, (DSS_SINEWAVE__AMPL / 2.0) * sin(m_phase) + DSS_SINEWAVE__BIAS);
+ /* Add DC Bias component */
+ }
+ else
+ {
+ set_output(0, 0);
+ }
+
+ /* Work out the phase step based on phase/freq & sample rate */
+ /* The enable input only curtails output, phase rotation */
+ /* still occurs */
+ /* phase step = 2Pi/(output period/sample period) */
+ /* boils out to */
+ /* phase step = (2Pi*output freq)/sample freq) */
+ /* Also keep the new phasor in the 2Pi range. */
+ m_phase=fmod((m_phase + ((2.0 * M_PI * DSS_SINEWAVE__FREQ) / this->sample_rate())), 2.0 * M_PI);
+}
+
+DISCRETE_RESET(dss_sinewave)
+{
+ double start;
+
+ /* Establish starting phase, convert from degrees to radians */
+ start = (DSS_SINEWAVE__PHASE / 360.0) * (2.0 * M_PI);
+ /* Make sure its always mod 2Pi */
+ m_phase = fmod(start, 2.0 * M_PI);
+ /* Step the output to make it correct */
+ this->step();
+}
+
+
+/************************************************************************
+ *
+ * DSS_SQUAREWAVE - Usage of node_description values for step function
+ *
+ * input0 - Enable input value
+ * input1 - Frequency input value
+ * input2 - Amplitude input value
+ * input3 - Duty Cycle
+ * input4 - DC Bias level
+ * input5 - Start Phase
+ *
+ ************************************************************************/
+#define DSS_SQUAREWAVE__ENABLE DISCRETE_INPUT(0)
+#define DSS_SQUAREWAVE__FREQ DISCRETE_INPUT(1)
+#define DSS_SQUAREWAVE__AMP DISCRETE_INPUT(2)
+#define DSS_SQUAREWAVE__DUTY DISCRETE_INPUT(3)
+#define DSS_SQUAREWAVE__BIAS DISCRETE_INPUT(4)
+#define DSS_SQUAREWAVE__PHASE DISCRETE_INPUT(5)
+
+DISCRETE_STEP(dss_squarewave)
+{
+ /* Establish trigger phase from duty */
+ m_trigger=((100-DSS_SQUAREWAVE__DUTY)/100)*(2.0*M_PI);
+
+ /* Set the output */
+ if(DSS_SQUAREWAVE__ENABLE)
+ {
+ if(m_phase>m_trigger)
+ set_output(0, DSS_SQUAREWAVE__AMP / 2.0 + DSS_SQUAREWAVE__BIAS);
+ else
+ set_output(0, - DSS_SQUAREWAVE__AMP / 2.0 + DSS_SQUAREWAVE__BIAS);
+ /* Add DC Bias component */
+ }
+ else
+ {
+ set_output(0, 0);
+ }
+
+ /* Work out the phase step based on phase/freq & sample rate */
+ /* The enable input only curtails output, phase rotation */
+ /* still occurs */
+ /* phase step = 2Pi/(output period/sample period) */
+ /* boils out to */
+ /* phase step = (2Pi*output freq)/sample freq) */
+ /* Also keep the new phasor in the 2Pi range. */
+ m_phase=fmod(m_phase + ((2.0 * M_PI * DSS_SQUAREWAVE__FREQ) / this->sample_rate()), 2.0 * M_PI);
+}
+
+DISCRETE_RESET(dss_squarewave)
+{
+ double start;
+
+ /* Establish starting phase, convert from degrees to radians */
+ start = (DSS_SQUAREWAVE__PHASE / 360.0) * (2.0 * M_PI);
+ /* Make sure its always mod 2Pi */
+ m_phase = fmod(start, 2.0 * M_PI);
+
+ /* Step the output */
+ this->step();
+}
+
+/************************************************************************
+ *
+ * DSS_SQUAREWFIX - Usage of node_description values for step function
+ *
+ * input0 - Enable input value
+ * input1 - Frequency input value
+ * input2 - Amplitude input value
+ * input3 - Duty Cycle
+ * input4 - DC Bias level
+ * input5 - Start Phase
+ *
+ ************************************************************************/
+#define DSS_SQUAREWFIX__ENABLE DISCRETE_INPUT(0)
+#define DSS_SQUAREWFIX__FREQ DISCRETE_INPUT(1)
+#define DSS_SQUAREWFIX__AMP DISCRETE_INPUT(2)
+#define DSS_SQUAREWFIX__DUTY DISCRETE_INPUT(3)
+#define DSS_SQUAREWFIX__BIAS DISCRETE_INPUT(4)
+#define DSS_SQUAREWFIX__PHASE DISCRETE_INPUT(5)
+
+DISCRETE_STEP(dss_squarewfix)
+{
+ m_t_left -= m_sample_step;
+
+ /* The enable input only curtails output, phase rotation still occurs */
+ while (m_t_left <= 0)
+ {
+ m_flip_flop = m_flip_flop ? 0 : 1;
+ m_t_left += m_flip_flop ? m_t_on : m_t_off;
+ }
+
+ if(DSS_SQUAREWFIX__ENABLE)
+ {
+ /* Add gain and DC Bias component */
+
+ m_t_off = 1.0 / DSS_SQUAREWFIX__FREQ; /* cycle time */
+ m_t_on = m_t_off * (DSS_SQUAREWFIX__DUTY / 100.0);
+ m_t_off -= m_t_on;
+
+ set_output(0, (m_flip_flop ? DSS_SQUAREWFIX__AMP / 2.0 : -(DSS_SQUAREWFIX__AMP / 2.0)) + DSS_SQUAREWFIX__BIAS);
+ }
+ else
+ {
+ set_output(0, 0);
+ }
+}
+
+DISCRETE_RESET(dss_squarewfix)
+{
+ m_sample_step = 1.0 / this->sample_rate();
+ m_flip_flop = 1;
+
+ /* Do the intial time shift and convert freq to off/on times */
+ m_t_off = 1.0 / DSS_SQUAREWFIX__FREQ; /* cycle time */
+ m_t_left = DSS_SQUAREWFIX__PHASE / 360.0; /* convert start phase to % */
+ m_t_left = m_t_left - (int)m_t_left; /* keep % between 0 & 1 */
+ m_t_left = (m_t_left < 0) ? 1.0 + m_t_left : m_t_left; /* if - then flip to + phase */
+ m_t_left *= m_t_off;
+ m_t_on = m_t_off * (DSS_SQUAREWFIX__DUTY / 100.0);
+ m_t_off -= m_t_on;
+
+ m_t_left = -m_t_left;
+
+ /* toggle output and work out intial time shift */
+ while (m_t_left <= 0)
+ {
+ m_flip_flop = m_flip_flop ? 0 : 1;
+ m_t_left += m_flip_flop ? m_t_on : m_t_off;
+ }
+
+ /* Step the output */
+ this->step();
+}
+
+
+/************************************************************************
+ *
+ * DSS_SQUAREWAVE2 - Usage of node_description values
+ *
+ * input0 - Enable input value
+ * input1 - Amplitude input value
+ * input2 - OFF Time
+ * input3 - ON Time
+ * input4 - DC Bias level
+ * input5 - Initial Time Shift
+ *
+ ************************************************************************/
+#define DSS_SQUAREWAVE2__ENABLE DISCRETE_INPUT(0)
+#define DSS_SQUAREWAVE2__AMP DISCRETE_INPUT(1)
+#define DSS_SQUAREWAVE2__T_OFF DISCRETE_INPUT(2)
+#define DSS_SQUAREWAVE2__T_ON DISCRETE_INPUT(3)
+#define DSS_SQUAREWAVE2__BIAS DISCRETE_INPUT(4)
+#define DSS_SQUAREWAVE2__SHIFT DISCRETE_INPUT(5)
+
+DISCRETE_STEP(dss_squarewave2)
+{
+ double newphase;
+
+ if(DSS_SQUAREWAVE2__ENABLE)
+ {
+ /* Establish trigger phase from time periods */
+ m_trigger = (DSS_SQUAREWAVE2__T_OFF / (DSS_SQUAREWAVE2__T_OFF + DSS_SQUAREWAVE2__T_ON)) * (2.0 * M_PI);
+
+ /* Work out the phase step based on phase/freq & sample rate */
+ /* The enable input only curtails output, phase rotation */
+ /* still occurs */
+
+ /* phase step = 2Pi/(output period/sample period) */
+ /* boils out to */
+ /* phase step = 2Pi/(output period*sample freq) */
+ newphase = m_phase + ((2.0 * M_PI) / ((DSS_SQUAREWAVE2__T_OFF + DSS_SQUAREWAVE2__T_ON) * this->sample_rate()));
+ /* Keep the new phasor in the 2Pi range.*/
+ m_phase = fmod(newphase, 2.0 * M_PI);
+
+ /* Add DC Bias component */
+ if(m_phase>m_trigger)
+ set_output(0, DSS_SQUAREWAVE2__AMP / 2.0 + DSS_SQUAREWAVE2__BIAS);
+ else
+ set_output(0, -DSS_SQUAREWAVE2__AMP / 2.0 + DSS_SQUAREWAVE2__BIAS);
+ }
+ else
+ {
+ set_output(0, 0);
+ }
+}
+
+DISCRETE_RESET(dss_squarewave2)
+{
+ double start;
+
+ /* Establish starting phase, convert from degrees to radians */
+ /* Only valid if we have set the on/off time */
+ if((DSS_SQUAREWAVE2__T_OFF + DSS_SQUAREWAVE2__T_ON) != 0.0)
+ start = (DSS_SQUAREWAVE2__SHIFT / (DSS_SQUAREWAVE2__T_OFF + DSS_SQUAREWAVE2__T_ON)) * (2.0 * M_PI);
+ else
+ start = 0.0;
+ /* Make sure its always mod 2Pi */
+ m_phase = fmod(start, 2.0 * M_PI);
+
+ /* Step the output */
+ this->step();
+}
+
+/************************************************************************
+ *
+ * DSS_INVERTER_OSC - Usage of node_description values
+ *
+ * input0 - Enable input value
+ * input1 - RC Resistor
+ * input2 - RP Resistor
+ * input3 - C Capacitor
+ * input4 - Desc
+ *
+ ************************************************************************/
+
+/*
+ * Taken from the transfer characteristerics diagram in CD4049UB datasheet (TI)
+ * There is no default trigger point and vI-vO is a continuous function
+ */
+
+inline double DISCRETE_CLASS_FUNC(dss_inverter_osc, tftab)(double x)
+{
+ DISCRETE_DECLARE_INFO(description)
+
+ x = x / info->vB;
+ if (x > 0)
+ return info->vB * exp(-mc_tf_a * pow(x, mc_tf_b));
+ else
+ return info->vB;
+}
+
+inline double DISCRETE_CLASS_FUNC(dss_inverter_osc, tf)(double x)
+{
+ DISCRETE_DECLARE_INFO(description)
+
+ if (x < 0.0)
+ return info->vB;
+ else if (x <= info->vB)
+ return mc_tf_tab[(int)((double)(DSS_INV_TAB_SIZE - 1) * x / info->vB)];
+ else
+ return mc_tf_tab[DSS_INV_TAB_SIZE - 1];
+}
+
+DISCRETE_STEP(dss_inverter_osc)
+{
+ DISCRETE_DECLARE_INFO(description)
+ double diff, vG1, vG2, vG3, vI;
+ double vMix, rMix;
+ int clamped;
+ double v_out;
+
+ /* Get new state */
+ vI = mc_v_cap + mc_v_g2_old;
+ switch (info->options & TYPE_MASK)
+ {
+ case IS_TYPE1:
+ case IS_TYPE3:
+ vG1 = this->tf(vI);
+ vG2 = this->tf(vG1);
+ vG3 = this->tf(vG2);
+ break;
+ case IS_TYPE2:
+ vG1 = 0;
+ vG3 = this->tf(vI);
+ vG2 = this->tf(vG3);
+ break;
+ case IS_TYPE4:
+ vI = MIN(I_ENABLE(), vI + 0.7);
+ vG1 = 0;
+ vG3 = this->tf(vI);
+ vG2 = this->tf(vG3);
+ break;
+ case IS_TYPE5:
+ vI = MAX(I_ENABLE(), vI - 0.7);
+ vG1 = 0;
+ vG3 = this->tf(vI);
+ vG2 = this->tf(vG3);
+ break;
+ default:
+ fatalerror("DISCRETE_INVERTER_OSC - Wrong type on NODE_%02d\n", this->index());
+ }
+
+ clamped = 0;
+ if (info->clamp >= 0.0)
+ {
+ if (vI < -info->clamp)
+ {
+ vI = -info->clamp;
+ clamped = 1;
+ }
+ else if (vI > info->vB+info->clamp)
+ {
+ vI = info->vB + info->clamp;
+ clamped = 1;
+ }
+ }
+
+ switch (info->options & TYPE_MASK)
+ {
+ case IS_TYPE1:
+ case IS_TYPE2:
+ case IS_TYPE3:
+ if (clamped)
+ {
+ double ratio = mc_rp / (mc_rp + mc_r1);
+ diff = vG3 * (ratio)
+ - (mc_v_cap + vG2)
+ + vI * (1.0 - ratio);
+ diff = diff - diff * mc_wc;
+ }
+ else
+ {
+ diff = vG3 - (mc_v_cap + vG2);
+ diff = diff - diff * mc_w;
+ }
+ break;
+ case IS_TYPE4:
+ /* FIXME handle r2 = 0 */
+ rMix = (mc_r1 * mc_r2) / (mc_r1 + mc_r2);
+ vMix = rMix* ((vG3 - vG2) / mc_r1 + (I_MOD() -vG2) / mc_r2);
+ if (vMix < (vI-vG2-0.7))
+ {
+ rMix = 1.0 / rMix + 1.0 / mc_rp;
+ rMix = 1.0 / rMix;
+ vMix = rMix* ( (vG3-vG2) / mc_r1 + (I_MOD() - vG2) / mc_r2
+ + (vI - 0.7 - vG2) / mc_rp);
+ }
+ diff = vMix - mc_v_cap;
+ diff = diff - diff * exp(-this->sample_time() / (mc_c * rMix));
+ break;
+ case IS_TYPE5:
+ /* FIXME handle r2 = 0 */
+ rMix = (mc_r1 * mc_r2) / (mc_r1 + mc_r2);
+ vMix = rMix* ((vG3 - vG2) / mc_r1 + (I_MOD() - vG2) / mc_r2);
+ if (vMix > (vI -vG2 + 0.7))
+ {
+ rMix = 1.0 / rMix + 1.0 / mc_rp;
+ rMix = 1.0 / rMix;
+ vMix = rMix * ( (vG3 - vG2) / mc_r1 + (I_MOD() - vG2) / mc_r2
+ + (vI + 0.7 - vG2) / mc_rp);
+ }
+ diff = vMix - mc_v_cap;
+ diff = diff - diff * exp(-this->sample_time()/(mc_c * rMix));
+ break;
+ default:
+ fatalerror("DISCRETE_INVERTER_OSC - Wrong type on NODE_%02d\n", this->index());
+ }
+
+ mc_v_cap += diff;
+ mc_v_g2_old = vG2;
+
+ if ((info->options & TYPE_MASK) == IS_TYPE3)
+ v_out = vG1;
+ else
+ v_out = vG3;
+
+ if (info->options & OUT_IS_LOGIC)
+ v_out = (v_out > info->vInFall);
+
+ set_output(0, v_out);
+}
+
+DISCRETE_RESET(dss_inverter_osc)
+{
+ DISCRETE_DECLARE_INFO(description)
+
+ int i;
+
+ /* exponent */
+ mc_w = exp(-this->sample_time() / (I_RC() * I_C()));
+ mc_wc = exp(-this->sample_time() / ((I_RC() * I_RP()) / (I_RP() + I_RC()) * I_C()));
+ set_output(0, 0);
+ mc_v_cap = 0;
+ mc_v_g2_old = 0;
+ mc_rp = I_RP();
+ mc_r1 = I_RC();
+ mc_r2 = I_R2();
+ mc_c = I_C();
+ mc_tf_b = (log(0.0 - log(info->vOutLow/info->vB)) - log(0.0 - log((info->vOutHigh/info->vB))) ) / log(info->vInRise / info->vInFall);
+ mc_tf_a = log(0.0 - log(info->vOutLow/info->vB)) - mc_tf_b * log(info->vInRise/info->vB);
+ mc_tf_a = exp(mc_tf_a);
+
+ for (i = 0; i < DSS_INV_TAB_SIZE; i++)
+ {
+ mc_tf_tab[i] = this->tftab((double)i / (double)(DSS_INV_TAB_SIZE - 1) * info->vB);
+ }
+}
+
+/************************************************************************
+ *
+ * DSS_TRIANGLEWAVE - Usage of node_description values for step function
+ *
+ * input0 - Enable input value
+ * input1 - Frequency input value
+ * input2 - Amplitde input value
+ * input3 - DC Bias value
+ * input4 - Initial Phase
+ *
+ ************************************************************************/
+#define DSS_TRIANGLEWAVE__ENABLE DISCRETE_INPUT(0)
+#define DSS_TRIANGLEWAVE__FREQ DISCRETE_INPUT(1)
+#define DSS_TRIANGLEWAVE__AMP DISCRETE_INPUT(2)
+#define DSS_TRIANGLEWAVE__BIAS DISCRETE_INPUT(3)
+#define DSS_TRIANGLEWAVE__PHASE DISCRETE_INPUT(4)
+
+DISCRETE_STEP(dss_trianglewave)
+{
+ if(DSS_TRIANGLEWAVE__ENABLE)
+ {
+ double v_out = m_phase < M_PI ? (DSS_TRIANGLEWAVE__AMP * (m_phase / (M_PI / 2.0) - 1.0)) / 2.0 :
+ (DSS_TRIANGLEWAVE__AMP * (3.0 - m_phase / (M_PI / 2.0))) / 2.0 ;
+
+ /* Add DC Bias component */
+ v_out += DSS_TRIANGLEWAVE__BIAS;
+ set_output(0, v_out);
+ }
+ else
+ {
+ set_output(0, 0);
+ }
+
+ /* Work out the phase step based on phase/freq & sample rate */
+ /* The enable input only curtails output, phase rotation */
+ /* still occurs */
+ /* phase step = 2Pi/(output period/sample period) */
+ /* boils out to */
+ /* phase step = (2Pi*output freq)/sample freq) */
+ /* Also keep the new phasor in the 2Pi range. */
+ m_phase=fmod((m_phase + ((2.0 * M_PI * DSS_TRIANGLEWAVE__FREQ) / this->sample_rate())), 2.0 * M_PI);
+}
+
+DISCRETE_RESET(dss_trianglewave)
+{
+ double start;
+
+ /* Establish starting phase, convert from degrees to radians */
+ start = (DSS_TRIANGLEWAVE__PHASE / 360.0) * (2.0 * M_PI);
+ /* Make sure its always mod 2Pi */
+ m_phase=fmod(start, 2.0 * M_PI);
+
+ /* Step to set the output */
+ this->step();
+}
+
+
+/************************************************************************
+ *
+ * DSS_ADSR - Attack Decay Sustain Release
+ *
+ * input0 - Enable input value
+ * input1 - Trigger value
+ * input2 - gain scaling factor
+ *
+ ************************************************************************/
+#define DSS_ADSR__ENABLE DISCRETE_INPUT(0)
+
+DISCRETE_STEP(dss_adsrenv)
+{
+ if(DSS_ADSR__ENABLE)
+ {
+ set_output(0, 0);
+ }
+ else
+ {
+ set_output(0, 0);
+ }
+}
+
+
+DISCRETE_RESET(dss_adsrenv)
+{
+ this->step();
+}
diff --git a/src/devices/sound/discrete.c b/src/devices/sound/discrete.c
new file mode 100644
index 00000000000..ae278d503db
--- /dev/null
+++ b/src/devices/sound/discrete.c
@@ -0,0 +1,1148 @@
+// license:???
+// copyright-holders:K.Wilkins, Derrick Renaud, Frank Palazzolo, Couriersud
+/************************************************************************
+ *
+ * MAME - Discrete sound system emulation library
+ *
+ * Written by K.Wilkins (mame@esplexo.co.uk)
+ *
+ * (c) K.Wilkins 2000
+ *
+ * Coding started in November 2000
+ * KW - Added Sawtooth waveforms Feb2003
+ *
+ ***********************************************************************
+ *
+ * SEE DISCRETE.H for documentation on usage
+ *
+ ***********************************************************************
+ *
+ * Each sound primative DSS_xxxx or DST_xxxx has its own implementation
+ * file. All discrete sound primatives MUST implement the following
+ * API:
+ *
+ * dsX_NAME_step(inputs, context, float timestep) - Perform time step
+ * return output value
+ * dsX_NAME_reset(context) - Reset to initial state
+ *
+ * Core software takes care of traversing the netlist in the correct
+ * order
+ *
+ * device_start - Read Node list, initialise & reset
+ * device_stop - Shutdown discrete sound system
+ * device_reset - Put sound system back to time 0
+ * discrete_stream_update() - This does the real update to the sim
+ *
+ ************************************************************************/
+
+#include "emu.h"
+#include "wavwrite.h"
+#include "discrete.h"
+
+// for now, make buggy GCC/Mingw STFU about I64FMT
+#if (defined(__MINGW32__) && (__GNUC__ >= 5))
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat"
+#pragma GCC diagnostic ignored "-Wformat-extra-args"
+#endif
+
+
+/* for_each collides with c++ standard libraries - include it here */
+#define for_each(_T, _e, _l) for (_T _e = (_l)->begin_ptr() ; _e <= (_l)->end_ptr(); _e++)
+
+// device type definition
+const device_type DISCRETE = &device_creator<discrete_sound_device>;
+
+/*************************************
+ *
+ * Performance
+ *
+ *************************************/
+
+/*
+ * Normally, the discrete core processes 960 samples per update.
+ * With the various buffers involved, this on a Core2 is not as
+ * performant as processing 240 samples 4 times.
+ * The setting most probably depends on CPU and which modules are
+ * run and how many tasks are defined.
+ *
+ * Values < 32 exhibit poor performance (too much overhead) while
+ * Values > 500 have a slightly worse performace (too much cache misses?).
+ */
+
+#define MAX_SAMPLES_PER_TASK_SLICE (960/4)
+
+/*************************************
+ *
+ * Debugging
+ *
+ *************************************/
+
+#define DISCRETE_DEBUGLOG (0)
+
+/*************************************
+ *
+ * Use tasks ?
+ *
+ *************************************/
+
+#define USE_DISCRETE_TASKS (1)
+
+/*************************************
+ *
+ * Internal classes
+ *
+ *************************************/
+
+struct output_buffer
+{
+ double *node_buf;
+ const double *source;
+ volatile double *ptr;
+ int node_num;
+};
+
+struct input_buffer
+{
+ volatile const double *ptr; /* pointer into linked_outbuf.nodebuf */
+ output_buffer * linked_outbuf; /* what output are we connected to ? */
+ double buffer; /* input[] will point here */
+};
+
+class discrete_task
+{
+ friend class discrete_device;
+public:
+ virtual ~discrete_task(void) { }
+
+ inline void step_nodes(void);
+ inline bool lock_threadid(INT32 threadid)
+ {
+ INT32 prev_id;
+ prev_id = compare_exchange32(&m_threadid, -1, threadid);
+ return (prev_id == -1 && m_threadid == threadid);
+ }
+ inline void unlock(void) { m_threadid = -1; }
+
+ //const linked_list_entry *list;
+ node_step_list_t step_list;
+
+ /* list of source nodes */
+ vector_t<input_buffer> source_list; /* discrete_source_node */
+
+ int task_group;
+
+
+protected:
+ discrete_task(discrete_device &pdev)
+ : task_group(0), m_device(pdev), m_threadid(-1)
+ {
+ source_list.clear();
+ step_list.clear();
+ m_buffers.clear();
+ }
+
+ static void *task_callback(void *param, int threadid);
+ inline bool process(void);
+
+ void check(discrete_task *dest_task);
+ void prepare_for_queue(int samples);
+
+ vector_t<output_buffer> m_buffers;
+ discrete_device & m_device;
+
+private:
+ volatile INT32 m_threadid;
+ volatile int m_samples;
+
+};
+
+
+/*************************************
+ *
+ * Included simulation objects
+ *
+ *************************************/
+
+#include "disc_sys.inc" /* discrete core modules and support functions */
+#include "disc_wav.inc" /* Wave sources - SINE/SQUARE/NOISE/etc */
+#include "disc_mth.inc" /* Math Devices - ADD/GAIN/etc */
+#include "disc_inp.inc" /* Input Devices - INPUT/CONST/etc */
+#include "disc_flt.inc" /* Filter Devices - RCF/HPF/LPF */
+#include "disc_dev.inc" /* Popular Devices - NE555/etc */
+
+/*************************************
+ *
+ * INLINEs
+ *
+ *************************************/
+
+
+
+/*************************************
+ *
+ * Task implementation
+ *
+ *************************************/
+
+inline void discrete_task::step_nodes(void)
+{
+ for_each(input_buffer *, sn, &source_list)
+ {
+ sn->buffer = *sn->ptr++;
+ }
+
+ if (EXPECTED(!m_device.profiling()))
+ {
+ for_each(discrete_step_interface **, entry, &step_list)
+ {
+ /* Now step the node */
+ (*entry)->step();
+ }
+ }
+ else
+ {
+ osd_ticks_t last = get_profile_ticks();
+
+ for_each(discrete_step_interface **, entry, &step_list)
+ {
+ discrete_step_interface *node = *entry;
+
+ node->run_time -= last;
+ node->step();
+ last = get_profile_ticks();
+ node->run_time += last;
+ }
+ }
+
+ /* buffer the outputs */
+ for_each(output_buffer *, outbuf, &m_buffers)
+ *(outbuf->ptr++) = *outbuf->source;
+}
+
+void *discrete_task::task_callback(void *param, int threadid)
+{
+ task_list_t *list = (task_list_t *) param;
+ do
+ {
+ for_each(discrete_task **, task, list)
+ {
+ /* try to lock */
+ if ((*task)->lock_threadid(threadid))
+ {
+ if (!(*task)->process())
+ return NULL;
+ (*task)->unlock();
+ }
+ }
+ } while (1);
+
+ return NULL;
+}
+
+bool discrete_task::process(void)
+{
+ int samples = MIN(m_samples, MAX_SAMPLES_PER_TASK_SLICE);
+
+ /* check dependencies */
+ for_each(input_buffer *, sn, &source_list)
+ {
+ int avail;
+
+ avail = sn->linked_outbuf->ptr - sn->ptr;
+ assert_always(avail >= 0, "task_callback: available samples are negative");
+ if (avail < samples)
+ samples = avail;
+ }
+
+ m_samples -= samples;
+ assert_always(m_samples >=0, "task_callback: task_samples got negative");
+ while (samples > 0)
+ {
+ /* step */
+ step_nodes();
+ samples--;
+ }
+ if (m_samples == 0)
+ {
+ /* return and keep the task locked so it is not picked up by other worker threads */
+ return false;
+ }
+ return true;
+}
+
+void discrete_task::prepare_for_queue(int samples)
+{
+ m_samples = samples;
+ /* set up task buffers */
+ for_each(output_buffer *, ob, &m_buffers)
+ ob->ptr = ob->node_buf;
+
+ /* initialize sources */
+ for_each(input_buffer *, sn, &source_list)
+ {
+ sn->ptr = sn->linked_outbuf->node_buf;
+ }
+}
+
+void discrete_task::check(discrete_task *dest_task)
+{
+ int inputnum;
+
+ /* Determine, which nodes in the task are referenced by nodes in dest_task
+ * and add them to the list of nodes to be buffered for further processing
+ */
+ for_each(discrete_step_interface **, node_entry, &step_list)
+ {
+ discrete_base_node *task_node = (*node_entry)->self;
+
+ for_each(discrete_step_interface **, step_entry, &dest_task->step_list)
+ {
+ discrete_base_node *dest_node = (*step_entry)->self;
+
+ /* loop over all active inputs */
+ for (inputnum = 0; inputnum < dest_node->active_inputs(); inputnum++)
+ {
+ int inputnode_num = dest_node->input_node(inputnum);
+ if IS_VALUE_A_NODE(inputnode_num)
+ {
+ /* Fixme: sub nodes ! */
+ if (NODE_DEFAULT_NODE(task_node->block_node()) == NODE_DEFAULT_NODE(inputnode_num))
+ {
+ input_buffer source;
+ int i, found = -1;
+ output_buffer *pbuf = NULL;
+
+ for (i = 0; i < m_buffers.count(); i++)
+// if (m_buffers[i].node->block_node() == inputnode_num)
+ if (m_buffers[i].node_num == inputnode_num)
+ {
+ found = i;
+ pbuf = &m_buffers[i];
+ break;
+ }
+
+ if (found<0)
+ {
+ output_buffer buf;
+
+ buf.node_buf = auto_alloc_array(m_device.machine(), double,
+ ((task_node->sample_rate() + sound_manager::STREAMS_UPDATE_FREQUENCY) / sound_manager::STREAMS_UPDATE_FREQUENCY));
+ buf.ptr = buf.node_buf;
+ buf.source = dest_node->m_input[inputnum];
+ buf.node_num = inputnode_num;
+ //buf.node = device->discrete_find_node(inputnode);
+ i = m_buffers.count();
+ pbuf = m_buffers.add(buf);
+ }
+ m_device.discrete_log("dso_task_start - buffering %d(%d) in task %p group %d referenced by %d group %d", NODE_INDEX(inputnode_num), NODE_CHILD_NODE_NUM(inputnode_num), this, task_group, dest_node->index(), dest_task->task_group);
+
+ /* register into source list */
+ //source = auto_alloc(device->machine(), discrete_source_node);
+ //source.task = this;
+ //source.output_node = i;
+ source.linked_outbuf = pbuf;
+ source.buffer = 0.0; /* please compiler */
+ source.ptr = NULL;
+ dest_task->source_list.add(source);
+
+ /* point the input to a buffered location */
+ dest_node->m_input[inputnum] = &dest_task->source_list[dest_task->source_list.count()-1].buffer; // was copied! &source.buffer;
+
+ }
+ }
+ }
+ }
+ }
+}
+
+/*************************************
+ *
+ * Base node implementation
+ *
+ *************************************/
+
+discrete_base_node::discrete_base_node() :
+ m_step_intf(NULL),
+ m_input_intf(NULL)
+{
+ m_output[0] = 0.0;
+}
+
+
+discrete_base_node::~discrete_base_node(void)
+{
+ /* currently noting */
+}
+
+void discrete_base_node::init(discrete_device *pdev, const discrete_block *xblock)
+{
+ m_device = pdev;
+ m_block = xblock;
+
+ m_custom = m_block->custom;
+ m_active_inputs = m_block->active_inputs;
+
+ m_step_intf = dynamic_cast<discrete_step_interface *>(this);
+ m_input_intf = dynamic_cast<discrete_input_interface *>(this);
+ m_output_intf = dynamic_cast<discrete_sound_output_interface *>(this);
+
+ if (m_step_intf)
+ {
+ m_step_intf->run_time = 0;
+ m_step_intf->self = this;
+ }
+}
+
+void discrete_base_node::save_state(void)
+{
+ if (m_block->node != NODE_SPECIAL)
+ m_device->save_item(NAME(m_output), m_block->node);
+}
+
+discrete_base_node *discrete_device::discrete_find_node(int node)
+{
+ if (node < NODE_START || node > NODE_END) return NULL;
+ return m_indexed_node[NODE_INDEX(node)];
+}
+
+void discrete_base_node::resolve_input_nodes(void)
+{
+ int inputnum;
+
+ /* loop over all active inputs */
+ for (inputnum = 0; inputnum < m_active_inputs; inputnum++)
+ {
+ int inputnode = m_block->input_node[inputnum];
+
+ /* if this input is node-based, find the node in the indexed list */
+ if IS_VALUE_A_NODE(inputnode)
+ {
+ //discrete_base_node *node_ref = m_device->m_indexed_node[NODE_INDEX(inputnode)];
+ discrete_base_node *node_ref = m_device->discrete_find_node(inputnode);
+ if (!node_ref)
+ fatalerror("discrete_start - NODE_%02d referenced a non existent node NODE_%02d\n", index(), NODE_INDEX(inputnode));
+
+ if ((NODE_CHILD_NODE_NUM(inputnode) >= node_ref->max_output()) /*&& (node_ref->module_type() != DST_CUSTOM)*/)
+ fatalerror("discrete_start - NODE_%02d referenced non existent output %d on node NODE_%02d\n", index(), NODE_CHILD_NODE_NUM(inputnode), NODE_INDEX(inputnode));
+
+ m_input[inputnum] = &(node_ref->m_output[NODE_CHILD_NODE_NUM(inputnode)]); /* Link referenced node out to input */
+ m_input_is_node |= 1 << inputnum; /* Bit flag if input is node */
+ }
+ else
+ {
+ /* warn if trying to use a node for an input that can only be static */
+ if IS_VALUE_A_NODE(m_block->initial[inputnum])
+ {
+ m_device->discrete_log("Warning - discrete_start - NODE_%02d trying to use a node on static input %d", index(), inputnum);
+ /* also report it in the error log so it is not missed */
+ logerror("Warning - discrete_start - NODE_%02d trying to use a node on static input %d", index(), inputnum);
+ }
+ else
+ {
+ m_input[inputnum] = &(m_block->initial[inputnum]);
+ }
+ }
+ }
+ for (inputnum = m_active_inputs; inputnum < DISCRETE_MAX_INPUTS; inputnum++)
+ {
+ /* FIXME: Check that no nodes follow ! */
+ m_input[inputnum] = &(m_block->initial[inputnum]);
+ }
+}
+
+const double *discrete_device::node_output_ptr(int onode)
+{
+ const discrete_base_node *node;
+ node = discrete_find_node(onode);
+
+ if (node != NULL)
+ {
+ return &(node->m_output[NODE_CHILD_NODE_NUM(onode)]);
+ }
+ else
+ return NULL;
+}
+
+/*************************************
+ *
+ * Device implementation
+ *
+ *************************************/
+
+
+//-------------------------------------------------
+// discrete_log: Debug logging
+//-------------------------------------------------
+
+void CLIB_DECL discrete_device::discrete_log(const char *text, ...) const
+{
+ if (DISCRETE_DEBUGLOG)
+ {
+ va_list arg;
+ va_start(arg, text);
+
+ if(m_disclogfile)
+ {
+ vfprintf(m_disclogfile, text, arg);
+ fprintf(m_disclogfile, "\n");
+ fflush(m_disclogfile);
+ }
+
+ va_end(arg);
+ }
+}
+
+//-------------------------------------------------
+// discrete_build_list: Build import list
+//-------------------------------------------------
+
+void discrete_device::discrete_build_list(const discrete_block *intf, sound_block_list_t &block_list)
+{
+ int node_count = 0;
+
+ for (; intf[node_count].type != DSS_NULL; )
+ {
+ /* scan imported */
+ if (intf[node_count].type == DSO_IMPORT)
+ {
+ discrete_log("discrete_build_list() - DISCRETE_IMPORT @ NODE_%02d", NODE_INDEX(intf[node_count].node) );
+ discrete_build_list((discrete_block *) intf[node_count].custom, block_list);
+ }
+ else if (intf[node_count].type == DSO_REPLACE)
+ {
+ bool found = false;
+ node_count++;
+ if (intf[node_count].type == DSS_NULL)
+ fatalerror("discrete_build_list: DISCRETE_REPLACE at end of node_list\n");
+
+ for (int i=0; i < block_list.count(); i++)
+ {
+ const discrete_block *block = block_list[i];
+
+ if (block->type != NODE_SPECIAL )
+ if (block->node == intf[node_count].node)
+ {
+ block_list[i] = &intf[node_count];
+ discrete_log("discrete_build_list() - DISCRETE_REPLACE @ NODE_%02d", NODE_INDEX(intf[node_count].node) );
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ fatalerror("discrete_build_list: DISCRETE_REPLACE did not found node %d\n", NODE_INDEX(intf[node_count].node));
+
+ }
+ else if (intf[node_count].type == DSO_DELETE)
+ {
+ vector_t<int> deletethem;
+
+ for (int i=0; i<block_list.count(); i++)
+ {
+ const discrete_block *block = block_list[i];
+
+ if ((block->node >= intf[node_count].input_node[0]) &&
+ (block->node <= intf[node_count].input_node[1]))
+ {
+ discrete_log("discrete_build_list() - DISCRETE_DELETE deleted NODE_%02d", NODE_INDEX(block->node) );
+ deletethem.add(i);
+ }
+ }
+ for_each (int *, i, &deletethem)
+ block_list.remove(*i);
+ }
+ else
+ {
+ discrete_log("discrete_build_list() - adding node %d\n", node_count);
+ block_list.add(&intf[node_count]);
+ }
+
+ node_count++;
+ }
+}
+
+//-------------------------------------------------
+// discrete_sanity_check: Sanity check list
+//-------------------------------------------------
+
+void discrete_device::discrete_sanity_check(const sound_block_list_t &block_list)
+{
+ int node_count = 0;
+
+ discrete_log("discrete_start() - Doing node list sanity check");
+ for (int i=0; i < block_list.count(); i++)
+ {
+ const discrete_block *block = block_list[i];
+
+ /* make sure we don't have too many nodes overall */
+ if (node_count > DISCRETE_MAX_NODES)
+ fatalerror("discrete_start() - Upper limit of %d nodes exceeded, have you terminated the interface block?\n", DISCRETE_MAX_NODES);
+
+ /* make sure the node number is in range */
+ if (block->node < NODE_START || block->node > NODE_END)
+ fatalerror("discrete_start() - Invalid node number on node %02d descriptor\n", block->node);
+
+ /* make sure the node type is valid */
+ if (block->type > DSO_OUTPUT)
+ fatalerror("discrete_start() - Invalid function type on NODE_%02d\n", NODE_INDEX(block->node) );
+
+ /* make sure this is a main node */
+ if (NODE_CHILD_NODE_NUM(block->node) > 0)
+ fatalerror("discrete_start() - Child node number on NODE_%02d\n", NODE_INDEX(block->node) );
+
+ node_count++;
+ }
+ discrete_log("discrete_start() - Sanity check counted %d nodes", node_count);
+
+}
+
+//-------------------------------------------------
+// discrete_sanity_check: Sanity check list
+//-------------------------------------------------
+
+/*************************************
+ *
+ * Master discrete system start
+ *
+ *************************************/
+
+
+/*************************************
+ *
+ * Master discrete system stop
+ *
+ *************************************/
+
+static UINT64 list_run_time(const node_list_t &list)
+{
+ UINT64 total = 0;
+
+ for_each(discrete_base_node **, node, &list)
+ {
+ discrete_step_interface *step;
+ if ((*node)->interface(step))
+ total += step->run_time;
+ }
+ return total;
+}
+
+static UINT64 step_list_run_time(const node_step_list_t &list)
+{
+ UINT64 total = 0;
+
+ for_each(discrete_step_interface **, node, &list)
+ {
+ total += (*node)->run_time;
+ }
+ return total;
+}
+
+void discrete_device::display_profiling(void)
+{
+ int count;
+ UINT64 total;
+ UINT64 tresh;
+ double tt;
+
+ /* calculate total time */
+ total = list_run_time(m_node_list);
+ count = m_node_list.count();
+ /* print statistics */
+ printf("Total Samples : %16" I64FMT "d\n", m_total_samples);
+ tresh = total / count;
+ printf("Threshold (mean): %16" I64FMT "d\n", tresh / m_total_samples );
+ for_each(discrete_base_node **, node, &m_node_list)
+ {
+ discrete_step_interface *step;
+ if ((*node)->interface(step))
+ if (step->run_time > tresh)
+ printf("%3d: %20s %8.2f %10.2f\n", (*node)->index(), (*node)->module_name(), (double) step->run_time / (double) total * 100.0, ((double) step->run_time) / (double) m_total_samples);
+ }
+
+ /* Task information */
+ for_each(discrete_task **, task, &task_list)
+ {
+ tt = step_list_run_time((*task)->step_list);
+
+ printf("Task(%d): %8.2f %15.2f\n", (*task)->task_group, tt / (double) total * 100.0, tt / (double) m_total_samples);
+ }
+
+ printf("Average samples/double->update: %8.2f\n", (double) m_total_samples / (double) m_total_stream_updates);
+}
+
+
+/*************************************
+ *
+ * First pass init of nodes
+ *
+ *************************************/
+
+
+void discrete_device::init_nodes(const sound_block_list_t &block_list)
+{
+ discrete_task *task = NULL;
+ /* list tail pointers */
+ int has_tasks = 0;
+
+ /* check whether we have tasks ... */
+ if (USE_DISCRETE_TASKS)
+ {
+ for (int i = 0; i < block_list.count(); i++)
+ {
+ if (block_list[i]->type == DSO_TASK_START)
+ has_tasks = 1;
+ }
+ }
+
+ if (!has_tasks)
+ {
+ /* make sure we have one simple task
+ * No need to create a node since there are no dependencies.
+ */
+ task = auto_alloc_clear(machine(), discrete_task(*this));
+ task_list.add(task);
+ }
+
+ /* loop over all nodes */
+ for (int i = 0; i < block_list.count(); i++)
+ {
+ const discrete_block *block = block_list[i];
+
+ //discrete_base_node *node = block->factory->Create(this, block);
+ discrete_base_node *node = block->factory(this, block);
+ /* keep track of special nodes */
+ if (block->node == NODE_SPECIAL)
+ {
+ switch(block->type)
+ {
+ /* Output Node */
+ case DSO_OUTPUT:
+ /* nothing -> handled later */
+ break;
+
+ /* CSVlog Node for debugging */
+ case DSO_CSVLOG:
+ break;
+
+ /* Wavelog Node for debugging */
+ case DSO_WAVLOG:
+ break;
+
+ /* Task processing */
+ case DSO_TASK_START:
+ if (USE_DISCRETE_TASKS)
+ {
+ if (task != NULL)
+ fatalerror("init_nodes() - Nested DISCRETE_START_TASK.\n");
+ task = auto_alloc_clear(machine(), discrete_task(*this));
+ task->task_group = block->initial[0];
+ if (task->task_group < 0 || task->task_group >= DISCRETE_MAX_TASK_GROUPS)
+ fatalerror("discrete_dso_task: illegal task_group %d\n", task->task_group);
+ //printf("task group %d\n", task->task_group);
+ task_list.add(task);
+ }
+ break;
+
+ case DSO_TASK_END:
+ if (USE_DISCRETE_TASKS)
+ {
+ if (task == NULL)
+ fatalerror("init_nodes() - NO DISCRETE_START_TASK.\n");
+ }
+ break;
+
+ default:
+ fatalerror("init_nodes() - Failed, trying to create unknown special discrete node.\n");
+ }
+ }
+
+ /* otherwise, make sure we are not a duplicate, and put ourselves into the indexed list */
+ else
+ {
+ if (m_indexed_node[NODE_INDEX(block->node)])
+ fatalerror("init_nodes() - Duplicate entries for NODE_%02d\n", NODE_INDEX(block->node));
+ m_indexed_node[NODE_INDEX(block->node)] = node;
+ }
+
+ /* add to node list */
+ m_node_list.add(node);
+
+ /* our running order just follows the order specified */
+ /* does the node step ? */
+ discrete_step_interface *step;
+ if (node->interface(step))
+ {
+ /* do we belong to a task? */
+ if (task == NULL)
+ fatalerror("init_nodes() - found node outside of task: %s\n", node->module_name() );
+ else
+ task->step_list.add(step);
+ }
+
+ if (USE_DISCRETE_TASKS && block->type == DSO_TASK_END)
+ {
+ task = NULL;
+ }
+
+ /* and register save state */
+ node->save_state();
+ }
+
+ if (!has_tasks)
+ {
+ }
+}
+
+
+/*************************************
+ *
+ * node_description implementation
+ *
+ *************************************/
+
+
+int discrete_device::same_module_index(const discrete_base_node &node)
+{
+ int index = 0;
+
+ for_each(discrete_base_node **, n, &m_node_list)
+ {
+ if (*n == &node)
+ return index;
+ if ((*n)->module_type() == node.module_type())
+ index++;
+ }
+ return -1;
+}
+
+
+//**************************************************************************
+// DEVICE CONFIGURATION
+//**************************************************************************
+
+//-------------------------------------------------
+// static_set_intf - configuration helper to set
+// the interface
+//-------------------------------------------------
+
+void discrete_device::static_set_intf(device_t &device, const discrete_block *intf)
+{
+ discrete_device &disc = downcast<discrete_device &>(device);
+ disc.m_intf = intf;
+}
+
+//-------------------------------------------------
+// discrete_device - constructor
+//-------------------------------------------------
+
+discrete_device::discrete_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, type, name, tag, owner, clock, "discrete", __FILE__),
+ m_intf(NULL),
+ m_sample_rate(0),
+ m_sample_time(0),
+ m_neg_sample_time(0),
+ m_indexed_node(NULL),
+ m_disclogfile(NULL),
+ m_queue(NULL),
+ m_profiling(0),
+ m_total_samples(0),
+ m_total_stream_updates(0)
+{
+}
+
+discrete_sound_device::discrete_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : discrete_device(mconfig, DISCRETE, "DISCRETE", tag, owner, clock),
+ device_sound_interface(mconfig, *this)
+{
+}
+
+discrete_device::~discrete_device(void)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void discrete_device::device_start()
+{
+ // create the stream
+ //m_stream = machine().sound().stream_alloc(*this, 0, 2, 22257);
+
+ const discrete_block *intf_start = m_intf;
+ char name[128];
+
+ /* If a clock is specified we will use it, otherwise run at the audio sample rate. */
+ if (this->clock())
+ m_sample_rate = this->clock();
+ else
+ m_sample_rate = this->machine().sample_rate();
+ m_sample_time = 1.0 / m_sample_rate;
+ m_neg_sample_time = - m_sample_time;
+
+ m_total_samples = 0;
+ m_total_stream_updates = 0;
+
+ /* create the logfile */
+ sprintf(name, "discrete%s.log", this->tag());
+ if (DISCRETE_DEBUGLOG)
+ m_disclogfile = fopen(name, "w");
+
+ /* enable profiling */
+ m_profiling = 0;
+ if (getenv("DISCRETE_PROFILING"))
+ m_profiling = atoi(getenv("DISCRETE_PROFILING"));
+
+ /* Build the final block list */
+ sound_block_list_t block_list;
+ discrete_build_list(intf_start, block_list);
+
+ /* first pass through the nodes: sanity check, fill in the indexed_nodes, and make a total count */
+ discrete_sanity_check(block_list);
+
+ /* Start with empty lists */
+ m_node_list.clear();
+
+ /* allocate memory to hold pointers to nodes by index */
+ m_indexed_node = auto_alloc_array_clear(this->machine(), discrete_base_node *, DISCRETE_MAX_NODES);
+
+ /* initialize the node data */
+ init_nodes(block_list);
+
+ /* now go back and find pointers to all input nodes */
+ for_each(discrete_base_node **, node, &m_node_list)
+ {
+ (*node)->resolve_input_nodes();
+ }
+
+ /* allocate a queue */
+ m_queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_MULTI | WORK_QUEUE_FLAG_HIGH_FREQ);
+
+ /* Process nodes which have a start func */
+ for_each(discrete_base_node **, node, &m_node_list)
+ {
+ (*node)->start();
+ }
+
+ /* Now set up tasks */
+ for_each(discrete_task **, task, &task_list)
+ {
+ for_each(discrete_task **, dest_task, &task_list)
+ {
+ if ((*task)->task_group > (*dest_task)->task_group)
+ (*dest_task)->check((*task));
+ }
+ }
+}
+
+void discrete_device::device_stop()
+{
+ if (m_queue)
+ {
+ osd_work_queue_free(m_queue);
+ }
+
+ if (m_profiling)
+ {
+ display_profiling();
+ }
+
+ /* Process nodes which have a stop func */
+
+ for_each(discrete_base_node **, node, &m_node_list)
+ {
+ (*node)->stop();
+ }
+
+ if (DISCRETE_DEBUGLOG)
+ {
+ /* close the debug log */
+ if (m_disclogfile)
+ fclose(m_disclogfile);
+ m_disclogfile = NULL;
+ }
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void discrete_sound_device::device_start()
+{
+ m_input_stream_list.clear();
+ m_output_list.clear();
+
+ /* call the parent */
+ discrete_device::device_start();
+
+ /* look for input stream nodes */
+ for_each(discrete_base_node **, node, &m_node_list)
+ {
+ /* if we are an stream input node, track that */
+ discrete_dss_input_stream_node *input_stream = dynamic_cast<discrete_dss_input_stream_node *>(*node);
+ if (input_stream != NULL)
+ {
+ m_input_stream_list.add(input_stream);
+ }
+ /* if this is an output interface, add it the output list */
+ discrete_sound_output_interface *out;
+ if ((*node)->interface(out))
+ m_output_list.add(out);
+ }
+
+ /* if no outputs, give an error */
+ if (m_output_list.count() == 0)
+ fatalerror("init_nodes() - Couldn't find an output node\n");
+
+ /* initialize the stream(s) */
+ m_stream = machine().sound().stream_alloc(*this,m_input_stream_list.count(), m_output_list.count(), m_sample_rate);
+
+ /* Finalize stream_input_nodes */
+ for_each(discrete_dss_input_stream_node **, node, &m_input_stream_list)
+ {
+ (*node)->stream_start();
+ }
+
+
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void discrete_device::device_reset()
+{
+ update_to_current_time();
+
+ /* loop over all nodes */
+ for_each (discrete_base_node **, node, &m_node_list)
+ {
+ /* Fimxe : node_level */
+ (*node)->m_output[0] = 0;
+
+ (*node)->reset();
+ }
+}
+
+void discrete_sound_device::device_reset()
+{
+ discrete_device::device_reset();
+}
+
+//-------------------------------------------------
+// discrete_device_process - process a number of
+// samples.
+//
+// input / output buffers are stream_sample_t
+// to not to have to convert the buffers.
+// a "discrete cpu" device will pass NULL here
+//-------------------------------------------------
+
+void discrete_device::process(int samples)
+{
+ if (samples == 0)
+ return;
+
+ /* Setup tasks */
+ for_each(discrete_task **, task, &task_list)
+ {
+ /* unlock the thread */
+ (*task)->unlock();
+
+ (*task)->prepare_for_queue(samples);
+ }
+
+ for_each(discrete_task **, task, &task_list)
+ {
+ /* Fire a work item for each task */
+ osd_work_item_queue(m_queue, discrete_task::task_callback, (void *) &task_list, WORK_ITEM_FLAG_AUTO_RELEASE);
+ }
+ osd_work_queue_wait(m_queue, osd_ticks_per_second()*10);
+
+ if (m_profiling)
+ {
+ m_total_samples += samples;
+ m_total_stream_updates++;
+ }
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void discrete_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int outputnum = 0;
+
+ if (samples == 0)
+ return;
+
+ /* Setup any output streams */
+ for_each(discrete_sound_output_interface **, node, &m_output_list)
+ {
+ (*node)->set_output_ptr(outputs[outputnum]);
+ outputnum++;
+ }
+
+ /* Setup any input streams */
+ for_each(discrete_dss_input_stream_node **, node, &m_input_stream_list)
+ {
+ (*node)->m_ptr = (stream_sample_t *) inputs[(*node)->m_stream_in_number];
+ }
+
+ /* just process it */
+ process(samples);
+}
+
+//-------------------------------------------------
+// read - read from the chip's registers and internal RAM
+//-------------------------------------------------
+
+READ8_MEMBER( discrete_device::read )
+{
+ const discrete_base_node *node = discrete_find_node(offset);
+
+ UINT8 data = 0;
+
+ /* Read the node input value if allowed */
+ if (node)
+ {
+ /* Bring the system up to now */
+ update_to_current_time();
+
+ data = (UINT8) node->m_output[NODE_CHILD_NODE_NUM(offset)];
+ }
+ else
+ fatalerror("discrete_sound_r read from non-existent NODE_%02d\n", offset-NODE_00);
+
+ return data;
+}
+
+//-------------------------------------------------
+// write - write to the chip's registers and internal RAM
+//-------------------------------------------------
+
+WRITE8_MEMBER( discrete_device::write )
+{
+ const discrete_base_node *node = discrete_find_node(offset);
+
+ /* Update the node input value if it's a proper input node */
+ if (node)
+ {
+ discrete_input_interface *intf;
+ if (node->interface(intf))
+ intf->input_write(0, data);
+ else
+ discrete_log("discrete_sound_w write to non-input NODE_%02d\n", offset-NODE_00);
+ }
+ else
+ {
+ discrete_log("discrete_sound_w write to non-existent NODE_%02d\n", offset-NODE_00);
+ }
+}
+
+#if (defined(__MINGW32__) && (__GNUC__ >= 5))
+#pragma GCC diagnostic pop
+#endif
diff --git a/src/devices/sound/discrete.h b/src/devices/sound/discrete.h
new file mode 100644
index 00000000000..d3dfd2c4e85
--- /dev/null
+++ b/src/devices/sound/discrete.h
@@ -0,0 +1,4728 @@
+// license:???
+// copyright-holders:K.Wilkins, Derrick Renaud, Frank Palazzolo, Couriersud
+#pragma once
+
+#ifndef __DISCRETE_H__
+#define __DISCRETE_H__
+
+#include "machine/rescap.h"
+
+/***********************************************************************
+ *
+ * MAME - Discrete sound system emulation library
+ *
+ * Written by K.Wilkins (mame@esplexo.co.uk)
+ *
+ * (c) K.Wilkins 2000
+ *
+ * Coding started in November 2000
+ *
+ * Additions/bugfix February 2003 - Derrick Renaud, F.Palazzolo, K.Wilkins
+ * Discrete parallel tasks 2009 - Couriersud
+ *
+ ***********************************************************************
+ *
+ * For free text books on electronic theory check out:
+ * http://www.ibiblio.org/obp/electricCircuits/
+ * For a free circuit simulator:
+ * http://qucs.sourceforge.net/index.html
+ * For a free waveform editor to view DISCRETE_WAVLOG dumps:
+ * http://audacity.sourceforge.net/
+ * http://www.sonicvisualiser.org/
+ *
+ ***********************************************************************
+ *
+ * Currently only one instance of a discrete sound system is supported.
+ * If more then one instance is required in the future, then a chip #
+ * will have to be added to the read/writes and the discrete inputs
+ * modified to match. This functionality should never be needed.
+ * There is no real need to run more then 1 discrete system.
+ *
+ * If a clock is specified in the machine driver setup, then this is
+ * used for the simulation sample rate. Otherwise it will default to
+ * run at the audio sample rate.
+ *
+ * Unused/Unconnected input nodes should be set to NODE_NC (No Connect)
+ *
+ * Each node can have many inputs from either constants or other
+ * nodes within the system.
+ *
+ * It should be remembered that the discrete sound system emulation
+ * does not do individual device emulation, but instead does a function
+ * emulation. So you will need to convert the schematic design into
+ * a logic block representation.
+ *
+ * There is the possibility to support multiple outputs per module.
+ * In this case, NODE_XXX is the default output. Alternative outputs may
+ * be accessed by using NODE_XXX_YY where 00<=Y<08.
+ *
+ * You may also access nodes with a macros:
+ *
+ * NODE_XXX = NODE_SUB(NODE_XXX, 0)
+ * NODE_XXX = NODE_XXX_00
+ * NODE_XXX = NODE(XXX)
+ * NODE_XXX_YY = NODE_SUB(NODE_XXX, YY)
+ *
+ * One node point may feed a number of inputs, for example you could
+ * connect the output of a DISCRETE_SINEWAVE to the AMPLITUDE input
+ * of another DISCRETE_SINEWAVE to amplitude modulate its output and
+ * also connect it to the frequency input of another to frequency
+ * modulate its output, the combinations are endless....
+ *
+ * Consider the circuit below:
+ *
+ * .--------. .----------. .-------.
+ * | | | | | |
+ * | SQUARE | Enable| SINEWAVE | | |
+ * | WAVE |-+---------->| 2000Hz |---------------->| |
+ * | | | | | | ADDER |-->OUT
+ * | NODE11 | | | NODE12 | | |
+ * '--------' | '----------' .->| |
+ * | | |NODE20 |
+ * | .------. .---------. | '-------'
+ * | |Logic | | | | ^
+ * | | INV | Enable | SINEWVE | | |
+ * '->| ERT |------------->| 4000Hz |--' .-------.
+ * | | | | | |
+ * |NODE13| | NODE14 | | INPUT |
+ * '------' '---------' | |
+ * |NODE01 |
+ * '-------'
+ *
+ * This should give you an alternating two tone sound switching
+ * between the 2000Hz and 4000Hz sine waves at the frequency of the
+ * square wave, with the memory mapped enable signal mapped onto NODE07
+ * so discrete_sound_w(NODE_01,1) will enable the sound, and
+ * discrete_sound_w(NODE_01,0) will disable the sound.
+ *
+ * DISCRETE_SOUND_START(test_interface)
+ * DISCRETE_INPUT_LOGIC(NODE_01)
+ * DISCRETE_SQUAREWFIX(NODE_11, 1, 0.5, 1, 50, 1.0/2, 0) // Output 0:1
+ * DISCRETE_SINEWAVE(NODE_12, NODE_11, 2000, 10000, 0, 0)
+ * DISCRETE_LOGIC_INVERT(NODE_13, NODE_11)
+ * DISCRETE_SINEWAVE(NODE_14, NODE_13, 4000, 10000, 0, 0)
+ * DISCRETE_ADDER2(NODE_20, NODE_01, NODE_12, NODE_14)
+ * DISCRETE_OUTPUT(NODE_20, 1)
+ * DISCRETE_SOUND_END
+ *
+ * To aid simulation speed it is preferable to use the enable/disable
+ * inputs to a block rather than setting the output amplitude to zero
+ *
+ * Feedback loops are allowed BUT they will always feedback one time
+ * step later, the loop over the netlist is only performed once per
+ * deltaT so feedback occurs in the next deltaT step. This is not
+ * the perfect solution but saves repeatedly traversing the netlist
+ * until all nodes have settled.
+ *
+ * The best way to work out your system is generally to use a pen and
+ * paper to draw a logical block diagram like the one above, it helps
+ * to understand the system ,map the inputs and outputs and to work
+ * out your node numbering scheme.
+ *
+ * Node numbers NODE_01 to NODE_299 are defined at present.
+ *
+ * It is recommended to put all Inputs at the start of the interface.
+ * That way they are updated first.
+ *
+ * Each sound effects final node should come after all nodes that
+ * create it. The final mixing of all sound effects should come
+ * at the end of the interface.
+ *
+ ***********************************************************************
+ *
+ * x_time - ANTI-ALIASING features.
+ *
+ * Certain modules make use of x_time. This is a feature that passes
+ * information between modules about how long in the current sample, the
+ * switch in state happened. This is a decimal value of the % of the
+ * full sample period that it has been in the new state.
+ * 0 means it has been at the same state the whole sample.
+ *
+ * Example: Here is the output of a clock source with x_time on the
+ * output. The square wave is the real world waveform we
+ * want. The ^'s are the sample point. The numbers under
+ * the ^'s are the node output with the logic state left of
+ * the decimal and the x_time to the right. Under that is
+ * what the node's anti-aliased output energy would be.
+ * Note: the example is not 4x sampling so the energy
+ * does not provide an accurate representation of the
+ * original waveform. This is intentional so it fits
+ * in this header file.
+ * 1 ____ ____ ____ ____ ____ ____ ____ ____
+ * 0 ___ ____ ____ ____ ____ ____ ____ ____ __
+ * ^....^....^....^....^....^....^....^....^....^....^....^....^
+ * x_time 0.2 1.4 0.6 1.8 1.2 0.4 1.6 0.8 0.2 1.4 0.6
+ * energy 0.8 0.4 0.4 0.8 0.2 0.6 0.6 0.2 0.8 0.4 0.4
+ *
+ * Some modules will just pass the x_time onto another module.
+ *
+ * Modules that process x_time will keep track of the node's previous
+ * state so they can calculate the actual energy at the sample time.
+ *
+ * Example: Say we have a 555 module that outputs a clock with x_time
+ * that is connected to a counter. The output of the counter
+ * is connected to DAC_R1.
+ * In this case the counter module continues counting dependant
+ * on the integer portion of the 555 output. But it also
+ * passes the decimal portion as the x_time.
+ * The DAC_R1 then uses this info to anti-alias its output.
+ * Consider the following counter outputs vs DAC_R1
+ * calculations. The count changes from 9 to 10. It has
+ * been at the new state for 75% of the sample.
+ *
+ * counter binary x_time -- DAC_R1 bit energy --
+ * out count D3 D2 D1 D0
+ * 9.0 1001 0.0 1.0 0.0 0.0 1.0
+ * 10.75 1010 0.75 1.0 0.0 0.75 0.25
+ * 10.0 1010 0.0 1.0 0.0 1.0 0.0
+ *
+ * The DAC_R1 uses these energy calculations to scale the
+ * voltages created on each of its resistors. This
+ * anti-aliases the waveform no mater what the resistor
+ * weighting is.
+ *
+ ***********************************************************************
+ *
+ * LIST OF CURRENTLY IMPLEMENTED DISCRETE BLOCKS
+ * ---------------------------------------------
+ *
+ * DISCRETE_SOUND_START(STRUCTURENAME)
+ * DISCRETE_SOUND_END
+ *
+ * DISCRETE_ADJUSTMENT(NODE,MIN,MAX,LOGLIN,TAG)
+ * DISCRETE_ADJUSTMENTX(NODE,MIN,MAX,LOGLIN,TAG,PMIN,PMAX)
+ * DISCRETE_CONSTANT(NODE,CONST0)
+ * DISCRETE_INPUT_DATA(NODE)
+ * DISCRETE_INPUTX_DATA(NODE,GAIN,OFFSET,INIT)
+ * DISCRETE_INPUT_LOGIC(NODE)
+ * DISCRETE_INPUTX_LOGIC(NODE,GAIN,OFFSET,INIT)
+ * DISCRETE_INPUT_NOT(NODE)
+ * DISCRETE_INPUTX_NOT(NODE,GAIN,OFFSET,INIT)
+ * DISCRETE_INPUT_PULSE(NODE,INIT)
+ * DISCRETE_INPUT_STREAM(NODE, NUM)
+ * DISCRETE_INPUTX_STREAM(NODE,NUM, GAIN,OFFSET)
+ *
+ * DISCRETE_COUNTER(NODE,ENAB,RESET,CLK,MIN,MAX,DIR,INIT0,CLKTYPE)
+ * DISCRETE_COUNTER_7492(NODE,ENAB,RESET,CLK,CLKTYPE)
+ * DISCRETE_LFSR_NOISE(NODE,ENAB,RESET,CLK,AMPL,FEED,BIAS,LFSRTB)
+ * DISCRETE_NOISE(NODE,ENAB,FREQ,AMP,BIAS)
+ * DISCRETE_NOTE(NODE,ENAB,CLK,DATA,MAX1,MAX2,CLKTYPE)
+ * DISCRETE_SAWTOOTHWAVE(NODE,ENAB,FREQ,AMP,BIAS,GRADIENT,PHASE)
+ * DISCRETE_SINEWAVE(NODE,ENAB,FREQ,AMP,BIAS,PHASE)
+ * DISCRETE_SQUAREWAVE(NODE,ENAB,FREQ,AMP,DUTY,BIAS,PHASE)
+ * DISCRETE_SQUAREWFIX(NODE,ENAB,FREQ,AMP,DUTY,BIAS,PHASE)
+ * DISCRETE_SQUAREWAVE2(NODE,ENAB,AMPL,T_OFF,T_ON,BIAS,TSHIFT)
+ * DISCRETE_TRIANGLEWAVE(NODE,ENAB,FREQ,AMP,BIAS,PHASE)
+ *
+ * DISCRETE_INVERTER_OSC(NODE,ENAB,MOD,RCHARGE,RP,C,R2,INFO)
+ * DISCRETE_OP_AMP_OSCILLATOR(NODE,ENAB,INFO)
+ * DISCRETE_OP_AMP_VCO1(NODE,ENAB,VMOD1,INFO)
+ * DISCRETE_OP_AMP_VCO2(NODE,ENAB,VMOD1,VMOD2,INFO)
+ * DISCRETE_SCHMITT_OSCILLATOR(NODE,ENAB,INP0,AMPL,TABLE)
+ *
+ * DISCRETE_ADDER2(NODE,ENAB,IN0,IN1)
+ * DISCRETE_ADDER3(NODE,ENAB,IN0,IN1,IN2)
+ * DISCRETE_ADDER4(NODE,ENAB,IN0,IN1,IN2,IN3)
+ * DISCRETE_CLAMP(NODE,IN0,MIN,MAX)
+ * DISCRETE_DIVIDE(NODE,ENAB,IN0,IN1)
+ * DISCRETE_GAIN(NODE,IN0,GAIN)
+ * DISCRETE_INVERT(NODE,IN0)
+ * DISCRETE_LOOKUP_TABLE(NODE,ADDR,SIZE,TABLE)
+ * DISCRETE_MULTIPLY(NODE,ENAB,IN0,IN1)
+ * DISCRETE_MULTADD(NODE,INP0,INP1,INP2)
+ * DISCRETE_ONESHOT(NODE,TRIG,AMPL,WIDTH,TYPE)
+ * DISCRETE_ONESHOTR(NODE,RESET,TRIG,AMPL,WIDTH,TYPE)
+ * DISCRETE_ONOFF(NODE,ENAB,INP0)
+ * DISCRETE_RAMP(NODE,ENAB,RAMP,GRAD,MIN,MAX,CLAMP)
+ * DISCRETE_SAMPLHOLD(NODE,INP0,CLOCK,CLKTYPE)
+ * DISCRETE_SWITCH(NODE,ENAB,SWITCH,INP0,INP1)
+ * DISCRETE_ASWITCH(NODE,CTRL,INP,THRESHOLD)
+ * DISCRETE_TRANSFORM2(NODE,INP0,INP1,FUNCT)
+ * DISCRETE_TRANSFORM3(NODE,INP0,INP1,INP2,FUNCT)
+ * DISCRETE_TRANSFORM4(NODE,INP0,INP1,INP2,INP3,FUNCT)
+ * DISCRETE_TRANSFORM5(NODE,INP0,INP1,INP2,INP3,INP4,FUNCT)
+ *
+ * DISCRETE_COMP_ADDER(NODE,DATA,TABLE)
+ * DISCRETE_DAC_R1(NODE,DATA,VDATA,LADDER)
+ * DISCRETE_DIODE_MIXER2(NODE,IN0,IN1,TABLE)
+ * DISCRETE_DIODE_MIXER3(NODE,IN0,IN1,IN2,TABLE)
+ * DISCRETE_DIODE_MIXER4(NODE,IN0,IN1,IN2,IN3,TABLE)
+ * DISCRETE_INTEGRATE(NODE,TRG0,TRG1,INFO)
+ * DISCRETE_MIXER2(NODE,ENAB,IN0,IN1,INFO)
+ * DISCRETE_MIXER3(NODE,ENAB,IN0,IN1,IN2,INFO)
+ * DISCRETE_MIXER4(NODE,ENAB,IN0,IN1,IN2,IN3,INFO)
+ * DISCRETE_MIXER5(NODE,ENAB,IN0,IN1,IN2,IN3,IN4,INFO)
+ * DISCRETE_MIXER6(NODE,ENAB,IN0,IN1,IN2,IN3,IN4,IN5,INFO)
+ * DISCRETE_MIXER7(NODE,ENAB,IN0,IN1,IN2,IN3,IN4,IN5,IN6,INFO)
+ * DISCRETE_MIXER8(NODE,ENAB,IN0,IN1,IN2,IN3,IN4,IN5,IN6,IN7,INFO)
+ * DISCRETE_OP_AMP(NODE,ENAB,IN0,IN1,INFO)
+ * DISCRETE_OP_AMP_ONESHOT(NODE,TRIG,INFO)
+ * DISCRETE_OP_AMP_TRIG_VCA(NODE,TRG0,TRG1,TRG2,IN0,IN1,INFO)
+ *
+ * DISCRETE_BIT_DECODE(NODE,INP,BIT_N,VOUT)
+ * DISCRETE_BITS_DECODE(NODE,INP,BIT_FROM,BIT_TO,VOUT)
+ *
+ * DISCRETE_LOGIC_INVERT(NODE,INP0)
+ * DISCRETE_LOGIC_AND(NODE,INP0,INP1)
+ * DISCRETE_LOGIC_AND3(NODE,INP0,INP1,INP2)
+ * DISCRETE_LOGIC_AND4(NODE,INP0,INP1,INP2,INP3)
+ * DISCRETE_LOGIC_NAND(NODE,INP0,INP1)
+ * DISCRETE_LOGIC_NAND3(NODE,INP0,INP1,INP2)
+ * DISCRETE_LOGIC_NAND4(NODE,INP0,INP1,INP2,INP3)
+ * DISCRETE_LOGIC_OR(NODE,INP0,INP1)
+ * DISCRETE_LOGIC_OR3(NODE,INP0,INP1,INP2)
+ * DISCRETE_LOGIC_OR4(NODE,INP0,INP1,INP2,INP3)
+ * DISCRETE_LOGIC_NOR(NODE,INP0,INP1)
+ * DISCRETE_LOGIC_NOR3(NODE,INP0,INP1,INP2)
+ * DISCRETE_LOGIC_NOR4(NODE,INP0,INP1,INP2,INP3)
+ * DISCRETE_LOGIC_XOR(NODE,INP0,INP1)
+ * DISCRETE_LOGIC_XNOR(NODE,INP0,INP1)
+ * DISCRETE_LOGIC_DFLIPFLOP(NODE,RESET,SET,CLK,INP)
+ * DISCRETE_LOGIC_JKFLIPFLOP(NODE,RESET,SET,CLK,J,K)
+ * DISCRETE_LOGIC_SHIFT(NODE,INP0,RESET,CLK,SIZE,OPTIONS)
+ * DISCRETE_MULTIPLEX2(NODE,ADDR,INP0,INP1)
+ * DISCRETE_MULTIPLEX4(NODE,ADDR,INP0,INP1,INP2,INP3)
+ * DISCRETE_MULTIPLEX8(NODE,ADDR,INP0,INP1,INP2,INP3,INP4,INP5,INP6,INP7)
+ * DISCRETE_XTIME_BUFFER(NODE,IN0,LOW,HIGH)
+ * DISCRETE_XTIME_INVERTER(NODE,IN0,LOW,HIGH)
+ * DISCRETE_XTIME_AND(NODE,IN0,IN1,LOW,HIGH)
+ * DISCRETE_XTIME_NAND(NODE,IN0,IN1,LOW,HIGH)
+ * DISCRETE_XTIME_OR(NODE,IN0,IN1,LOW,HIGH)
+ * DISCRETE_XTIME_NOR(NODE,IN0,IN1,LOW,HIGH)
+ * DISCRETE_XTIME_XOR(NODE,IN0,IN1,LOW,HIGH)
+ * DISCRETE_XTIME_XNOR(NODE,IN0,IN1,LOW,HIGH)
+ *
+ * DISCRETE_FILTER1(NODE,ENAB,INP0,FREQ,TYPE)
+ * DISCRETE_FILTER2(NODE,ENAB,INP0,FREQ,DAMP,TYPE)
+ *
+ * DISCRETE_CRFILTER(NODE,IN0,RVAL,CVAL)
+ * DISCRETE_CRFILTER_VREF(NODE,IN0,RVAL,CVAL,VREF)
+ * DISCRETE_OP_AMP_FILTER(NODE,ENAB,INP0,INP1,TYPE,INFO)
+ * DISCRETE_RC_CIRCUIT_1(NODE,INP0,INP1,RVAL,CVAL)
+ * DISCRETE_RCDISC(NODE,ENAB,IN0,RVAL,CVAL)
+ * DISCRETE_RCDISC2(NODE,SWITCH,INP0,RVAL0,INP1,RVAL1,CVAL)
+ * DISCRETE_RCDISC3(NODE,ENAB,INP0,RVAL0,RVAL1,CVAL, DJV)
+ * DISCRETE_RCDISC4(NODE,ENAB,INP0,RVAL0,RVAL1,RVAL2,CVAL,VP,TYPE)
+ * DISCRETE_RCDISC5(NODE,ENAB,IN0,RVAL,CVAL)
+ * DISCRETE_RCINTEGRATE(NODE,INP0,RVAL0,RVAL1,RVAL2,CVAL,vP,TYPE)
+ * DISCRETE_RCDISC_MODULATED(NODE,INP0,INP1,RVAL0,RVAL1,RVAL2,RVAL3,CVAL,VP)
+ * DISCRETE_RCFILTER(NODE,IN0,RVAL,CVAL)
+ * DISCRETE_RCFILTER_VREF(NODE,IN0,RVAL,CVAL,VREF)
+ *
+ * DISCRETE_555_ASTABLE(NODE,RESET,R1,R2,C,OPTIONS)
+ * DISCRETE_555_ASTABLE_CV(NODE,RESET,R1,R2,C,CTRLV,OPTIONS)
+ * DISCRETE_555_MSTABLE(NODE,RESET,TRIG,R,C,OPTIONS)
+ * DISCRETE_555_CC(NODE,RESET,VIN,R,C,RBIAS,RGND,RDIS,OPTIONS)
+ * DISCRETE_555_VCO1(NODE,RESET,VIN,OPTIONS)
+ * DISCRETE_555_VCO1_CV(NODE,RESET,VIN,CTRLV,OPTIONS)
+ * DISCRETE_566(NODE,VMOD,R,C,VPOS,VNEG,VCHARGE,OPTIONS)
+ * DISCRETE_74LS624(NODE,ENAB,VMOD,VRNG,C,R_FREQ_IN,C_FREQ_IN,R_RNG_IN,OUTTYPE)
+ *
+ * DISCRETE_CUSTOM1(NODE,IN0,INFO)
+ * DISCRETE_CUSTOM2(NODE,IN0,IN1,INFO)
+ * DISCRETE_CUSTOM3(NODE,IN0,IN1,IN2,INFO)
+ * DISCRETE_CUSTOM4(NODE,IN0,IN1,IN2,IN3,INFO)
+ * DISCRETE_CUSTOM5(NODE,IN0,IN1,IN2,IN3,IN4,INFO)
+ * DISCRETE_CUSTOM6(NODE,IN0,IN1,IN2,IN3,IN4,IN5,INFO)
+ * DISCRETE_CUSTOM7(NODE,IN0,IN1,IN2,IN3,IN4,IN5,IN6,INFO)
+ * DISCRETE_CUSTOM8(NODE,IN0,IN1,IN2,IN3,IN4,IN5,IN6,IN7,INFO)
+ * DISCRETE_CUSTOM9(NODE,IN0,IN1,IN2,IN3,IN4,IN5,IN6,IN7,IN8,INFO)
+ *
+ * DISCRETE_CSVLOG1(NODE1)
+ * DISCRETE_CSVLOG2(NODE1,NODE2)
+ * DISCRETE_CSVLOG3(NODE1,NODE2,NODE3)
+ * DISCRETE_CSVLOG4(NODE1,NODE2,NODE3,NODE4)
+ * DISCRETE_CSVLOG5(NODE1,NODE2,NODE3,NODE4,NODE5)
+ * DISCRETE_WAVLOG1(NODE1,GAIN1)
+ * DISCRETE_WAVLOG2(NODE1,GAIN1,NODE2,GAIN2)
+ * DISCRETE_OUTPUT(OPNODE,GAIN)
+ *
+ ***********************************************************************
+ =======================================================================
+ * from from disc_inp.inc
+ =======================================================================
+ ***********************************************************************
+ *
+ * DISCRETE_ADJUSTMENT - Adjustable constant set by the UI [~] menu.
+ *
+ * .----------.
+ * | |
+ * | ADJUST.. |--------> Netlist node
+ * | |
+ * '----------'
+ * Declaration syntax
+ *
+ * DISCRETE_ADJUSTMENT(name of node,
+ * static minimum value the node can take,
+ * static maximum value the node can take,
+ * log/linear scale 0=Linear !0=Logarithmic,
+ * port tag name of the adjuster)
+ *
+ * Note: When using DISC_LOGADJ, the min/max values must be > 0.
+ * If they are <=0, they will be forced to 1.
+ * Min can be a higher value then max.
+ * Min/max is just how the slider is displayed.
+ *
+ * Example config line
+ *
+ * DISCRETE_ADJUSTMENT(NODE_01,0.0,5.0,DISC_LINADJ,0,"pot")
+ *
+ * Define an adjustment slider that takes a 0-100 input from input
+ * port "pot", scaling between 0.0 and 5.0. Adjustment scaling is Linear.
+ *
+ * DISC_LOGADJ 1.0
+ * DISC_LINADJ 0.0
+ *
+ * EXAMPLES: see Hit Me, Fire Truck
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_CONSTANT - Single output, fixed at compile time.
+ * This is usefull as a placeholder for
+ * incomplete circuits.
+ *
+ * .----------.
+ * | |
+ * | CONSTANT |--------> Netlist node
+ * | |
+ * '----------'
+ * Declaration syntax
+ *
+ * DISCRETE_CONSTANT(name of node, constant value)
+ *
+ * Example config line
+ *
+ * DISCRETE_CONSTANT(NODE_01, 100)
+ *
+ * Define a node that has a constant value of 100
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_INPUT_DATA - accepts 8-bit data. Value at reset is 0.
+ * DISCRETE_INPUT_LOGIC - 0 if data=0; 1 if data=1. Value at reset is 0.
+ * DISCRETE_INPUT_NOT - 0 if data=1; 1 if data=0. Value at reset is 1.
+ *
+ * DISCRETE_INPUTX_xx - same as above, but will modify the value by the
+ * given GAIN and OFFSET. At reset the value will
+ * be INIT modified by GAIN and OFFSET.
+ *
+ * DISCRETE_INPUT_PULSE - Same as normal input node but the netlist
+ * node output returns to INIT after a single
+ * cycle of sound output. To allow for scenarios
+ * whereby the register write pulse is used as
+ * a reset to a system.
+ *
+ * .----------.
+ * -----\| |
+ * discrete_sound_w data | INPUT(A) |----> Netlist node
+ * Write -----/| |
+ * '----------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_INPUT_DATA (name of node)
+ * DISCRETE_INPUT_LOGIC (name of node)
+ * DISCRETE_INPUT_NOT (name of node)
+ * DISCRETE_INPUTX_DATA (name of node, gain, offset, initial value)
+ * DISCRETE_INPUTX_LOGIC(name of node, gain, offset, initial value)
+ * DISCRETE_INPUTX_NOT (name of node, gain, offset, initial value)
+ * DISCRETE_INPUT_PULSE (name of node, default value)
+ *
+ * Can be written to with: discrete_sound_w(NODE_xx, data);
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_INPUT_STREAM(NODE,NUM) - Accepts stream input NUM
+ * DISCRETE_INPUTX_STREAM(NODE,NUM,GAIN,OFFSET) - Accepts a stream input and
+ * applies a gain and offset.
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_INPUT_STREAM (name of node, stream number, )
+ * DISCRETE_INPUTX_STREAM(name of node, stream nubmer, gain, offset)
+ *
+ * Note: The discrete system is floating point based. So when routing a stream
+ * set it's gain to 100% and then use DISCRETE_INPUTX_STREAM to adjust
+ * it if needed.
+ * If you need to access a stream from a discrete task, the stream node
+ * must be part of that task. If a given stream is used in two tasks or
+ * a task and the main task, you must declare two stream nodes acccessing the
+ * same stream input NUM.
+ *
+ * EXAMPLES: see scramble, frogger
+ *
+ ***********************************************************************
+ =======================================================================
+ * from from disc_wav.inc
+ * Generic modules
+ =======================================================================
+ ***********************************************************************
+ *
+ * DISCRETE_COUNTER - up/down counter.
+ *
+ * This counter counts up/down from MIN to MAX. When the enable is low, the output
+ * is held at it's last value. When reset is high, the reset value is loaded
+ * into the output. The counter can be clocked internally or externally. It also
+ * supports x_time used by the clock modules to pass on anti-aliasing info.
+ *
+ * Declaration syntax
+ *
+ * where: direction: DISC_COUNT_DOWN = 0 = down
+ * DISC_COUNT_UP = 1 = up
+ *
+ * clock type: DISC_CLK_ON_F_EDGE - toggle on falling edge.
+ * DISC_CLK_ON_R_EDGE - toggle on rising edge.
+ * DISC_CLK_BY_COUNT - toggle specified number of times.
+ * DISC_CLK_IS_FREQ - internally clock at this frequency.
+ *
+ * x_time options: you can also | these x_time features to the basic
+ * types above if needed, or use separately with 7492.
+ * DISC_OUT_IS_ENERGY - This will uses the x_time to
+ * anti-alias the count. Might be
+ * usefull if not connected to other
+ * modules.
+ * DISC_OUT_HAS_XTIME - This will generate x_time if
+ * being used with DISC_CLK_IS_FREQ.
+ * It will pass x_time for the
+ * other clock types.
+ *
+ * DISCRETE_COUNTER(name of node,
+ * enable node or static value,
+ * reset node or static value, (reset when TRUE)
+ * clock node or static value,
+ * min count static value,
+ * max count static value,
+ * direction node or static value,
+ * reset value node or static value,
+ * clock type static value)
+ *
+ * DISCRETE_COUNTER_7492(name of node,
+ * enable node or static value,
+ * reset node or static value,
+ * clock node or static value,
+ * clock type static value)
+ *
+ * Note: A 7492 counter outputs a special bit pattern on its /6 stage.
+ * A 7492 clocks on the falling edge,
+ * so it is not recommended to use DISC_CLK_ON_R_EDGE for a 7492.
+ * This module emulates the /6 stage only.
+ * Use another DISCRETE_COUNTER for the /2 stage.
+ *
+ * EXAMPLES: see Fire Truck, Monte Carlo, Super Bug, Polaris
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_LFSR_NOISE - Noise waveform generator node, generates
+ * psuedo random digital stream at the requested
+ * clock frequency.
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_LFSR_NOISE(name of node,
+ * enable node or static value,
+ * reset node or static value,
+ * clock node or static value,
+ * amplitude node or static value,
+ * forced infeed bit to shift reg,
+ * bias node or static value,
+ * LFSR noise descriptor structure)
+ *
+ * discrete_lfsr_desc = {clock type, (see DISCRETE_COUNTER),
+ * bitlength, reset_value,
+ * feedback_bitsel0, feedback_bitsel1,
+ * feedback_function0, feedback_function1, feedback_function2,
+ * feedback_function2_mask, flags, output_bit}
+ *
+ * flags: DISC_LFSR_FLAG_OUT_INVERT - invert output
+ * DISC_LFSR_FLAG_RESET_TYPE_L - reset when LOW (Defalut)
+ * DISC_LFSR_FLAG_RESET_TYPE_H - reset when HIGH
+ * DISC_LFSR_FLAG_OUTPUT_F0 - output is result of F0
+ * DISC_LFSR_FLAG_OUTPUT_SR_SN1 - output shift register to sub-node output #1
+ *
+ * The diagram below outlines the structure of the LFSR model.
+ *
+ * .-------.
+ * FEED | |
+ * ----->| F1 |<--------------------------------------------.
+ * | | |
+ * '-------' BS - Bit Select |
+ * | Fx - Programmable Function |
+ * | .-------. PI - Programmable Inversion |
+ * | | | |
+ * | .---- | SR>>1 |<--------. |
+ * | | | | | |
+ * V V '-------' | .---- |
+ * .------. +->| BS |--. .------. |
+ * BITMASK | | .-------------. | '----' '-| | |
+ * ------->| F2 |-+->| Shift Reg |--+ | F0 |--'
+ * | | | '-------------' | .----. .-| |
+ * '------' | ^ '->| BS |--' '------'
+ * | | '----'
+ * CLOCK | RESET VAL
+ * ----> | .----. .----.
+ * '----------------------| BS |--| PI |--->OUTPUT
+ * '----' '----'
+ *
+ * EXAMPLES: see Fire Truck, Monte Carlo, Super Bug, Polaris
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_NOISE - Noise waveform generator node, generates
+ * random noise of the chosen frequency.
+ *
+ * .------------.
+ * | |
+ * ENABLE -0------>| |
+ * | |
+ * FREQUENCY -1------>| NOISE |----> Netlist node
+ * | |
+ * AMPLITUDE -2------>| |
+ * | |
+ * BIAS -3------>| |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_NOISE(name of node,
+ * enable node or static value,
+ * frequency node or static value,
+ * amplitude node or static value)
+ *
+ * Example config line
+ *
+ * DISCRETE_NOISE(NODE_03,1,5000,NODE_01,0)
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_NOTE - Note generator. This takes a chosen clock, and
+ * clocks an up counter that is preloaded with the data
+ * value at every max 1 count. Every time max 1 count
+ * is reached, the output counts up one and rolls over
+ * to 0 at max 2 count.
+ * When the data value is the same as max count 1, the
+ * counter no longer counts.
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_NOTE(name of node,
+ * enable node or static value,
+ * clock node or static value,
+ * data node or static value,
+ * max 1 count static value,
+ * max 2 count static value,
+ * clock type (see DISCRETE_COUNTER))
+ *
+ * EXAMPLES: see Polaris, Blockade
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_SAWTOOTHWAVE - Saw tooth shape waveform generator, rapid
+ * rise and then graduated fall
+ *
+ * .------------.
+ * | |
+ * ENABLE -0------>| |
+ * | |
+ * FREQUENCY -1------>| |
+ * | |
+ * AMPLITUDE -2------>| SAWTOOTH |----> Netlist Node
+ * | WAVE |
+ * BIAS -3------>| |
+ * | |
+ * GRADIENT -4------>| |
+ * | |
+ * PHASE -5------>| |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_SAWTOOTHWAVE(name of node,
+ * enable node or static value,
+ * frequency node or static value,
+ * amplitude node or static value,
+ * dc bias value for waveform,
+ * gradient of wave ==0 //// !=0 \\\\,
+ * starting phase value in degrees)
+ *
+ * Example config line
+ *
+ * DISCRETE_SAWTOOTHWAVE(NODE_03,1,5000,NODE_01,0,0,90)
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_SINEWAVE - Sinewave waveform generator node, has four
+ * input nodes FREQUENCY, AMPLITUDE, ENABLE and
+ * PHASE, if a node is not connected it will
+ * default to the initialised value in the macro
+ *
+ * .------------.
+ * | |
+ * ENABLE -0------>| |
+ * | |
+ * FREQUENCY -1------>| |
+ * | SINEWAVE |----> Netlist node
+ * AMPLITUDE -2------>| |
+ * | |
+ * BIAS -3------>| |
+ * | |
+ * PHASE -4------>| |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_SINEWAVE (name of node,
+ * enable node or static value,
+ * frequency node or static value,
+ * amplitude node or static value,
+ * dc bias value for waveform,
+ * starting phase value in degrees)
+ *
+ * Example config line
+ *
+ * DISCRETE_SINEWAVE(NODE_03,NODE_01,NODE_02,10000,5000.0,90)
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_SQUAREWAVE - Squarewave waveform generator node.
+ * DISCRETE_SQUAREWFIX Waveform is defined by frequency and duty
+ * cycle.
+ *
+ * .------------.
+ * | |
+ * ENABLE -0------>| |
+ * | |
+ * FREQUENCY -1------>| |
+ * | |
+ * AMPLITUDE -2------>| SQUAREWAVE |----> Netlist node
+ * | |
+ * DUTY CYCLE -3------>| |
+ * | |
+ * BIAS -4------>| |
+ * | |
+ * PHASE -5------>| |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_SQUAREWAVE(name of node,
+ * enable node or static value,
+ * frequency node or static value,
+ * amplitude node or static value,
+ * duty cycle node or static value,
+ * dc bias value for waveform,
+ * starting phase value in degrees)
+ *
+ * Example config line
+ *
+ * DISCRETE_SQUAREWAVE(NODE_03,NODE_01,NODE_02,100,50,0,90)
+ *
+ * NOTE: DISCRETE_SQUAREWFIX is used the same as DISCRETE_SQUAREWAVE.
+ * BUT... It does not stay in sync when you change the freq or
+ * duty values while enabled. This should be used only
+ * when these values are stable while the wave is enabled.
+ * It takes up less CPU time then DISCRETE_SQUAREWAVE and
+ * should be used whenever possible.
+ *
+ * EXAMPLES: see Polaris
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_SQUAREWAVE2 - Squarewave waveform generator node.
+ * Waveform is defined by it's off/on time
+ * periods.
+ *
+ * .------------.
+ * | |
+ * ENABLE -0------>| |
+ * | |
+ * AMPLITUDE -1------>| |
+ * | |
+ * OFF TIME -2------>| SQUAREWAVE |----> Netlist node
+ * | |
+ * ON TIME -3------>| |
+ * | |
+ * BIAS -4------>| |
+ * | |
+ * TIME SHIFT -5------>| |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_SQUAREWAVE2(name of node,
+ * enable node or static value,
+ * amplitude node or static value,
+ * off time node or static value in seconds,
+ * on time node or static value in seconds,
+ * dc bias value for waveform,
+ * starting phase value in seconds)
+ *
+ * Example config line
+ *
+ * DISCRETE_SQUAREWAVE2(NODE_03,NODE_01,NODE_02,0.01,0.001,0.0,0.001)
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_TRIANGLEW - Triagular waveform generator, generates
+ * equal ramp up/down at chosen frequency
+ *
+ * .------------.
+ * | |
+ * ENABLE -0------>| |
+ * | |
+ * FREQUENCY -1------>| TRIANGLE |----> Netlist node
+ * | WAVE |
+ * AMPLITUDE -2------>| |
+ * | |
+ * BIAS -3------>| |
+ * | |
+ * PHASE -4------>| |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_TRIANGLEWAVE(name of node,
+ * enable node or static value,
+ * frequency node or static value,
+ * amplitude node or static value,
+ * dc bias value for waveform,
+ * starting phase value in degrees)
+ *
+ * Example config line
+ *
+ * DISCRETE_TRIANGLEWAVE(NODE_03,1,5000,NODE_01,0.0,0.0)
+ *
+ ***********************************************************************
+ =======================================================================
+ * from from disc_wav.inc
+ * Component specific modules
+ =======================================================================
+ ***********************************************************************
+ *
+ * DISCRETE_OP_AMP_OSCILLATOR - Various single power supply op-amp oscillator circuits
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_OP_AMP_OSCILLATOR(name of node,
+ * enable node or static value,
+ * address of dss_op_amp_osc_context structure)
+ *
+ * discrete_op_amp_osc_info = {type, r1, r2, r3, r4, r5, r6, r7, r8, c, vP}
+ *
+ * Note: Set all unused components to 0.
+ * _OUT_SQW can also be replaced with
+ * _OUT_ENERGY, _OUT_LOGIC_X, _OUT_COUNT_F_X, _OUT_COUNT_R_X
+ *
+ * Types:
+ *
+ * DISC_OP_AMP_OSCILLATOR_1 | DISC_OP_AMP_IS_NORTON
+ * Basic Norton Op Amp Oscillator circuit.
+ *
+ * vP >-.
+ * | c
+ * Z .---||----+-------------------------> DISC_OP_AMP_OSCILLATOR_OUT_CAP
+ * Z r1 | |
+ * Z | |\ |
+ * | | | \ | |\
+ * '-----+---|- \ | r3 | \
+ * | >-+----ZZZZ----|- \
+ * |+ / | >--+-----> DISC_OP_AMP_OSCILLATOR_OUT_SQW
+ * .---| / .--|+ / |
+ * | |/ r5 | | / |
+ * vP >-. | vP >--ZZZZ---+ |/ |
+ * | Z | |
+ * Z Z r2 | r4 |
+ * Z 1k Z '--ZZZZ---+
+ * Z | |
+ * |\ | r6 | |
+ * Enable >---| >-+-ZZZZ---+-----------------------------'
+ * |/ O.C.
+ *
+ * Note: R1 - R5 can be nodes.
+ *
+ * EXAMPLES: see Polaris, Amazing Maze
+ *
+ * --------------------------------------------------
+ *
+ * DISC_OP_AMP_OSCILLATOR_2 | DISC_OP_AMP_IS_NORTON
+ * Basic Norton Op Amp Oscillator circuit.
+ *
+ * .-------------------------------------------> DISC_OP_AMP_OSCILLATOR_OUT_CAP
+ * |
+ * | r1
+ * +------ZZZZ-----.
+ * | |
+ * | r5 |
+ * +--ZZZZ---|>|---.
+ * | |
+ * | r6 |
+ * +--ZZZZ---|<|---.
+ * | |
+ * | |\ |
+ * | r2 | \ |
+ * +---ZZZZ--|- \ |
+ * | | >-+-------> DISC_OP_AMP_OSCILLATOR_OUT_SQW
+ * --- c |+ / |
+ * --- .---| / |
+ * | | |/ |
+ * gnd | |
+ * | r3 |
+ * +--ZZZZ---'
+ * |
+ * Z
+ * Z r4
+ * Z
+ * |
+ * ^
+ * vP
+ *
+ * Note: All values are static.
+ *
+ * EXAMPLES: see Space Walk, Blue Shark
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_OP_AMP_VCOn - Various single power supply op-amp VCO circuits
+ * (n = 1 or 2)
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_OP_AMP_VCOn(name of node,
+ * enable node or static value,
+ * modulation voltage 1 node or static value,
+ * modulation voltage 2 node or static value, [optional]
+ * address of dss_op_amp_osc_context structure)
+ *
+ * discrete_op_amp_osc_info = {type, r1, r2, r3, r4, r5, r6, r7, r8, c, vP}
+ *
+ * Note: Set all unused components to 0.
+ * _OUT_SQW can also be replaced with
+ * _OUT_ENERGY, _OUT_LOGIC_X, _OUT_COUNT_F_X, _OUT_COUNT_R_X
+ *
+ * Types:
+ *
+ * DISC_OP_AMP_OSCILLATOR_VCO_1
+ * Basic Op Amp Voltage Controlled Oscillator circuit.
+ * Note that this circuit has only 1 modulation voltage.
+ * So it is used only with DISCRETE_OP_AMP_VCO1.
+ *
+ * c
+ * .------------------------+---||----+---------------------------> DISC_OP_AMP_OSCILLATOR_OUT_CAP
+ * | | |
+ * | | |\ |
+ * | r1 | | \ | |\
+ * | vMod1 >--+--ZZZZ-------+---|- \ | | \
+ * | | | >-+------------|- \
+ * | | r2 |+ / | >--+-------> DISC_OP_AMP_OSCILLATOR_OUT_SQW
+ * Z '--ZZZZ--+--------| / .--|+ / |
+ * Z r6 | |/ r4 | | / |
+ * Z Z vP/2 >--ZZZZ---+ |/ |
+ * | Z r5 | |
+ * .----. Z | r3 |
+ * | sw |<--------. | '--ZZZZ---+
+ * '----' | gnd |
+ * | | |
+ * gnd '----------------------------------------'
+ *
+ * Notes: The 'sw' block can be a transistor or 4066 switch. It connects
+ * r6 to ground when 'sw' is high.
+ *
+ * --------------------------------------------------
+ *
+ * DISC_OP_AMP_OSCILLATOR_VCO_1 | DISC_OP_AMP_IS_NORTON
+ * Basic Norton Op Amp Voltage Controlled Oscillator circuit.
+ * When disabled, c discharges and sqw out goes high.
+ *
+ * .---------------------------> DISC_OP_AMP_OSCILLATOR_OUT_CAP
+ * c |
+ * r6 .---||----+
+ * vP >--ZZZZ---. | | r5 |\
+ * | | |\ | vP >--ZZZZ-. | \
+ * r7 | r1 | | \ | '-|- \
+ * vMod1 >--ZZZZ---+--ZZZZ-------+---|- \ | r3 | >--+-------> DISC_OP_AMP_OSCILLATOR_OUT_SQW
+ * | | >-+----ZZZZ----+--|+ / |
+ * r8 | r2 .----. |+ / | | / |
+ * vMod2 >--ZZZZ---+--ZZZZ---| sw |--| / | |/ |
+ * '----' |/ | |
+ * ^ ^ | r4 |
+ * | | '--ZZZZ---+
+ * | | |
+ * Enable >---------' | |
+ * '--------------------------------'
+ *
+ * EXAMPLES: see Polaris
+ *
+ * --------------------------------------------------
+ *
+ * DISC_OP_AMP_OSCILLATOR_VCO_2 | DISC_OP_AMP_IS_NORTON
+ * Basic Norton Op Amp Voltage Controlled Oscillator circuit.
+ * Note that this circuit has only 1 modulation voltage.
+ * So it is used only with DISCRETE_OP_AMP_VCO1.
+ * When vMod1 goes to 0V, the oscillator is disabled.
+ * c fully charges and the sqw out goes low.
+ *
+ * .---------------------------> DISC_OP_AMP_OSCILLATOR_OUT_CAP
+ * |
+ * | r4
+ * c | .--ZZZZ--.
+ * .---||----+ | |
+ * | | r5 | |\ |
+ * | |\ | vP >--ZZZZ-+ | \ |
+ * r1 | | \ | '-|+ \ |
+ * vMod1 >--ZZZZ-----------------+---|- \ | r3 | >--+-------> DISC_OP_AMP_OSCILLATOR_OUT_SQW
+ * | >-+----ZZZZ-------|- / |
+ * r2 |+ / | / |
+ * vP >--ZZZZ-----------------+---| / |/ |
+ * | |/ |
+ * r6 .----. | |
+ * vP >--ZZZZ-----|-sw-|------' |
+ * '----' |
+ * ^ |
+ * | |
+ * '-----------------------------------------'
+ *
+ * EXAMPLES: see Double Play
+ *
+ * --------------------------------------------------
+ *
+ * DISC_OP_AMP_OSCILLATOR_VCO_3 | DISC_OP_AMP_IS_NORTON
+ * Basic Norton Op Amp Voltage Controlled Oscillator circuit.
+ *
+ *
+ * c
+ * r7 .---||----+---------------------------> DISC_OP_AMP_OSCILLATOR_OUT_CAP
+ * vP >--ZZZZ---. | |
+ * | | |\ |
+ * r1 | | | \ | |\
+ * vMod1 >--ZZZZ---+---------+---|- \ | r3 | \
+ * | | >-+----ZZZZ----|- \
+ * r6 | |+ / | >--+-------> DISC_OP_AMP_OSCILLATOR_OUT_SQW
+ * vMod2 >--ZZZZ---' .---| / .--|+ / |
+ * | |/ r5 | | / |
+ * vP >-. | vP >--ZZZZ---+ |/ |
+ * | Z | |
+ * Z Z r2 | r4 |
+ * Z 1k Z '--ZZZZ---+
+ * Z | |
+ * |\ | r8 | |
+ * Enable >---| >-+-ZZZZ---+-----------------------------'
+ * |/ O.C.
+ *
+ * EXAMPLES: see Space Encounter, Blue Shark
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_SCHMITT_OSCILLATOR - Schmitt Inverter gate oscillator
+ *
+ * rFeedback
+ * .---ZZZ----. .--< Amplitude
+ * | | |
+ * | |\ | .------. |
+ * rIn | | \ | 0/1 | AND/ | .-.
+ * INP0 >---ZZZ--+--|S >o---+----->|NAND/ |--->|*|-----> Netlist Node
+ * | | / | OR/ | '-'
+ * | |/ .->| NOR |
+ * --- | '------'
+ * --- C |
+ * | ^
+ * gnd Enable
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_SCHMITT_OSCILLATOR(name of node,
+ * enable node or static value,
+ * Input 0 node or static value,
+ * Amplitude node or static value,
+ * address of discrete_schmitt_osc_desc structure)
+ *
+ * discrete_schmitt_osc_desc = {rIn, rFeedback, c, trshRise, trshFall, vGate, options}
+ *
+ * Note: trshRise, trshFall, vGate can be replaced with one of these common types:
+ * DEFAULT_7414_VALUES or DEFAULT_74LS14_VALUES (the LS makes a difference)
+ * eg: {rIn, rFeedback, c, DEFAULT_7414_VALUES, options}
+ *
+ * Where:
+ * trshRise is the voltage level that triggers the gate input to go high (vGate) on rise.
+ * trshFall is the voltage level that triggers the gate input to go low (0V) on fall.
+ * vGate is the output high voltage of the gate that gets fedback through rFeedback.
+ *
+ * Input Options:
+ * DISC_SCHMITT_OSC_IN_IS_LOGIC (DEFAULT)
+ * DISC_SCHMITT_OSC_IN_IS_VOLTAGE
+ *
+ * Enable Options: (ORed with input options)
+ * DISC_SCHMITT_OSC_ENAB_IS_AND (DEFAULT)
+ * DISC_SCHMITT_OSC_ENAB_IS_NAND
+ * DISC_SCHMITT_OSC_ENAB_IS_OR
+ * DISC_SCHMITT_OSC_ENAB_IS_NOR
+ *
+ * EXAMPLES: see Fire Truck, Monte Carlo, Super Bug
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_INVERTER_OSC - Inverter gate oscillator circuits
+ *
+ * TYPE 1/3
+ * .----------------------------> Netlist Node (Type 3)
+ * |
+ * |\ | |\ |\
+ * | \ | | \ | \
+ * +--| >o--+--|-->o--+--| >o--+--------> Netlist Node (Type 1)
+ * | | / | / | | / |
+ * | |/ |/ | |/ |
+ * Z | |
+ * Z RP --- |
+ * Z --- C |
+ * | | R1 |
+ * '-------------------+----ZZZ--'
+ *
+ * TYPE 2
+ *
+ * |\ |\
+ * | \ | \
+ * +--| >o--+--|-->o--+-------> Netlist Node
+ * | | / | | / |
+ * | |/ | |/ |
+ * Z Z |
+ * Z RP Z R1 ---
+ * Z Z --- C
+ * | | |
+ * '---------+---------'
+ *
+ *
+ * TYPE 4 / see vicdual
+ *
+ * |\ |\
+ * | \ | \
+ * Enable >-+-----+--|>o-+--|-->o--+-------> Netlist Node
+ * | | / | | / |
+ * | |/ | |/ |
+ * Z Z |
+ * Z RP Z R1 ---
+ * Z Z --- C
+ * | D | |
+ * '------|>|---+---------'
+ * |
+ * Mod >-----ZZZ------'
+ * R2
+ *
+ * TYPE 5 / see vicdual
+ * Diode will cause inverted input behaviour and inverted output
+ *
+ * |\ |\
+ * | \ | \
+ * Enable >-+-----+--|>o-+--|-->o--+-------> Netlist Node
+ * | | / | | / |
+ * | |/ | |/ |
+ * Z Z |
+ * Z RP Z R1 ---
+ * Z Z --- C
+ * | D | |
+ * '------|<|---+---------'
+ * |
+ * Mod >-----ZZZ------'
+ * R2
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_INVERTER_OSC( name of node,
+ * enable node or static value,
+ * modulation node or static value (0 when not used),
+ * R1 static value,
+ * RP static value
+ * C static value,
+ * R2 static value (0 when not used),
+ * address of discrete_inverter_osc_desc structure)
+ *
+ * discrete_inverter_osc_desc = {vB, vOutLow, vOutHigh, vInRise, vInFall, clamp, options}
+ *
+ * Where
+ * vB Supply Voltage
+ * vOutLow Low Output voltage
+ * vOutHigh High Output voltage
+ * vInRise voltage that triggers the gate input to go high (vGate) on rise
+ * vInFall voltage that triggers the gate input to go low (0V) on fall
+ * clamp internal diode clamp: [-clamp ... vb+clamp] if clamp>= 0
+ * options bitmaped options
+ *
+ * There is a macro DEFAULT_CD40XX_VALUES(_vB) which may be used to initialize the
+ * structure with .... = { 5, DEFAULT_CD40XX_VALUES(5), DISC_OSC_INVERTER_IS_TYPE1}
+ *
+ * The parameters are used to construct a input/output transfer function.
+ *
+ * Option Values
+ *
+ * DISC_OSC_INVERTER_IS_TYPE1
+ * DISC_OSC_INVERTER_IS_TYPE2
+ * DISC_OSC_INVERTER_IS_TYPE3
+ * DISC_OSC_INVERTER_IS_TYPE4
+ * DISC_OSC_INVERTER_OUT_IS_LOGIC
+ *
+ * EXAMPLES: see dkong
+ *
+ ***********************************************************************
+ =======================================================================
+ * from from disc_wav.inc
+ * Not yet implemented
+ =======================================================================
+ ***********************************************************************
+ *
+ * DISCRETE_ADSR_ENV - Attack Decay Sustain Release envelope generator
+ *
+ * Note: Not yet implemented.
+ *
+ * .------------.
+ * | |
+ * ENABLE -0------>| |
+ * | /\__ |
+ * TRIGGER -1------>| / \ |----> Netlist node
+ * | ADSR |
+ * GAIN -2------>| Env |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_ADSR_ENV (name of node,
+ * enable node or static value,
+ * envelope gain node or static value,
+ * envelope descriptor struct)
+ *
+ * Example config line
+ *
+ * DISCRETE_ADSR_ENV(NODE_3,1,NODE_21,1.0,&adsrdesc)
+ *
+ ***********************************************************************
+ =======================================================================
+ * from from disc_mth.inc
+ * Generic modules
+ =======================================================================
+ ***********************************************************************
+ *
+ * DISCRETE_ADDER - Node addition function, available in three
+ * lovely flavours, ADDER2,ADDER3,ADDER4
+ * that perform a summation of incoming nodes
+ *
+ * .------------.
+ * | |
+ * INPUT0 -0------>| |
+ * | |
+ * INPUT1 -1------>| | |
+ * | -+- |----> Netlist node
+ * INPUT2 -2------>| | |
+ * | |
+ * INPUT3 -3------>| |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_ADDERx (name of node,
+ * (x=2/3/4) enable node or static value,
+ * input0 node or static value,
+ * input1 node or static value,
+ * input2 node or static value, [optional]
+ * input3 node or static value) [optional]
+ *
+ * Example config line
+ *
+ * DISCRETE_ADDER2(NODE_03,1,NODE_12,-2000)
+ *
+ * Always enabled, subtracts 2000 from the output of NODE_12
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_CLAMP - Force a signal to stay within bounds MIN/MAX
+ *
+ * .------------.
+ * | |
+ * INP0 -0------>| |
+ * | |
+ * MIN -1------>| CLAMP |----> Netlist node
+ * | |
+ * MAX -2------>| |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_CLAMP(name of node,
+ * input node,
+ * minimum node or static value,
+ * maximum node or static value),
+ *
+ * Example config line
+ *
+ * DISCRETE_CLAMP(NODE_9,NODE_10,2.0,10.0)
+ *
+ * Force the value on the node output, to be within the MIN/MAX
+ * boundary. In this example the output is clamped to the range
+ * of 2.0 to 10.0 inclusive.
+ *
+ * EXAMPLES: Sprint 8
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_DIVIDE - Node division function
+ *
+ * .------------.
+ * | |
+ * ENAB -0------>| |
+ * | o |
+ * INPUT1 -1------>| --- |----> Netlist node
+ * | o |
+ * INPUT2 -2------>| |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_DIVIDE (name of node,
+ * enable node or static value,
+ * input0 node or static value,
+ * input1 node or static value)
+ *
+ * Example config line
+ *
+ * DISCRETE_DIVIDE(NODE_03,1.0,NODE_12,50.0)
+ *
+ * Always enabled, divides the input NODE_12 by 50.0. Note that a
+ * divide by zero condition will give a LARGE number output, it
+ * will not stall the machine or simulation. It will also attempt
+ * to write a divide by zero error to the Mame log if enabled.
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_BIT_DECODE - Decode a bit from value
+ * DISCRETE_BITS_DECODE - Decode a range of bits from value
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_BIT_DECODE(name of node,
+ * input0 node or static value,
+ * bit number static value,
+ * output voltage (logic high) static value)
+ *
+ * Note: This module can decode x_time from counters, etc.
+ * If you set the output voltage to 0, then 0/1 with x_time will be output.
+ * Otherwise it will be used as energy based on the output voltage.
+ *
+ * Example config lines
+ *
+ * DISCRETE_BIT_DECODE(NODE_03,7,0,5)
+ *
+ * Node output is 5
+ *
+ * DISCRETE_BIT_DECODE(NODE_03,7,3,5)
+ *
+ * Node output is 0
+ *
+ * if the range variant is used, you may access the bits (up to 8)
+ * by using NODE_SUB, i.e.
+ *
+ * DISCRETE_BITS_DECODE(NODE_03,5,0,4,5)
+ *
+ * NODE_SUB(NODE_03, 0) = 5
+ * NODE_SUB(NODE_03, 1) = 0
+ * NODE_SUB(NODE_03, 2) = 5
+ * NODE_SUB(NODE_03, 3) = 0
+ * NODE_SUB(NODE_03, 4) = 0
+ *
+ * EXAMPLES: galaxian, dkong, mario
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_LOGIC_INVERT - Logic invertor
+ * DISCRETE_LOGIC_AND - Logic AND gate (3 & 4 input also available)
+ * DISCRETE_LOGIC_NAND - Logic NAND gate (3 & 4 input also available)
+ * DISCRETE_LOGIC_OR - Logic OR gate (3 & 4 input also available)
+ * DISCRETE_LOGIC_NOR - Logic NOR gate (3 & 4 input also available)
+ * DISCRETE_LOGIC_XOR - Logic XOR gate
+ * DISCRETE_LOGIC_XNOR - Logic NXOR gate
+ *
+ * .------------.
+ * | |
+ * INPUT0 -0------>| |
+ * | LOGIC |
+ * [INPUT1] -1------>| FUNCTION |----> Netlist node
+ * | !&|^ |
+ * [INPUT2] -2------>| |
+ * | |
+ * [INPUT3] -3------>| |
+ * | |
+ * [] - Optional '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_LOGIC_XXXn(name of node,
+ * (X=INV/AND/etc)
+ * (n=Blank/2/3) input0 node or static value,
+ * [input1 node or static value],
+ * [input2 node or static value],
+ * [input3 node or static value])
+ *
+ * Example config lines
+ *
+ * DISCRETE_LOGIC_INVERT(NODE_03,NODE_12)
+ * DISCRETE_LOGIC_AND(NODE_03,NODE_12,NODE_13)
+ * DISCRETE_LOGIC_NOR4(NODE_03,NODE_12,NODE_13,NODE_14,NODE_15)
+ *
+ * Node output is always either 0.0 or 1.0 any input value !=0.0 is
+ * taken as a logic 1.
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_XTIME_BUFFER
+ * DISCRETE_XTIME_INVERTER
+ * DISCRETE_XTIME_AND
+ * DISCRETE_XTIME_NAND
+ * DISCRETE_XTIME_OR
+ * DISCRETE_XTIME_NOR
+ * DISCRETE_XTIME_XOR
+ * DISCRETE_XTIME_XNOR
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_XTIME_xxx(name of node,
+ * (xxx=INV/AND/etc)
+ * input0 node or static value,
+ * [input1 node or static value],
+ * logic Low voltage (static value),
+ * logic High voltage (static value))
+ *
+ * These modules all take 0/1 with x_time data and perform the logic
+ * while keeping and using the x_time anti-alaising data.
+ * If both logic Low and High are set to 0, the 0/1 + x_time data
+ * will be output. Otherwise the Low/High voltages will be used
+ * to convert the x_time to energy.
+ *
+ * EXAMPLES: see Mario Bros.; Donkey Kong Jr
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_LOGIC_DFLIPFLOP - Standard D-type flip-flop.
+ * Changes on rising edge of clock.
+ *
+ * /SET -2 ------------.
+ * v
+ * .-----o------.
+ * | |
+ * DATA -4 ----->| |
+ * | FLIPFLOP |
+ * | Q|----> Netlist node
+ * | |
+ * CLOCK -3 ----->| |
+ * | |
+ * '-----o------'
+ * ^
+ * /RESET -1 ------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_LOGIC_DFLIPFLOP(name of node,
+ * reset node or static value,
+ * set node or static value,
+ * clock node,
+ * data node or static value)
+ *
+ * Example config line
+ *
+ * DISCRETE_LOGIC_DFLIPFLOP(NODE_7,NODE_17,0,NODE_13,1)
+ *
+ * A flip-flop that clocks a logic 1 through on the rising edge of
+ * NODE_13. A logic 1 on NODE_17 resets the output to 0.
+ *
+ * EXAMPLES: see Hit Me, Polaris
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_LOGIC_JKFLIPFLOP - Standard JK-type flip-flop.
+ * Changes on falling edge of clock.
+ *
+ * /SET -2 ------------.
+ * v
+ * .-----o------.
+ * | |
+ * J -4 ----->| |
+ * | FLIPFLOP |
+ * CLOCK -3 ----->| Q|----> Netlist node
+ * | |
+ * K -5 ----->| |
+ * | |
+ * '-----o------'
+ * ^
+ * /RESET -1 ------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_LOGIC_JKFLIPFLOP(name of node,
+ * reset node or static value,
+ * set node or static value,
+ * clock node,
+ * J node or static value,
+ * K node or static value)
+ *
+ * EXAMPLES: see Amazing Maze
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_LOOKUP_TABLE - returns the value in a table
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_LOOKUP_TABLE(name of node,
+ * address node,
+ * size of table static value,
+ * address of table of double values)
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_MULTIPLEX - 1 of 2/4/8 multiplexer
+ *
+ * .-------------.
+ * Input 0 >-----|>-<. |
+ * | \ |
+ * Input 1 >-----|>- \ |
+ * | \ |
+ * Input 2 >-----|>- |\ |
+ * | | \ |
+ * Input 3 >-----|>- | o-->|------> Netlist Node
+ * | | |
+ * Input 4 >-----|>- | |
+ * | | |
+ * Input 5 >-----|>- '------|----< Address
+ * | | (0 shown)
+ * Input 6 >-----|>- |
+ * | |
+ * Input 7 >-----|>- |
+ * '-------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_MULTIPLEXx(name of node,
+ * (x=2/4/8) address node,
+ * input 0 node or static value,
+ * input 1 node or static value, ...)
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_LOGIC_SHIFT - shift register
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_LOGIC_SHIFT(name of node,
+ * input node,
+ * reset node or static value,
+ * clock node or static value,
+ * size static value,
+ * options static value)
+ *
+ * Options:
+ * reset type: DISC_LOGIC_SHIFT__RESET_L
+ * DISC_LOGIC_SHIFT__RESET_H
+ * shift type: DISC_LOGIC_SHIFT__LEFT
+ * DISC_LOGIC_SHIFT__RIGHT
+ * clock type: DISC_CLK_ON_F_EDGE - toggle on falling edge.
+ * DISC_CLK_ON_R_EDGE - toggle on rising edge.
+ * DISC_CLK_BY_COUNT - toggle specified number of times.
+ * DISC_CLK_IS_FREQ - internally clock at this frequency.
+ *
+ * EXAMPLES: see Sky Raider
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_GAIN - Node multiplication function output is equal
+ * DISCRETE_MULTIPLY to INPUT0 * INPUT1
+ * DISCRETE_MULTADD to (INPUT0 * INPUT1) + INPUT 2
+ *
+ * .------------.
+ * | |
+ * INPUT0 -1------>| \|/ |
+ * | -+- |----> Netlist node
+ * INPUT1 -2------>| /|\ |
+ * | |
+ * INPUT2 -3------>| |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_MULTIPLY (name of node,
+ * input0 node or static value,
+ * input1 node or static value)
+ *
+ * DISCRETE_MULTADD (name of node,
+ * input0 node or static value,
+ * input1 node or static value,
+ * input2 node or static value)
+ *
+ * DISCRETE_GAIN (name of node,
+ * input0 node or static value,
+ * static value for gain)
+ * Example config line
+ *
+ * DISCRETE_GAIN(NODE_03,NODE_12,112.0)
+ *
+ * Always enabled, multiplies the input NODE_12 by 112.0
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_ONESHOT - Monostable multivibrator, no reset
+ * DISCRETE_ONESHOTR - Monostable multivibrator, with reset
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_ONESHOT (name of node,
+ * trigger node,
+ * amplitude node or static value,
+ * width (in seconds) node or static value,
+ * type of oneshot static value)
+ *
+ * DISCRETE_ONESHOTR (name of node,
+ * reset node or static value,
+ * trigger node,
+ * amplitude node or static value,
+ * width (in seconds) node or static value,
+ * type of oneshot static value)
+ *
+ * Types:
+ *
+ * DISC_ONESHOT_FEDGE 0x00 - trigger on falling edge (DEFAULT)
+ * DISC_ONESHOT_REDGE 0x01 - trigger on rising edge
+ *
+ * DISC_ONESHOT_NORETRIG 0x00 - non-retriggerable (DEFAULT)
+ * DISC_ONESHOT_RETRIG 0x02 - retriggerable
+ *
+ * DISC_OUT_ACTIVE_LOW 0x04 - output active low
+ * DISC_OUT_ACTIVE_HIGH 0x00 - output active high (DEFAULT)
+ *
+ * NOTE: A width of 0 seconds will output a pulse of 1 sample.
+ * This is useful for a guaranteed minimun pulse, regardless
+ * of the sample rate.
+ *
+ * EXAMPLES: see Polaris
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_RAMP - Ramp up/down circuit with clamps & reset
+ *
+ * .------------.
+ * | |
+ * ENAB -0------>| FREE/CLAMP |
+ * | |
+ * RAMP -1------>| FW/REV |
+ * | |
+ * GRAD -2------>| Grad/sec |
+ * | |----> Netlist node
+ * START -3------>| Start clamp|
+ * | |
+ * END -4------>| End clamp |
+ * | |
+ * CLAMP -5------>| off clamp |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_RAMP(name of node,
+ * enable,
+ * ramp forward/reverse node (or value),
+ * gradient node (or static value),
+ * start node or static value,
+ * end node or static value,
+ * clamp node or static value when disabled)
+ *
+ * Example config line
+ *
+ * DISCRETE_RAMP(NODE_9,NODE_10,NODE_11,10.0,-10.0,10.0,0)
+ *
+ * Node10 when not zero will allow ramp to operate, when 0 then output
+ * is clamped to clamp value specified. Node11 ramp when 0 change
+ * gradient from start to end. 1 is reverse. Output is clamped to max-
+ * min values. Gradient is specified in change/second.
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_SAMPHOLD - Sample & Hold circuit
+ *
+ * .------------.
+ * | |
+ * ENAB -0------>| |
+ * | |
+ * INP0 -1------>| SAMPLE |
+ * | & |----> Netlist node
+ * CLOCK -2------>| HOLD |
+ * | |
+ * CLKTYPE -3------>| |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_SAMPHOLD(name of node,
+ * enable,
+ * input node,
+ * clock node or static value,
+ * input clock type)
+ *
+ * Example config line
+ *
+ * DISCRETE_SAMPHOLD(NODE_9,1,NODE_11,NODE_12,DISC_SAMPHOLD_REDGE)
+ *
+ * Node9 will sample the input node 11 on the rising edge (REDGE) of
+ * the input clock signal of node 12.
+ *
+ * DISC_SAMPHOLD_REDGE - Rising edge clock
+ * DISC_SAMPHOLD_FEDGE - Falling edge clock
+ * DISC_SAMPHOLD_HLATCH - Output is latched whilst clock is high
+ * DISC_SAMPHOLD_LLATCH - Output is latched whilst clock is low
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_SWITCH - Node switch function, output node is switched
+ * by switch input to take one node/contst or
+ * other. Can be nodes or constants.
+ *
+ * SWITCH -0--------------.
+ * V
+ * .------------.
+ * | | |
+ * INPUT0 -1------}|----o |
+ * | .--- |----> Netlist node
+ * INPUT1 -2------>|----o / |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_SWITCH (name of node,
+ * enable node or static value,
+ * switch node or static value,
+ * input0 node or static value,
+ * input1 node or static value)
+ *
+ * Example config line
+ *
+ * DISCRETE_SWITCH(NODE_03,1,NODE_10,NODE_90,5.0)
+ *
+ * Always enabled, NODE_10 switches output to be either NODE_90 or
+ * constant value 5.0. Switch==0 inp0=output else inp1=output
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_ASWITCH - Node switch function, output node is same
+ * as input when CTRL is above threshold.
+ *
+ * CTRL -0--------------.
+ * V
+ * .------------.
+ * | | |
+ * INPUT0 -1------ |----- . --- |----> Netlist node
+ * | |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_ASWITCH (name of node,
+ * ctrl node or static value,
+ * input node or static value,
+ * threshold satic value )
+ *
+ * Example config line
+ *
+ * DISCRETE_ASWITCH(NODE_03,NODE_10,NODE_90, 2.73)
+ *
+ * Always enabled, NODE_10 switches output to be either NODE_90 or
+ * constant value 0.0. Ctrl>2.73 output=NODE_90 else output=0
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_TRANSFORMn - Node arithmatic logic (postfix arithmatic)
+ * (n=2,3,4,5)
+ * .------------.
+ * | |
+ * INPUT0 -0------>| |
+ * | |
+ * INPUT1 -1------>| Postfix |
+ * | stack |----> Netlist node
+ * INPUT2 -2------>| maths |
+ * | |
+ * INPUT3 -3------>| |
+ * | |
+ * INPUT4 -4------>| |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_TRANSFORMn(name of node,
+ * input0 node or static value,
+ * input1 node or static value,
+ * input2 node or static value, [optional]
+ * input3 node or static value, [optional]
+ * input4 node or static value, [optional]
+ * maths string)
+ *
+ * Example config line
+ *
+ * DISCRETE_TRANSFORM4(NODE_12,NODE_22,50.0,120.0,33.33,"01*2+3/")
+ *
+ * Arithmetic uses stack based arithmetic similar to Forth, the maths
+ * has 5 registers 0-4 and various arithmetic operations. The math
+ * string is processed from left to right in the following manner:
+ * 0 - Push input 0 to stack
+ * 1 - Push input 1 to stack
+ * 2 - Push input 2 to stack
+ * 3 - Push input 3 to stack
+ * 4 - Push input 4 to stack
+ * - - Pop two values from stack, subtract and push result to stack
+ * + - Pop two values from stack, add and push result to stack
+ * / - Pop two values from stack, divide and push result to stack
+ * * - Pop two values from stack, multiply and push result to stack
+ * a - Pop one value from stack, multiply -1 if less than 0 and push result to stack
+ * i - Pop one value from stack, multiply -1 and push result to stack
+ * ! - Pop one value from stack, logical invert, push result to stack
+ * = - Pop two values from stack, logical = and push result to stack
+ * > - Pop two values from stack, logical > and push result to stack
+ * < - Pop two values from stack, logical < and push result to stack
+ * & - Pop two values from stack, binary AND and push result to stack
+ * | - Pop two values from stack, binary OR and push result to stack
+ * ^ - Pop two values from stack, binary XOR and push result to stack
+ * P - Push a duplicate of the last stack value back on the stack
+ *
+ * EXAMPLES: see Polaris
+ *
+ ***********************************************************************
+ =======================================================================
+ * from from disc_mth.inc
+ * Component specific modules
+ =======================================================================
+ ***********************************************************************
+ *
+ * DISCRETE_COMP_ADDER - Selecatable parallel component adder.
+ * The total netlist out will be the parallel sum of all
+ * components with their corresponding data bit = 1.
+ * Set cDefault to 0 if not used.
+ *
+ * common >---cDefault---.
+ * data&0x01 >-----c[0]-----+
+ * data&0x02 >-----c[1]-----+
+ * data&0x04 >-----c[2]-----+
+ * data&0x08 >-----c[3]-----+-----> netlist node
+ * data&0x10 >-----c[4]-----+
+ * data&0x20 >-----c[5]-----+
+ * data&0x40 >-----c[6]-----+
+ * data&0x80 >-----c[7]-----'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_COMP_ADDER(name of node,
+ * data node (static value is useless),
+ * address of discrete_comp_adder_table structure)
+ *
+ * discrete_comp_adder_table = {type, cDefault, length, c{}}
+ * note: length can be a maximum of 8
+ *
+ * Circuit Types:
+ * DISC_COMP_P_CAPACITOR - parallel capacitors
+ * DISC_COMP_P_RESISTOR - parallel resistors
+ *
+ * EXAMPLES: see Hit Me
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_DAC_R1 - R1 ladder DAC with cap smoothing and external bias
+ *
+ * rBias
+ * data&0x01 >--/\R[0]/\--+-----/\/\----< vBias
+ * data&0x02 >--/\R[1]/\--|
+ * data&0x04 >--/\R[2]/\--|
+ * data&0x08 >--/\R[3]/\--|
+ * data&0x10 >--/\R[4]/\--|
+ * data&0x20 >--/\R[5]/\--|
+ * data&0x40 >--/\R[6]/\--|
+ * data&0x80 >--/\R[7]/\--+-------------+-----> Netlist node
+ * | |
+ * Z ---
+ * Z rGnd --- cFilter
+ * | |
+ * gnd gnd
+ *
+ * NOTES: rBias and vBias are used together. If not needed they should
+ * be set to 0. If used, they should both have valid values.
+ * rGnd and cFilter should be 0 if not needed.
+ * A resistor value should be properly set for each resistor
+ * up to the ladder length. Remember 0 is a short circuit.
+ * The data node is bit mapped to the ladder. valid int 0-255.
+ * TTL logic 0 is actually 0.2V but 0V is used. The other parts
+ * have a tolerance that more then makes up for this.
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_DAC_R1(name of node,
+ * data node (static value is useless),
+ * vData static value (voltage when a bit is on ),
+ * address of discrete_dac_r1_ladder structure)
+ *
+ * discrete_dac_r1_ladder = {ladderLength, r{}, vBias, rBias, rGnd, cFilter}
+ *
+ * Note: Resistors in the ladder that are set to 0, will be handled like they
+ * are out of circuit. So the bit selecting them will have no effect
+ * on the DAC output voltage.
+ *
+ * x_time - this modules automatically handles any non-integer value
+ * on the data input as x_time.
+ *
+ * EXAMPLES: see Fire Truck, Monte Carlo, Super Bug, Polaris
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_DIODE_MIXER - mixes inputs through diodes
+ *
+ *
+ * input 0 >----|>|---.
+ * |
+ * input 1 >----|>|---+----------> Netlist Node
+ * |
+ * input 2 >----|>|---+
+ * |
+ * input 3 >----|>|---+--/\/\/\--.
+ * |
+ * gnd
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_DIODE_MIXERx(name of node,
+ * (x = 2/3/4) input 0 node,
+ * input 1 node,
+ * ...,
+ * address of v_junction table)
+ *
+ * v_junction table can be set to NULL if you want all diodes to
+ * default to a 0.5V drop. Otherwise use a
+ * table of doubles to specify juntion voltages.
+ *
+ * EXAMPLES: see dkong
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_INTEGRATE - Various Integration circuits
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_INTEGRATE(name of node,
+ * trigger 0 node or static value,
+ * trigger 1 node or static value,
+ * address of discrete_integrate_info)
+ *
+ * discrete_integrate_info = {type, r1, r2, r3, c, v1, vP, f0, f1, f2}
+ *
+ * Note: Set all unused components to 0.
+ * These are all single supply circuits going from gnd(0V) to vP(B+),
+ * so be sure to specify the vP power source.
+ *
+ * Types:
+ *
+ * DISC_INTEGRATE_OP_AMP_1
+ *
+ * v1 >----+-------.
+ * | | c
+ * Z Z .---||----.
+ * Z r1 Z r2 | |
+ * Z Z | |\ |
+ * | | | | \ |
+ * +--------------+--|- \ |
+ * | | | >--+----> Netlist Node
+ * / +---------|+ /
+ * |/ | | /
+ * Trig0 >--| NPN Z |/
+ * |\ Z r3
+ * > Z
+ * | |
+ * gnd gnd
+ *
+ *
+ * EXAMPLES: see Tank8
+ *
+ * --------------------------------------------------
+ *
+ * DISC_INTEGRATE_OP_AMP_1 | DISC_OP_AMP_IS_NORTON
+ *
+ * c
+ * .---||----.
+ * | |
+ * | |\ |
+ * r1 | | \ |
+ * v1 >----ZZZZ--------+--|- \ |
+ * | >--+----> Netlist Node
+ * r2 .--|+ /
+ * Trig0 >----ZZZZ--------' | /
+ * |/
+ *
+ * Note: Trig0 is voltage level, not logic.
+ * No functions are used so set them to 0, or DISC_OP_AMP_TRIGGER_FUNCTION_NONE.
+ * You can also use DISCRETE_OP_AMP with type DISC_OP_AMP_IS_NORTON to emulate this.
+ *
+ * EXAMPLES: see Double Play
+ *
+ * --------------------------------------------------
+ *
+ * DISC_INTEGRATE_OP_AMP_2 | DISC_OP_AMP_IS_NORTON
+ *
+ * c
+ * .---||----.
+ * r1a | |
+ * v1 >----ZZZZ---. | |\ |
+ * .----. | r1b Diode | | \ |
+ * | F0 |--+--ZZZZ----|>|--+--|- \ |
+ * '----' | >--+----> Netlist Node
+ * r2a r2b .--|+ /
+ * v1 >----ZZZZ---+--ZZZZ---------+ | /
+ * .----. | | |/
+ * | F1 |--' |
+ * '----' |
+ * r3a r3b Diode |
+ * v1 >----ZZZZ---+--ZZZZ----|>|--'
+ * .----. |
+ * | F2 |--'
+ * '----'
+ *
+ * Note: For an explanation of the functions and trigger inputs,
+ * see DISCRETE_OP_AMP_TRIG_VCA below.
+ *
+ * EXAMPLES: see Polaris
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_MIXER - Mixes multiple input signals.
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_MIXERx(name of node,
+ * (x = 2 to 8) enable node or static value,
+ * input 0 node,
+ * input 1 node,
+ * input 2 node, (if used)
+ * input 3 node, (if used)
+ * input 4 node, (if used)
+ * input 5 node, (if used)
+ * input 6 node, (if used)
+ * input 7 node, (if used)
+ * address of discrete_mixer_info structure)
+ *
+ * discrete_mixer_desc = {type, r{}, r_node{}, c{}, rI, rF, cF, cAmp, vRef, gain}
+ *
+ * Note: Set all unused components to 0.
+ * If an rNode is not used it should also be set to 0.
+ *
+ * Types:
+ *
+ * DISC_MIXER_IS_RESISTOR
+ *
+ * rNode[0] r[0] c[0]
+ * IN0 >--zzzz-----zzzz----||---.
+ * |
+ * rNode[1] r[1] c[1] |
+ * IN1 >--zzzz-----zzzz----||---+--------.
+ * . . . . | | cAmp
+ * . . . . | Z<------||---------> Netlist Node
+ * . . . . | Z
+ * . rNode[7] r[7] c[7] | Z rF
+ * IN7 >--zzzz-----zzzz----||---+ |
+ * | |
+ * --- |
+ * cF --- |
+ * | |
+ * gnd gnd
+ *
+ * Note: The variable resistor is used in it's full volume position.
+ * MAME's built in volume is used for adjustment.
+ *
+ * EXAMPLES: see Polaris, Super Bug
+ *
+ * --------------------------------------------------
+ *
+ * DISC_MIXER_IS_OP_AMP
+ *
+ * cF
+ * .----||---.
+ * | |
+ * rNode[0] r[0] c[0] | rF |
+ * IN0 >--zzzz------zzzz----||---. +---ZZZZ--+
+ * | | |
+ * rNode[1] r[1] c[1] | rI | |\ |
+ * IN1 >--zzzz------zzzz----||---+--zzzz--+ | \ |
+ * . . . . | '--|- \ | cAmp
+ * . . . . | | >--+---||-----> Netlist Node
+ * . . . . | .--|+ /
+ * . rNode[7] r[7] c[7] | | | /
+ * IN7 >--zzzz------zzzz----||---' | |/
+ * |
+ * vRef >----------------------------------'
+ *
+ * Note: rI is not always used and should then be 0.
+ *
+ * EXAMPLES: see Fire Truck, Monte Carlo
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_OP_AMP - Various op-amp circuits
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_OP_AMP(name of node,
+ * enable node or static value,
+ * input 0 node or static value,
+ * input 1 node or static value,
+ * address of discrete_op_amp_info structure)
+ *
+ * discrete_op_amp_info = {type, r1, r2, r3, r4, c, vN, vP}
+ *
+ * Note: Set all unused components to 0.
+ *
+ * Types:
+ *
+ * DISC_OP_AMP_IS_NORTON
+ *
+ * c
+ * .----||---.
+ * | |
+ * r3 | r4 | vP = B+
+ * vP >---ZZZZ------+---ZZZZ--+ vN = B-
+ * | |
+ * r1 | |\ | Note: r2 must always be used
+ * IN0 >---ZZZZ------+ | \ |
+ * '--|- \ |
+ * r2 | >--+-----> Netlist Node
+ * IN1 >---ZZZZ---------|+ /
+ * | /
+ * |/
+ *
+ * EXAMPLES: see Space Encounter
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_OP_AMP_ONESHOT - Various op-amp one shot circuits
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_OP_AMP_ONESHOT(name of node,
+ * trigger node (voltage level),
+ * address of discrete_op_amp_1sht_info structure)
+ *
+ * discrete_op_amp_1sht_info = {type, r1, r2, r3, r4, r5, c1, c2, vN, vP}
+ *
+ * Types:
+ *
+ * DISC_OP_AMP_1SHT_1 | DISC_OP_AMP_IS_NORTON
+ *
+ * c1 .---|>|---.
+ * gnd >----||---+---+ |
+ * | | r4 | vP = B+
+ * Z '---ZZZZ--+ vN = B-
+ * Z r3 |
+ * Z |\ | Note: all components must be used
+ * r1 | | \ | The oneshot is cancelled when TRIG goes low
+ * vP >---ZZZZ--+------|- \ |
+ * | >--+-----> Netlist Node
+ * c2 r2 .--|+ / |
+ * TRIG >--||---ZZZZ--+ | / |
+ * | |/ |
+ * | r5 |
+ * '---ZZZZ--'
+ *
+ *
+ * EXAMPLES: see Space Encounter
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_OP_AMP_TRIG_VCA - Triggered Norton op amp voltage controlled amplifier.
+ * This means the cap is rapidly charged through r5 when F2=1.
+ * Then it discharges through r6+r7 when F2=0.
+ * This voltage controls the amplitude.
+ * While the diagram looks complex, usually only parts of it are used.
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_OP_AMP_TRIG_VCA(name of node,
+ * trigger 0 node or static value,
+ * trigger 1 node or static value,
+ * trigger 2 node or static value,
+ * input 0 node or static value,
+ * input 1 node or static value,
+ * address of discrete_op_amp_tvca_info structure)
+ *
+ * discrete_op_amp_tvca_info = { r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, c1, c2, c3, c4, v1, v2, v3, vP, f0, f1, f2, f3, f4, f5}
+ *
+ * Note: Set all unused components to 0.
+ * Set all unused functions to DISC_OP_AMP_TRIGGER_FUNCTION_NONE
+ * Set all unused nodes to NODE_NC.
+ * If function F3 is not used then set r6=0 and use only r7.
+ * r2 = r2a + r2b. r3 = r3a + r3b.
+ * vP is the op-amp B+.
+ *
+ * r2a
+ * IN0 >----ZZZZ-----. r1 c4
+ * .----. | vP >------ZZZZ---+---||----.
+ * | F0 |----+ | |
+ * '----' | r2b | r4 |
+ * r3a '---------------ZZZZ---+---ZZZZ--+
+ * IN1 >----ZZZZ---. | |
+ * .----. | r3b | |\ |
+ * | F1 |--+-----------------ZZZZ---+ | \ |
+ * '----' '--|- \ |
+ * .----. diode r6 r7 | >--+----> Netlist Node
+ * | F2 |--+--|>|--+--ZZZZ---+--ZZZZ-+-|+ /
+ * '----' | | | | | /
+ * | --- .----. | |/
+ * r5 | --- c1 | F3 | |
+ * v1 >----ZZZZ---' | '----' |
+ * gnd |
+ * |
+ * .----. diode r9 |
+ * | F4 |--+--|>|-----------+---ZZZZ-+
+ * '----' | c2 | |
+ * r8 | gnd >---||---' |
+ * v2 >----ZZZZ---' |
+ * .----. diode r11 |
+ * | F5 |--+--|>|-----------+---ZZZZ-'
+ * '----' | c3 |
+ * r10 | gnd >---||---'
+ * v3 >----ZZZZ---'
+ *
+ * Function types:
+ *
+ * Trigger 0, 1 and 2 are used for the functions F0 - F5.
+ * When the output of the function is 0, then the connection is held at 0V or gnd.
+ * When the output of the function is 1, then the function is an open circuit.
+ *
+ * DISC_OP_AMP_TRIGGER_FUNCTION_NONE - Not used, cicuit open.
+ * DISC_OP_AMP_TRIGGER_FUNCTION_TRG0 - Gnd when trigger 0 is 0.
+ * DISC_OP_AMP_TRIGGER_FUNCTION_TRG0_INV - Gnd when trigger 0 is 1.
+ * DISC_OP_AMP_TRIGGER_FUNCTION_TRG1 - Gnd when trigger 1 is 0.
+ * DISC_OP_AMP_TRIGGER_FUNCTION_TRG1_INV - Gnd when trigger 1 is 1.
+ * DISC_OP_AMP_TRIGGER_FUNCTION_TRG2 - Gnd when trigger 2 is 0.
+ * DISC_OP_AMP_TRIGGER_FUNCTION_TRG2_INV - Gnd when trigger 2 is 1.
+ * DISC_OP_AMP_TRIGGER_FUNCTION_TRG01_AND - Gnd when trigger 0 or 1 are 0.
+ * DISC_OP_AMP_TRIGGER_FUNCTION_TRG01_NAND - Gnd when trigger 0 and 1 are 1.
+ *
+ * EXAMPLES: see Polaris
+ *
+ ***********************************************************************
+ =======================================================================
+ * from from disc_flt.inc
+ * Generic modules
+ =======================================================================
+ ***********************************************************************
+ *
+ * DISCRETE_FILTER1
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_FILTER1(name of node,
+ * enable node or static value,
+ * input node,
+ * filter center frequency static value,
+ * filter type static value)
+ *
+ * Filter types: DISC_FILTER_LOWPASS,
+ * DISC_FILTER_HIGHPASS
+ * DISC_FILTER_BANDPASS
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_FILTER2
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_FILTER2(name of node,
+ * enable node or static value,
+ * input node,
+ * filter center frequency static value,
+ * damp static value,
+ * filter type static value)
+ *
+ * Filter types: DISC_FILTER_LOWPASS,
+ * DISC_FILTER_HIGHPASS
+ * DISC_FILTER_BANDPASS
+ *
+ * Note: Damp = 1/Q
+ *
+ ***********************************************************************
+ =======================================================================
+ * from from disc_flt.inc
+ * Component specific modules
+ =======================================================================
+ ***********************************************************************
+ *
+ * DISCRETE_CRFILTER - Simple single pole CR filter network (vRef = 0)
+ * DISCRETE_CRFILTER_VREF - Same but referenced to vRef not 0V
+ *
+ * .------------.
+ * | |
+ * | CR FILTER |
+ * | |
+ * INPUT1 -0------}| --| |-+-- |
+ * | C | |----} Netlist node
+ * RVAL -1------}| Z |
+ * | Z R |
+ * CVAL -2------}| | |
+ * | vRef |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_CRFILTER(name of node,
+ * input node (or value)
+ * resistor node or static value in OHMS
+ * capacitor node or static value in FARADS)
+ *
+ * DISCRETE_CRFILTER_VREF(name of node,
+ * input node (or value)
+ * resistor value in OHMS
+ * capacitor value in FARADS,
+ * vRef node or static value)
+ *
+ * Example config line
+ *
+ * DISCRETE_CRFILTER(NODE_11,NODE_10,100,CAP_U(1))
+ *
+ * Defines a CR filter with a 100R & 1uF network
+ * the input is fed from NODE_10.
+ *
+ * This can be also thought of as a high pass filter with a 3dB cutoff
+ * at:
+ * 1
+ * Fcuttoff = --------------
+ * 2*Pi*RVAL*CVAL
+ *
+ * (3dB cutoff is where the output power has dropped by 3dB ie Half)
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_OP_AMP_FILTER - Various Op Amp Filters.
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_OP_AMP_FILTER(name of node,
+ * enable node or static value,
+ * input 1 node or static value,
+ * input 2 node or static value,
+ * type static value,
+ * address of discrete_op_amp_filt_info)
+ *
+ * discrete_op_amp_filt_info = {r1, r2, r3, r4, rF, c1, c2, c3, vRef, vP, vN}
+ *
+ * Note: Set all unused components to 0.
+ * vP and vN are the +/- op-amp power supplies.
+ * vRef is 0 if Gnd.
+ *
+ * Types:
+ *
+ * DISC_OP_AMP_FILTER_IS_LOW_PASS_1
+ * First Order Low Pass Filter
+ *
+ * c1
+ * .-------||---------.
+ * | |
+ * r1 | rF |
+ * IN0 >--ZZZZ--. +------ZZZZ--------+
+ * | | |
+ * r2 | | |\ |
+ * IN1 >--ZZZZ--+------+--------+ | \ |
+ * | '--|- \ |
+ * r3 | | >--+----------> Netlist Node
+ * vRef >--ZZZZ--' .--|+ /
+ * | | /
+ * vRef >-----------------------' |/
+ *
+ * --------------------------------------------------
+ *
+ * DISC_OP_AMP_FILTER_IS_LOW_PASS_1_A
+ * First Order Low Pass Filter
+ *
+ * c1
+ * .-------||---------.
+ * | |
+ * r1 | rF |
+ * IN0 >--ZZZZ--. +------ZZZZ--------+
+ * | | |
+ * r2 | | |\ |
+ * VP >--ZZZZ--+------+--------+ | \ |
+ * | '--|- \ |
+ * r3 | | >--+----------> Netlist Node
+ * VN >--ZZZZ--' .--|+ /
+ * | | /
+ * IN1 >------------------------' |/
+ *
+ * --------------------------------------------------
+ *
+ * DISC_OP_AMP_FILTER_IS_HIGH_PASS_1
+ * First Order High Pass Filter
+ *
+ * r1 rF
+ * IN0 >--ZZZZ--. .------ZZZZ--------.
+ * | | |
+ * r2 | c1 | |\ |
+ * IN1 >--ZZZZ--+--||--+--------+ | \ |
+ * | '--|- \ |
+ * r3 | | >--+----------> Netlist Node
+ * vRef >--ZZZZ--' .--|+ /
+ * | | /
+ * vRef >-----------------------' |/
+ *
+ * --------------------------------------------------
+ *
+ * DISC_OP_AMP_FILTER_IS_BAND_PASS_1
+ * First Order Band Pass Filter
+ *
+ * c1
+ * .-------||---------.
+ * | |
+ * r1 | rF |
+ * IN0 >--ZZZZ--. +------ZZZZ--------+
+ * | | |
+ * r2 | c2 | |\ |
+ * IN1 >--ZZZZ--+--||--+--------+ | \ |
+ * | '--|- \ |
+ * r3 | | >--+----------> Netlist Node
+ * vRef >--ZZZZ--' .--|+ /
+ * | | /
+ * vRef >-----------------------' |/
+ *
+ * --------------------------------------------------
+ *
+ * DISC_OP_AMP_FILTER_IS_BAND_PASS_1M
+ * Single Pole Multiple Feedback Band Pass Filter
+ *
+ * c1
+ * .--||----+---------.
+ * | | |
+ * r1 | Z |
+ * IN0 >--ZZZZ--. | Z rF |
+ * | | Z |
+ * r2 | | c2 | |\ |
+ * IN1 >--ZZZZ--+------+--||----+ | \ |
+ * | '--|- \ |
+ * r3 | | >--+----------> Netlist Node
+ * vRef >--ZZZZ--' .--|+ /
+ * | | /
+ * vRef >-----------------------' |/
+ *
+ * EXAMPLES: see Tank 8, Atari Baseball, Monte Carlo
+ *
+ * --------------------------------------------------
+ *
+ * DISC_OP_AMP_FILTER_IS_BAND_PASS_1M | DISC_OP_AMP_IS_NORTON
+ * Single Pole Multiple Feedback Band Pass Filter
+ *
+ * c1
+ * .--||----+---------.
+ * | | |
+ * | Z |
+ * | Z rF |
+ * | Z |
+ * r1 | c2 | |\ |
+ * IN0 >--ZZZZ--+------+--||----+ | \ |
+ * | '--|- \ |
+ * r2 | | >--+----------> Netlist Node
+ * vRef >--ZZZZ--' .--|+ /
+ * r3 | | /
+ * vP >-----------ZZZZ--------' |/
+ *
+ * EXAMPLES: see Space Encounter
+ *
+ * --------------------------------------------------
+ *
+ * DISC_OP_AMP_FILTER_IS_HIGH_PASS_0 | DISC_OP_AMP_IS_NORTON
+ * Basic Norton High Pass Filter
+ *
+ * rF
+ * r1 = r1a + r1b .--ZZZZ---.
+ * | |
+ * r1a c1 r1b | |\ |
+ * IN1 >--ZZZZ---||---ZZZZ------+ | \ |
+ * '--|- \ |
+ * | >--+----------> Netlist Node
+ * .--|+ /
+ * r4 | | /
+ * vRef >------------ZZZZ-------' |/
+ *
+ * EXAMPLES: see Polaris
+ *
+ * --------------------------------------------------
+ *
+ * DISC_OP_AMP_FILTER_IS_BAND_PASS_0 | DISC_OP_AMP_IS_NORTON
+ * Basic Norton Band Pass Filter
+ *
+ * rF
+ * r3 = r3a + r3b .--ZZZZ---.
+ * | |
+ * r1 r2 r3a c3 r3b | |\ |
+ * IN1 >---ZZZZ--+--ZZZZ--+--ZZZZ---||----ZZZZ---+ | \ |
+ * | | '--|- \ |
+ * --- --- | >--+---> Netlist Node
+ * --- c1 --- c2 .--|+ /
+ * | | | | /
+ * gnd gnd | |/
+ * r4 |
+ * vRef >--------------------------------ZZZZ----'
+ *
+ * EXAMPLES: see Polaris
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_SALLEN_KEY_FILTER - Sallen key low pass filter
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_SALLEN_KEY_FILTER(name of node,
+ * enable node or static value,
+ * input node or static value,
+ * type static value,
+ * address of discrete_op_amp_filt_info)
+ *
+ * discrete_op_amp_filt_info = {r1, r2, r3, r4, rF, c1, c2, c3, vRef, vP, vN}
+ *
+ * Note: Set all unused components to 0.
+ *
+ * Types:
+ *
+ * DISC_SALLEN_KEY_LOWPASS
+ *
+ * .---------.
+ * | |
+ * | |\ |
+ * | | \ |
+ * `--|- \ |
+ * R1 R2 | >--+----> Netlist Node
+ * IN >---ZZZZ--+--ZZZZ--+------|+ / |
+ * | | | / |
+ * --- --- |/ |
+ * --- C1 --- C2 |
+ * | | |
+ * | gnd |
+ * | |
+ * `----------------------'
+ *
+ * EXAMPLES: see moon patrol, dkong
+ *
+ * References:
+ * http://www.t-linespeakers.org/tech/filters/Sallen-Key.html
+ * http://en.wikipedia.org/wiki/Sallen_Key_filter
+ ***********************************************************************
+ *
+ * DISCRETE_RC_CIRCUIT_1 - RC charge/discharge circuit
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_RC_CIRCUIT_1(name of node,
+ * In0 (Logic) node,
+ * In1 (Logic) node,
+ * R static value,
+ * C static value)
+ *
+ * 5V
+ * v
+ * |
+ * .-------.
+ * | 4066 |
+ * In0 >---|c |
+ * '-------'
+ * |
+ * +------------.
+ * | |
+ * .-------. --- C
+ * | 4066 | ---
+ * In1 >---|c | |
+ * '-------' gnd
+ * |
+ * +----> Node Output
+ * |
+ * Z
+ * Z R
+ * Z
+ * |
+ * gnd
+ *
+ * EXAMPLES: see Sky Raider, Battlezone
+ *
+ ************************************************************************
+ *
+ * DISCRETE_RCDISC - Simple single pole RC discharge network
+ *
+ * .------------.
+ * | |
+ * | RC |
+ * | |
+ * INPUT1 -0------>| -ZZZZ-+-- |
+ * | R | |----> Netlist node
+ * RVAL -1------>| --- |
+ * | ---C |
+ * CVAL -2------>| | |
+ * | vref |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_RCFILTER(name of node,
+ * input node (or value),
+ * resistor value in OHMS,
+ * capacitor value in FARADS)
+ *
+ * Example config line
+ *
+ * DISCRETE_RCDISC(NODE_11,10,100,CAP_U(1))
+ *
+ * C discharges from 10v as indicated by RC of 100R & 1uF.
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_RCDISC2 - Switched input RC discharge network
+ *
+ * .------------.
+ * | |
+ * SWITCH -0------>| IP0 | IP1 |
+ * | |
+ * INPUT0 -1------>| -ZZZZ-. |
+ * | R0 | |
+ * RVAL0 -2------>| | |
+ * | | |
+ * INPUT1 -3------>| -ZZZZ-+-- |
+ * | R1 | |----> Netlist node
+ * RVAL1 -4------>| --- |
+ * | ---C |
+ * CVAL -5------>| | |
+ * | |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_RCDISC2(name of node,
+ * switch,
+ * input0 node (or value),
+ * resistor0 value in OHMS,
+ * input1 node (or value),
+ * resistor1 value in OHMS,
+ * capacitor value in FARADS)
+ *
+ * Example config line
+ *
+ * DISCRETE_RCDISC2(NODE_9,NODE_10,10.0,100,0.0,100,CAP_U(1))
+ *
+ * When switched by NODE_10, C charges/discharges from 10v/0v
+ * as dictated by R0/C & R1/C combos respectively
+ * of 100R & 1uF.
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_RCDISC3 - RC discharge network
+ *
+ * FIXME: Diode direction (for bzone)
+ *
+ * .-----------------.
+ * | |
+ * ENAB -0------>| |
+ * | diode R2 |
+ * JV -5------>| -+-|>|--ZZZZ-+- |----> Netlist node (JV < 0)
+ * | |
+ * | diode R2 |
+ * INPUT1 -1------>| -+-|<|--ZZZZ-+- |----> Netlist node (JV > 0)
+ * | | | |
+ * RVAL1 -2------>| '-ZZZZ-+----' |
+ * | R1 | |
+ * RVAL2 -3------>| --- |
+ * | ---C |
+ * CVAL -4------>| | |
+ * | gnd |
+ * '-----------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_RCDISC3(name of node,
+ * enable,
+ * input node (or value),
+ * R1 resistor value in OHMS,
+ * R2 resistor value in OHMS,
+ * capacitor value in FARADS,
+ * diode junction voltage)
+ *
+ * The polarity of the diode junction voltage determines the polarity of the diode.
+ *
+ * Example config line
+ *
+ * DISCRETE_RCDISC3(NODE_11,NODE_10,10,100,220,CAP_U(1), 0.5)
+ *
+ * When enabled by NODE_10, C charges from 10v as indicated by RC
+ * of 100R & 1uF.
+ *
+ * EXAMPLES: see Tank8, bzone
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_RCDISC4 - RC discharge networks triggered by logic levels
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_RCDISC4(name of node,
+ * enable,
+ * logic input node,
+ * R1 resistor static value in OHMS,
+ * R2 resistor static value in OHMS,
+ * R3 resistor static value in OHMS,
+ * C1 capacitor static value in FARADS,
+ * vP static value in VOLTS,
+ * circuit type static value)
+ *
+ * Type: 1
+ *
+ * vP >---.
+ * | .------.
+ * Z | |
+ * Z R2 | |\ |
+ * O.C. Z '-|-\ |
+ * |\ Diode R1 | | >-+---> node
+ * Input >---| o----|<|------ZZZZ---+--------+-------|+/
+ * |/ | | |/
+ * --- -----
+ * C1 --- \ / Diode
+ * | V
+ * gnd ---
+ * |
+ * Z
+ * Z R3
+ * Z
+ * |
+ * gnd
+ *
+ * EXAMPLES: see Phoenix
+ *
+ * --------------------------------------------------
+ *
+ * Type: 2
+ *
+ * 5V >---. .------.
+ * Z | |
+ * Z 1k | |\ |
+ * Z '-|-\ |
+ * | R1 C1 Diode | >-+---> node
+ * Input >---+--ZZZZ----||----+-----|>|----+--------|+/
+ * | | |/
+ * ----- Z
+ * ^ Z R2
+ * / \ Diode Z
+ * ----- |
+ * | gnd
+ * gnd
+ *
+ * EXAMPLES: see
+ *
+ * --------------------------------------------------
+ *
+ * Type: 3
+ *
+ * 5V >---. .------.
+ * Z | |
+ * Z 1k | |\ |
+ * Z '-|-\ |
+ * | R1 Diode | >-+---> node
+ * Input >---+--ZZZZ-----|>|------+---------+--------|+/
+ * | | |/
+ * --- C1 Z
+ * --- Z R2
+ * | Z
+ * gnd |
+ * gnd
+ *
+ *
+ * EXAMPLES: see
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_RCDISC5 - Diode in series with R//C
+ *
+ * .---------------------.
+ * | |
+ * ENAB -0------>| -----------. |
+ * | -- |
+ * INPUT1 -1------>| -|>|--+--|SW|---+- |----> Netlist node
+ * | | -- | |
+ * RVAL -2------>| --- Z |
+ * | C--- Z R |
+ * CVAL -3------>| | Z |
+ * | -----+----- |
+ * | |gnd |
+ * '---------------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_RCDISC5(name of node,
+ * enable,
+ * input node (or value),
+ * resistor value in OHMS,
+ * capacitor value in FARADS)
+ *
+ * Example config line
+ *
+ * DISCRETE_RCDISC5(NODE_11,NODE_10,10,100,CAP_U(1))
+ *
+ * When enabled by NODE_10, C discharges from 10v as indicated by RC
+ * of 100R & 1uF. If not enabled, the capcitors keeps it load and may
+ * still be charged through input1. The switch is assumed to be a CD4066,
+ * thus if not enabled the output will be drawn by R to GND since
+ * the switch is in high impedance mode.
+ *
+ * EXAMPLES: see Spiders, Galaxian
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_RCDISC_MODULATED - RC triggered by logic and modulated
+ *
+ * vP >---.
+ * |
+ * Z
+ * Z R1
+ * O.C. Z
+ * |\ | R2 C1 R3
+ * INPUT1 >---| o---+--ZZZ---||------+----+---ZZZ------+---> node
+ * |/ | | /
+ * / \ Z |/
+ * Diode ----- Z R4 .---| NPN
+ * | Z | |\
+ * | | | >
+ * gnd gnd | |
+ * | gnd
+ * INPUT2 >----------ZZZ------------------------.
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_RCDISC_MODULATED(name of node,
+ * INPUT1 node (or value),
+ * INPUT2 node (or value),
+ * R1 value in OHMS (static value),
+ * R2 value in OHMS (static value),
+ * R3 value in OHMS (static value),
+ * R4 value in OHMS (static value),
+ * C1 value in FARADS (static value),
+ * vP value in VOLTS)
+ *
+ * EXAMPLES: dkong
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_RCFILTER - Simple single pole RC filter network (vRef = 0)
+ * DISCRETE_RCFILTER_VREF - Same but referenced to vRef not 0V
+ *
+ * .------------.
+ * | |
+ * ENAB -0------}| RC FILTER |
+ * | |
+ * INPUT1 -1------}| -ZZZZ-+-- |
+ * | R | |----} Netlist node
+ * RVAL -2------}| --- |
+ * | ---C |
+ * CVAL -3------}| | |
+ * | vRef |
+ * '------------'
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_RCFILTER(name of node,
+ * enable
+ * input node (or value)
+ * resistor value in OHMS
+ * capacitor value in FARADS)
+ *
+ * DISCRETE_RCFILTER_VREF(name of node,
+ * enable
+ * input node (or value)
+ * resistor value in OHMS
+ * capacitor value in FARADS,
+ * vRef static value)
+ *
+ * Example config line
+ *
+ * DISCRETE_RCFILTER(NODE_11,1,NODE_10,100,CAP_U(1))
+ *
+ * Defines an always enabled RC filter with a 100R & 1uF network
+ * the input is fed from NODE_10.
+ *
+ * This can be also thought of as a low pass filter with a 3dB cutoff
+ * at:
+ * 1
+ * Fcuttoff = --------------
+ * 2*Pi*RVAL*CVAL
+ *
+ * (3dB cutoff is where the output power has dropped by 3dB ie Half)
+ *
+ * EXAMPLES: see Polaris
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_RCFILTER_SW - Multiple switchable RC filters
+ *
+ * R
+ * INPUT >-----------ZZZZ-+-------+----......-----> Output
+ * | |
+ * +-+ +-+
+ * SWITCH > Bit 0 ---->F1 | | F2 | |
+ * '-' ^ '-'
+ * Bit 1 ---------|----' |
+ * | |
+ * Bit ... --- ---
+ * --- C1 --- C2
+ * | |
+ * GND GND
+ *
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_RCFILTER_SW(name of node,
+ * enable,
+ * input node (or value),
+ * switch node (or value),
+ * R in Ohms (static value),
+ * C1 in Farads (static value),
+ * C2 in Farads (static value),
+ * C3 in Farads (static value),
+ * C4 in Farads (static value))
+ *
+ * This is a typical filter circuit in circusc or scramble.
+ * Switches are usually CD4066 with a "open" resistance of
+ * typical 470 Ohms at 5V.
+ * This circuit supports up to 4 filters.
+ *
+ * EXAMPLES: see circusc
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_RCINTEGRATE - RC integration circuit/amplifier
+ *
+ *
+ * vP >-------------------+
+ * |
+ * Z
+ * Z R3
+ * Z
+ * |
+ * +-----------------> node (Type 3)
+ * /
+ * |/
+ * INPUT >---------------| NPN
+ * \ .--------------> node (Type 2)
+ * > | R1
+ * +--+--ZZZ-+-------> node (Type 1)
+ * | |
+ * Z ---
+ * Z R2 C---
+ * Z |
+ * | |
+ * gnd gnd
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_RCINTEGRATE(name of node,
+ * INPUT node (or value),
+ * R1 value in OHMS,
+ * R2 value in OHMS,
+ * R3 value in OHMS,
+ * C value in FARADS,
+ * vP node (or value in VOLTS)
+ * TYPE)
+ *
+ * TYPE: RC_INTEGRATE_TYPE1, RC_INTEGRATE_TYPE2, RC_INTEGRATE_TYPE3
+ *
+ * Actually an amplifier as well. Primary reason for implementation was integration.
+ * The integration configuration (TYPE3, R3=0) works quite well, the amplifying
+ * configuration is missing a good, yet simple ( :-) ) transistor model. Around the
+ * defined working point the amplifier delivers results.
+ *
+ * EXAMPLES: dkong
+ *
+ *
+ ***********************************************************************
+ =======================================================================
+ * from from disc_dev.inc
+ * Component specific modules
+ =======================================================================
+ ***********************************************************************
+ *
+ * DISCRETE_555_ASTABLE - NE555 Chip simulation (astable mode).
+ * DISCRETE_555_ASTABLE_CV - NE555 Chip simulation (astable mode) with CV control.
+ *
+ * v_charge v_pos
+ * V V
+ * | |
+ * | |
+ * | |
+ * Z |8
+ * _FAST_CHARGE_DIODE R1 Z .---------.
+ * (optional) | 7| Vcc |
+ * +---------> +-----|Discharge|
+ * | | | |
+ * --- Z | 555 |3
+ * \ / R2 Z | Out|---> Netlist Node
+ * V | 6| |
+ * --- +-----|Threshold|
+ * | | | |
+ * +---------> +-----|Trigger |
+ * | 2| |---< Control Voltage
+ * | | Reset |5
+ * | '---------'
+ * --- 4|
+ * C --- |
+ * | ^
+ * gnd Reset
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_555_ASTABLE(name of node,
+ * reset node (or value),
+ * R1 node (or value) in ohms,
+ * R2 node (or value) in ohms,
+ * C node (or value) in farads,
+ * address of discrete_555_desc structure)
+ *
+ * DISCRETE_555_ASTABLE_CV(name of node,
+ * reset node (or value),
+ * R1 node (or value) in ohms,
+ * R2 node (or value) in ohms,
+ * C node (or value) in farads,
+ * Control Voltage node (or value),
+ * address of discrete_555_desc structure)
+ *
+ * discrete_555_desc =
+ * {
+ * options, - bit mapped options
+ * v_pos, - B+ voltage of 555
+ * v_charge, - voltage (or node) to charge circuit (Defaults to v_pos)
+ * v_out_high - High output voltage of 555 (Defaults to v_pos - 1.2V)
+ * }
+ *
+ * The last 2 options of discrete_555_desc can use the following defaults:
+ * DEFAULT_555_CHARGE - to connect v_charge to v_pos
+ * DEFAULT_555_HIGH - to use the normal output voltage based on v_pos
+ * or combine both as:
+ * DEFAULT_555_VALUES
+ *
+ * eg. {DISC_555_OUT_SQW | DISC_555_OUT_DC, 12, DEFAULT_555_VALUES}
+ *
+ * Output Types: (only needed with DISC_555_OUT_SQW, DISC_555_OUT_CAP
+ * and DISC_555_OUT_ENERGY)
+ * DISC_555_OUT_DC - Output is actual DC. (DEFAULT)
+ * DISC_555_OUT_AC - A cheat to make the waveform AC.
+ *
+ * Waveform Types: (ORed with output types)
+ * DISC_555_OUT_SQW - Output is Squarewave. 0 or v_out_high. (DEFAULT)
+ * When the state changes from low to high (or high to low)
+ * during a sample, the output will high (or low) for that
+ * sample. This can cause alaising effects.
+ * DISC_555_OUT_CAP - Output is Timing Capacitor 'C' voltage.
+ * DISC_555_OUT_COUNT_F - If the 555 frequency is greater then half the sample
+ * rate, then the output may change state more then once
+ * during the sample. Using this flag will cause
+ * the output to be the number of falling edges that
+ * happened during the sample. This is usefull to feed
+ * to counter circuits. The Output Type flag is ingnored
+ * when this flag is used.
+ * DISC_555_OUT_COUNT_R - Same as DISC_555_OUT_COUNT_F but with rising edges.
+ * DISC_555_OUT_ENERGY - Same SQW, but will help reduce aliasing effects.
+ * This should be used when the 555 squarewave output is used
+ * as a final output and not as a clock source.
+ * If the state changes from low to high 1/4 of the way
+ * through the sample, then the output will be 75% of the
+ * normal high value.
+ * DISC_555_OUT_LOGIC_X - This will output the 0/1 level of the flip-flop with
+ * some eXtra info. This x_time is in decimal remainder.
+ * It lets you know the percent of sample time where the
+ * flip-flop changed state. If 0, the change did not happen
+ * during the sample. 1.75 means the flip-flop is 1 and
+ * switched over 1/4 of the way through the sample.
+ * 0.2 means the flip-flop is 0 and switched over 4/5 of
+ * the way through the sample.
+ * X modules can be used with counters to reduce alaising.
+ * DISC_555_OUT_COUNT_F_X - Same as DISC_555_OUT_COUNT_F but with x_time.
+ * DISC_555_OUT_COUNT_R_X - Same as DISC_555_OUT_COUNT_R but with x_time.
+ *
+ * other options - DISCRETE_555_ASTABLE only:
+ * DISC_555_ASTABLE_HAS_FAST_CHARGE_DIODE - diode used to bypass rDischarge
+ * when charging for quicker charge.
+ *
+ * EXAMPLES: see Hit Me, Canyon Bomber, Sky Diver
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_555_MSTABLE - NE555 Chip simulation (monostable mode)
+ * - Triggered on falling edge.
+ *
+ * v_charge v_pos
+ * V V
+ * | |
+ * | |
+ * | |
+ * Z |
+ * R Z .---------.
+ * | | Vcc |
+ * +-----|Discharge|
+ * | | |
+ * | | 555 |
+ * | | Out|---> Netlist Node
+ * | | |
+ * +-----|Threshold|
+ * | | |
+ * | | Trigger|--------< Trigger
+ * | | CV|---.
+ * | | Reset | |
+ * | '---------' --- not
+ * --- | --- needed
+ * C --- | |
+ * | ^ gnd
+ * gnd Reset
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_555_MSTABLE(name of node,
+ * reset node (or value),
+ * Trigger node,
+ * R node (or value) in ohms,
+ * C node (or value) in farads,
+ * address of discrete_555_desc structure)
+ *
+ * discrete_555_desc = See DISCRETE_555_ASTABLE for description.
+ * Note: v_charge can not be a node for this circuit.
+ *
+ * Trigger Types
+ * DISC_555_TRIGGER_IS_LOGIC - Input is (0 or !0) logic (DEFAULT)
+ * DISC_555_TRIGGER_IS_VOLTAGE - Input is actual voltage.
+ * Voltage must drop below
+ * trigger to activate.
+ * DISC_555_TRIGGER_IS_COUNT - 1 when trigger, allows passing of x_time.
+ * Mainly connected with other module using
+ * a xxx_COUNT_F_X type.
+ * DISC_555_TRIGGER_DISCHARGES_CAP - some circuits connect an external
+ * device (transistor) to the cap to
+ * discharge it when the trigger is
+ * enabled. Thereby allowing the one-shot
+ * to retrigger.
+ *
+ * Output Types: (ORed with trigger types)
+ * DISC_555_OUT_DC - Output is actual DC. (DEFAULT)
+ * DISC_555_OUT_AC - A cheat to make the waveform AC.
+ *
+ * Waveform Types: (ORed with trigger types)
+ * DISC_555_OUT_SQW - Output is Squarewave. 0 or v_out_high. (DEFAULT)
+ * DISC_555_OUT_CAP - Output is Timing Capacitor 'C' voltage.
+ * DISC_555_OUT_ENERGY - see DISCRETE_555_MSTABLE.
+ *
+ * EXAMPLES: see Frogs, Sprint 8
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_555_CC - Constant Current Controlled 555 Oscillator
+ * Which works out to a VCO when R is fixed.
+ *
+ * v_cc_source v_pos
+ * V V
+ * | .----------------------+
+ * | | |
+ * | | .---------.
+ * | | rDischarge | Vcc |
+ * Z Z .---+-----|Discharge|
+ * Z R Z rBias | | | |
+ * | | | Z | 555 |
+ * | | | Z | Out|---> Netlist Node
+ * .----. | >-' | | |
+ * Vin >--| CC |--+--> option +-----|Threshold|
+ * '----' >-----+ | |
+ * +-----|Trigger |
+ * | | |
+ * .------+-----' | Reset |
+ * | | '---------'
+ * --- Z |
+ * --- C Z rGnd |
+ * | | ^
+ * gnd gnd Reset
+ *
+ * Notes: R sets the current and should NEVER be 0 (short).
+ * The current follows the voltage I=Vin/R and charges C.
+ * rBias, rDischarge and rGnd should be 0 if not used.
+ * Reset is active low for the module.
+ *
+ * Note that the CC source can be connected two different ways.
+ * See the option flags below for more info.
+ *
+ * DISC_555_OUT_SQW mode only:
+ * When there is no rDischarge there is a very short discharge
+ * cycle (almost 0s), so the module triggers the output for 1
+ * sample. This does not effect the timing, just the duty cycle.
+ * But frequencies more the half the sample frequency will be
+ * limited to a max of half the sample frequency.
+ * This mode should be used to drive a counter for any real use.
+ * Just like the real thing.
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_555_CC(name of node,
+ * reset node or static value,
+ * Vin node or static value,
+ * R node or static value,
+ * C node or static value,
+ * rBias node or static value,
+ * rGnd node or static value,
+ * rDischarge node or static value,
+ * address of discrete_555_cc_desc structure)
+ *
+ * discrete_555_cc_desc =
+ * {
+ * options; - bit mapped options
+ * v_pos; - B+ voltage of 555
+ * v_cc_source; - Voltage of the Constant Current source
+ * v_out_high; - High output voltage of 555 (Defaults to v_pos - 1.2V)
+ * v_cc_junction; - The voltage drop of the Constant Current source transitor
+ * (0 if Op Amp)
+ * }
+ *
+ * The last 2 options of discrete_555_desc can use the following defaults:
+ * DEFAULT_555_CC_SOURCE - to connect v_cc_source to v_pos
+ * DEFAULT_555_HIGH - to use the normal output voltage based on v_pos
+ * or combine both as:
+ * DEFAULT_555_VALUES
+ *
+ * Output Types:
+ * See DISCRETE_555_ASTABLE for description.
+ *
+ * Waveform Types: (ORed with output types)
+ * See DISCRETE_555_ASTABLE for description.
+ *
+ * Other Flags:
+ * DISCRETE_555_CC_TO_DISCHARGE_PIN - The CC source connects to the
+ * discharge pin. (Default)
+ * DISCRETE_555_CC_TO_CAP - The CC source connects to the
+ * threshold pin. This is not fully
+ * implemented yet. It only works properly
+ * when only rDischarge is defined.
+ *
+ * EXAMPLES: see Fire Truck, Monte Carlo, Super Bug
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_555_VCO1 - Op-Amp based 555 VCO circuit.
+ * DISCRETE_555_VCO1_CV - Op-Amp based 555 VCO circuit with CV control.
+ *
+ * c
+ * .------------------------+---||----+---------------------------> DISC_555_OUT_CAP
+ * | | |
+ * | | |\ |
+ * | r1 | | \ | .------------.
+ * | vIn1 >--+--ZZZZ-------+---|- \ | | |
+ * | | | >-+---+--|Threshold |
+ * | | r2 |+ / | | Out|------> DISC_555_OUT_xx
+ * Z '--ZZZZ--+--------| / '--|Trigger |
+ * Z r4 | |/ | |
+ * Z Z | Reset|------< Reset
+ * | Z r3 vIn2 >--|CV |
+ * .----. Z | |
+ * | En|<--------. | .---|Discharge |
+ * '----' | gnd | '------------'
+ * | | |
+ * gnd '-----------------------+---ZZZZ------> v_charge (ignored)
+ * rX
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_555_VCO1(name of node,
+ * reset node or static value,
+ * Vin1 node or static value,
+ * address of discrete_555_vco1_desc structure)
+ *
+ * DISCRETE_555_VCO1_CV(name of node,
+ * reset node or static value,
+ * Vin1 node or static value,
+ * Vin2 (CV) node or static value,
+ * address of discrete_555_vco1_desc structure)
+ *
+ * discrete_555_vco1_desc =
+ * {
+ * options, - bit mapped options
+ * r1, r2, r3, r4, c,
+ * v_pos, - B+ voltage of 555
+ * v_out_high, - High output voltage of 555 (Defaults to v_pos - 1.2V)
+ * }
+ *
+ * The last option of discrete_555_vco1_desc can use the following default:
+ * DEFAULT_555_HIGH - to use the normal output voltage based on v_pos
+ *
+ * Notes: The value of resistor rX is not needed. It is just a pull-up
+ * for the discharge output.
+ * The 'En' block can be a transistor or 4066 switch. It connects
+ * r4 to ground when En is high.
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_566 - NE566 VCO simulation.
+ *
+ * v_charge v_pos
+ * V V
+ * | |
+ * | |
+ * | R .-------.
+ * '---/\/\--|6 8 |
+ * | |
+ * vMod >------------|5 3/4|---------> Netlist Node
+ * | |
+ * .---|7 1 |
+ * | '-------'
+ * --- |
+ * --- C |
+ * | |
+ * v_neg v_neg
+ *
+ * Note: There is usually a 0.001uF cap between pins 5 & 6.
+ * This is for circuit stability and can be ignored for simulation purposes.
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_566(name of node,
+ * vMod node or static value,
+ * R node or static value in ohms,
+ * C node or static value in Farads,
+ * v_pos static value
+ * v_neg static value
+ * v_charge node or static value
+ * options)
+ *
+ * Output Types:
+ * DISC_566_OUT_DC - Output is actual DC. (DEFAULT)
+ * DISC_566_OUT_AC - A cheat to make the waveform AC.
+ *
+ * Waveform Types:
+ * DISC_566_OUT_SQUARE - Pin 3 Square Wave Output (DEFAULT)
+ * DISC_566_OUT_ENERGY - Pin 3 anti-alaised Square Wave Output
+ * DISC_566_OUT_TRIANGLE - Pin 4 Triangle Wave Output
+ * DISC_566_OUT_LOGIC - Internal Flip/Flop Output
+ * DISC_566_COUNT_F - # of falling edges
+ * DISC_566_COUNT_R - # of rising edges
+ * DISC_566_COUNT_F_X - # of falling edges with x-time
+ * DISC_566_COUNT_R_X - # of rising edges with x-time
+ *
+ * EXAMPLES: see Starship 1
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_74LS624 - VCO. 1/2 of 74LS629.
+ *
+ * The datasheet gives no formulae. The implementation is based on
+ * testing a 74LS629.
+ *
+ * For a LS628, use VRng = 3.2
+ *
+ * V+
+ * |
+ * R_rng_in .---------.
+ * vRng >-----------ZZZZ-------|Rng V+ |
+ * R_freq_in | |
+ * vMod >---ZZZZ-+-------------|Freq Z |---------> Netlist Node
+ * | | |
+ * C_freq_in --- .---|CX1 |
+ * --- | | |
+ * | --- | |
+ * | C --- | |
+ * Gnd | | |
+ * '---|CX2 |
+ * '---------'
+ * |
+ * GND
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_74LS624(name of node,(NODE,ENAB,VMOD,VRNG,C,R_FREQ_IN,C_FREQ_IN,R_RNG_IN,OUTTYPE)
+ * enable node or static value,
+ * vMod node or static value,
+ * vRng static value,
+ * C static value in Farads,
+ * R_freq_in static value in Ohms,
+ * C_freq_in static value in Farads,
+ * R_rng_in static value in Ohms,
+ * Type of output static value)
+ *
+ * Type of Output
+ * DISC_LS624_OUT_SQUARE - 4.4V square wave
+ * DISC_LS624_OUT_ENERGY - 4.4V anti-aliased square wave
+ * DISC_LS624_OUT_LOGIC - Logic ( 0 or 1)
+ * DISC_LS624_OUT_LOGIC_X - Logic ( 0 or 1) with x_time
+ * DISC_LS624_OUT_COUNT_F - Number of Falling edges
+ * DISC_LS624_OUT_COUNT_F_X - Number of Falling edges with x_time
+ * DISC_LS624_OUT_COUNT_R - Number of Rising edges
+ * DISC_LS624_OUT_COUNT_R_X - Number of Rising edges with x_time
+ *
+ *
+ * EXAMPLES: see Donkey Kong Jr.; Mario Bros.
+ *
+ ***********************************************************************
+ *
+ * DISCRETE_CUSTOMx - Link to custom code
+ * where x = 1 to 9
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_CUSTOMx(name of node,
+ * input 0 node or static value, ...)
+ *
+ * discrete_custom_info = {discrete_module, custom}
+ * discrete_module = discrete module definition
+ * custom = address of specific initialization data
+ *
+ * In most case, you should be able to use
+ *
+ * discrete_custom_info = {DISCRETE_CUSTOM_MODULE(basename, context type), custom}
+ *
+ * if you have used DISCRETE_STEP(basename) and DISCRETE_RESET(basename) to define
+ * the step/reset procedures.
+ *
+ * EXAMPLES: see Sky Raider, Donkey Kong
+ *
+ ***********************************************************************
+ =======================================================================
+ * Debugging modules.
+ =======================================================================
+ ***********************************************************************
+ *
+ * DISCRETE_CSVLOGx - Dump n nodes into a csv (comma separated value) file
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_CSVLOGx(node 1, ...)
+ * where x = 1 to 5
+ *
+ * WARNING: This can rapidally use up a lot of hard drive space.
+ * 48kHz sampling of 5 nodes used 217M after 80 seconds.
+ *
+ * Use this to monitor nodes while debugging the driver. You should
+ * remove these nodes from the final driver. You can use up to a maximum
+ * DISCRETE_MAX_CSVLOGS. Each file will be called discreteX_Y.csv,
+ * where X is the sound tag. Y is 0-9, in the order the file is
+ * created in the driver.
+ *
+ * This can be used to monitor how multiple nodes relate to each other.
+ * The resulting file can be imported to a spreadsheet.
+ *
+ ************************************************************************
+ *
+ * DISCRETE_WAVLOG - Dump nodes into a wav file
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_WAVLOG1(node,
+ * static gain for node)
+ *
+ * DISCRETE_WAVLOG2(left node,
+ * static gain for left node,
+ * right node,
+ * static gain for right node)
+ *
+ * Use this to monitor nodes while debugging the driver. You should
+ * remove these nodes from the final driver. You can use up to a maximum
+ * of DISCRETE_MAX_WAVLOGS. Each file will be called discreteX_Y.wav,
+ * where X is the sound tag. Y is 0-9, in the order the file is
+ * created in the driver.
+ *
+ * This can be used to monitor how a node's input affects it's output.
+ * Monitor the input trigger against the final effect, etc. The resulting
+ * file can be played/viewed etc. by music player/editor software.
+ *
+ * When logging nodes that are voltage levels, you may want to use a
+ * gain of 1000. This will make the wav sample level reflect milli-volts.
+ *
+ ************************************************************************
+ =======================================================================
+ * Must be last module.
+ =======================================================================
+ ***********************************************************************
+ *
+ * DISCRETE_OUTPUT - Single output node to Mame mixer and output
+ *
+ * .----------. .
+ * | | .-/|
+ * Netlist node -------->| OUTPUT |----| | Sound Output
+ * | | '-\|
+ * '----------' '
+ *
+ * Declaration syntax
+ *
+ * DISCRETE_OUTPUT(name of output node, gain)
+ *
+ * Example config line
+ *
+ * DISCRETE_OUTPUT(NODE_02, 1000)
+ *
+ * Output stream will be generated from the NODE_02 output stream * 1000.
+ *
+ * Multiple outputs can be used up to DISCRETE_MAX_OUTPUTS.
+ *
+ ************************************************************************/
+
+#include "wavwrite.h"
+
+/*************************************
+ *
+ * macros
+ * see also: emu\machine\rescap.h
+ *
+ *************************************/
+
+/* calculate charge exponent using discrete sample time */
+#define RC_CHARGE_EXP(rc) (1.0 - exp(-this->sample_time() / (rc)))
+/* calculate charge exponent using given sample time */
+#define RC_CHARGE_EXP_DT(rc, dt) (1.0 - exp(-(dt) / (rc)))
+#define RC_CHARGE_NEG_EXP_DT(rc, dt) (1.0 - exp((dt) / (rc)))
+
+/* calculate discharge exponent using discrete sample time */
+#define RC_DISCHARGE_EXP(rc) (exp(-this->sample_time() / (rc)))
+/* calculate discharge exponent using given sample time */
+#define RC_DISCHARGE_EXP_DT(rc, dt) (exp(-(dt) / (rc)))
+#define RC_DISCHARGE_NEG_EXP_DT(rc, dt) (exp((dt) / (rc)))
+
+#define FREQ_OF_555(_r1, _r2, _c) (1.49 / ((_r1 + 2 * _r2) * _c))
+
+/*************************************
+ *
+ * Interface & Naming
+ *
+ *************************************/
+
+#define DISCRETE_CLASS_FUNC(_class, _func) DISCRETE_CLASS_NAME(_class) :: _func
+
+#define DISCRETE_STEP(_class) void DISCRETE_CLASS_FUNC(_class, step)(void)
+#define DISCRETE_RESET(_class) void DISCRETE_CLASS_FUNC(_class, reset)(void)
+#define DISCRETE_START(_class) void DISCRETE_CLASS_FUNC(_class, start)(void)
+#define DISCRETE_STOP(_class) void DISCRETE_CLASS_FUNC(_class, stop)(void)
+#define DISCRETE_DECLARE_INFO(_name) const _name *info = (const _name *)this->custom_data();
+
+//#define DISCRETE_INPUT(_num) (*(this->m_input[_num]))
+#define DISCRETE_INPUT(_num) (input(_num))
+
+/*************************************
+ *
+ * Core constants
+ *
+ *************************************/
+
+#define DISCRETE_MAX_NODES 300
+#define DISCRETE_MAX_INPUTS 10
+#define DISCRETE_MAX_OUTPUTS 8
+
+#define DISCRETE_MAX_TASK_GROUPS 10
+
+
+/*************************************
+ *
+ * Node-specific constants
+ *
+ *************************************/
+
+#define DEFAULT_TTL_V_LOGIC_1 3.4
+
+#define DISC_LOGADJ 1.0
+#define DISC_LINADJ 0.0
+
+/* DISCRETE_COMP_ADDER types */
+#define DISC_COMP_P_CAPACITOR 0x00
+#define DISC_COMP_P_RESISTOR 0x01
+
+/* clk types */
+#define DISC_CLK_MASK 0x03
+#define DISC_CLK_ON_F_EDGE 0x00
+#define DISC_CLK_ON_R_EDGE 0x01
+#define DISC_CLK_BY_COUNT 0x02
+#define DISC_CLK_IS_FREQ 0x03
+
+#define DISC_COUNT_DOWN 0
+#define DISC_COUNT_UP 1
+
+#define DISC_COUNTER_IS_7492 0x08
+
+#define DISC_OUT_MASK 0x30
+#define DISC_OUT_DEFAULT 0x00
+#define DISC_OUT_IS_ENERGY 0x10
+#define DISC_OUT_HAS_XTIME 0x20
+
+/* Function possibilities for the LFSR feedback nodes */
+/* 2 inputs, one output */
+#define DISC_LFSR_XOR 0
+#define DISC_LFSR_OR 1
+#define DISC_LFSR_AND 2
+#define DISC_LFSR_XNOR 3
+#define DISC_LFSR_NOR 4
+#define DISC_LFSR_NAND 5
+#define DISC_LFSR_IN0 6
+#define DISC_LFSR_IN1 7
+#define DISC_LFSR_NOT_IN0 8
+#define DISC_LFSR_NOT_IN1 9
+#define DISC_LFSR_REPLACE 10
+#define DISC_LFSR_XOR_INV_IN0 11
+#define DISC_LFSR_XOR_INV_IN1 12
+
+/* LFSR Flag Bits */
+#define DISC_LFSR_FLAG_OUT_INVERT 0x01
+#define DISC_LFSR_FLAG_RESET_TYPE_L 0x00
+#define DISC_LFSR_FLAG_RESET_TYPE_H 0x02
+#define DISC_LFSR_FLAG_OUTPUT_F0 0x04
+#define DISC_LFSR_FLAG_OUTPUT_SR_SN1 0x08
+
+/* Sample & Hold supported clock types */
+#define DISC_SAMPHOLD_REDGE 0
+#define DISC_SAMPHOLD_FEDGE 1
+#define DISC_SAMPHOLD_HLATCH 2
+#define DISC_SAMPHOLD_LLATCH 3
+
+/* Shift options */
+#define DISC_LOGIC_SHIFT__RESET_L 0x00
+#define DISC_LOGIC_SHIFT__RESET_H 0x10
+#define DISC_LOGIC_SHIFT__LEFT 0x00
+#define DISC_LOGIC_SHIFT__RIGHT 0x20
+
+/* Maximum number of resistors in ladder chain */
+#define DISC_LADDER_MAXRES 8
+
+/* Filter types */
+#define DISC_FILTER_LOWPASS 0
+#define DISC_FILTER_HIGHPASS 1
+#define DISC_FILTER_BANDPASS 2
+
+/* Mixer types */
+#define DISC_MIXER_IS_RESISTOR 0
+#define DISC_MIXER_IS_OP_AMP 1
+#define DISC_MIXER_IS_OP_AMP_WITH_RI 2 /* Used only internally. Use DISC_MIXER_IS_OP_AMP */
+
+/* Triggered Op Amp Functions */
+enum
+{
+ DISC_OP_AMP_TRIGGER_FUNCTION_NONE,
+ DISC_OP_AMP_TRIGGER_FUNCTION_TRG0,
+ DISC_OP_AMP_TRIGGER_FUNCTION_TRG0_INV,
+ DISC_OP_AMP_TRIGGER_FUNCTION_TRG1,
+ DISC_OP_AMP_TRIGGER_FUNCTION_TRG1_INV,
+ DISC_OP_AMP_TRIGGER_FUNCTION_TRG2,
+ DISC_OP_AMP_TRIGGER_FUNCTION_TRG2_INV,
+ DISC_OP_AMP_TRIGGER_FUNCTION_TRG01_AND,
+ DISC_OP_AMP_TRIGGER_FUNCTION_TRG01_NAND
+};
+
+
+/* Common Op Amp Flags and values */
+#define DISC_OP_AMP_IS_NORTON 0x100
+#define OP_AMP_NORTON_VBE 0.5 // This is the norton junction voltage. Used only internally.
+#define OP_AMP_VP_RAIL_OFFSET 1.5 // This is how close an op-amp can get to the vP rail. Used only internally.
+
+/* Integrate options */
+#define DISC_INTEGRATE_OP_AMP_1 0x00
+#define DISC_INTEGRATE_OP_AMP_2 0x10
+
+/* op amp 1 shot types */
+#define DISC_OP_AMP_1SHT_1 0x00
+
+/* Op Amp Filter Options */
+#define DISC_OP_AMP_FILTER_IS_LOW_PASS_1 0x00
+#define DISC_OP_AMP_FILTER_IS_HIGH_PASS_1 0x10
+#define DISC_OP_AMP_FILTER_IS_BAND_PASS_1 0x20
+#define DISC_OP_AMP_FILTER_IS_BAND_PASS_1M 0x30
+#define DISC_OP_AMP_FILTER_IS_HIGH_PASS_0 0x40
+#define DISC_OP_AMP_FILTER_IS_BAND_PASS_0 0x50
+#define DISC_OP_AMP_FILTER_IS_LOW_PASS_1_A 0x60
+
+#define DISC_OP_AMP_FILTER_TYPE_MASK (0xf0 | DISC_OP_AMP_IS_NORTON) // Used only internally.
+
+/* Sallen-Key filter Opions */
+#define DISC_SALLEN_KEY_LOW_PASS 0x01
+#define DISC_SALLEN_KEY_HIGH_PASS 0x02
+
+
+/* Op Amp Oscillator Flags */
+#define DISC_OP_AMP_OSCILLATOR_TYPE_MASK (0xf0 | DISC_OP_AMP_IS_NORTON) // Used only internally.
+#define DISC_OP_AMP_OSCILLATOR_1 0x00
+#define DISC_OP_AMP_OSCILLATOR_2 0x10
+#define DISC_OP_AMP_OSCILLATOR_VCO_1 0x20
+#define DISC_OP_AMP_OSCILLATOR_VCO_2 0x30
+#define DISC_OP_AMP_OSCILLATOR_VCO_3 0x40
+
+#define DISC_OP_AMP_OSCILLATOR_OUT_MASK 0x07
+#define DISC_OP_AMP_OSCILLATOR_OUT_CAP 0x00
+#define DISC_OP_AMP_OSCILLATOR_OUT_SQW 0x01
+#define DISC_OP_AMP_OSCILLATOR_OUT_ENERGY 0x02
+#define DISC_OP_AMP_OSCILLATOR_OUT_LOGIC_X 0x03
+#define DISC_OP_AMP_OSCILLATOR_OUT_COUNT_F_X 0x04
+#define DISC_OP_AMP_OSCILLATOR_OUT_COUNT_R_X 0x05
+
+/* Schmitt Oscillator Options */
+#define DISC_SCHMITT_OSC_IN_IS_LOGIC 0x00
+#define DISC_SCHMITT_OSC_IN_IS_VOLTAGE 0x01
+
+#define DISC_SCHMITT_OSC_ENAB_IS_AND 0x00
+#define DISC_SCHMITT_OSC_ENAB_IS_NAND 0x02
+#define DISC_SCHMITT_OSC_ENAB_IS_OR 0x04
+#define DISC_SCHMITT_OSC_ENAB_IS_NOR 0x06
+
+#define DISC_SCHMITT_OSC_ENAB_MASK 0x06 /* Bits that define output enable type.
+ * Used only internally in module. */
+
+/* 555 Common output flags */
+#define DISC_555_OUT_DC 0x00
+#define DISC_555_OUT_AC 0x10
+
+#define DISC_555_TRIGGER_IS_LOGIC 0x00
+#define DISC_555_TRIGGER_IS_VOLTAGE 0x20
+#define DISC_555_TRIGGER_IS_COUNT 0x40
+#define DSD_555_TRIGGER_TYPE_MASK 0x60
+#define DISC_555_TRIGGER_DISCHARGES_CAP 0x80
+
+#define DISC_555_OUT_SQW 0x00 /* Squarewave */
+#define DISC_555_OUT_CAP 0x01 /* Cap charge waveform */
+#define DISC_555_OUT_COUNT_F 0x02 /* Falling count */
+#define DISC_555_OUT_COUNT_R 0x03 /* Rising count */
+#define DISC_555_OUT_ENERGY 0x04
+#define DISC_555_OUT_LOGIC_X 0x05
+#define DISC_555_OUT_COUNT_F_X 0x06
+#define DISC_555_OUT_COUNT_R_X 0x07
+
+#define DISC_555_OUT_MASK 0x07 /* Bits that define output type.
+ * Used only internally in module. */
+
+#define DISC_555_ASTABLE_HAS_FAST_CHARGE_DIODE 0x80
+#define DISCRETE_555_CC_TO_DISCHARGE_PIN 0x00
+#define DISCRETE_555_CC_TO_CAP 0x80
+
+/* 566 output flags */
+#define DISC_566_OUT_DC 0x00
+#define DISC_566_OUT_AC 0x10
+
+#define DISC_566_OUT_SQUARE 0x00 /* Squarewave */
+#define DISC_566_OUT_ENERGY 0x01 /* anti-alaised Squarewave */
+#define DISC_566_OUT_TRIANGLE 0x02 /* Triangle waveform */
+#define DISC_566_OUT_LOGIC 0x03 /* 0/1 logic output */
+#define DISC_566_OUT_COUNT_F 0x04
+#define DISC_566_OUT_COUNT_R 0x05
+#define DISC_566_OUT_COUNT_F_X 0x06
+#define DISC_566_OUT_COUNT_R_X 0x07
+#define DISC_566_OUT_MASK 0x07 /* Bits that define output type.
+ * Used only internally in module. */
+
+/* LS624 output flags */
+#define DISC_LS624_OUT_SQUARE 0x01
+#define DISC_LS624_OUT_ENERGY 0x02
+#define DISC_LS624_OUT_LOGIC 0x03
+#define DISC_LS624_OUT_LOGIC_X 0x04
+#define DISC_LS624_OUT_COUNT_F 0x05
+#define DISC_LS624_OUT_COUNT_R 0x06
+#define DISC_LS624_OUT_COUNT_F_X 0x07
+#define DISC_LS624_OUT_COUNT_R_X 0x08
+
+/* Oneshot types */
+#define DISC_ONESHOT_FEDGE 0x00
+#define DISC_ONESHOT_REDGE 0x01
+
+#define DISC_ONESHOT_NORETRIG 0x00
+#define DISC_ONESHOT_RETRIG 0x02
+
+#define DISC_OUT_ACTIVE_LOW 0x04
+#define DISC_OUT_ACTIVE_HIGH 0x00
+
+#define DISC_CD4066_THRESHOLD 2.75
+
+/* Integrate */
+
+#define DISC_RC_INTEGRATE_TYPE1 0x00
+#define DISC_RC_INTEGRATE_TYPE2 0x01
+#define DISC_RC_INTEGRATE_TYPE3 0x02
+
+/*************************************
+ *
+ * Classes and structs to handle
+ * linked lists.
+ *
+ *************************************/
+
+/*
+ * add and delete may be slow - the focus is on access!
+ */
+
+ // TODO: replace with vector from utils
+template<class _ElementType> struct vector_t
+{
+public:
+ vector_t(int initial) {
+ m_count = 0;
+ m_allocated = initial;
+ m_arr = global_alloc_array_clear(_ElementType, m_allocated);
+ }
+ vector_t() {
+ m_count = 0;
+ m_allocated = 16;
+ m_arr = global_alloc_array_clear(_ElementType, m_allocated);
+ }
+ ~vector_t() {
+ global_free_array(m_arr);
+ }
+ _ElementType& operator [] (unsigned int index) const // get array item
+ {
+ return m_arr[index];
+ }
+
+ vector_t(const vector_t &a) // copy constructor
+ {
+ m_allocated = a.count();
+ if (m_allocated < 16)
+ m_allocated = 16;
+ m_count = a.count();
+ m_arr = global_alloc_array_clear(_ElementType, m_allocated);
+ for (int i=0; i < m_count; i++)
+ m_arr[i] = a[i];
+ }
+ vector_t& operator = (const vector_t &a) // assignment operator
+ {
+ if (this == &a) return *this;
+ m_allocated = a.count();
+ if (m_allocated < 16)
+ m_allocated = 16;
+ m_count = a.count();
+ m_arr = global_alloc_array_clear(_ElementType, m_allocated);
+ for (int i=0; i < m_count; i++)
+ m_arr[i] = a[i];
+ return *this;
+ }
+
+ inline _ElementType* add(_ElementType object)
+ {
+ if (m_count >= m_allocated)
+ {
+ m_allocated *= 2;
+ _ElementType *newarr = global_alloc_array_clear(_ElementType, m_allocated);
+ for (int i=0; i < m_count; i++)
+ newarr[i] = m_arr[i];
+ global_free_array(m_arr);
+ m_arr = newarr;
+ }
+ m_arr[m_count] = object;
+ m_count++;
+ return &m_arr[m_count-1];
+ }
+ inline void remove(int index)
+ {
+ for (int i=index+1; i < m_count; i++)
+ m_arr[i-1] = m_arr[i];
+ m_count--;
+ }
+ inline void clear(void) { m_count = 0; }
+ inline int count(void) const { return m_count; }
+ inline _ElementType *begin_ptr(void) const { return m_arr; }
+ inline _ElementType *end_ptr(void) const { return m_arr + (m_count - 1); }
+private:
+ _ElementType *m_arr;
+ int m_count;
+ int m_allocated;
+};
+
+/*************************************
+ *
+ * Node-specific struct types
+ *
+ *************************************/
+
+struct discrete_lfsr_desc
+{
+ int clock_type;
+ int bitlength;
+ int reset_value;
+
+ int feedback_bitsel0;
+ int feedback_bitsel1;
+ int feedback_function0; /* Combines bitsel0 & bitsel1 */
+
+ int feedback_function1; /* Combines funct0 & infeed bit */
+
+ int feedback_function2; /* Combines funct1 & shifted register */
+ int feedback_function2_mask; /* Which bits are affected by function 2 */
+
+ int flags;
+
+ int output_bit;
+};
+
+
+struct discrete_op_amp_osc_info
+{
+ UINT32 type;
+ double r1;
+ double r2;
+ double r3;
+ double r4;
+ double r5;
+ double r6;
+ double r7;
+ double r8;
+ double c;
+ double vP; // Op amp B+
+};
+
+
+#define DEFAULT_7414_VALUES 1.7, 0.9, 3.4
+
+#define DEFAULT_74LS14_VALUES 1.6, 0.8, 3.4
+
+struct discrete_schmitt_osc_desc
+{
+ double rIn;
+ double rFeedback;
+ double c;
+ double trshRise; // voltage that triggers the gate input to go high (vGate) on rise
+ double trshFall; // voltage that triggers the gate input to go low (0V) on fall
+ double vGate; // the output high voltage of the gate that gets fedback through rFeedback
+ int options; // bitmaped options
+};
+
+
+struct discrete_comp_adder_table
+{
+ int type;
+ double cDefault; // Default componet. 0 if not used.
+ int length;
+ double c[DISC_LADDER_MAXRES]; // Componet table
+};
+
+
+struct discrete_dac_r1_ladder
+{
+ int ladderLength; // 2 to DISC_LADDER_MAXRES. 1 would be useless.
+ double r[DISC_LADDER_MAXRES]; // Don't use 0 for valid resistors. That is a short.
+ double vBias; // Voltage Bias resistor is tied to (0 = not used)
+ double rBias; // Additional resistor tied to vBias (0 = not used)
+ double rGnd; // Resistor tied to ground (0 = not used)
+ double cFilter; // Filtering cap (0 = not used)
+};
+
+
+struct discrete_integrate_info
+{
+ UINT32 type;
+ double r1; // r1a + r1b
+ double r2; // r2a + r2b
+ double r3; // r3a + r3b
+ double c;
+ double v1;
+ double vP;
+ double f0;
+ double f1;
+ double f2;
+};
+
+
+#define DISC_MAX_MIXER_INPUTS 8
+struct discrete_mixer_desc
+{
+ int type;
+ double r[DISC_MAX_MIXER_INPUTS]; /* static input resistance values. These are in series with rNode, if used. */
+ int r_node[DISC_MAX_MIXER_INPUTS]; /* variable resistance nodes, if needed. 0 if not used. */
+ double c[DISC_MAX_MIXER_INPUTS];
+ double rI;
+ double rF;
+ double cF;
+ double cAmp;
+ double vRef;
+ double gain; /* Scale value to get output close to +/- 32767 */
+};
+
+
+struct discrete_op_amp_info
+{
+ UINT32 type;
+ double r1;
+ double r2;
+ double r3;
+ double r4;
+ double c;
+ double vN; // Op amp B-
+ double vP; // Op amp B+
+};
+
+
+struct discrete_op_amp_1sht_info
+{
+ UINT32 type;
+ double r1;
+ double r2;
+ double r3;
+ double r4;
+ double r5;
+ double c1;
+ double c2;
+ double vN; // Op amp B-
+ double vP; // Op amp B+
+};
+
+
+struct discrete_op_amp_tvca_info
+{
+ double r1;
+ double r2; // r2a + r2b
+ double r3; // r3a + r3b
+ double r4;
+ double r5;
+ double r6;
+ double r7;
+ double r8;
+ double r9;
+ double r10;
+ double r11;
+ double c1;
+ double c2;
+ double c3;
+ double c4;
+ double v1;
+ double v2;
+ double v3;
+ double vP;
+ int f0;
+ int f1;
+ int f2;
+ int f3;
+ int f4;
+ int f5;
+};
+
+
+struct discrete_op_amp_filt_info
+{
+ double r1;
+ double r2;
+ double r3;
+ double r4;
+ double rF;
+ double c1;
+ double c2;
+ double c3;
+ double vRef;
+ double vP;
+ double vN;
+};
+
+
+#define DEFAULT_555_CHARGE -1
+#define DEFAULT_555_HIGH -1
+#define DEFAULT_555_VALUES DEFAULT_555_CHARGE, DEFAULT_555_HIGH
+
+struct discrete_555_desc
+{
+ int options; /* bit mapped options */
+ double v_pos; /* B+ voltage of 555 */
+ double v_charge; /* voltage to charge circuit (Defaults to v_pos) */
+ double v_out_high; /* High output voltage of 555 (Defaults to v_pos - 1.2V) */
+};
+
+#define DEFAULT_555_CC_SOURCE DEFAULT_555_CHARGE
+
+struct discrete_555_cc_desc
+{
+ int options; /* bit mapped options */
+ double v_pos; /* B+ voltage of 555 */
+ double v_cc_source; /* Voltage of the Constant Current source */
+ double v_out_high; /* High output voltage of 555 (Defaults to v_pos - 1.2V) */
+ double v_cc_junction; /* The voltage drop of the Constant Current source transitor (0 if Op Amp) */
+};
+
+
+struct discrete_555_vco1_desc
+{
+ int options; /* bit mapped options */
+ double r1, r2, r3, r4, c;
+ double v_pos; /* B+ voltage of 555 */
+ double v_charge; /* (ignored) */
+ double v_out_high; /* High output voltage of 555 (Defaults to v_pos - 1.2V) */
+};
+
+
+struct discrete_adsr
+{
+ double attack_time; /* All times are in seconds */
+ double attack_value;
+ double decay_time;
+ double decay_value;
+ double sustain_time;
+ double sustain_value;
+ double release_time;
+ double release_value;
+};
+
+
+/*************************************
+ *
+ * The node numbers themselves
+ *
+ *************************************/
+
+#define NODE0_DEF(_x) NODE_ ## 0 ## _x = (0x40000000 + (_x) * DISCRETE_MAX_OUTPUTS), \
+ NODE_ ## 0 ## _x ## _00 = NODE_ ## 0 ## _x, NODE_ ## 0 ## _x ## _01, NODE_ ## 0 ## _x ## _02, NODE_ ## 0 ## _x ## _03, \
+ NODE_ ## 0 ## _x ## _04, NODE_ ## 0 ## _x ## _05, NODE_ ## 0 ## _x ## _06, NODE_ ## 0 ## _x ## _07
+#define NODE_DEF(_x) NODE_ ## _x = (0x40000000 + (_x) * DISCRETE_MAX_OUTPUTS), \
+ NODE_ ## _x ## _00 = NODE_ ## _x, NODE_ ## _x ## _01, NODE_ ## _x ## _02, NODE_ ## _x ## _03, \
+ NODE_ ## _x ## _04, NODE_ ## _x ## _05, NODE_ ## _x ## _06, NODE_ ## _x ## _07
+
+enum {
+ NODE0_DEF(0), NODE0_DEF(1), NODE0_DEF(2), NODE0_DEF(3), NODE0_DEF(4), NODE0_DEF(5), NODE0_DEF(6), NODE0_DEF(7), NODE0_DEF(8), NODE0_DEF(9),
+ NODE_DEF(10), NODE_DEF(11), NODE_DEF(12), NODE_DEF(13), NODE_DEF(14), NODE_DEF(15), NODE_DEF(16), NODE_DEF(17), NODE_DEF(18), NODE_DEF(19),
+ NODE_DEF(20), NODE_DEF(21), NODE_DEF(22), NODE_DEF(23), NODE_DEF(24), NODE_DEF(25), NODE_DEF(26), NODE_DEF(27), NODE_DEF(28), NODE_DEF(29),
+ NODE_DEF(30), NODE_DEF(31), NODE_DEF(32), NODE_DEF(33), NODE_DEF(34), NODE_DEF(35), NODE_DEF(36), NODE_DEF(37), NODE_DEF(38), NODE_DEF(39),
+ NODE_DEF(40), NODE_DEF(41), NODE_DEF(42), NODE_DEF(43), NODE_DEF(44), NODE_DEF(45), NODE_DEF(46), NODE_DEF(47), NODE_DEF(48), NODE_DEF(49),
+ NODE_DEF(50), NODE_DEF(51), NODE_DEF(52), NODE_DEF(53), NODE_DEF(54), NODE_DEF(55), NODE_DEF(56), NODE_DEF(57), NODE_DEF(58), NODE_DEF(59),
+ NODE_DEF(60), NODE_DEF(61), NODE_DEF(62), NODE_DEF(63), NODE_DEF(64), NODE_DEF(65), NODE_DEF(66), NODE_DEF(67), NODE_DEF(68), NODE_DEF(69),
+ NODE_DEF(70), NODE_DEF(71), NODE_DEF(72), NODE_DEF(73), NODE_DEF(74), NODE_DEF(75), NODE_DEF(76), NODE_DEF(77), NODE_DEF(78), NODE_DEF(79),
+ NODE_DEF(80), NODE_DEF(81), NODE_DEF(82), NODE_DEF(83), NODE_DEF(84), NODE_DEF(85), NODE_DEF(86), NODE_DEF(87), NODE_DEF(88), NODE_DEF(89),
+ NODE_DEF(90), NODE_DEF(91), NODE_DEF(92), NODE_DEF(93), NODE_DEF(94), NODE_DEF(95), NODE_DEF(96), NODE_DEF(97), NODE_DEF(98), NODE_DEF(99),
+ NODE_DEF(100),NODE_DEF(101),NODE_DEF(102),NODE_DEF(103),NODE_DEF(104),NODE_DEF(105),NODE_DEF(106),NODE_DEF(107),NODE_DEF(108),NODE_DEF(109),
+ NODE_DEF(110),NODE_DEF(111),NODE_DEF(112),NODE_DEF(113),NODE_DEF(114),NODE_DEF(115),NODE_DEF(116),NODE_DEF(117),NODE_DEF(118),NODE_DEF(119),
+ NODE_DEF(120),NODE_DEF(121),NODE_DEF(122),NODE_DEF(123),NODE_DEF(124),NODE_DEF(125),NODE_DEF(126),NODE_DEF(127),NODE_DEF(128),NODE_DEF(129),
+ NODE_DEF(130),NODE_DEF(131),NODE_DEF(132),NODE_DEF(133),NODE_DEF(134),NODE_DEF(135),NODE_DEF(136),NODE_DEF(137),NODE_DEF(138),NODE_DEF(139),
+ NODE_DEF(140),NODE_DEF(141),NODE_DEF(142),NODE_DEF(143),NODE_DEF(144),NODE_DEF(145),NODE_DEF(146),NODE_DEF(147),NODE_DEF(148),NODE_DEF(149),
+ NODE_DEF(150),NODE_DEF(151),NODE_DEF(152),NODE_DEF(153),NODE_DEF(154),NODE_DEF(155),NODE_DEF(156),NODE_DEF(157),NODE_DEF(158),NODE_DEF(159),
+ NODE_DEF(160),NODE_DEF(161),NODE_DEF(162),NODE_DEF(163),NODE_DEF(164),NODE_DEF(165),NODE_DEF(166),NODE_DEF(167),NODE_DEF(168),NODE_DEF(169),
+ NODE_DEF(170),NODE_DEF(171),NODE_DEF(172),NODE_DEF(173),NODE_DEF(174),NODE_DEF(175),NODE_DEF(176),NODE_DEF(177),NODE_DEF(178),NODE_DEF(179),
+ NODE_DEF(180),NODE_DEF(181),NODE_DEF(182),NODE_DEF(183),NODE_DEF(184),NODE_DEF(185),NODE_DEF(186),NODE_DEF(187),NODE_DEF(188),NODE_DEF(189),
+ NODE_DEF(190),NODE_DEF(191),NODE_DEF(192),NODE_DEF(193),NODE_DEF(194),NODE_DEF(195),NODE_DEF(196),NODE_DEF(197),NODE_DEF(198),NODE_DEF(199),
+ NODE_DEF(200),NODE_DEF(201),NODE_DEF(202),NODE_DEF(203),NODE_DEF(204),NODE_DEF(205),NODE_DEF(206),NODE_DEF(207),NODE_DEF(208),NODE_DEF(209),
+ NODE_DEF(210),NODE_DEF(211),NODE_DEF(212),NODE_DEF(213),NODE_DEF(214),NODE_DEF(215),NODE_DEF(216),NODE_DEF(217),NODE_DEF(218),NODE_DEF(219),
+ NODE_DEF(220),NODE_DEF(221),NODE_DEF(222),NODE_DEF(223),NODE_DEF(224),NODE_DEF(225),NODE_DEF(226),NODE_DEF(227),NODE_DEF(228),NODE_DEF(229),
+ NODE_DEF(230),NODE_DEF(231),NODE_DEF(232),NODE_DEF(233),NODE_DEF(234),NODE_DEF(235),NODE_DEF(236),NODE_DEF(237),NODE_DEF(238),NODE_DEF(239),
+ NODE_DEF(240),NODE_DEF(241),NODE_DEF(242),NODE_DEF(243),NODE_DEF(244),NODE_DEF(245),NODE_DEF(246),NODE_DEF(247),NODE_DEF(248),NODE_DEF(249),
+ NODE_DEF(250),NODE_DEF(251),NODE_DEF(252),NODE_DEF(253),NODE_DEF(254),NODE_DEF(255),NODE_DEF(256),NODE_DEF(257),NODE_DEF(258),NODE_DEF(259),
+ NODE_DEF(260),NODE_DEF(261),NODE_DEF(262),NODE_DEF(263),NODE_DEF(264),NODE_DEF(265),NODE_DEF(266),NODE_DEF(267),NODE_DEF(268),NODE_DEF(269),
+ NODE_DEF(270),NODE_DEF(271),NODE_DEF(272),NODE_DEF(273),NODE_DEF(274),NODE_DEF(275),NODE_DEF(276),NODE_DEF(277),NODE_DEF(278),NODE_DEF(279),
+ NODE_DEF(280),NODE_DEF(281),NODE_DEF(282),NODE_DEF(283),NODE_DEF(284),NODE_DEF(285),NODE_DEF(286),NODE_DEF(287),NODE_DEF(288),NODE_DEF(289),
+ NODE_DEF(290),NODE_DEF(291),NODE_DEF(292),NODE_DEF(293),NODE_DEF(294),NODE_DEF(295),NODE_DEF(296),NODE_DEF(297),NODE_DEF(298),NODE_DEF(299)
+};
+
+/* Some Pre-defined nodes for convenience */
+
+#define NODE(_x) (NODE_00 + (_x) * DISCRETE_MAX_OUTPUTS)
+#define NODE_SUB(_x, _y) ((_x) + (_y))
+
+#if DISCRETE_MAX_OUTPUTS == 8
+#define NODE_CHILD_NODE_NUM(_x) ((int)(_x) & 7)
+#define NODE_DEFAULT_NODE(_x) ((int)(_x) & ~7)
+#define NODE_INDEX(_x) (((int)(_x) - NODE_START)>>3)
+#else
+#error "DISCRETE_MAX_OUTPUTS != 8"
+#endif
+
+#define NODE_RELATIVE(_x, _y) (NODE(NODE_INDEX(_x) + (_y)))
+
+#define NODE_NC NODE_00
+#define NODE_SPECIAL NODE(DISCRETE_MAX_NODES)
+
+#define NODE_START NODE_00
+#define NODE_END NODE_SPECIAL
+
+#define IS_VALUE_A_NODE(val) (((val) > NODE_START) && ((val) <= NODE_END))
+
+// Optional node such as used in CR_FILTER
+#define OPT_NODE(val) (int) val
+/*************************************
+ *
+ * Enumerated values for Node types
+ * in the simulation
+ *
+ * DSS - Discrete Sound Source
+ * DST - Discrete Sound Transform
+ * DSD - Discrete Sound Device
+ * DSO - Discrete Sound Output
+ *
+ *************************************/
+
+enum discrete_node_type
+{
+ DSS_NULL, /* Nothing, nill, zippo, only to be used as terminating node */
+ DSS_NOP, /* just do nothing, placeholder for potential DISCRETE_REPLACE in parent block */
+
+ /* standard node */
+
+ DSS_NODE, /* a standard node */
+
+ /* Custom */
+ DST_CUSTOM, /* whatever you want */
+
+ /* Debugging */
+ DSO_CSVLOG, /* Dump nodes as csv file */
+ DSO_WAVLOG, /* Dump nodes as wav file */
+
+ /* Parallel execution */
+ DSO_TASK_START, /* start of parallel task */
+ DSO_TASK_END, /* end of parallel task */
+
+ /* Output Node -- this must be the last entry in this enum! */
+ DSO_OUTPUT, /* The final output node */
+
+ /* Import another blocklist */
+ DSO_IMPORT, /* import from another discrete block */
+ DSO_REPLACE, /* replace next node */
+ DSO_DELETE, /* delete nodes */
+
+ /* Marks end of this enum -- must be last entry ! */
+ DSO_LAST
+};
+
+/*************************************
+ *
+ * Forward declarations
+ *
+ *************************************/
+
+struct discrete_block;
+class discrete_node_base_factory;
+class discrete_task;
+class discrete_base_node;
+class discrete_dss_input_stream_node;
+class discrete_device;
+typedef vector_t<discrete_base_node *> node_list_t;
+typedef vector_t<discrete_dss_input_stream_node *> istream_node_list_t;
+typedef vector_t<discrete_task *> task_list_t;
+
+
+/*************************************
+ *
+ * Discrete module definition
+ *
+ *************************************/
+
+
+/*************************************
+ *
+ * The discrete sound blocks as
+ * defined in the drivers
+ *
+ *************************************/
+
+struct discrete_block
+{
+ int node; /* Output node number */
+ discrete_base_node *(*factory)(discrete_device * pdev, const discrete_block *block);
+ int type; /* see defines below */
+ int active_inputs; /* Number of active inputs on this node type */
+ int input_node[DISCRETE_MAX_INPUTS];/* input/control nodes */
+ double initial[DISCRETE_MAX_INPUTS]; /* Initial values */
+ const void * custom; /* Custom function specific initialisation data */
+ const char * name; /* Node Name */
+ const char * mod_name; /* Module / class name */
+};
+typedef vector_t<const discrete_block *> sound_block_list_t;
+
+/*************************************
+ *
+ * Node interfaces
+ *
+ *************************************/
+
+class discrete_step_interface
+{
+public:
+ virtual ~discrete_step_interface() { }
+
+ virtual void step(void) = 0;
+ osd_ticks_t run_time;
+ discrete_base_node * self;
+};
+typedef vector_t<discrete_step_interface *> node_step_list_t;
+
+class discrete_input_interface
+{
+public:
+ virtual ~discrete_input_interface() { }
+
+ virtual void input_write(int sub_node, UINT8 data ) = 0;
+};
+
+class discrete_sound_output_interface
+{
+public:
+ virtual ~discrete_sound_output_interface() { }
+
+ virtual void set_output_ptr(stream_sample_t *ptr) = 0;
+};
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_DISCRETE_ADD(_tag, _clock, _intf) \
+ MCFG_DEVICE_ADD(_tag, DISCRETE, _clock) \
+ MCFG_DISCRETE_INTF(_intf)
+
+#define MCFG_DISCRETE_REPLACE(_tag, _clock, _intf) \
+ MCFG_DEVICE_REPLACE(_tag, DISCRETE, _clock) \
+ MCFG_DISCRETE_INTF(_intf)
+
+#define MCFG_DISCRETE_INTF(_intf) \
+ discrete_device::static_set_intf(*device, (const discrete_block *)&(_intf##_discrete_interface));
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+class discrete_sound_output_interface;
+typedef vector_t<discrete_sound_output_interface *> node_output_list_t;
+
+
+// ======================> discrete_device
+
+class discrete_device : public device_t
+{
+ //friend class discrete_base_node;
+
+protected:
+ // construction/destruction
+ discrete_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock);
+
+public:
+ // inline configuration helpers
+ static void static_set_intf(device_t &device, const discrete_block *intf);
+
+ DECLARE_READ8_MEMBER(read);
+ DECLARE_WRITE8_MEMBER(write);
+ virtual ~discrete_device(void);
+
+ /* --------------------------------- */
+
+ virtual void update_to_current_time(void) const { }
+
+ /* process a number of samples */
+ void process(int samples);
+
+ /* access to the discrete_logging facility */
+ void CLIB_DECL discrete_log(const char *text, ...) const ATTR_PRINTF(2,3);
+
+ /* get pointer to a info struct node ref */
+ const double *node_output_ptr(int onode);
+
+ /* FIXME: this is used by csv and wav logs - going forward, identifiers should be explicitly passed */
+ int same_module_index(const discrete_base_node &node);
+
+ /* get node */
+ discrete_base_node *discrete_find_node(int node);
+
+ /* are we profiling */
+ inline int profiling(void) { return m_profiling; }
+
+ inline int sample_rate(void) { return m_sample_rate; }
+ inline double sample_time(void) { return m_sample_time; }
+
+
+protected:
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_stop();
+
+ // configuration state
+ const discrete_block *m_intf;
+
+ // internal state
+
+ /* --------------------------------- */
+
+ /* emulation info */
+ int m_sample_rate;
+ double m_sample_time;
+ double m_neg_sample_time;
+
+ /* list of all nodes */
+ node_list_t m_node_list; /* node_description * */
+
+private:
+ void discrete_build_list(const discrete_block *intf, sound_block_list_t &block_list);
+ void discrete_sanity_check(const sound_block_list_t &block_list);
+ void display_profiling(void);
+ void init_nodes(const sound_block_list_t &block_list);
+
+ /* internal node tracking */
+ discrete_base_node ** m_indexed_node;
+
+ /* tasks */
+ task_list_t task_list; /* discrete_task_context * */
+
+ /* debugging statistics */
+ FILE * m_disclogfile;
+
+ /* parallel tasks */
+ osd_work_queue * m_queue;
+
+ /* profiling */
+ int m_profiling;
+ UINT64 m_total_samples;
+ UINT64 m_total_stream_updates;
+};
+
+// ======================> discrete_sound_device
+
+class discrete_sound_device : public discrete_device,
+ public device_sound_interface
+{
+public:
+ // construction/destruction
+ discrete_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ virtual ~discrete_sound_device(void) { };
+
+ /* --------------------------------- */
+
+ virtual void update_to_current_time(void) const { m_stream->update(); }
+
+ sound_stream *get_stream(void) { return m_stream; }
+protected:
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ /* the output stream */
+ sound_stream *m_stream;
+
+ /* the input streams */
+ istream_node_list_t m_input_stream_list;
+ /* output node tracking */
+ node_output_list_t m_output_list;
+};
+
+// device type definition
+extern const device_type DISCRETE;
+
+/*************************************
+ *
+ * Node class
+ *
+ *************************************/
+
+class discrete_base_node
+{
+ friend class discrete_device;
+ template <class C> friend class discrete_node_factory;
+ friend class discrete_task;
+
+public:
+
+ virtual void reset(void) { }
+ virtual void start(void) { }
+ virtual void stop(void) { }
+ virtual void save_state(void);
+
+ virtual int max_output(void) { return 1; };
+
+ inline bool interface(discrete_step_interface *&intf) const { intf = m_step_intf; return (intf != NULL); }
+ inline bool interface(discrete_input_interface *&intf) const { intf = m_input_intf; return (intf != NULL); }
+ inline bool interface(discrete_sound_output_interface *&intf) const { intf = m_output_intf; return (intf != NULL); }
+
+ /* get the input value from node #n */
+ inline double input(int n) { return *(m_input[n]); }
+
+ /* set an output */
+ inline void set_output(int n, double val) { m_output[n] = val; }
+
+ /* Return the node index, i.e. X from NODE(X) */
+ inline int index(void) { return NODE_INDEX(m_block->node); }
+
+ /* Return the node number, i.e. NODE(X) */
+ inline int block_node(void) const { return m_block->node; }
+
+ /* Custom function specific initialisation data */
+ inline const void *custom_data(void) { return m_custom; }
+
+ inline int input_node(int inputnum) { return m_block->input_node[inputnum]; }
+
+ /* Number of active inputs on this node type */
+ inline int active_inputs(void) { return m_active_inputs; }
+ /* Bit Flags. 1 in bit location means input_is_node */
+ inline int input_is_node(void) { return m_input_is_node; }
+
+ inline double sample_time(void) { return m_device->sample_time(); }
+ inline int sample_rate(void) { return m_device->sample_rate(); }
+
+ const char * module_name(void) { return m_block->mod_name; }
+ inline int module_type(void) const { return m_block->type; }
+
+protected:
+
+ discrete_base_node();
+ virtual ~discrete_base_node();
+
+ /* finish node setup after allocation is complete */
+ void init(discrete_device * pdev, const discrete_block *block);
+
+ void resolve_input_nodes(void);
+
+ double m_output[DISCRETE_MAX_OUTPUTS]; /* The node's last output value */
+ const double * m_input[DISCRETE_MAX_INPUTS]; /* Addresses of Input values */
+ discrete_device * m_device; /* Points to the parent */
+
+private:
+
+ const discrete_block * m_block; /* Points to the node's setup block. */
+ int m_active_inputs; /* Number of active inputs on this node type */
+
+ const void * m_custom; /* Custom function specific initialisation data */
+ int m_input_is_node;
+
+ discrete_step_interface * m_step_intf;
+ discrete_input_interface * m_input_intf;
+ discrete_sound_output_interface * m_output_intf;
+};
+
+class discrete_node_base_factory
+{
+public:
+ virtual discrete_base_node *Create(discrete_device * pdev, const discrete_block *block) = 0;
+ virtual ~discrete_node_base_factory() {}
+};
+
+template <class C>
+class discrete_node_factory : public discrete_node_base_factory
+{
+public:
+ discrete_base_node *Create(discrete_device * pdev, const discrete_block *block);
+};
+
+template <class C>
+discrete_base_node * discrete_node_factory<C>::Create(discrete_device * pdev, const discrete_block *block)
+{
+ discrete_base_node *r = auto_alloc_clear(pdev->machine(), C);
+
+ r->init(pdev, block);
+ return r;
+}
+
+/*************************************
+ *
+ * Class definitions for nodes
+ *
+ *************************************/
+
+#include "disc_cls.h"
+
+/*************************************
+ *
+ * Encapsulation macros for defining
+ * your simulation
+ *
+ *************************************/
+
+template <class C>
+discrete_base_node *discrete_create_node(discrete_device * pdev, const discrete_block *block)
+{
+ return discrete_node_factory< C >().Create(pdev, block);
+}
+
+#define DISCRETE_SOUND_EXTERN(name) extern const discrete_block name##_discrete_interface[]
+#define DISCRETE_SOUND_START(name) const discrete_block name##_discrete_interface[] = {
+//#define DSC_SND_ENTRY(_nod, _class, _dss, _num, _iact, _iinit, _custom, _name) { _nod, new discrete_node_factory< DISCRETE_CLASS_NAME(_class) >, _dss, _num, _iact, _iinit, _custom, _name, # _class }
+#define DSC_SND_ENTRY(_nod, _class, _dss, _num, _iact, _iinit, _custom, _name) { _nod, &discrete_create_node< DISCRETE_CLASS_NAME(_class) >, _dss, _num, _iact, _iinit, _custom, _name, # _class }
+
+
+#define DISCRETE_SOUND_END DSC_SND_ENTRY( NODE_00, special, DSS_NULL , 0, DSE( NODE_NC ), DSE( 0 ) ,NULL ,"DISCRETE_SOUND_END" ) };
+#define DSE( ... ) { __VA_ARGS__ }
+
+/* Module Name out, enum value, #in, {variable inputs}, {static inputs}, data pointer, "name" */
+
+/* from disc_inp.inc */
+#define DISCRETE_ADJUSTMENT(NODE,MIN,MAX,LOGLIN,TAG) DSC_SND_ENTRY( NODE, dss_adjustment , DSS_NODE , 6, DSE( NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC ), DSE( MIN,MAX,LOGLIN,0 ,0 ,100 ), TAG , "DISCRETE_ADJUSTMENT" ),
+#define DISCRETE_ADJUSTMENTX(NODE,MIN,MAX,LOGLIN,TAG,PMIN,PMAX) DSC_SND_ENTRY( NODE, dss_adjustment , DSS_NODE , 6, DSE( NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC ), DSE( MIN,MAX,LOGLIN,0 ,PMIN,PMAX ), TAG , "DISCRETE_ADJUSTMENTX" ),
+#define DISCRETE_CONSTANT(NODE,CONST) DSC_SND_ENTRY( NODE, dss_constant , DSS_NODE , 1, DSE( NODE_NC ), DSE( CONST ) ,NULL ,"DISCRETE_CONSTANT" ),
+#define DISCRETE_INPUT_DATA(NODE) DSC_SND_ENTRY( NODE, dss_input_data , DSS_NODE , 3, DSE( NODE_NC,NODE_NC,NODE_NC ), DSE( 1,0,0 ), NULL, "DISCRETE_INPUT_DATA" ),
+#define DISCRETE_INPUTX_DATA(NODE,GAIN,OFFSET,INIT) DSC_SND_ENTRY( NODE, dss_input_data , DSS_NODE , 3, DSE( NODE_NC,NODE_NC,NODE_NC ), DSE( GAIN,OFFSET,INIT ), NULL, "DISCRETE_INPUTX_DATA" ),
+#define DISCRETE_INPUT_LOGIC(NODE) DSC_SND_ENTRY( NODE, dss_input_logic , DSS_NODE , 3, DSE( NODE_NC,NODE_NC,NODE_NC ), DSE( 1,0,0 ), NULL, "DISCRETE_INPUT_LOGIC" ),
+#define DISCRETE_INPUTX_LOGIC(NODE,GAIN,OFFSET,INIT) DSC_SND_ENTRY( NODE, dss_input_logic , DSS_NODE , 3, DSE( NODE_NC,NODE_NC,NODE_NC ), DSE( GAIN,OFFSET,INIT ), NULL, "DISCRETE_INPUTX_LOGIC" ),
+#define DISCRETE_INPUT_NOT(NODE) DSC_SND_ENTRY( NODE, dss_input_not , DSS_NODE , 3, DSE( NODE_NC,NODE_NC,NODE_NC ), DSE( 1,0,0 ), NULL, "DISCRETE_INPUT_NOT" ),
+#define DISCRETE_INPUTX_NOT(NODE,GAIN,OFFSET,INIT) DSC_SND_ENTRY( NODE, dss_input_not , DSS_NODE , 3, DSE( NODE_NC,NODE_NC,NODE_NC ), DSE( GAIN,OFFSET,INIT ), NULL, "DISCRETE_INPUTX_NOT" ),
+#define DISCRETE_INPUT_PULSE(NODE,INIT) DSC_SND_ENTRY( NODE, dss_input_pulse , DSS_NODE , 3, DSE( NODE_NC,NODE_NC,NODE_NC ), DSE( 1,0,INIT ), NULL, "DISCRETE_INPUT_PULSE" ),
+
+#define DISCRETE_INPUT_STREAM(NODE, NUM) DSC_SND_ENTRY( NODE, dss_input_stream, DSS_NODE , 3, DSE( NUM,NODE_NC,NODE_NC ), DSE( NUM,1,0 ), NULL, "DISCRETE_INPUT_STREAM" ),
+#define DISCRETE_INPUTX_STREAM(NODE, NUM, GAIN,OFFSET) DSC_SND_ENTRY( NODE, dss_input_stream, DSS_NODE , 3, DSE( NUM,NODE_NC,NODE_NC ), DSE( NUM,GAIN,OFFSET ), NULL, "DISCRETE_INPUTX_STREAM" ),
+
+#define DISCRETE_INPUT_BUFFER(NODE, NUM) DSC_SND_ENTRY( NODE, dss_input_buffer, DSS_NODE , 3, DSE( NUM,NODE_NC,NODE_NC ), DSE( NUM,1,0 ), NULL, "DISCRETE_INPUT_BUFFER" ),
+
+/* from disc_wav.inc */
+/* generic modules */
+#define DISCRETE_COUNTER(NODE,ENAB,RESET,CLK,MIN,MAX,DIR,INIT0,CLKTYPE) DSC_SND_ENTRY( NODE, dss_counter , DSS_NODE , 8, DSE( ENAB,RESET,CLK,NODE_NC,NODE_NC,DIR,INIT0,NODE_NC ), DSE( ENAB,RESET,CLK,MIN,MAX,DIR,INIT0,CLKTYPE ), NULL, "DISCRETE_COUNTER" ),
+#define DISCRETE_COUNTER_7492(NODE,ENAB,RESET,CLK,CLKTYPE) DSC_SND_ENTRY( NODE, dss_counter , DSS_NODE , 8, DSE( ENAB,RESET,CLK,NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC ), DSE( ENAB,RESET,CLK,CLKTYPE,0,1,0,DISC_COUNTER_IS_7492 ), NULL, "DISCRETE_COUNTER_7492" ),
+#define DISCRETE_LFSR_NOISE(NODE,ENAB,RESET,CLK,AMPL,FEED,BIAS,LFSRTB) DSC_SND_ENTRY( NODE, dss_lfsr_noise , DSS_NODE , 6, DSE( ENAB,RESET,CLK,AMPL,FEED,BIAS ), DSE( ENAB,RESET,CLK,AMPL,FEED,BIAS ), LFSRTB, "DISCRETE_LFSR_NOISE" ),
+#define DISCRETE_NOISE(NODE,ENAB,FREQ,AMPL,BIAS) DSC_SND_ENTRY( NODE, dss_noise , DSS_NODE , 4, DSE( ENAB,FREQ,AMPL,BIAS ), DSE( ENAB,FREQ,AMPL,BIAS ), NULL, "DISCRETE_NOISE" ),
+#define DISCRETE_NOTE(NODE,ENAB,CLK,DATA,MAX1,MAX2,CLKTYPE) DSC_SND_ENTRY( NODE, dss_note , DSS_NODE , 6, DSE( ENAB,CLK,DATA,NODE_NC,NODE_NC,NODE_NC ), DSE( ENAB,CLK,DATA,MAX1,MAX2,CLKTYPE ), NULL, "DISCRETE_NOTE" ),
+#define DISCRETE_SAWTOOTHWAVE(NODE,ENAB,FREQ,AMPL,BIAS,GRAD,PHASE) DSC_SND_ENTRY( NODE, dss_sawtoothwave, DSS_NODE , 6, DSE( ENAB,FREQ,AMPL,BIAS,NODE_NC,NODE_NC ), DSE( ENAB,FREQ,AMPL,BIAS,GRAD,PHASE ), NULL, "DISCRETE_SAWTOOTHWAVE" ),
+#define DISCRETE_SINEWAVE(NODE,ENAB,FREQ,AMPL,BIAS,PHASE) DSC_SND_ENTRY( NODE, dss_sinewave , DSS_NODE , 5, DSE( ENAB,FREQ,AMPL,BIAS,NODE_NC ), DSE( ENAB,FREQ,AMPL,BIAS,PHASE ), NULL, "DISCRETE_SINEWAVE" ),
+#define DISCRETE_SQUAREWAVE(NODE,ENAB,FREQ,AMPL,DUTY,BIAS,PHASE) DSC_SND_ENTRY( NODE, dss_squarewave , DSS_NODE , 6, DSE( ENAB,FREQ,AMPL,DUTY,BIAS,NODE_NC ), DSE( ENAB,FREQ,AMPL,DUTY,BIAS,PHASE ), NULL, "DISCRETE_SQUAREWAVE" ),
+#define DISCRETE_SQUAREWFIX(NODE,ENAB,FREQ,AMPL,DUTY,BIAS,PHASE) DSC_SND_ENTRY( NODE, dss_squarewfix , DSS_NODE , 6, DSE( ENAB,FREQ,AMPL,DUTY,BIAS,NODE_NC ), DSE( ENAB,FREQ,AMPL,DUTY,BIAS,PHASE ), NULL, "DISCRETE_SQUAREWFIX" ),
+#define DISCRETE_SQUAREWAVE2(NODE,ENAB,AMPL,T_OFF,T_ON,BIAS,TSHIFT) DSC_SND_ENTRY( NODE, dss_squarewave2 , DSS_NODE , 6, DSE( ENAB,AMPL,T_OFF,T_ON,BIAS,NODE_NC ), DSE( ENAB,AMPL,T_OFF,T_ON,BIAS,TSHIFT ), NULL, "DISCRETE_SQUAREWAVE2" ),
+#define DISCRETE_TRIANGLEWAVE(NODE,ENAB,FREQ,AMPL,BIAS,PHASE) DSC_SND_ENTRY( NODE, dss_trianglewave, DSS_NODE , 5, DSE( ENAB,FREQ,AMPL,BIAS,NODE_NC ), DSE( ENAB,FREQ,AMPL,BIAS,PHASE ), NULL, "DISCRETE_TRIANGLEWAVE" ),
+/* Component specific */
+#define DISCRETE_INVERTER_OSC(NODE,ENAB,MOD,RCHARGE,RP,C,R2,INFO) DSC_SND_ENTRY( NODE, dss_inverter_osc, DSS_NODE , 6, DSE( ENAB,MOD,NODE_NC,NODE_NC,NODE_NC,NODE_NC ), DSE( ENAB,MOD,RCHARGE,RP,C,R2 ), INFO, "DISCRETE_INVERTER_OSC" ),
+#define DISCRETE_OP_AMP_OSCILLATOR(NODE,ENAB,INFO) DSC_SND_ENTRY( NODE, dss_op_amp_osc , DSS_NODE , 1, DSE( ENAB ), DSE( ENAB ), INFO, "DISCRETE_OP_AMP_OSCILLATOR" ),
+#define DISCRETE_OP_AMP_VCO1(NODE,ENAB,VMOD1,INFO) DSC_SND_ENTRY( NODE, dss_op_amp_osc , DSS_NODE , 2, DSE( ENAB,VMOD1 ), DSE( ENAB,VMOD1 ), INFO, "DISCRETE_OP_AMP_VCO1" ),
+#define DISCRETE_OP_AMP_VCO2(NODE,ENAB,VMOD1,VMOD2,INFO) DSC_SND_ENTRY( NODE, dss_op_amp_osc , DSS_NODE , 3, DSE( ENAB,VMOD1,VMOD2 ), DSE( ENAB,VMOD1,VMOD2 ), INFO, "DISCRETE_OP_AMP_VCO2" ),
+#define DISCRETE_SCHMITT_OSCILLATOR(NODE,ENAB,INP0,AMPL,TABLE) DSC_SND_ENTRY( NODE, dss_schmitt_osc , DSS_NODE , 3, DSE( ENAB,INP0,AMPL ), DSE( ENAB,INP0,AMPL ), TABLE, "DISCRETE_SCHMITT_OSCILLATOR" ),
+/* Not yet implemented */
+#define DISCRETE_ADSR_ENV(NODE,ENAB,TRIGGER,GAIN,ADSRTB) DSC_SND_ENTRY( NODE, dss_adsr , DSS_NODE , 3, DSE( ENAB,TRIGGER,GAIN ), DSE( ENAB,TRIGGER,GAIN ), ADSRTB, "DISCRETE_ADSR_ENV" ),
+
+/* from disc_mth.inc */
+/* generic modules */
+#define DISCRETE_ADDER2(NODE,ENAB,INP0,INP1) DSC_SND_ENTRY( NODE, dst_adder , DSS_NODE , 3, DSE( ENAB,INP0,INP1 ), DSE( ENAB,INP0,INP1 ), NULL, "DISCRETE_ADDER2" ),
+#define DISCRETE_ADDER3(NODE,ENAB,INP0,INP1,INP2) DSC_SND_ENTRY( NODE, dst_adder , DSS_NODE , 4, DSE( ENAB,INP0,INP1,INP2 ), DSE( ENAB,INP0,INP1,INP2 ), NULL, "DISCRETE_ADDER3" ),
+#define DISCRETE_ADDER4(NODE,ENAB,INP0,INP1,INP2,INP3) DSC_SND_ENTRY( NODE, dst_adder , DSS_NODE , 5, DSE( ENAB,INP0,INP1,INP2,INP3 ), DSE( ENAB,INP0,INP1,INP2,INP3 ), NULL, "DISCRETE_ADDER4" ),
+#define DISCRETE_CLAMP(NODE,INP0,MIN,MAX) DSC_SND_ENTRY( NODE, dst_clamp , DSS_NODE , 3, DSE( INP0,MIN,MAX ), DSE( INP0,MIN,MAX ), NULL, "DISCRETE_CLAMP" ),
+#define DISCRETE_DIVIDE(NODE,ENAB,INP0,INP1) DSC_SND_ENTRY( NODE, dst_divide , DSS_NODE , 3, DSE( ENAB,INP0,INP1 ), DSE( ENAB,INP0,INP1 ), NULL, "DISCRETE_DIVIDE" ),
+#define DISCRETE_GAIN(NODE,INP0,GAIN) DSC_SND_ENTRY( NODE, dst_gain , DSS_NODE , 3, DSE( INP0,NODE_NC,NODE_NC ), DSE( INP0,GAIN,0 ), NULL, "DISCRETE_GAIN" ),
+#define DISCRETE_INVERT(NODE,INP0) DSC_SND_ENTRY( NODE, dst_gain , DSS_NODE , 3, DSE( INP0,NODE_NC,NODE_NC ), DSE( INP0,-1,0 ), NULL, "DISCRETE_INVERT" ),
+#define DISCRETE_LOGIC_INVERT(NODE,INP0) DSC_SND_ENTRY( NODE, dst_logic_inv , DSS_NODE , 1, DSE( INP0 ), DSE( INP0 ), NULL, "DISCRETE_LOGIC_INVERT" ),
+
+#define DISCRETE_BIT_DECODE(NODE, INP, BIT_N, VOUT) DSC_SND_ENTRY( NODE, dst_bits_decode , DSS_NODE , 4, DSE( INP,NODE_NC,NODE_NC,NODE_NC ), DSE( INP,BIT_N,BIT_N,VOUT ), NULL, "DISCRETE_BIT_DECODE" ),
+#define DISCRETE_BITS_DECODE(NODE, INP, BIT_FROM, BIT_TO, VOUT) DSC_SND_ENTRY( NODE, dst_bits_decode , DSS_NODE , 4, DSE( INP,NODE_NC,NODE_NC,NODE_NC ), DSE( INP,BIT_FROM,BIT_TO,VOUT ), NULL, "DISCRETE_BITS_DECODE" ),
+
+#define DISCRETE_LOGIC_AND(NODE,INP0,INP1) DSC_SND_ENTRY( NODE, dst_logic_and , DSS_NODE , 4, DSE( INP0,INP1,NODE_NC,NODE_NC ), DSE( INP0,INP1,1.0,1.0 ), NULL, "DISCRETE_LOGIC_AND" ),
+#define DISCRETE_LOGIC_AND3(NODE,INP0,INP1,INP2) DSC_SND_ENTRY( NODE, dst_logic_and , DSS_NODE , 4, DSE( INP0,INP1,INP2,NODE_NC ), DSE( INP0,INP1,INP2,1.0 ), NULL, "DISCRETE_LOGIC_AND3" ),
+#define DISCRETE_LOGIC_AND4(NODE,INP0,INP1,INP2,INP3) DSC_SND_ENTRY( NODE, dst_logic_and , DSS_NODE , 4, DSE( INP0,INP1,INP2,INP3 ), DSE( INP0,INP1,INP2,INP3 ) ,NULL, "DISCRETE_LOGIC_AND4" ),
+#define DISCRETE_LOGIC_NAND(NODE,INP0,INP1) DSC_SND_ENTRY( NODE, dst_logic_nand , DSS_NODE , 4, DSE( INP0,INP1,NODE_NC,NODE_NC ), DSE( INP0,INP1,1.0,1.0 ), NULL, "DISCRETE_LOGIC_NAND" ),
+#define DISCRETE_LOGIC_NAND3(NODE,INP0,INP1,INP2) DSC_SND_ENTRY( NODE, dst_logic_nand , DSS_NODE , 4, DSE( INP0,INP1,INP2,NODE_NC ), DSE( INP0,INP1,INP2,1.0 ), NULL, "DISCRETE_LOGIC_NAND3" ),
+#define DISCRETE_LOGIC_NAND4(NODE,INP0,INP1,INP2,INP3) DSC_SND_ENTRY( NODE, dst_logic_nand , DSS_NODE , 4, DSE( INP0,INP1,INP2,INP3 ), DSE( INP0,INP1,INP2,INP3 ), NULL, ")DISCRETE_LOGIC_NAND4" ),
+#define DISCRETE_LOGIC_OR(NODE,INP0,INP1) DSC_SND_ENTRY( NODE, dst_logic_or , DSS_NODE , 4, DSE( INP0,INP1,NODE_NC,NODE_NC ), DSE( INP0,INP1,0.0,0.0 ), NULL, "DISCRETE_LOGIC_OR" ),
+#define DISCRETE_LOGIC_OR3(NODE,INP0,INP1,INP2) DSC_SND_ENTRY( NODE, dst_logic_or , DSS_NODE , 4, DSE( INP0,INP1,INP2,NODE_NC ), DSE( INP0,INP1,INP2,0.0 ), NULL, "DISCRETE_LOGIC_OR3" ),
+#define DISCRETE_LOGIC_OR4(NODE,INP0,INP1,INP2,INP3) DSC_SND_ENTRY( NODE, dst_logic_or , DSS_NODE , 4, DSE( INP0,INP1,INP2,INP3 ), DSE( INP0,INP1,INP2,INP3 ), NULL, "DISCRETE_LOGIC_OR4" ),
+#define DISCRETE_LOGIC_NOR(NODE,INP0,INP1) DSC_SND_ENTRY( NODE, dst_logic_nor , DSS_NODE , 4, DSE( INP0,INP1,NODE_NC,NODE_NC ), DSE( INP0,INP1,0.0,0.0 ), NULL, "DISCRETE_LOGIC_NOR" ),
+#define DISCRETE_LOGIC_NOR3(NODE,INP0,INP1,INP2) DSC_SND_ENTRY( NODE, dst_logic_nor , DSS_NODE , 4, DSE( INP0,INP1,INP2,NODE_NC ), DSE( INP0,INP1,INP2,0.0 ), NULL, "DISCRETE_LOGIC_NOR3" ),
+#define DISCRETE_LOGIC_NOR4(NODE,INP0,INP1,INP2,INP3) DSC_SND_ENTRY( NODE, dst_logic_nor , DSS_NODE , 4, DSE( INP0,INP1,INP2,INP3 ), DSE( INP0,INP1,INP2,INP3 ), NULL, "DISCRETE_LOGIC_NOR4" ),
+#define DISCRETE_LOGIC_XOR(NODE,INP0,INP1) DSC_SND_ENTRY( NODE, dst_logic_xor , DSS_NODE , 2, DSE( INP0,INP1 ), DSE( INP0,INP1 ), NULL, "DISCRETE_LOGIC_XOR" ),
+#define DISCRETE_LOGIC_XNOR(NODE,INP0,INP1) DSC_SND_ENTRY( NODE, dst_logic_nxor , DSS_NODE , 2, DSE( INP0,INP1 ), DSE( INP0,INP1 ), NULL, "DISCRETE_LOGIC_XNOR" ),
+#define DISCRETE_LOGIC_DFLIPFLOP(NODE,RESET,SET,CLK,INP) DSC_SND_ENTRY( NODE, dst_logic_dff , DSS_NODE , 4, DSE( RESET,SET,CLK,INP ), DSE( RESET,SET,CLK,INP ), NULL, "DISCRETE_LOGIC_DFLIPFLOP" ),
+#define DISCRETE_LOGIC_JKFLIPFLOP(NODE,RESET,SET,CLK,J,K) DSC_SND_ENTRY( NODE, dst_logic_jkff , DSS_NODE , 5, DSE( RESET,SET,CLK,J,K ), DSE( RESET,SET,CLK,J,K ), NULL, "DISCRETE_LOGIC_JKFLIPFLOP" ),
+#define DISCRETE_LOGIC_SHIFT(NODE,INP0,RESET,CLK,SIZE,OPTIONS) DSC_SND_ENTRY( NODE, dst_logic_shift , DSS_NODE , 5, DSE( INP0,RESET,CLK,NODE_NC,NODE_NC ), DSE( INP0,RESET,CLK,SIZE,OPTIONS ), NULL, "DISCRETE_LOGIC_SHIFT" ),
+#define DISCRETE_LOOKUP_TABLE(NODE,ADDR,SIZE,TABLE) DSC_SND_ENTRY( NODE, dst_lookup_table, DSS_NODE , 2, DSE( ADDR,NODE_NC ), DSE( ADDR,SIZE ), TABLE, "DISCRETE_LOOKUP_TABLE" ),
+#define DISCRETE_MULTIPLEX2(NODE,ADDR,INP0,INP1) DSC_SND_ENTRY( NODE, dst_multiplex , DSS_NODE , 3, DSE( ADDR,INP0,INP1 ), DSE( ADDR,INP0,INP1 ), NULL, "DISCRETE_MULTIPLEX2" ),
+#define DISCRETE_MULTIPLEX4(NODE,ADDR,INP0,INP1,INP2,INP3) DSC_SND_ENTRY( NODE, dst_multiplex , DSS_NODE , 5, DSE( ADDR,INP0,INP1,INP2,INP3 ), DSE( ADDR,INP0,INP1,INP2,INP3 ), NULL, "DISCRETE_MULTIPLEX4" ),
+#define DISCRETE_MULTIPLEX8(NODE,ADDR,INP0,INP1,INP2,INP3,INP4,INP5,INP6,INP7) DSC_SND_ENTRY( NODE, dst_multiplex, DSS_NODE , 9, DSE( ADDR,INP0,INP1,INP2,INP3,INP4,INP5,INP6,INP7 ), DSE( ADDR,INP0,INP1,INP2,INP3,INP4,INP5,INP6,INP7 ), NULL, "DISCRETE_MULTIPLEX8" ),
+#define DISCRETE_MULTIPLY(NODE,INP0,INP1) DSC_SND_ENTRY( NODE, dst_gain , DSS_NODE , 3, DSE( INP0,INP1,NODE_NC ), DSE( INP0,INP1,0 ), NULL, "DISCRETE_MULTIPLY" ),
+#define DISCRETE_MULTADD(NODE,INP0,INP1,INP2) DSC_SND_ENTRY( NODE, dst_gain , DSS_NODE , 3, DSE( INP0,INP1,INP2 ), DSE( INP0,INP1,INP2 ), NULL, "DISCRETE_MULTADD" ),
+#define DISCRETE_ONESHOT(NODE,TRIG,AMPL,WIDTH,TYPE) DSC_SND_ENTRY( NODE, dst_oneshot , DSS_NODE , 5, DSE( 0,TRIG,AMPL,WIDTH,NODE_NC ), DSE( 0,TRIG,AMPL,WIDTH,TYPE ), NULL, "DISCRETE_ONESHOT" ),
+#define DISCRETE_ONESHOTR(NODE,RESET,TRIG,AMPL,WIDTH,TYPE) DSC_SND_ENTRY( NODE, dst_oneshot , DSS_NODE , 5, DSE( RESET,TRIG,AMPL,WIDTH,NODE_NC ), DSE( RESET,TRIG,AMPL,WIDTH,TYPE ), NULL, "One Shot Resetable" ),
+#define DISCRETE_ONOFF(NODE,ENAB,INP0) DSC_SND_ENTRY( NODE, dst_gain , DSS_NODE , 3, DSE( ENAB,INP0,NODE_NC ), DSE( 0,1,0 ), NULL, "DISCRETE_ONOFF" ),
+#define DISCRETE_RAMP(NODE,ENAB,RAMP,GRAD,START,END,CLAMP) DSC_SND_ENTRY( NODE, dst_ramp , DSS_NODE , 6, DSE( ENAB,RAMP,GRAD,START,END,CLAMP ), DSE( ENAB,RAMP,GRAD,START,END,CLAMP ), NULL, "DISCRETE_RAMP" ),
+#define DISCRETE_SAMPLHOLD(NODE,INP0,CLOCK,CLKTYPE) DSC_SND_ENTRY( NODE, dst_samphold , DSS_NODE , 3, DSE( INP0,CLOCK,NODE_NC ), DSE( INP0,CLOCK,CLKTYPE ), NULL, "DISCRETE_SAMPLHOLD" ),
+#define DISCRETE_SWITCH(NODE,ENAB,SWITCH,INP0,INP1) DSC_SND_ENTRY( NODE, dst_switch , DSS_NODE , 4, DSE( ENAB,SWITCH,INP0,INP1 ), DSE( ENAB,SWITCH,INP0,INP1 ), NULL, "DISCRETE_SWITCH" ),
+#define DISCRETE_ASWITCH(NODE,CTRL,INP,THRESHOLD) DSC_SND_ENTRY( NODE, dst_aswitch , DSS_NODE , 3, DSE( CTRL,INP,THRESHOLD ), DSE( CTRL,INP, THRESHOLD), NULL, "Analog Switch" ),
+#define DISCRETE_TRANSFORM2(NODE,INP0,INP1,FUNCT) DSC_SND_ENTRY( NODE, dst_transform , DSS_NODE , 2, DSE( INP0,INP1 ), DSE( INP0,INP1 ), FUNCT, "DISCRETE_TRANSFORM2" ),
+#define DISCRETE_TRANSFORM3(NODE,INP0,INP1,INP2,FUNCT) DSC_SND_ENTRY( NODE, dst_transform , DSS_NODE , 3, DSE( INP0,INP1,INP2 ), DSE( INP0,INP1,INP2 ), FUNCT, "DISCRETE_TRANSFORM3" ),
+#define DISCRETE_TRANSFORM4(NODE,INP0,INP1,INP2,INP3,FUNCT) DSC_SND_ENTRY( NODE, dst_transform , DSS_NODE , 4, DSE( INP0,INP1,INP2,INP3 ), DSE( INP0,INP1,INP2,INP3 ), FUNCT, "DISCRETE_TRANSFORM4" ),
+#define DISCRETE_TRANSFORM5(NODE,INP0,INP1,INP2,INP3,INP4,FUNCT) DSC_SND_ENTRY( NODE, dst_transform , DSS_NODE , 5, DSE( INP0,INP1,INP2,INP3,INP4 ), DSE( INP0,INP1,INP2,INP3,INP4 ), FUNCT, "DISCRETE_TRANSFORM5" ),
+/* Component specific */
+#define DISCRETE_COMP_ADDER(NODE,DATA,TABLE) DSC_SND_ENTRY( NODE, dst_comp_adder , DSS_NODE , 1, DSE( DATA ), DSE( DATA ), TABLE, "DISCRETE_COMP_ADDER" ),
+#define DISCRETE_DAC_R1(NODE,DATA,VDATA,LADDER) DSC_SND_ENTRY( NODE, dst_dac_r1 , DSS_NODE , 2, DSE( DATA,NODE_NC ), DSE( DATA,VDATA ), LADDER, "DISCRETE_DAC_R1" ),
+#define DISCRETE_DIODE_MIXER2(NODE,IN0,IN1,TABLE) DSC_SND_ENTRY( NODE, dst_diode_mix , DSS_NODE , 2, DSE( IN0,IN1 ), DSE( IN0,IN1 ), TABLE, "DISCRETE_DIODE_MIXER2" ),
+#define DISCRETE_DIODE_MIXER3(NODE,IN0,IN1,IN2,TABLE) DSC_SND_ENTRY( NODE, dst_diode_mix , DSS_NODE , 3, DSE( IN0,IN1,IN2 ), DSE( IN0,IN1,IN2 ), TABLE, "DISCRETE_DIODE_MIXER3" ),
+#define DISCRETE_DIODE_MIXER4(NODE,IN0,IN1,IN2,IN3,TABLE) DSC_SND_ENTRY( NODE, dst_diode_mix , DSS_NODE , 4, DSE( IN0,IN1,IN2,IN3 ), DSE( IN0,IN1,IN2,IN3 ), TABLE, "DISCRETE_DIODE_MIXER4" ),
+#define DISCRETE_INTEGRATE(NODE,TRG0,TRG1,INFO) DSC_SND_ENTRY( NODE, dst_integrate , DSS_NODE , 2, DSE( TRG0,TRG1 ), DSE( TRG0,TRG1 ), INFO, "DISCRETE_INTEGRATE" ),
+#define DISCRETE_MIXER2(NODE,ENAB,IN0,IN1,INFO) DSC_SND_ENTRY( NODE, dst_mixer , DSS_NODE , 3, DSE( ENAB,IN0,IN1 ), DSE( ENAB,IN0,IN1 ), INFO, "DISCRETE_MIXER2" ),
+#define DISCRETE_MIXER3(NODE,ENAB,IN0,IN1,IN2,INFO) DSC_SND_ENTRY( NODE, dst_mixer , DSS_NODE , 4, DSE( ENAB,IN0,IN1,IN2 ), DSE( ENAB,IN0,IN1,IN2 ), INFO, "DISCRETE_MIXER3" ),
+#define DISCRETE_MIXER4(NODE,ENAB,IN0,IN1,IN2,IN3,INFO) DSC_SND_ENTRY( NODE, dst_mixer , DSS_NODE , 5, DSE( ENAB,IN0,IN1,IN2,IN3 ), DSE( ENAB,IN0,IN1,IN2,IN3 ), INFO, "DISCRETE_MIXER4" ),
+#define DISCRETE_MIXER5(NODE,ENAB,IN0,IN1,IN2,IN3,IN4,INFO) DSC_SND_ENTRY( NODE, dst_mixer , DSS_NODE , 6, DSE( ENAB,IN0,IN1,IN2,IN3,IN4 ), DSE( ENAB,IN0,IN1,IN2,IN3,IN4 ), INFO, "DISCRETE_MIXER5" ),
+#define DISCRETE_MIXER6(NODE,ENAB,IN0,IN1,IN2,IN3,IN4,IN5,INFO) DSC_SND_ENTRY( NODE, dst_mixer , DSS_NODE , 7, DSE( ENAB,IN0,IN1,IN2,IN3,IN4,IN5 ), DSE( ENAB,IN0,IN1,IN2,IN3,IN4,IN5 ), INFO, "DISCRETE_MIXER6" ),
+#define DISCRETE_MIXER7(NODE,ENAB,IN0,IN1,IN2,IN3,IN4,IN5,IN6,INFO) DSC_SND_ENTRY( NODE, dst_mixer , DSS_NODE , 8, DSE( ENAB,IN0,IN1,IN2,IN3,IN4,IN5,IN6 ), DSE( ENAB,IN0,IN1,IN2,IN3,IN4,IN5,IN6 ), INFO, "DISCRETE_MIXER7" ),
+#define DISCRETE_MIXER8(NODE,ENAB,IN0,IN1,IN2,IN3,IN4,IN5,IN6,IN7,INFO) DSC_SND_ENTRY( NODE, dst_mixer , DSS_NODE , 9, DSE( ENAB,IN0,IN1,IN2,IN3,IN4,IN5,IN6,IN7 ), DSE( ENAB,IN0,IN1,IN2,IN3,IN4,IN5,IN6,IN7 ), INFO, "DISCRETE_MIXER8" ),
+#define DISCRETE_OP_AMP(NODE,ENAB,IN0,IN1,INFO) DSC_SND_ENTRY( NODE, dst_op_amp , DSS_NODE , 3, DSE( ENAB,IN0,IN1 ), DSE( ENAB,IN0,IN1 ), INFO, "DISCRETE_OP_AMP" ),
+#define DISCRETE_OP_AMP_ONESHOT(NODE,TRIG,INFO) DSC_SND_ENTRY( NODE, dst_op_amp_1sht , DSS_NODE , 1, DSE( TRIG ), DSE( TRIG ), INFO, "DISCRETE_OP_AMP_ONESHOT" ),
+#define DISCRETE_OP_AMP_TRIG_VCA(NODE,TRG0,TRG1,TRG2,IN0,IN1,INFO) DSC_SND_ENTRY( NODE, dst_tvca_op_amp , DSS_NODE , 5, DSE( TRG0,TRG1,TRG2,IN0,IN1 ), DSE( TRG0,TRG1,TRG2,IN0,IN1 ), INFO, "DISCRETE_OP_AMP_TRIG_VCA" ),
+#define DISCRETE_VCA(NODE,ENAB,IN0,CTRL,TYPE) DSC_SND_ENTRY( NODE, dst_vca , DSS_NODE , 4, DSE( ENAB,IN0,CTRL,NODE_NC ), DSE( ENAB,IN0,CTRL,TYPE ), NULL, "DISCRETE_VCA" ),
+#define DISCRETE_XTIME_BUFFER(NODE,IN0,LOW,HIGH) DSC_SND_ENTRY( NODE, dst_xtime_buffer, DSS_NODE , 4, DSE( IN0,LOW,HIGH,NODE_NC ), DSE( IN0,LOW,HIGH,0 ), NULL, "DISCRETE_XTIME_BUFFER" ),
+#define DISCRETE_XTIME_INVERTER(NODE,IN0,LOW,HIGH) DSC_SND_ENTRY( NODE, dst_xtime_buffer, DSS_NODE , 4, DSE( IN0,LOW,HIGH,NODE_NC ), DSE( IN0,LOW,HIGH,1 ), NULL, "DISCRETE_XTIME_INVERTER" ),
+#define DISCRETE_XTIME_AND(NODE,IN0,IN1,LOW,HIGH) DSC_SND_ENTRY( NODE, dst_xtime_and , DSS_NODE , 5, DSE( IN0,IN1,LOW,HIGH,NODE_NC ), DSE( IN0,IN1,LOW,HIGH,0 ), NULL, "DISCRETE_XTIME_AND" ),
+#define DISCRETE_XTIME_NAND(NODE,IN0,IN1,LOW,HIGH) DSC_SND_ENTRY( NODE, dst_xtime_and , DSS_NODE , 5, DSE( IN0,IN1,LOW,HIGH,NODE_NC ), DSE( IN0,IN1,LOW,HIGH,1 ), NULL, "DISCRETE_XTIME_NAND" ),
+#define DISCRETE_XTIME_OR(NODE,IN0,IN1,LOW,HIGH) DSC_SND_ENTRY( NODE, dst_xtime_or , DSS_NODE , 5, DSE( IN0,IN1,LOW,HIGH,NODE_NC ), DSE( IN0,IN1,LOW,HIGH,0 ), NULL, "DISCRETE_XTIME_OR" ),
+#define DISCRETE_XTIME_NOR(NODE,IN0,IN1,LOW,HIGH) DSC_SND_ENTRY( NODE, dst_xtime_or , DSS_NODE , 5, DSE( IN0,IN1,LOW,HIGH,NODE_NC ), DSE( IN0,IN1,LOW,HIGH,1 ), NULL, "DISCRETE_XTIME_NOR" ),
+#define DISCRETE_XTIME_XOR(NODE,IN0,IN1,LOW,HIGH) DSC_SND_ENTRY( NODE, dst_xtime_xor , DSS_NODE , 5, DSE( IN0,IN1,LOW,HIGH,NODE_NC ), DSE( IN0,IN1,LOW,HIGH,0 ), NULL, "DISCRETE_XTIME_XOR" ),
+#define DISCRETE_XTIME_XNOR(NODE,IN0,IN1,LOW,HIGH) DSC_SND_ENTRY( NODE, dst_xtime_xnor , DSS_NODE , 5, DSE( IN0,IN1,LOW,HIGH,NODE_NC ), DSE( IN0,IN1,LOW,HIGH,1 ), NULL, "DISCRETE_XTIME_XNOR" ),
+
+/* from disc_flt.inc */
+/* generic modules */
+#define DISCRETE_FILTER1(NODE,ENAB,INP0,FREQ,TYPE) DSC_SND_ENTRY( NODE, dst_filter1 , DSS_NODE , 4, DSE( ENAB,INP0,NODE_NC,NODE_NC ), DSE( ENAB,INP0,FREQ,TYPE ), NULL, "DISCRETE_FILTER1" ),
+#define DISCRETE_FILTER2(NODE,ENAB,INP0,FREQ,DAMP,TYPE) DSC_SND_ENTRY( NODE, dst_filter2 , DSS_NODE , 5, DSE( ENAB,INP0,NODE_NC,NODE_NC,NODE_NC ), DSE( ENAB,INP0,FREQ,DAMP,TYPE ), NULL, "DISCRETE_FILTER2" ),
+/* Component specific */
+#define DISCRETE_SALLEN_KEY_FILTER(NODE,ENAB,INP0,TYPE,INFO) DSC_SND_ENTRY( NODE, dst_sallen_key , DSS_NODE , 3, DSE( ENAB,INP0,NODE_NC ), DSE( ENAB,INP0,TYPE ), INFO, "DISCRETE_SALLEN_KEY_FILTER" ),
+#define DISCRETE_CRFILTER(NODE,INP0,RVAL,CVAL) DSC_SND_ENTRY( NODE, dst_crfilter , DSS_NODE , 3, DSE( INP0,OPT_NODE(RVAL),OPT_NODE(CVAL) ), DSE( INP0,RVAL,CVAL ), NULL, "DISCRETE_CRFILTER" ),
+#define DISCRETE_CRFILTER_VREF(NODE,INP0,RVAL,CVAL,VREF) DSC_SND_ENTRY( NODE, dst_crfilter , DSS_NODE , 4, DSE( INP0,OPT_NODE(RVAL),OPT_NODE(CVAL),VREF ), DSE( INP0,RVAL,CVAL,VREF ), NULL, "DISCRETE_CRFILTER_VREF" ),
+#define DISCRETE_OP_AMP_FILTER(NODE,ENAB,INP0,INP1,TYPE,INFO) DSC_SND_ENTRY( NODE, dst_op_amp_filt , DSS_NODE , 4, DSE( ENAB,INP0,INP1,NODE_NC ), DSE( ENAB,INP0,INP1,TYPE ), INFO, "DISCRETE_OP_AMP_FILTER" ),
+#define DISCRETE_RC_CIRCUIT_1(NODE,INP0,INP1,RVAL,CVAL) DSC_SND_ENTRY( NODE, dst_rc_circuit_1, DSS_NODE , 4, DSE( INP0,INP1,NODE_NC,NODE_NC ), DSE( INP0,INP1,RVAL,CVAL ), NULL, "DISCRETE_RC_CIRCUIT_1" ),
+#define DISCRETE_RCDISC(NODE,ENAB,INP0,RVAL,CVAL) DSC_SND_ENTRY( NODE, dst_rcdisc , DSS_NODE , 4, DSE( ENAB,INP0,NODE_NC,NODE_NC ), DSE( ENAB,INP0,RVAL,CVAL ), NULL, "DISCRETE_RCDISC" ),
+#define DISCRETE_RCDISC2(NODE,SWITCH,INP0,RVAL0,INP1,RVAL1,CVAL) DSC_SND_ENTRY( NODE, dst_rcdisc2 , DSS_NODE , 6, DSE( SWITCH,INP0,NODE_NC,INP1,NODE_NC,NODE_NC ), DSE( SWITCH,INP0,RVAL0,INP1,RVAL1,CVAL ), NULL, "DISCRETE_RCDISC2" ),
+#define DISCRETE_RCDISC3(NODE,ENAB,INP0,RVAL0,RVAL1,CVAL,DJV) DSC_SND_ENTRY( NODE, dst_rcdisc3 , DSS_NODE , 6, DSE( ENAB,INP0,NODE_NC,NODE_NC,NODE_NC,NODE_NC ), DSE( ENAB,INP0,RVAL0,RVAL1,CVAL,DJV ), NULL, "DISCRETE_RCDISC3" ),
+#define DISCRETE_RCDISC4(NODE,ENAB,INP0,RVAL0,RVAL1,RVAL2,CVAL,VP,TYPE) DSC_SND_ENTRY( NODE, dst_rcdisc4 , DSS_NODE , 8, DSE( ENAB,INP0,NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC ), DSE( ENAB,INP0,RVAL0,RVAL1,RVAL2,CVAL,VP,TYPE ), NULL, "DISCRETE_RCDISC4" ),
+#define DISCRETE_RCDISC5(NODE,ENAB,INP0,RVAL,CVAL) DSC_SND_ENTRY( NODE, dst_rcdisc5 , DSS_NODE , 4, DSE( ENAB,INP0,NODE_NC,NODE_NC ), DSE( ENAB,INP0,RVAL,CVAL ), NULL, "DISCRETE_RCDISC5" ),
+#define DISCRETE_RCDISC_MODULATED(NODE,INP0,INP1,RVAL0,RVAL1,RVAL2,RVAL3,CVAL,VP) DSC_SND_ENTRY( NODE, dst_rcdisc_mod, DSS_NODE , 8, DSE( INP0,INP1,NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC ), DSE( INP0,INP1,RVAL0,RVAL1,RVAL2,RVAL3,CVAL,VP ), NULL, "DISCRETE_RCDISC_MODULATED" ),
+#define DISCRETE_RCFILTER(NODE,INP0,RVAL,CVAL) DSC_SND_ENTRY( NODE, dst_rcfilter , DSS_NODE , 3, DSE( INP0,OPT_NODE(RVAL),OPT_NODE(CVAL) ), DSE( INP0,RVAL,CVAL ), NULL, "DISCRETE_RCFILTER" ),
+#define DISCRETE_RCFILTER_VREF(NODE,INP0,RVAL,CVAL,VREF) DSC_SND_ENTRY( NODE, dst_rcfilter , DSS_NODE , 4, DSE( INP0,OPT_NODE(RVAL),OPT_NODE(CVAL),VREF ), DSE( INP0,RVAL,CVAL,VREF ), NULL, "DISCRETE_RCFILTER_VREF" ),
+#define DISCRETE_RCFILTER_SW(NODE,ENAB,INP0,SW,RVAL,CVAL1,CVAL2,CVAL3,CVAL4) DSC_SND_ENTRY( NODE, dst_rcfilter_sw, DSS_NODE , 8, DSE( ENAB,INP0,SW,NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC ), DSE( ENAB,INP0,SW,RVAL,CVAL1,CVAL2,CVAL3,CVAL4 ), NULL, "DISCRETE_RCFILTER_SW" ),
+#define DISCRETE_RCINTEGRATE(NODE,INP0,RVAL0,RVAL1,RVAL2,CVAL,vP,TYPE) DSC_SND_ENTRY( NODE, dst_rcintegrate , DSS_NODE , 7, DSE( INP0,NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC ), DSE( INP0,RVAL0,RVAL1,RVAL2,CVAL,vP,TYPE ), NULL, "DISCRETE_RCINTEGRATE" ),
+/* For testing - seem to be buggered. Use versions not ending in N. */
+#define DISCRETE_RCDISCN(NODE,ENAB,INP0,RVAL,CVAL) DSC_SND_ENTRY( NODE, dst_rcdiscn , DSS_NODE , 4, DSE( ENAB,INP0,NODE_NC,NODE_NC ), DSE( ENAB,INP0,RVAL,CVAL ), NULL, "DISCRETE_RCDISCN" ),
+#define DISCRETE_RCDISC2N(NODE,SWITCH,INP0,RVAL0,INP1,RVAL1,CVAL) DSC_SND_ENTRY( NODE, dst_rcdisc2n , DSS_NODE , 6, DSE( SWITCH,INP0,NODE_NC,INP1,NODE_NC,NODE_NC ), DSE( SWITCH,INP0,RVAL0,INP1,RVAL1,CVAL ), NULL, "DISCRETE_RCDISC2N" ),
+#define DISCRETE_RCFILTERN(NODE,ENAB,INP0,RVAL,CVAL) DSC_SND_ENTRY( NODE, dst_rcfiltern , DSS_NODE , 4, DSE( ENAB,INP0,NODE_NC,NODE_NC ), DSE( ENAB,INP0,RVAL,CVAL ), NULL, "DISCRETE_RCFILTERN" ),
+
+/* from disc_dev.inc */
+/* generic modules */
+#define DISCRETE_CUSTOM1(NODE,CLASS,IN0,INFO) DSC_SND_ENTRY( NODE, CLASS, DST_CUSTOM , 1, DSE( IN0 ), DSE( IN0 ), INFO, "DISCRETE_CUSTOM1" ),
+#define DISCRETE_CUSTOM2(NODE,CLASS,IN0,IN1,INFO) DSC_SND_ENTRY( NODE, CLASS, DST_CUSTOM , 2, DSE( IN0,IN1 ), DSE( IN0,IN1 ), INFO, "DISCRETE_CUSTOM2" ),
+#define DISCRETE_CUSTOM3(NODE,CLASS,IN0,IN1,IN2,INFO) DSC_SND_ENTRY( NODE, CLASS, DST_CUSTOM , 3, DSE( IN0,IN1,IN2 ), DSE( IN0,IN1,IN2 ), INFO, "DISCRETE_CUSTOM3" ),
+#define DISCRETE_CUSTOM4(NODE,CLASS,IN0,IN1,IN2,IN3,INFO) DSC_SND_ENTRY( NODE, CLASS, DST_CUSTOM , 4, DSE( IN0,IN1,IN2,IN3 ), DSE( IN0,IN1,IN2,IN3 ), INFO, "DISCRETE_CUSTOM4" ),
+#define DISCRETE_CUSTOM5(NODE,CLASS,IN0,IN1,IN2,IN3,IN4,INFO) DSC_SND_ENTRY( NODE, CLASS, DST_CUSTOM , 5, DSE( IN0,IN1,IN2,IN3,IN4 ), DSE( IN0,IN1,IN2,IN3,IN4 ), INFO, "DISCRETE_CUSTOM5" ),
+#define DISCRETE_CUSTOM6(NODE,CLASS,IN0,IN1,IN2,IN3,IN4,IN5,INFO) DSC_SND_ENTRY( NODE, CLASS, DST_CUSTOM , 6, DSE( IN0,IN1,IN2,IN3,IN4,IN5 ), DSE( IN0,IN1,IN2,IN3,IN4,IN5 ), INFO, "DISCRETE_CUSTOM6" ),
+#define DISCRETE_CUSTOM7(NODE,CLASS,IN0,IN1,IN2,IN3,IN4,IN5,IN6,INFO) DSC_SND_ENTRY( NODE, CLASS, DST_CUSTOM , 7, DSE( IN0,IN1,IN2,IN3,IN4,IN5,IN6 ), DSE( IN0,IN1,IN2,IN3,IN4,IN5,IN6 ), INFO, "DISCRETE_CUSTOM7" ),
+#define DISCRETE_CUSTOM8(NODE,CLASS,IN0,IN1,IN2,IN3,IN4,IN5,IN6,IN7,INFO) DSC_SND_ENTRY( NODE, CLASS, DST_CUSTOM , 8, DSE( IN0,IN1,IN2,IN3,IN4,IN5,IN6,IN7 ), DSE( IN0,IN1,IN2,IN3,IN4,IN5,IN6,IN7 ), INFO, "DISCRETE_CUSTOM8" ),
+#define DISCRETE_CUSTOM9(NODE,CLASS,IN0,IN1,IN2,IN3,IN4,IN5,IN6,IN7,IN8,INFO) DSC_SND_ENTRY( NODE, CLASS, DST_CUSTOM , 9, DSE( IN0,IN1,IN2,IN3,IN4,IN5,IN6,IN7,IN8 ), DSE( IN0,IN1,IN2,IN3,IN4,IN5,IN6,IN7,IN8 ), INFO, "DISCRETE_CUSTOM9" ),
+
+/* Component specific */
+#define DISCRETE_555_ASTABLE(NODE,RESET,R1,R2,C,OPTIONS) DSC_SND_ENTRY( NODE, dsd_555_astbl , DSS_NODE , 5, DSE( RESET,R1,R2,C,NODE_NC ), DSE( RESET,R1,R2,C,-1 ), OPTIONS, "DISCRETE_555_ASTABLE" ),
+#define DISCRETE_555_ASTABLE_CV(NODE,RESET,R1,R2,C,CTRLV,OPTIONS) DSC_SND_ENTRY( NODE, dsd_555_astbl , DSS_NODE , 5, DSE( RESET,R1,R2,C,CTRLV ), DSE( RESET,R1,R2,C,CTRLV ), OPTIONS, "DISCRETE_555_ASTABLE_CV" ),
+#define DISCRETE_555_MSTABLE(NODE,RESET,TRIG,R,C,OPTIONS) DSC_SND_ENTRY( NODE, dsd_555_mstbl , DSS_NODE , 4, DSE( RESET,TRIG,R,C ), DSE( RESET,TRIG,R,C ), OPTIONS, "DISCRETE_555_MSTABLE" ),
+#define DISCRETE_555_CC(NODE,RESET,VIN,R,C,RBIAS,RGND,RDIS,OPTIONS) DSC_SND_ENTRY( NODE, dsd_555_cc , DSS_NODE , 7, DSE( RESET,VIN,R,C,RBIAS,RGND,RDIS ), DSE( RESET,VIN,R,C,RBIAS,RGND,RDIS ), OPTIONS, "DISCRETE_555_CC" ),
+#define DISCRETE_555_VCO1(NODE,RESET,VIN,OPTIONS) DSC_SND_ENTRY( NODE, dsd_555_vco1 , DSS_NODE , 3, DSE( RESET,VIN,NODE_NC ), DSE( RESET,VIN,-1 ), OPTIONS, "DISCRETE_555_VCO1" ),
+#define DISCRETE_555_VCO1_CV(NODE,RESET,VIN,CTRLV,OPTIONS) DSC_SND_ENTRY( NODE, dsd_555_vco1 , DSS_NODE , 3, DSE( RESET,VIN,CTRLV ), DSE( RESET,VIN,CTRLV ), OPTIONS, "DISCRETE_555_VCO1_CV" ),
+#define DISCRETE_566(NODE,VMOD,R,C,VPOS,VNEG,VCHARGE,OPTIONS) DSC_SND_ENTRY( NODE, dsd_566 , DSS_NODE , 7, DSE( VMOD,R,C,NODE_NC,NODE_NC,VCHARGE,NODE_NC ), DSE( VMOD,R,C,VPOS,VNEG,VCHARGE,OPTIONS ), NULL, "DISCRETE_566" ),
+#define DISCRETE_74LS624(NODE,ENAB,VMOD,VRNG,C,R_FREQ_IN,C_FREQ_IN,R_RNG_IN,OUTTYPE) DSC_SND_ENTRY( NODE, dsd_ls624 , DSS_NODE , 8, DSE( ENAB,VMOD,NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC ), DSE( ENAB,VMOD,VRNG,C,R_FREQ_IN,C_FREQ_IN,R_RNG_IN,OUTTYPE ), NULL, "DISCRETE_74LS624" ),
+
+/* NOP */
+#define DISCRETE_NOP(NODE) DSC_SND_ENTRY( NODE, dss_nop , DSS_NOP , 0, DSE( 0 ), DSE( 0 ), NULL, "DISCRETE_NOP" ),
+
+/* logging */
+#define DISCRETE_CSVLOG1(NODE1) DSC_SND_ENTRY( NODE_SPECIAL, dso_csvlog , DSO_CSVLOG , 1, DSE( NODE1 ), DSE( NODE1 ), NULL, "DISCRETE_CSVLOG1" ),
+#define DISCRETE_CSVLOG2(NODE1,NODE2) DSC_SND_ENTRY( NODE_SPECIAL, dso_csvlog , DSO_CSVLOG , 2, DSE( NODE1,NODE2 ), DSE( NODE1,NODE2 ), NULL, "DISCRETE_CSVLOG2" ),
+#define DISCRETE_CSVLOG3(NODE1,NODE2,NODE3) DSC_SND_ENTRY( NODE_SPECIAL, dso_csvlog , DSO_CSVLOG , 3, DSE( NODE1,NODE2,NODE3 ), DSE( NODE1,NODE2,NODE3 ), NULL, "DISCRETE_CSVLOG3" ),
+#define DISCRETE_CSVLOG4(NODE1,NODE2,NODE3,NODE4) DSC_SND_ENTRY( NODE_SPECIAL, dso_csvlog , DSO_CSVLOG , 4, DSE( NODE1,NODE2,NODE3,NODE4 ), DSE( NODE1,NODE2,NODE3,NODE4 ), NULL, "DISCRETE_CSVLOG4" ),
+#define DISCRETE_CSVLOG5(NODE1,NODE2,NODE3,NODE4,NODE5) DSC_SND_ENTRY( NODE_SPECIAL, dso_csvlog , DSO_CSVLOG , 5, DSE( NODE1,NODE2,NODE3,NODE4,NODE5 ), DSE( NODE1,NODE2,NODE3,NODE4,NODE5 ), NULL, "DISCRETE_CSVLOG5" ),
+#define DISCRETE_WAVLOG1(NODE1,GAIN1) DSC_SND_ENTRY( NODE_SPECIAL, dso_wavlog , DSO_WAVLOG , 2, DSE( NODE1,NODE_NC ), DSE( NODE1,GAIN1 ), NULL, "DISCRETE_WAVLOG1" ),
+#define DISCRETE_WAVLOG2(NODE1,GAIN1,NODE2,GAIN2) DSC_SND_ENTRY( NODE_SPECIAL, dso_wavlog , DSO_WAVLOG , 4, DSE( NODE1,NODE_NC,NODE2,NODE_NC ), DSE( NODE1,GAIN1,NODE2,GAIN2 ), NULL, "DISCRETE_WAVLOG2" ),
+
+/* import */
+#define DISCRETE_IMPORT(INFO) DSC_SND_ENTRY( NODE_SPECIAL, special , DSO_IMPORT , 0, DSE( 0 ), DSE( 0 ), &(INFO##_discrete_interface), "DISCRETE_IMPORT" ),
+#define DISCRETE_DELETE(NODE_FROM, NODE_TO) DSC_SND_ENTRY( NODE_SPECIAL, special , DSO_DELETE , 2, DSE( NODE_FROM, NODE_TO ), DSE( NODE_FROM, NODE_TO ), NULL, "DISCRETE_DELETE" ),
+#define DISCRETE_REPLACE DSC_SND_ENTRY( NODE_SPECIAL, special , DSO_REPLACE , 0, DSE( 0 ), DSE( 0 ), NULL, "DISCRETE_REPLACE" ),
+
+/* parallel tasks */
+
+#define DISCRETE_TASK_START(TASK_GROUP) DSC_SND_ENTRY( NODE_SPECIAL, special , DSO_TASK_START, 2, DSE( NODE_NC, NODE_NC ), DSE( TASK_GROUP, 0 ), NULL, "DISCRETE_TASK_START" ),
+#define DISCRETE_TASK_END() DSC_SND_ENTRY( NODE_SPECIAL, special , DSO_TASK_END , 0, DSE( 0 ), DSE( 0 ), NULL, "DISCRETE_TASK_END" ),
+//#define DISCRETE_TASK_SYNC() DSC_SND_ENTRY( NODE_SPECIAL, special , DSO_TASK_SYNC , 0, DSE( 0 ), DSE( 0 ), NULL, "DISCRETE_TASK_SYNC" ),
+
+/* output */
+#define DISCRETE_OUTPUT(OPNODE,GAIN) DSC_SND_ENTRY( NODE_SPECIAL, dso_output , DSO_OUTPUT ,2, DSE( OPNODE,NODE_NC ), DSE( 0,GAIN ), NULL, "DISCRETE_OUTPUT" ),
+
+
+
+#endif /* __DISCRETE_H__ */
diff --git a/src/devices/sound/dmadac.c b/src/devices/sound/dmadac.c
new file mode 100644
index 00000000000..622b566f1ee
--- /dev/null
+++ b/src/devices/sound/dmadac.c
@@ -0,0 +1,238 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ DMA-driven DAC driver
+ by Aaron Giles
+
+***************************************************************************/
+
+#include "emu.h"
+#include "dmadac.h"
+
+
+
+/*************************************
+ *
+ * Debugging
+ *
+ *************************************/
+
+#define VERBOSE 0
+
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+/*************************************
+ *
+ * Constants
+ *
+ *************************************/
+
+#define DEFAULT_SAMPLE_RATE (44100)
+
+#define BUFFER_SIZE 32768
+
+
+/*************************************
+ *
+ * Sound hardware init
+ *
+ *************************************/
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void dmadac_sound_device::device_start()
+{
+ /* allocate a clear a buffer */
+ m_buffer = auto_alloc_array_clear(machine(), INT16, BUFFER_SIZE);
+
+ /* reset the state */
+ m_volume = 0x100;
+
+ /* allocate a stream channel */
+ m_channel = machine().sound().stream_alloc(*this, 0, 1, DEFAULT_SAMPLE_RATE);
+
+ /* register with the save state system */
+ save_item(NAME(m_bufin));
+ save_item(NAME(m_bufout));
+ save_item(NAME(m_volume));
+ save_item(NAME(m_enabled));
+ save_item(NAME(m_frequency));
+ save_pointer(NAME(m_buffer), BUFFER_SIZE);
+}
+
+
+
+/*************************************
+ *
+ * Primary transfer routine
+ *
+ *************************************/
+
+void dmadac_transfer(dmadac_sound_device **devlist, UINT8 num_channels, offs_t channel_spacing, offs_t frame_spacing, offs_t total_frames, INT16 *data)
+{
+ int i;
+
+ /* flush out as much data as we can */
+ for (i = 0; i < num_channels; i++)
+ {
+ devlist[i]->flush();
+ }
+
+ /* loop over all channels and accumulate the data */
+ for (i = 0; i < num_channels; i++)
+ {
+ devlist[i]->transfer(i, channel_spacing, frame_spacing, total_frames, data);
+ }
+}
+
+void dmadac_sound_device::flush()
+{
+ m_channel->update();
+}
+
+void dmadac_sound_device::transfer(int channel, offs_t channel_spacing, offs_t frame_spacing, offs_t total_frames, INT16 *data)
+{
+ int j;
+
+ /* loop over all channels and accumulate the data */
+ if (m_enabled)
+ {
+ int maxin = (m_bufout + BUFFER_SIZE - 1) % BUFFER_SIZE;
+ INT16 *src = data + channel * channel_spacing;
+ int curin = m_bufin;
+
+ /* copy the data */
+ for (j = 0; j < total_frames && curin != maxin; j++)
+ {
+ m_buffer[curin] = *src;
+ curin = (curin + 1) % BUFFER_SIZE;
+ src += frame_spacing;
+ }
+ m_bufin = curin;
+
+ /* log overruns */
+ if (j != total_frames)
+ logerror("dmadac_transfer: buffer overrun (short %d frames)\n", total_frames - j);
+ }
+
+ //LOG(("dmadac_transfer - %d samples, %d effective, %d in buffer\n", total_frames, (int)(total_frames * (double)DEFAULT_SAMPLE_RATE / dmadac[first_channel].frequency), dmadac[first_channel].curinpos - dmadac[first_channel].curoutpos));
+}
+
+
+
+/*************************************
+ *
+ * Enable/disable DMA channel(s)
+ *
+ *************************************/
+
+void dmadac_enable(dmadac_sound_device **devlist, UINT8 num_channels, UINT8 enable)
+{
+ int i;
+
+ /* flush out as much data as we can */
+ for (i = 0; i < num_channels; i++)
+ {
+ devlist[i]->enable(enable);
+ }
+}
+
+
+void dmadac_sound_device::enable(UINT8 enable)
+{
+ m_channel->update();
+ m_enabled = enable;
+ if (!enable)
+ m_bufin = m_bufout = 0;
+}
+
+/*************************************
+ *
+ * Set the frequency on DMA channel(s)
+ *
+ *************************************/
+
+void dmadac_set_frequency(dmadac_sound_device **devlist, UINT8 num_channels, double frequency)
+{
+ int i;
+
+ /* set the sample rate on each channel */
+ for (i = 0; i < num_channels; i++)
+ {
+ devlist[i]->set_frequency(frequency);
+ }
+}
+
+
+void dmadac_sound_device::set_frequency(double frequency)
+{
+ m_channel->set_sample_rate(frequency);
+}
+
+/*************************************
+ *
+ * Set the volume on DMA channel(s)
+ *
+ *************************************/
+
+void dmadac_set_volume(dmadac_sound_device **devlist, UINT8 num_channels, UINT16 volume)
+{
+ int i;
+
+ /* flush out as much data as we can */
+ for (i = 0; i < num_channels; i++)
+ {
+ devlist[i]->set_volume(volume);
+ }
+}
+
+void dmadac_sound_device::set_volume(UINT16 volume)
+{
+ m_channel->update();
+ m_volume = volume;
+}
+
+const device_type DMADAC = &device_creator<dmadac_sound_device>;
+
+dmadac_sound_device::dmadac_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, DMADAC, "DMA-driven DAC", tag, owner, clock, "dmadac", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_buffer(NULL),
+ m_bufin(0),
+ m_bufout(0),
+ m_volume(0),
+ m_enabled(0),
+ m_frequency(0)
+{
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void dmadac_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *output = outputs[0];
+ INT16 *source = m_buffer;
+ UINT32 curout = m_bufout;
+ UINT32 curin = m_bufin;
+ int volume = m_volume;
+
+ /* feed as much as we can */
+ while (curout != curin && samples-- > 0)
+ {
+ *output++ = (source[curout] * volume) >> 8;
+ curout = (curout + 1) % BUFFER_SIZE;
+ }
+
+ /* fill the rest with silence */
+ while (samples-- > 0)
+ *output++ = 0;
+
+ /* save the new output pointer */
+ m_bufout = curout;
+}
diff --git a/src/devices/sound/dmadac.h b/src/devices/sound/dmadac.h
new file mode 100644
index 00000000000..65bfe16b3ff
--- /dev/null
+++ b/src/devices/sound/dmadac.h
@@ -0,0 +1,56 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/**********************************************************************************************
+ *
+ * DMA-driven DAC driver
+ * by Aaron Giles
+ *
+ **********************************************************************************************/
+
+#pragma once
+
+#ifndef __DMADAC_H__
+#define __DMADAC_H__
+
+
+class dmadac_sound_device : public device_t,
+ public device_sound_interface
+{
+public:
+ dmadac_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ void flush();
+ void transfer(int channel, offs_t channel_spacing, offs_t frame_spacing, offs_t total_frames, INT16 *data);
+ void enable(UINT8 enable);
+ void set_frequency(double frequency);
+ void set_volume(UINT16 volume);
+
+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:
+ // internal state
+ /* sound stream and buffers */
+ sound_stream * m_channel;
+ INT16 * m_buffer;
+ UINT32 m_bufin;
+ UINT32 m_bufout;
+
+ /* per-channel parameters */
+ INT16 m_volume;
+ UINT8 m_enabled;
+ double m_frequency;
+};
+
+extern const device_type DMADAC;
+
+
+void dmadac_transfer(dmadac_sound_device **devlist, UINT8 num_channels, offs_t channel_spacing, offs_t frame_spacing, offs_t total_frames, INT16 *data);
+void dmadac_enable(dmadac_sound_device **devlist, UINT8 num_channels, UINT8 enable);
+void dmadac_set_frequency(dmadac_sound_device **devlist, UINT8 num_channels, double frequency);
+void dmadac_set_volume(dmadac_sound_device **devlist, UINT8 num_channels, UINT16 volume);
+
+#endif /* __DMADAC_H__ */
diff --git a/src/devices/sound/es1373.c b/src/devices/sound/es1373.c
new file mode 100644
index 00000000000..509b4a873d2
--- /dev/null
+++ b/src/devices/sound/es1373.c
@@ -0,0 +1,499 @@
+// license:BSD-3-Clause
+// copyright-holders:Ted Green
+#include "es1373.h"
+
+#define LOG_ES (0)
+#define LOG_ES_REG (0)
+#define LOG_ES_FILE (0)
+
+
+static MACHINE_CONFIG_FRAGMENT( es1373 )
+ MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
+MACHINE_CONFIG_END
+
+machine_config_constructor es1373_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( es1373 );
+}
+
+const device_type ES1373 = &device_creator<es1373_device>;
+
+DEVICE_ADDRESS_MAP_START(map, 32, es1373_device)
+ AM_RANGE(0x00, 0x3f) AM_READWRITE (reg_r, reg_w)
+ADDRESS_MAP_END
+
+es1373_device::es1373_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : pci_device(mconfig, ES1373, "Creative Labs Ensoniq AudioPCI97 ES1373", tag, owner, clock, "es1373", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_eslog(NULL),
+ m_irq_num(-1)
+{
+}
+
+void es1373_device::set_irq_info(const char *tag, const int irq_num)
+{
+ m_cpu_tag = tag;
+ m_irq_num = irq_num;
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+void es1373_device::device_stop()
+{
+ /* debugging */
+ if (LOG_ES_FILE && m_eslog)
+ {
+ fclose(m_eslog);
+ m_eslog = NULL;
+ }
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+void es1373_device::device_start()
+{
+ m_cpu = machine().device<cpu_device>(m_cpu_tag);
+ pci_device::device_start();
+ add_map(0x40, M_IO, FUNC(es1373_device::map));
+
+ // create the stream
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, 44100/2);
+
+ m_timer = timer_alloc(0, NULL);
+ m_timer->adjust(attotime::zero, 0, attotime::from_hz(44100/2/16));
+
+}
+
+void es1373_device::device_reset()
+{
+ // debugging
+ m_tempCount = 0;
+ if (LOG_ES_FILE && m_eslog)
+ {
+ fclose(m_eslog);
+ m_eslog = NULL;
+ }
+ if (LOG_ES_FILE && !m_eslog)
+ m_eslog = fopen("es.log", "w");
+
+ pci_device::device_reset();
+ memset(m_es_regs, 0, sizeof(m_es_regs));
+ memset(m_ac97_regs, 0, sizeof(m_ac97_regs));
+ m_ac97_regs[0] = 0x0800;
+ // Reset ADC channel info
+ m_adc.number = 0;
+ m_adc.enable = false;
+ m_adc.initialized = false;
+ m_adc.buf_rptr = 0x20;
+ m_adc.buf_wptr = 0x20;
+ // Reset DAC1 channel info
+ m_dac1.number = 1;
+ m_dac1.enable = false;
+ m_dac1.initialized = false;
+ m_dac1.buf_rptr = 0x0;
+ m_dac1.buf_wptr = 0x0;
+ // Reset DAC2 channel info
+ m_dac2.number = 2;
+ m_dac2.enable = false;
+ m_dac2.initialized = false;
+ m_dac2.buf_rptr = 0x10;
+ m_dac2.buf_wptr = 0x10; // Start PCI writing to bottom half of buffer
+
+ m_stream->update();
+}
+
+void es1373_device::map_extra(UINT64 memory_window_start, UINT64 memory_window_end, UINT64 memory_offset, address_space *memory_space,
+ UINT64 io_window_start, UINT64 io_window_end, UINT64 io_offset, address_space *io_space)
+{
+ m_memory_space = memory_space;
+}
+
+//-------------------------------------------------
+// device_timer - called when our device timer expires
+//-------------------------------------------------
+void es1373_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
+{
+ m_stream->update();
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests for
+// our sound stream
+//-------------------------------------------------
+void es1373_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ if (m_dac1.enable) {
+ logerror("%s: sound_stream_update DAC1 not implemented yet\n", tag());
+ }
+
+ if (m_dac2.enable) {
+ send_audio_out(m_dac2, ICSTATUS_DAC2_INT_MASK, outputs[0], outputs[1], samples);
+ }
+
+ if (m_adc.enable) {
+ if (m_adc.format!=SCTRL_16BIT_MONO) {
+ logerror("%s: sound_stream_update Only SCTRL_16BIT_MONO recorded supported\n", tag());
+ } else {
+ for (int i=0; i<samples; i++) {
+ if (m_adc.buf_count<=m_adc.buf_size) {
+ if (LOG_ES)
+ logerror("%s: ADC buf_count: %i buf_size: %i buf_rptr: %i buf_wptr: %i\n", machine().describe_context(),
+ m_adc.buf_count, m_adc.buf_size, m_adc.buf_rptr, m_adc.buf_wptr);
+ if ((m_adc.buf_count&0x1)) {
+ m_adc.buf_wptr++;
+ }
+ m_adc.buf_count++;
+ if (m_adc.buf_count>m_adc.buf_size) {
+ if (m_adc.loop_en) {
+ // Keep playing
+ m_adc.buf_count = 0;
+ if (LOG_ES)
+ logerror("%X: send_audio_out ADC clearing buf_count\n", machine().device("maincpu")->safe_pc());
+ }
+ if (m_adc.int_en) {
+ m_es_regs[ES_INT_CS_STATUS] |= ICSTATUS_ADC_INT_MASK;
+ if (LOG_ES)
+ logerror("%X: send_audio_out Setting ADC interrupt\n", machine().device("maincpu")->safe_pc());
+ }
+ }
+ if (!(m_adc.buf_count&1) && !(m_adc.buf_wptr&0xf)) {
+ m_adc.buf_wptr -= 0x10;
+ }
+ // PCI Write Transfer
+ if (command & 0x4) {
+ if ((m_adc.buf_rptr&8)^(m_adc.buf_wptr&8)) {
+ transfer_pci_audio(m_adc, ES_PCI_WRITE);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (m_es_regs[ES_INT_CS_STATUS]&(ICSTATUS_DAC1_INT_MASK|ICSTATUS_DAC2_INT_MASK|ICSTATUS_ADC_INT_MASK)) {
+ m_es_regs[ES_INT_CS_STATUS] |= ICSTATUS_INTR_MASK;
+ // Assert interrupt
+ //m_cpu->set_input_line(ES_IRQ_NUM, ASSERT_LINE);
+ if (m_irq_num!=-1) {
+ m_cpu->set_input_line(m_irq_num, ASSERT_LINE);
+ }
+ }
+}
+
+//-------------------------------------------------
+// send_audio_out - Sends channel audio output data
+//-------------------------------------------------
+void es1373_device::send_audio_out(chan_info& chan, UINT32 intr_mask, stream_sample_t *outL, stream_sample_t *outR, int samples)
+{
+ // Only transfer PCI data if bus mastering is enabled
+ // Fill initial half buffer
+ if (1 && (command & 0x4) && (!chan.initialized)) {
+ chan.initialized = true;
+ transfer_pci_audio(chan, ES_PCI_READ);
+ }
+ //UINT32 sample_size = calc_size(chan.format);
+ // Send data to sound stream
+ bool buf_row_done;
+ for (int i=0; i<samples; i++) {
+ buf_row_done = false;
+ if (chan.buf_count<=chan.buf_size) {
+ // Only transfer PCI data if bus mastering is enabled
+ // Fill half-buffer when read pointer is at start of next half
+ //if ((command & 0x4) && ((chan.buf_rptr&8)^(chan.buf_wptr&8)) && !(m_es_regs[ES_INT_CS_STATUS] & intr_mask)) {
+ if ((command & 0x4) && ((chan.buf_rptr&8)^(chan.buf_wptr&8))) {
+ transfer_pci_audio(chan, ES_PCI_READ);
+ }
+ if (LOG_ES && i==0)
+ logerror("%X: chan: %X samples: %i buf_count: %X buf_size: %X buf_rptr: %X buf_wptr: %X\n",
+ machine().device("maincpu")->safe_pc(), chan.number, samples, chan.buf_count, chan.buf_size, chan.buf_rptr, chan.buf_wptr);
+ // Buffer is 4 bytes per location, need to switch on sample mode
+ switch (chan.format) {
+ case SCTRL_8BIT_MONO:
+ logerror("es1373_device::send_audio_out SCTRL_8BIT_MONO not implemented yet\n");
+ break;
+ case SCTRL_8BIT_STEREO:
+ logerror("es1373_device::send_audio_out SCTRL_8BIT_STEREO not implemented yet\n");
+ break;
+ case SCTRL_16BIT_MONO:
+ // The sound cache is 32 bit wide fifo, so each entry is two mono 16 bit samples
+ if ((chan.buf_count&0x1)) {
+ // Read high 16 bits
+ outL[i] = outR[i] = (INT16)(m_sound_cache[chan.buf_rptr]>>16);
+ chan.buf_rptr++;
+ buf_row_done = true;
+ } else {
+ // Read low 16 bits
+ outL[i] = outR[i] = (INT16)(m_sound_cache[chan.buf_rptr]&0xffff);
+ }
+ break;
+ case SCTRL_16BIT_STEREO:
+ // The sound cache is 32 bit wide fifo, so each entry is one stereo 16 bit sample
+ outL[i] = (INT16) m_sound_cache[chan.buf_rptr]&0xffff;
+ outR[i] = (INT16) m_sound_cache[chan.buf_rptr]>>16;
+ chan.buf_rptr++;
+ buf_row_done = true;
+ break;
+ }
+ if (LOG_ES_FILE && m_tempCount<1000000) {
+ m_tempCount++;
+ //logerror("es1373_device::sound_stream_update count: %i samp16: %X\n", i, samp16);
+ //if (LOG_ES_FILE && m_eslog)
+ //fprintf(m_eslog, "%i\n", samp16);
+ }
+ chan.buf_count++;
+ if (chan.buf_count > chan.buf_size) {
+ if (chan.loop_en) {
+ // Keep playing
+ //chan.buf_count -= 1; // Should check SCTRL_P2_END_MASK
+ chan.buf_count = 0;
+ //chan.buf_rptr -= 1;
+ if (LOG_ES)
+ logerror("%X: send_audio_out DAC2 clearing buf_count\n", machine().device("maincpu")->safe_pc());
+ }
+ if (chan.int_en) {
+ m_es_regs[ES_INT_CS_STATUS] |= intr_mask;
+ if (LOG_ES)
+ logerror("%X: send_audio_out Setting DAC2 interrupt\n", machine().device("maincpu")->safe_pc());
+ }
+ }
+ if (buf_row_done && !(chan.buf_rptr&0xf)) {
+ chan.buf_rptr -= 0x10;
+ }
+ } else {
+ // Send zeros?
+ outL[i] = outR[i] = 0;
+ }
+ }
+}
+
+void es1373_device::transfer_pci_audio(chan_info& chan, int type)
+{
+ UINT32 pci_addr, data;
+ pci_addr = chan.pci_addr + (chan.pci_count<<2);
+ if (LOG_ES)
+ logerror("%s: transfer_pci_audio start chan: %X pci_addr: %08X pci_count: %X pci_size: %X buf_rptr: %X buf_wptr: %X\n",
+ machine().describe_context(), chan.number, pci_addr, chan.pci_count, chan.pci_size, chan.buf_rptr, chan.buf_wptr);
+ // Always transfer 8 longwords
+ for (int i=0; i<8; i++) {
+ pci_addr = chan.pci_addr + (chan.pci_count<<2);
+ if (type==ES_PCI_READ) {
+ data = m_memory_space->read_dword(pci_addr, 0xffffffff);
+ m_sound_cache[chan.buf_wptr++] = data;
+ if (!(chan.buf_wptr&0xf)) {
+ chan.buf_wptr -= 0x10;
+ }
+ } else {
+ data = m_sound_cache[chan.buf_rptr++];
+ m_memory_space->write_dword(pci_addr, data);
+ if (!(chan.buf_rptr&0xf)) {
+ chan.buf_rptr -= 0x10;
+ }
+ }
+ if (chan.pci_count==chan.pci_size) {
+ chan.pci_count = 0;
+ } else {
+ chan.pci_count++;
+ }
+ }
+}
+
+UINT32 es1373_device::calc_size(const UINT8 &format)
+{
+ switch (format) {
+ case SCTRL_8BIT_MONO:
+ return 1;
+ break;
+ case SCTRL_8BIT_STEREO:
+ return 2;
+ break;
+ case SCTRL_16BIT_MONO:
+ return 2;
+ break;
+ case SCTRL_16BIT_STEREO:
+ return 4;
+ break;
+ }
+ logerror("%s: calc_size Invalid format = %X specified\n", tag(), format);
+ return 0;
+}
+
+READ32_MEMBER (es1373_device::reg_r)
+{
+ UINT32 result = m_es_regs[offset];
+ switch (offset) {
+ case ES_CODEC:
+ break;
+ case ES_DAC2_CNT:
+ result = ((m_dac2.buf_size-m_dac2.buf_count)<<16) | m_dac2.buf_size;
+ break;
+ case ES_HOST_IF0: // 0x30
+ result = m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x0];
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ result = m_dac1.pci_addr;
+ break;
+ case 0xd:
+ result = m_adc.pci_addr;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ES_HOST_IF1: // 0x34
+ result = m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x1];
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ result = (m_dac1.pci_count<<16) | m_dac1.pci_size;
+ break;
+ case 0xd:
+ result = (m_adc.pci_count<<16) | m_adc.pci_size;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ES_HOST_IF2: // 0x38
+ result = m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x2];
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ result = m_dac2.pci_addr;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ES_HOST_IF3: // 0x3C
+ result = m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x3];
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ result = ((m_dac2.pci_count)<<16) | m_dac2.pci_size;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ if (LOG_ES_REG)
+ logerror("%08X:ES1373 read from offset %02X = %08X & %08X\n", machine().device("maincpu")->safe_pc(), offset*4, result, mem_mask);
+ return result;
+}
+
+WRITE32_MEMBER(es1373_device::reg_w)
+{
+ COMBINE_DATA(&m_es_regs[offset]);
+ switch (offset) {
+ case ES_INT_CS_CTRL:
+ m_dac1.enable = (m_es_regs[ES_INT_CS_CTRL] & ICCTRL_DAC1_EN_MASK);
+ m_dac2.enable = (m_es_regs[ES_INT_CS_CTRL] & ICCTRL_DAC2_EN_MASK);
+ m_adc.enable = (m_es_regs[ES_INT_CS_CTRL] & ICCTRL_ADC_EN_MASK);
+ break;
+ case ES_SRC_IF:
+ if (data&(1<<24)) {
+ // Write to Sample Rate Converter Ram
+ m_src_ram[(data>>25)&0x7F] = data&0xFFFF;
+ } else {
+ // Read From Sample Rate Converter Ram
+ m_es_regs[offset] = (data&0xFFFF0000) | m_src_ram[(data>>25)&0x7F];
+ }
+ break;
+ case ES_CODEC:
+ if (data&(1<<23)) {
+ // Read from AC97 codec registers
+ m_es_regs[offset] = (data&0xFFFF0000) | m_ac97_regs[(data>>16)&0x7f] | 0x80000000;
+ } else {
+ // Write to AC97 codec registers
+ m_ac97_regs[(data>>16)&0x7f] = data&0xFFFF;
+ }
+ break;
+ case ES_SERIAL_CTRL:
+ m_adc.loop_en = !(m_es_regs[ES_SERIAL_CTRL] & SCTRL_R1_LOOP_MASK);
+ m_dac2.loop_en = !(m_es_regs[ES_SERIAL_CTRL] & SCTRL_P2_LOOP_MASK);
+ m_dac1.loop_en = !(m_es_regs[ES_SERIAL_CTRL] & SCTRL_P1_LOOP_MASK);
+ m_adc.int_en = m_es_regs[ES_SERIAL_CTRL] & SCTRL_R1_INT_EN_MASK;
+ m_dac2.int_en = m_es_regs[ES_SERIAL_CTRL] & SCTRL_P2_INT_EN_MASK;
+ m_dac1.int_en = m_es_regs[ES_SERIAL_CTRL] & SCTRL_P1_INT_EN_MASK;
+ m_adc.format = (m_es_regs[ES_SERIAL_CTRL] & SCTRL_R1_S_MASK)>>4;
+ m_dac2.format = (m_es_regs[ES_SERIAL_CTRL] & SCTRL_P2_S_MASK)>>2;
+ m_dac1.format = (m_es_regs[ES_SERIAL_CTRL] & SCTRL_P1_S_MASK)>>0;
+ if (!m_adc.int_en) m_es_regs[ES_INT_CS_STATUS] &= ~ICSTATUS_ADC_INT_MASK;
+ if (!m_dac1.int_en) m_es_regs[ES_INT_CS_STATUS] &= ~ICSTATUS_DAC1_INT_MASK;
+ if (!m_dac2.int_en) m_es_regs[ES_INT_CS_STATUS] &= ~ICSTATUS_DAC2_INT_MASK;
+ // Clear the summary interrupt and irq line
+ if (!(m_es_regs[ES_INT_CS_STATUS]&(ICSTATUS_DAC1_INT_MASK|ICSTATUS_DAC2_INT_MASK|ICSTATUS_ADC_INT_MASK))) {
+ // Deassert interrupt
+ if (m_es_regs[ES_INT_CS_STATUS]&ICSTATUS_INTR_MASK && m_irq_num!=-1) {
+ m_cpu->set_input_line(m_irq_num, CLEAR_LINE);
+ m_es_regs[ES_INT_CS_STATUS] &= ~ICSTATUS_INTR_MASK;
+ if (0 && LOG_ES_REG)
+ logerror("%X: es1373_device::reg_w Clearing interrupt\n", machine().device("maincpu")->safe_pc());
+ }
+ }
+ if (0 && LOG_ES_REG)
+ logerror("%s: es1373_device::reg_w adc_int_en: %i dac1_int_en: %i dac2_int_en: %i\n", tag(), m_adc.int_en, m_dac1.int_en, m_dac2.int_en);
+ break;
+ case ES_DAC2_CNT:
+ m_dac2.buf_count = 0;
+ m_dac2.buf_size = data&0xffff;
+ break;
+ case ES_HOST_IF0: // 0x30
+ m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x0] = data;
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ m_dac1.pci_addr = data;
+ break;
+ case 0xd:
+ m_adc.pci_addr = data;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ES_HOST_IF1: // 0x34
+ m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x1] = data;
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ m_dac1.pci_count = (data>>16)&0xffff;
+ m_dac1.pci_size = data&0xffff;
+ break;
+ case 0xd:
+ m_adc.pci_count = (data>>16)&0xffff;
+ m_adc.pci_size = data&0xffff;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ES_HOST_IF2: // 0x38
+ m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x2] = data;
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ m_dac2.pci_addr = data;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ES_HOST_IF3: // 0x3C
+ m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x3] = data;
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ m_dac2.pci_count = (data>>16)&0xffff;
+ m_dac2.pci_size = data&0xffff;
+ if (LOG_ES_REG)
+ logerror("%08X:ES1373 write to offset %02X = %08X & %08X\n", machine().device("maincpu")->safe_pc(), offset*4, data, mem_mask);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (LOG_ES_REG)
+ logerror("%08X:ES1373 write to offset %02X = %08X & %08X\n", machine().device("maincpu")->safe_pc(), offset*4, data, mem_mask);
+
+}
diff --git a/src/devices/sound/es1373.h b/src/devices/sound/es1373.h
new file mode 100644
index 00000000000..a5c7e974204
--- /dev/null
+++ b/src/devices/sound/es1373.h
@@ -0,0 +1,145 @@
+// license:BSD-3-Clause
+// copyright-holders:Ted Green
+// Creative Labs Ensonic AudioPCI97 ES1373
+
+#pragma once
+
+#ifndef ES1373_H
+#define ES1373_H
+
+#include "machine/pci.h"
+
+// No interrupts
+#define MCFG_ES1373_ADD(_tag) \
+ MCFG_PCI_DEVICE_ADD(_tag, ES1373, 0x12741371, 0x04, 0x040100, 0x12741371)
+
+#define MCFG_ES1373_IRQ_ADD(_cpu_tag, _irq_num) \
+ downcast<es1373_device *>(device)->set_irq_info(_cpu_tag, _irq_num);
+
+/* Ensonic ES1373 registers 0x00-0x3f */
+#define ES_INT_CS_CTRL (0x00/4)
+#define ES_INT_CS_STATUS (0x04/4)
+#define ES_UART_DATA (0x08/4)
+#define ES_UART_STATUS (0x09/4)
+#define ES_UART_CTRL (0x09/4)
+#define ES_UART_RSVD (0x0A/4)
+#define ES_MEM_PAGE (0x0C/4)
+#define ES_SRC_IF (0x10/4)
+#define ES_CODEC (0x14/4)
+#define ES_LEGACY (0x18/4)
+#define ES_CHAN_CTRL (0x1C/4)
+#define ES_SERIAL_CTRL (0x20/4)
+#define ES_DAC1_CNT (0x24/4)
+#define ES_DAC2_CNT (0x28/4)
+#define ES_ADC_CNT (0x2C/4)
+#define ES_HOST_IF0 (0x30/4)
+#define ES_HOST_IF1 (0x34/4)
+#define ES_HOST_IF2 (0x38/4)
+#define ES_HOST_IF3 (0x3C/4)
+
+// Interrupt/Chip Select Control Register (ES_INT_CS_CTRL) bits
+#define ICCTRL_ADC_STOP_MASK 0x00002000
+#define ICCTRL_DAC1_EN_MASK 0x00000040
+#define ICCTRL_DAC2_EN_MASK 0x00000020
+#define ICCTRL_ADC_EN_MASK 0x00000010
+#define ICCTRL_UART_EN_MASK 0x00000008
+#define ICCTRL_JYSTK_EN_MASK 0x00000004
+
+// Interrupt/Chip Select Status Register (ES_INT_CS_STATUS) bits
+#define ICSTATUS_INTR_MASK 0x80000000
+#define ICSTATUS_DAC1_INT_MASK 0x00000004
+#define ICSTATUS_DAC2_INT_MASK 0x00000002
+#define ICSTATUS_ADC_INT_MASK 0x00000001
+
+// Serial Interface Control Register (ES_SERIAL_CTRL) bits
+#define SCTRL_P2_END_MASK 0x00380000
+#define SCTRL_P2_START_MASK 0x00070000
+#define SCTRL_R1_LOOP_MASK 0x00008000
+#define SCTRL_P2_LOOP_MASK 0x00004000
+#define SCTRL_P1_LOOP_MASK 0x00002000
+#define SCTRL_P2_PAUSE_MASK 0x00001000
+#define SCTRL_P1_PAUSE_MASK 0x00000800
+#define SCTRL_R1_INT_EN_MASK 0x00000400
+#define SCTRL_P2_INT_EN_MASK 0x00000200
+#define SCTRL_P1_INT_EN_MASK 0x00000100
+#define SCTRL_P1_RELOAD_MASK 0x00000080
+#define SCTRL_P2_STOP_MASK 0x00000040
+#define SCTRL_R1_S_MASK 0x00000030
+#define SCTRL_P2_S_MASK 0x0000000C
+#define SCTRL_P1_S_MASK 0x00000003
+
+#define SCTRL_8BIT_MONO 0x0
+#define SCTRL_8BIT_STEREO 0x1
+#define SCTRL_16BIT_MONO 0x2
+#define SCTRL_16BIT_STEREO 0x3
+
+#define ES_PCI_READ 0
+#define ES_PCI_WRITE 1
+
+struct chan_info {
+ int number;
+ bool enable;
+ bool int_en;
+ bool loop_en;
+ bool initialized;
+ UINT8 format; // Format of channel
+ UINT32 buf_wptr; // Address to sample cache memory
+ UINT32 buf_rptr; // Address to sample cache memory
+ UINT16 buf_count; // Number of samples that have been played
+ UINT16 buf_size; // Number of samples minus one to play
+ UINT32 pci_addr; // PCI Addresss for system memory accesses
+ UINT16 pci_count; // Number of 32 bits transfered
+ UINT16 pci_size; // Total number of words (32 bits) minus one in system memory
+};
+
+class es1373_device : public pci_device, public device_sound_interface
+{
+public:
+ es1373_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ virtual void map_extra(UINT64 memory_window_start, UINT64 memory_window_end, UINT64 memory_offset, address_space *memory_space,
+ UINT64 io_window_start, UINT64 io_window_end, UINT64 io_offset, address_space *io_space);
+
+ void set_irq_info(const char *tag, const int irq_num);
+
+ DECLARE_READ32_MEMBER (reg_r);
+ DECLARE_WRITE32_MEMBER(reg_w);
+
+ // optional information overrides
+ virtual machine_config_constructor device_mconfig_additions() const;
+
+ // Sound stream
+ sound_stream *m_stream;
+
+protected:
+ virtual void device_start();
+ virtual void device_stop();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ FILE *m_eslog;
+
+private:
+ UINT32 m_tempCount;
+ emu_timer *m_timer;
+ address_space *m_memory_space;
+ const char *m_cpu_tag;
+ cpu_device *m_cpu;
+ int m_irq_num;
+ DECLARE_ADDRESS_MAP(map, 32);
+ UINT16 m_ac97_regs[0x80];
+ UINT32 m_es_regs[0x10];
+ UINT32 m_sound_cache[0x40];
+ UINT16 m_src_ram[0x80];
+ chan_info m_dac1;
+ chan_info m_dac2;
+ chan_info m_adc;
+ void transfer_pci_audio(chan_info& chan, int type);
+ UINT32 calc_size(const UINT8 &format);
+ void send_audio_out(chan_info& chan, UINT32 intr_mask, stream_sample_t *outL, stream_sample_t *outR, int samples);
+
+};
+
+extern const device_type ES1373;
+
+#endif
diff --git a/src/devices/sound/es5503.c b/src/devices/sound/es5503.c
new file mode 100644
index 00000000000..b1881412da2
--- /dev/null
+++ b/src/devices/sound/es5503.c
@@ -0,0 +1,451 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+/*
+
+ ES5503 - Ensoniq ES5503 "DOC" emulator v2.1.1
+ By R. Belmont.
+
+ Copyright R. Belmont.
+
+ History: the ES5503 was the next design after the famous C64 "SID" by Bob Yannes.
+ It powered the legendary Mirage sampler (the first affordable pro sampler) as well
+ as the ESQ-1 synth/sequencer. The ES5505 (used in Taito's F3 System) and 5506
+ (used in the "Soundscape" series of ISA PC sound cards) followed on a fundamentally
+ similar architecture.
+
+ Bugs: On the real silicon, oscillators 30 and 31 have random volume fluctuations and are
+ unusable for playback. We don't attempt to emulate that. :-)
+
+ Additionally, in "swap" mode, there's one cycle when the switch takes place where the
+ oscillator's output is 0x80 (centerline) regardless of the sample data. This can
+ cause audible clicks and a general degradation of audio quality if the correct sample
+ data at that point isn't 0x80 or very near it.
+
+ Changes:
+ 0.2 (RB) - improved behavior for volumes > 127, fixes missing notes in Nucleus & missing voices in Thexder
+ 0.3 (RB) - fixed extraneous clicking, improved timing behavior for e.g. Music Construction Set & Music Studio
+ 0.4 (RB) - major fixes to IRQ semantics and end-of-sample handling.
+ 0.5 (RB) - more flexible wave memory hookup (incl. banking) and save state support.
+ 1.0 (RB) - properly respects the input clock
+ 2.0 (RB) - C++ conversion, more accurate oscillator IRQ timing
+ 2.1 (RB) - Corrected phase when looping; synthLAB, Arkanoid, and Arkanoid II no longer go out of tune
+ 2.1.1 (RB) - Fixed issue introduced in 2.0 where IRQs were delayed
+*/
+
+#include "emu.h"
+#include "es5503.h"
+
+// device type definition
+const device_type ES5503 = &device_creator<es5503_device>;
+
+// useful constants
+static const UINT16 wavesizes[8] = { 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 };
+static const UINT32 wavemasks[8] = { 0x1ff00, 0x1fe00, 0x1fc00, 0x1f800, 0x1f000, 0x1e000, 0x1c000, 0x18000 };
+static const UINT32 accmasks[8] = { 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff };
+static const int resshifts[8] = { 9, 10, 11, 12, 13, 14, 15, 16 };
+
+// default address map
+static ADDRESS_MAP_START( es5503, AS_0, 8, es5503_device )
+ AM_RANGE(0x000000, 0x1ffff) AM_ROM
+ADDRESS_MAP_END
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// es5503_device - constructor
+//-------------------------------------------------
+
+es5503_device::es5503_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, ES5503, "Ensoniq ES5503", tag, owner, clock, "es5503", __FILE__),
+ device_sound_interface(mconfig, *this),
+ device_memory_interface(mconfig, *this),
+ m_space_config("es5503_samples", ENDIANNESS_LITTLE, 8, 17, 0, NULL, *ADDRESS_MAP_NAME(es5503)),
+ m_irq_func(*this),
+ m_adc_func(*this)
+{
+}
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *es5503_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == 0) ? &m_space_config : NULL;
+}
+
+//-------------------------------------------------
+// static_set_type - configuration helper to set
+// the IRQ callback
+//-------------------------------------------------
+
+void es5503_device::static_set_channels(device_t &device, int channels)
+{
+ es5503_device &es5503 = downcast<es5503_device &>(device);
+ es5503.output_channels = channels;
+}
+
+//-------------------------------------------------
+// device_timer - called when our device timer expires
+//-------------------------------------------------
+
+void es5503_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
+{
+ m_stream->update();
+}
+
+// halt_osc: handle halting an oscillator
+// chip = chip ptr
+// onum = oscillator #
+// type = 1 for 0 found in sample data, 0 for hit end of table size
+void es5503_device::halt_osc(int onum, int type, UINT32 *accumulator, int resshift)
+{
+ ES5503Osc *pOsc = &oscillators[onum];
+ ES5503Osc *pPartner = &oscillators[onum^1];
+ int mode = (pOsc->control>>1) & 3;
+
+ // if 0 found in sample data or mode is not free-run, halt this oscillator
+ if ((mode != MODE_FREE) || (type != 0))
+ {
+ pOsc->control |= 1;
+ }
+ else // preserve the relative phase of the oscillator when looping
+ {
+ UINT16 wtsize = pOsc->wtsize - 1;
+ UINT32 altram = (*accumulator) >> resshift;
+
+ if (altram > wtsize)
+ {
+ altram -= wtsize;
+ }
+ else
+ {
+ altram = 0;
+ }
+
+ *accumulator = altram << resshift;
+ }
+ int omode = (pPartner->control>>1) & 3;
+
+ // if swap mode, start the partner
+ if ((mode == MODE_SWAP) || (omode == MODE_SWAP))
+ {
+ pPartner->control &= ~1; // clear the halt bit
+ pPartner->accumulator = 0; // and make sure it starts from the top (does this also need phase preservation?)
+ }
+
+ // IRQ enabled for this voice?
+ if (pOsc->control & 0x08)
+ {
+ pOsc->irqpend = 1;
+
+ m_irq_func(1);
+ }
+}
+
+void es5503_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ static INT32 mix[(44100/60)*2*8];
+ INT32 *mixp;
+ int osc, snum, i;
+ UINT32 ramptr;
+
+ assert(samples < (44100/60)*2);
+ memset(mix, 0, sizeof(mix));
+
+ for (int chan = 0; chan < output_channels; chan++)
+ {
+ for (osc = 0; osc < (oscsenabled+1); osc++)
+ {
+ ES5503Osc *pOsc = &oscillators[osc];
+
+ if (!(pOsc->control & 1) && ((pOsc->control >> 4) & (output_channels - 1)) == chan)
+ {
+ UINT32 wtptr = pOsc->wavetblpointer & wavemasks[pOsc->wavetblsize], altram;
+ UINT32 acc = pOsc->accumulator;
+ UINT16 wtsize = pOsc->wtsize - 1;
+ UINT8 ctrl = pOsc->control;
+ UINT16 freq = pOsc->freq;
+ INT16 vol = pOsc->vol;
+ INT8 data = -128;
+ int resshift = resshifts[pOsc->resolution] - pOsc->wavetblsize;
+ UINT32 sizemask = accmasks[pOsc->wavetblsize];
+ mixp = &mix[0] + chan;
+
+ for (snum = 0; snum < samples; snum++)
+ {
+ altram = acc >> resshift;
+ ramptr = altram & sizemask;
+
+ acc += freq;
+
+ // channel strobe is always valid when reading; this allows potentially banking per voice
+ m_channel_strobe = (ctrl>>4) & 0xf;
+ data = (INT32)m_direct->read_byte(ramptr + wtptr) ^ 0x80;
+
+ if (m_direct->read_byte(ramptr + wtptr) == 0x00)
+ {
+ halt_osc(osc, 1, &acc, resshift);
+ }
+ else
+ {
+ *mixp += data * vol;
+ mixp += output_channels;
+
+ if (altram >= wtsize)
+ {
+ halt_osc(osc, 0, &acc, resshift);
+ }
+ }
+
+ // if oscillator halted, we've got no more samples to generate
+ if (pOsc->control & 1)
+ {
+ ctrl |= 1;
+ break;
+ }
+ }
+
+ pOsc->control = ctrl;
+ pOsc->accumulator = acc;
+ pOsc->data = data ^ 0x80;
+ }
+ }
+ }
+
+ mixp = &mix[0];
+ for (i = 0; i < samples; i++)
+ for (int chan = 0; chan < output_channels; chan++)
+ outputs[chan][i] = (*mixp++)>>1;
+}
+
+
+void es5503_device::device_start()
+{
+ int osc;
+
+ // find our direct access
+ m_direct = &space().direct();
+
+ m_irq_func.resolve_safe();
+ m_adc_func.resolve_safe(0);
+
+ rege0 = 0xff;
+
+ for (osc = 0; osc < 32; osc++)
+ {
+ save_item(NAME(oscillators[osc].freq), osc);
+ save_item(NAME(oscillators[osc].wtsize), osc);
+ save_item(NAME(oscillators[osc].control), osc);
+ save_item(NAME(oscillators[osc].vol), osc);
+ save_item(NAME(oscillators[osc].data), osc);
+ save_item(NAME(oscillators[osc].wavetblpointer), osc);
+ save_item(NAME(oscillators[osc].wavetblsize), osc);
+ save_item(NAME(oscillators[osc].resolution), osc);
+ save_item(NAME(oscillators[osc].accumulator), osc);
+ save_item(NAME(oscillators[osc].irqpend), osc);
+ }
+
+ output_rate = (clock()/8)/34; // (input clock / 8) / # of oscs. enabled + 2
+ m_stream = machine().sound().stream_alloc(*this, 0, output_channels, output_rate);
+
+ m_timer = timer_alloc(0, NULL);
+ m_timer->adjust(attotime::from_hz(output_rate), 0, attotime::from_hz(output_rate));
+}
+
+void es5503_device::device_reset()
+{
+ rege0 = 0xff;
+
+ for (int osc = 0; osc < 32; osc++)
+ {
+ oscillators[osc].freq = 0;
+ oscillators[osc].wtsize = 0;
+ oscillators[osc].control = 0;
+ oscillators[osc].vol = 0;
+ oscillators[osc].data = 0x80;
+ oscillators[osc].wavetblpointer = 0;
+ oscillators[osc].wavetblsize = 0;
+ oscillators[osc].resolution = 0;
+ oscillators[osc].accumulator = 0;
+ oscillators[osc].irqpend = 0;
+ }
+
+ oscsenabled = 1;
+
+ m_channel_strobe = 0;
+
+ output_rate = (clock()/8)/34; // (input clock / 8) / # of oscs. enabled + 2
+}
+
+READ8_MEMBER( es5503_device::read )
+{
+ UINT8 retval;
+ int i;
+
+ m_stream->update();
+
+ if (offset < 0xe0)
+ {
+ int osc = offset & 0x1f;
+
+ switch(offset & 0xe0)
+ {
+ case 0: // freq lo
+ return (oscillators[osc].freq & 0xff);
+
+ case 0x20: // freq hi
+ return (oscillators[osc].freq >> 8);
+
+ case 0x40: // volume
+ return oscillators[osc].vol;
+
+ case 0x60: // data
+ return oscillators[osc].data;
+
+ case 0x80: // wavetable pointer
+ return (oscillators[osc].wavetblpointer>>8) & 0xff;
+
+ case 0xa0: // oscillator control
+ return oscillators[osc].control;
+
+ case 0xc0: // bank select / wavetable size / resolution
+ retval = 0;
+ if (oscillators[osc].wavetblpointer & 0x10000)
+ {
+ retval |= 0x40;
+ }
+
+ retval |= (oscillators[osc].wavetblsize<<3);
+ retval |= oscillators[osc].resolution;
+ return retval;
+ }
+ }
+ else // global registers
+ {
+ switch (offset)
+ {
+ case 0xe0: // interrupt status
+ retval = rege0;
+
+ m_irq_func(0);
+
+ // scan all oscillators
+ for (i = 0; i < oscsenabled+1; i++)
+ {
+ if (oscillators[i].irqpend)
+ {
+ // signal this oscillator has an interrupt
+ retval = i<<1;
+
+ rege0 = retval | 0x80;
+
+ // and clear its flag
+ oscillators[i].irqpend = 0;
+ break;
+ }
+ }
+
+ // if any oscillators still need to be serviced, assert IRQ again immediately
+ for (i = 0; i < oscsenabled+1; i++)
+ {
+ if (oscillators[i].irqpend)
+ {
+ m_irq_func(1);
+ break;
+ }
+ }
+
+ return retval;
+
+ case 0xe1: // oscillator enable
+ return oscsenabled<<1;
+
+ case 0xe2: // A/D converter
+ return m_adc_func();
+ }
+ }
+
+ return 0;
+}
+
+WRITE8_MEMBER( es5503_device::write )
+{
+ m_stream->update();
+
+ if (offset < 0xe0)
+ {
+ int osc = offset & 0x1f;
+
+ switch(offset & 0xe0)
+ {
+ case 0: // freq lo
+ oscillators[osc].freq &= 0xff00;
+ oscillators[osc].freq |= data;
+ break;
+
+ case 0x20: // freq hi
+ oscillators[osc].freq &= 0x00ff;
+ oscillators[osc].freq |= (data<<8);
+ break;
+
+ case 0x40: // volume
+ oscillators[osc].vol = data;
+ break;
+
+ case 0x60: // data - ignore writes
+ break;
+
+ case 0x80: // wavetable pointer
+ oscillators[osc].wavetblpointer = (data<<8);
+ break;
+
+ case 0xa0: // oscillator control
+ // if a fresh key-on, reset the ccumulator
+ if ((oscillators[osc].control & 1) && (!(data&1)))
+ {
+ oscillators[osc].accumulator = 0;
+ }
+
+ oscillators[osc].control = data;
+ break;
+
+ case 0xc0: // bank select / wavetable size / resolution
+ if (data & 0x40) // bank select - not used on the Apple IIgs
+ {
+ oscillators[osc].wavetblpointer |= 0x10000;
+ }
+ else
+ {
+ oscillators[osc].wavetblpointer &= 0xffff;
+ }
+
+ oscillators[osc].wavetblsize = ((data>>3) & 7);
+ oscillators[osc].wtsize = wavesizes[oscillators[osc].wavetblsize];
+ oscillators[osc].resolution = (data & 7);
+ break;
+ }
+ }
+ else // global registers
+ {
+ switch (offset)
+ {
+ case 0xe0: // interrupt status
+ break;
+
+ case 0xe1: // oscillator enable
+ oscsenabled = (data>>1) & 0x1f;
+
+ output_rate = (clock()/8)/(2+oscsenabled);
+ m_stream->set_sample_rate(output_rate);
+ m_timer->adjust(attotime::from_hz(output_rate), 0, attotime::from_hz(output_rate));
+ break;
+
+ case 0xe2: // A/D converter
+ break;
+ }
+ }
+}
diff --git a/src/devices/sound/es5503.h b/src/devices/sound/es5503.h
new file mode 100644
index 00000000000..582456e830b
--- /dev/null
+++ b/src/devices/sound/es5503.h
@@ -0,0 +1,108 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+#pragma once
+
+#ifndef __ES5503_H__
+#define __ES5503_H__
+
+// channels must be a power of two
+
+#define MCFG_ES5503_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, ES5503, _clock)
+
+#define MCFG_ES5503_OUTPUT_CHANNELS(_channels) \
+ es5503_device::static_set_channels(*device, _channels);
+
+#define MCFG_ES5503_IRQ_FUNC(_write) \
+ devcb = &es5503_device::static_set_irqf(*device, DEVCB_##_write);
+
+#define MCFG_ES5503_ADC_FUNC(_read) \
+ devcb = &es5503_device::static_set_adcf(*device, DEVCB_##_read);
+
+// ======================> es5503_device
+
+class es5503_device : public device_t,
+ public device_sound_interface,
+ public device_memory_interface
+{
+public:
+ // construction/destruction
+ es5503_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void static_set_channels(device_t &device, int channels);
+
+ template<class _Object> static devcb_base &static_set_irqf(device_t &device, _Object object) { return downcast<es5503_device &>(device).m_irq_func.set_callback(object); }
+ template<class _Object> static devcb_base &static_set_adcf(device_t &device, _Object object) { return downcast<es5503_device &>(device).m_adc_func.set_callback(object); }
+
+ DECLARE_READ8_MEMBER(read);
+ DECLARE_WRITE8_MEMBER(write);
+
+ UINT8 get_channel_strobe() { return m_channel_strobe; }
+
+ sound_stream *m_stream;
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr);
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ const address_space_config m_space_config;
+
+ devcb_write_line m_irq_func;
+ devcb_read8 m_adc_func;
+
+ emu_timer *m_sync_timer;
+
+private:
+ enum
+ {
+ MODE_FREE = 0,
+ MODE_ONESHOT = 1,
+ MODE_SYNCAM = 2,
+ MODE_SWAP = 3
+ };
+
+ struct ES5503Osc
+ {
+ UINT16 freq;
+ UINT16 wtsize;
+ UINT8 control;
+ UINT8 vol;
+ UINT8 data;
+ UINT32 wavetblpointer;
+ UINT8 wavetblsize;
+ UINT8 resolution;
+
+ UINT32 accumulator;
+ UINT8 irqpend;
+ };
+
+ ES5503Osc oscillators[32];
+
+ INT8 oscsenabled; // # of oscillators enabled
+ int rege0; // contents of register 0xe0
+
+ UINT8 m_channel_strobe;
+
+ int output_channels;
+ UINT32 output_rate;
+
+ emu_timer *m_timer;
+
+ direct_read_data *m_direct;
+
+ void halt_osc(int onum, int type, UINT32 *accumulator, int resshift);
+};
+
+
+// device type definition
+extern const device_type ES5503;
+
+#endif /* __ES5503_H__ */
diff --git a/src/devices/sound/es5506.c b/src/devices/sound/es5506.c
new file mode 100644
index 00000000000..2c46b7560d6
--- /dev/null
+++ b/src/devices/sound/es5506.c
@@ -0,0 +1,2274 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/**********************************************************************************************
+
+ Ensoniq ES5505/6 driver
+ by Aaron Giles
+
+Ensoniq OTIS - ES5505 Ensoniq OTTO - ES5506
+
+ OTIS is a VLSI device designed in a 2 micron double metal OTTO is a VLSI device designed in a 1.5 micron double metal
+ CMOS process. The device is the next generation of audio CMOS process. The device is the next generation of audio
+ technology from ENSONIQ. This new chip achieves a new technology from ENSONIQ. All calculations in the device are
+ level of audio fidelity performance. These improvements made with at least 18-bit accuracy.
+ are achieved through the use of frequency interpolation
+ and on board real time digital filters. All calculations The major features of OTTO are:
+ in the device are made with at least 16 bit accuracy. - 68 pin PLCC package
+ - On chip real time digital filters
+ The major features of OTIS are: - Frequency interpolation
+ - 48 Pin dual in line package - 32 independent voices
+ - On chip real time digital filters - Loop start and stop posistions for each voice
+ - Frequency interpolation - Bidirectional and reverse looping
+ - 32 independent voices (up from 25 in DOCII) - 68000 compatibility for asynchronous bus communication
+ - Loop start and stop positions for each voice - separate host and sound memory interface
+ - Bidirectional and reverse looping - 6 channel stereo serial communication port
+ - 68000 compatibility for asynchronous bus communication - Programmable clocks for defining serial protocol
+ - On board pulse width modulation D to A - Internal volume multiplication and stereo panning
+ - 4 channel stereo serial communication port - A to D input for pots and wheels
+ - Internal volume multiplication and stereo panning - Hardware support for envelopes
+ - A to D input for pots and wheels - Support for dual OTTO systems
+ - Up to 10MHz operation - Optional compressed data format for sample data
+ - Up to 16MHz operation
+ ______ ______
+ _|o \__/ |_
+ A17/D13 - |_|1 48|_| - VSS A A A A A A
+ _| |_ 2 1 1 1 1 1 A
+ A18/D14 - |_|2 47|_| - A16/D12 0 9 8 7 6 5 1
+ _| |_ / / / / / / 4
+ A19/D15 - |_|3 46|_| - A15/D11 H H H H H H H V V H D D D D D D /
+ _| |_ D D D D D D D S D D 1 1 1 1 1 1 D
+ BS - |_|4 45|_| - A14/D10 0 1 2 3 4 5 6 S D 7 5 4 3 2 1 0 9
+ _| |_ ------------------------------------+
+ PWZERO - |_|5 44|_| - A13/D9 / 9 8 7 6 5 4 3 2 1 6 6 6 6 6 6 6 6 |
+ _| |_ / 8 7 6 5 4 3 2 1 |
+ SER0 - |_|6 43|_| - A12/D8 | |
+ _| E |_ SER0|10 60|A13/D8
+ SER1 - |_|7 N 42|_| - A11/D7 SER1|11 59|A12/D7
+ _| S |_ SER2|12 58|A11/D6
+ SER2 - |_|8 O 41|_| - A10/D6 SER3|13 ENSONIQ 57|A10/D5
+ _| N |_ SER4|14 56|A9/D4
+ SER3 - |_|9 I 40|_| - A9/D5 SER5|15 55|A8/D3
+ _| Q |_ WCLK|16 54|A7/D2
+ SERWCLK - |_|10 39|_| - A8/D4 LRCLK|17 ES5506 53|A6/D1
+ _| |_ BCLK|18 52|A5/D0
+ SERLR - |_|11 38|_| - A7/D3 RESB|19 51|A4
+ _| |_ HA5|20 50|A3
+ SERBCLK - |_|12 E 37|_| - A6/D2 HA4|21 OTTO 49|A2
+ _| S |_ HA3|22 48|A1
+ RLO - |_|13 5 36|_| - A5/D1 HA2|23 47|A0
+ _| 5 |_ HA1|24 46|BS1
+ RHI - |_|14 0 35|_| - A4/D0 HA0|25 45|BS0
+ _| 5 |_ POT_IN|26 44|DTACKB
+ LLO - |_|15 34|_| - CLKIN | 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 |
+ _| |_ | 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 |
+ LHI - |_|16 33|_| - CAS +--------------------------------------+
+ _| |_ B E E B E B B D S B B B E K B W W
+ POT - |_|17 O 32|_| - AMUX S B L N L S S D S S X S L Q / /
+ _| T |_ E E R E H M C V V A U A C R R R
+ DTACK - |_|18 I 31|_| - RAS R R D H R M C I M
+ _| S |_ _ D A
+ R/W - |_|19 30|_| - E T
+ _| |_ O
+ MS - |_|20 29|_| - IRQ P
+ _| |_
+ CS - |_|21 28|_| - A3
+ _| |_
+ RES - |_|22 27|_| - A2
+ _| |_
+ VSS - |_|23 26|_| - A1
+ _| |_
+ VDD - |_|24 25|_| - A0
+ |________________|
+
+***********************************************************************************************/
+
+#include "emu.h"
+#include "es5506.h"
+
+
+/**********************************************************************************************
+
+ CONSTANTS
+
+***********************************************************************************************/
+
+#define LOG_COMMANDS 0
+#define RAINE_CHECK 0
+
+#if MAKE_WAVS
+#include "wavwrite.h"
+#endif
+
+
+#define MAX_SAMPLE_CHUNK 10000
+#define ULAW_MAXBITS 8
+
+#define CONTROL_BS1 0x8000
+#define CONTROL_BS0 0x4000
+#define CONTROL_CMPD 0x2000
+#define CONTROL_CA2 0x1000
+#define CONTROL_CA1 0x0800
+#define CONTROL_CA0 0x0400
+#define CONTROL_LP4 0x0200
+#define CONTROL_LP3 0x0100
+#define CONTROL_IRQ 0x0080
+#define CONTROL_DIR 0x0040
+#define CONTROL_IRQE 0x0020
+#define CONTROL_BLE 0x0010
+#define CONTROL_LPE 0x0008
+#define CONTROL_LEI 0x0004
+#define CONTROL_STOP1 0x0002
+#define CONTROL_STOP0 0x0001
+
+#define CONTROL_BSMASK (CONTROL_BS1 | CONTROL_BS0)
+#define CONTROL_CAMASK (CONTROL_CA2 | CONTROL_CA1 | CONTROL_CA0)
+#define CONTROL_LPMASK (CONTROL_LP4 | CONTROL_LP3)
+#define CONTROL_LOOPMASK (CONTROL_BLE | CONTROL_LPE)
+#define CONTROL_STOPMASK (CONTROL_STOP1 | CONTROL_STOP0)
+
+
+es550x_device::es550x_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_sample_rate(0),
+ m_write_latch(0),
+ m_read_latch(0),
+ m_master_clock(0),
+ m_current_page(0),
+ m_active_voices(0),
+ m_mode(0),
+ m_wst(0),
+ m_wend(0),
+ m_lrend(0),
+ m_irqv(0),
+ m_scratch(NULL),
+ m_ulaw_lookup(NULL),
+ m_volume_lookup(NULL),
+ #if MAKE_WAVS
+ m_wavraw(NULL),
+ #endif
+ m_eslog(NULL),
+ m_region0(NULL),
+ m_region1(NULL),
+ m_region2(NULL),
+ m_region3(NULL),
+ m_channels(0),
+ m_irq_cb(*this),
+ m_read_port_cb(*this)
+{
+ for (int i = 0; i < 4; i++)
+ {
+ m_region_base[i] = NULL;
+ }
+}
+
+const device_type ES5506 = &device_creator<es5506_device>;
+
+es5506_device::es5506_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : es550x_device(mconfig, ES5506, "ES5506", tag, owner, clock, "es5506", __FILE__)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+void es550x_device::device_start()
+{
+}
+
+void es5506_device::device_start()
+{
+ int j;
+ UINT32 accum_mask;
+ int channels = 1; /* 1 channel by default, for backward compatibility */
+
+ /* only override the number of channels if the value is in the valid range 1 .. 6 */
+ if (1 <= m_channels && m_channels <= 6)
+ channels = m_channels;
+
+ /* debugging */
+ if (LOG_COMMANDS && !m_eslog)
+ m_eslog = fopen("es.log", "w");
+
+ /* create the stream */
+ m_stream = machine().sound().stream_alloc(*this, 0, 2 * channels, clock() / (16*32));
+
+ /* initialize the regions */
+ m_region_base[0] = m_region0 ? (UINT16 *)machine().root_device().memregion(m_region0)->base() : NULL;
+ m_region_base[1] = m_region1 ? (UINT16 *)machine().root_device().memregion(m_region1)->base() : NULL;
+ m_region_base[2] = m_region2 ? (UINT16 *)machine().root_device().memregion(m_region2)->base() : NULL;
+ m_region_base[3] = m_region3 ? (UINT16 *)machine().root_device().memregion(m_region3)->base() : NULL;
+
+ /* initialize the rest of the structure */
+ m_master_clock = clock();
+ m_irq_cb.resolve();
+ m_read_port_cb.resolve();
+ m_irqv = 0x80;
+ m_channels = channels;
+
+ /* KT-76 assumes all voices are active on an ES5506 without setting them! */
+ m_active_voices = 31;
+ m_sample_rate = m_master_clock / (16 * (m_active_voices + 1));
+ m_stream->set_sample_rate(m_sample_rate);
+
+ /* compute the tables */
+ compute_tables();
+
+ /* init the voices */
+ accum_mask = 0xffffffff;
+ for (j = 0; j < 32; j++)
+ {
+ m_voice[j].index = j;
+ m_voice[j].control = CONTROL_STOPMASK;
+ m_voice[j].lvol = 0xffff;
+ m_voice[j].rvol = 0xffff;
+ m_voice[j].exbank = 0;
+ m_voice[j].accum_mask = accum_mask;
+ }
+
+ /* allocate memory */
+ m_scratch = auto_alloc_array_clear(machine(), INT32, 2 * MAX_SAMPLE_CHUNK);
+
+ /* register save */
+ save_item(NAME(m_sample_rate));
+ save_item(NAME(m_write_latch));
+ save_item(NAME(m_read_latch));
+
+ save_item(NAME(m_current_page));
+ save_item(NAME(m_active_voices));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_wst));
+ save_item(NAME(m_wend));
+ save_item(NAME(m_lrend));
+ save_item(NAME(m_irqv));
+
+ save_pointer(NAME(m_scratch), 2 * MAX_SAMPLE_CHUNK);
+
+ for (j = 0; j < 32; j++)
+ {
+ save_item(NAME(m_voice[j].control), j);
+ save_item(NAME(m_voice[j].freqcount), j);
+ save_item(NAME(m_voice[j].start), j);
+ save_item(NAME(m_voice[j].lvol), j);
+ save_item(NAME(m_voice[j].end), j);
+ save_item(NAME(m_voice[j].lvramp), j);
+ save_item(NAME(m_voice[j].accum), j);
+ save_item(NAME(m_voice[j].rvol), j);
+ save_item(NAME(m_voice[j].rvramp), j);
+ save_item(NAME(m_voice[j].ecount), j);
+ save_item(NAME(m_voice[j].k2), j);
+ save_item(NAME(m_voice[j].k2ramp), j);
+ save_item(NAME(m_voice[j].k1), j);
+ save_item(NAME(m_voice[j].k1ramp), j);
+ save_item(NAME(m_voice[j].o4n1), j);
+ save_item(NAME(m_voice[j].o3n1), j);
+ save_item(NAME(m_voice[j].o3n2), j);
+ save_item(NAME(m_voice[j].o2n1), j);
+ save_item(NAME(m_voice[j].o2n2), j);
+ save_item(NAME(m_voice[j].o1n1), j);
+ save_item(NAME(m_voice[j].exbank), j);
+ save_item(NAME(m_voice[j].filtcount), j);
+ }
+
+ /* success */
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void es550x_device::device_reset()
+{
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void es550x_device::device_stop()
+{
+ /* debugging */
+ if (LOG_COMMANDS && m_eslog)
+ {
+ fclose(m_eslog);
+ m_eslog = NULL;
+ }
+
+ #if MAKE_WAVS
+ {
+ int i;
+
+ for (i = 0; i < MAX_ES5506; i++)
+ {
+ if (es5506[i].m_wavraw)
+ wav_close(es5506[i].m_wavraw);
+ }
+ }
+ #endif
+}
+
+const device_type ES5505 = &device_creator<es5505_device>;
+
+es5505_device::es5505_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : es550x_device(mconfig, ES5505, "ES5505", tag, owner, clock, "es5505", __FILE__)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void es5505_device::device_start()
+{
+ int j;
+ UINT32 accum_mask;
+ int channels = 1; /* 1 channel by default, for backward compatibility */
+
+ /* only override the number of channels if the value is in the valid range 1 .. 4 */
+ if (1 <= m_channels && m_channels <= 4)
+ channels = m_channels;
+
+ /* debugging */
+ if (LOG_COMMANDS && !m_eslog)
+ m_eslog = fopen("es.log", "w");
+
+ /* create the stream */
+ m_stream = machine().sound().stream_alloc(*this, 0, 2 * channels, clock() / (16*32));
+
+ /* initialize the regions */
+ m_region_base[0] = m_region0 ? (UINT16 *)machine().root_device().memregion(m_region0)->base() : NULL;
+ m_region_base[1] = m_region1 ? (UINT16 *)machine().root_device().memregion(m_region1)->base() : NULL;
+
+ /* initialize the rest of the structure */
+ m_master_clock = clock();
+ m_irq_cb.resolve();
+ m_read_port_cb.resolve();
+ m_irqv = 0x80;
+ m_channels = channels;
+
+ /* compute the tables */
+ compute_tables();
+
+ /* init the voices */
+ accum_mask = 0x7fffffff;
+ for (j = 0; j < 32; j++)
+ {
+ m_voice[j].index = j;
+ m_voice[j].control = CONTROL_STOPMASK;
+ m_voice[j].lvol = 0xffff;
+ m_voice[j].rvol = 0xffff;
+ m_voice[j].exbank = 0;
+ m_voice[j].accum_mask = accum_mask;
+ }
+
+ /* allocate memory */
+ m_scratch = auto_alloc_array_clear(machine(), INT32, 2 * MAX_SAMPLE_CHUNK);
+
+ /* register save */
+ save_item(NAME(m_sample_rate));
+ save_item(NAME(m_write_latch));
+ save_item(NAME(m_read_latch));
+
+ save_item(NAME(m_current_page));
+ save_item(NAME(m_active_voices));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_wst));
+ save_item(NAME(m_wend));
+ save_item(NAME(m_lrend));
+ save_item(NAME(m_irqv));
+
+ save_pointer(NAME(m_scratch), 2 * MAX_SAMPLE_CHUNK);
+
+ for (j = 0; j < 32; j++)
+ {
+ save_item(NAME(m_voice[j].control), j);
+ save_item(NAME(m_voice[j].freqcount), j);
+ save_item(NAME(m_voice[j].start), j);
+ save_item(NAME(m_voice[j].lvol), j);
+ save_item(NAME(m_voice[j].end), j);
+ save_item(NAME(m_voice[j].lvramp), j);
+ save_item(NAME(m_voice[j].accum), j);
+ save_item(NAME(m_voice[j].rvol), j);
+ save_item(NAME(m_voice[j].rvramp), j);
+ save_item(NAME(m_voice[j].ecount), j);
+ save_item(NAME(m_voice[j].k2), j);
+ save_item(NAME(m_voice[j].k2ramp), j);
+ save_item(NAME(m_voice[j].k1), j);
+ save_item(NAME(m_voice[j].k1ramp), j);
+ save_item(NAME(m_voice[j].o4n1), j);
+ save_item(NAME(m_voice[j].o3n1), j);
+ save_item(NAME(m_voice[j].o3n2), j);
+ save_item(NAME(m_voice[j].o2n1), j);
+ save_item(NAME(m_voice[j].o2n2), j);
+ save_item(NAME(m_voice[j].o1n1), j);
+ save_item(NAME(m_voice[j].exbank), j);
+ save_item(NAME(m_voice[j].filtcount), j);
+ }
+
+ /* success */
+}
+
+
+/**********************************************************************************************
+
+ update_irq_state -- update the IRQ state
+
+***********************************************************************************************/
+
+
+void es550x_device::update_irq_state()
+{
+ /* ES5505/6 irq line has been set high - inform the host */
+ if (!m_irq_cb.isnull())
+ m_irq_cb(1); /* IRQB set high */
+}
+
+void es550x_device::update_internal_irq_state()
+{
+ /* Host (cpu) has just read the voice interrupt vector (voice IRQ ack).
+
+ Reset the voice vector to show the IRQB line is low (top bit set).
+ If we have any stacked interrupts (other voices waiting to be
+ processed - with their IRQ bit set) then they will be moved into
+ the vector next time the voice is processed. In emulation
+ terms they get updated next time generate_samples() is called.
+ */
+
+ m_irqv=0x80;
+
+ if (!m_irq_cb.isnull())
+ m_irq_cb(0); /* IRQB set low */
+}
+
+/**********************************************************************************************
+
+ compute_tables -- compute static tables
+
+***********************************************************************************************/
+
+void es550x_device::compute_tables()
+{
+ int i;
+
+ /* allocate ulaw lookup table */
+ m_ulaw_lookup = auto_alloc_array_clear(machine(), INT16, 1 << ULAW_MAXBITS);
+
+ /* generate ulaw lookup table */
+ for (i = 0; i < (1 << ULAW_MAXBITS); i++)
+ {
+ UINT16 rawval = (i << (16 - ULAW_MAXBITS)) | (1 << (15 - ULAW_MAXBITS));
+ UINT8 exponent = rawval >> 13;
+ UINT32 mantissa = (rawval << 3) & 0xffff;
+
+ if (exponent == 0)
+ m_ulaw_lookup[i] = (INT16)mantissa >> 7;
+ else
+ {
+ mantissa = (mantissa >> 1) | (~mantissa & 0x8000);
+ m_ulaw_lookup[i] = (INT16)mantissa >> (7 - exponent);
+ }
+ }
+
+ /* allocate volume lookup table */
+ m_volume_lookup = auto_alloc_array_clear(machine(), UINT16, 4096);
+
+ /* generate volume lookup table */
+ for (i = 0; i < 4096; i++)
+ {
+ UINT8 exponent = i >> 8;
+ UINT32 mantissa = (i & 0xff) | 0x100;
+
+ m_volume_lookup[i] = (mantissa << 11) >> (20 - exponent);
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ interpolate -- interpolate between two samples
+
+***********************************************************************************************/
+
+#define interpolate(sample1, sample2, accum) \
+ (sample1 * (INT32)(0x800 - (accum & 0x7ff)) + \
+ sample2 * (INT32)(accum & 0x7ff)) >> 11;
+
+
+
+/**********************************************************************************************
+
+ apply_filters -- apply the 4-pole digital filter to the sample
+
+***********************************************************************************************/
+
+#define apply_filters(voice, sample) \
+do \
+{ \
+ /* pole 1 is always low-pass using K1 */ \
+ sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o1n1) / 16384) + voice->o1n1; \
+ voice->o1n1 = sample; \
+ \
+ /* pole 2 is always low-pass using K1 */ \
+ sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o2n1) / 16384) + voice->o2n1; \
+ voice->o2n2 = voice->o2n1; \
+ voice->o2n1 = sample; \
+ \
+ /* remaining poles depend on the current filter setting */ \
+ switch (voice->control & CONTROL_LPMASK) \
+ { \
+ case 0: \
+ /* pole 3 is high-pass using K2 */ \
+ sample = sample - voice->o2n2 + ((INT32)(voice->k2 >> 2) * voice->o3n1) / 32768 + voice->o3n1 / 2; \
+ voice->o3n2 = voice->o3n1; \
+ voice->o3n1 = sample; \
+ \
+ /* pole 4 is high-pass using K2 */ \
+ sample = sample - voice->o3n2 + ((INT32)(voice->k2 >> 2) * voice->o4n1) / 32768 + voice->o4n1 / 2; \
+ voice->o4n1 = sample; \
+ break; \
+ \
+ case CONTROL_LP3: \
+ /* pole 3 is low-pass using K1 */ \
+ sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \
+ voice->o3n2 = voice->o3n1; \
+ voice->o3n1 = sample; \
+ \
+ /* pole 4 is high-pass using K2 */ \
+ sample = sample - voice->o3n2 + ((INT32)(voice->k2 >> 2) * voice->o4n1) / 32768 + voice->o4n1 / 2; \
+ voice->o4n1 = sample; \
+ break; \
+ \
+ case CONTROL_LP4: \
+ /* pole 3 is low-pass using K2 */ \
+ sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \
+ voice->o3n2 = voice->o3n1; \
+ voice->o3n1 = sample; \
+ \
+ /* pole 4 is low-pass using K2 */ \
+ sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o4n1) / 16384) + voice->o4n1; \
+ voice->o4n1 = sample; \
+ break; \
+ \
+ case CONTROL_LP4 | CONTROL_LP3: \
+ /* pole 3 is low-pass using K1 */ \
+ sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \
+ voice->o3n2 = voice->o3n1; \
+ voice->o3n1 = sample; \
+ \
+ /* pole 4 is low-pass using K2 */ \
+ sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o4n1) / 16384) + voice->o4n1; \
+ voice->o4n1 = sample; \
+ break; \
+ } \
+} while (0)
+
+
+
+/**********************************************************************************************
+
+ update_envelopes -- update the envelopes
+
+***********************************************************************************************/
+
+#define update_envelopes(voice, samples) \
+do \
+{ \
+ int count = (samples > 1 && samples > voice->ecount) ? voice->ecount : samples; \
+ \
+ /* decrement the envelope counter */ \
+ voice->ecount -= count; \
+ \
+ /* ramp left volume */ \
+ if (voice->lvramp) \
+ { \
+ voice->lvol += (INT8)voice->lvramp * count; \
+ if ((INT32)voice->lvol < 0) voice->lvol = 0; \
+ else if (voice->lvol > 0xffff) voice->lvol = 0xffff; \
+ } \
+ \
+ /* ramp right volume */ \
+ if (voice->rvramp) \
+ { \
+ voice->rvol += (INT8)voice->rvramp * count; \
+ if ((INT32)voice->rvol < 0) voice->rvol = 0; \
+ else if (voice->rvol > 0xffff) voice->rvol = 0xffff; \
+ } \
+ \
+ /* ramp k1 filter constant */ \
+ if (voice->k1ramp && ((INT32)voice->k1ramp >= 0 || !(voice->filtcount & 7))) \
+ { \
+ voice->k1 += (INT8)voice->k1ramp * count; \
+ if ((INT32)voice->k1 < 0) voice->k1 = 0; \
+ else if (voice->k1 > 0xffff) voice->k1 = 0xffff; \
+ } \
+ \
+ /* ramp k2 filter constant */ \
+ if (voice->k2ramp && ((INT32)voice->k2ramp >= 0 || !(voice->filtcount & 7))) \
+ { \
+ voice->k2 += (INT8)voice->k2ramp * count; \
+ if ((INT32)voice->k2 < 0) voice->k2 = 0; \
+ else if (voice->k2 > 0xffff) voice->k2 = 0xffff; \
+ } \
+ \
+ /* update the filter constant counter */ \
+ voice->filtcount += count; \
+ \
+} while (0)
+
+
+
+/**********************************************************************************************
+
+ check_for_end_forward
+ check_for_end_reverse -- check for loop end and loop appropriately
+
+***********************************************************************************************/
+
+#define check_for_end_forward(voice, accum) \
+do \
+{ \
+ /* are we past the end? */ \
+ if (accum > voice->end && !(voice->control & CONTROL_LEI)) \
+ { \
+ /* generate interrupt if required */ \
+ if (voice->control&CONTROL_IRQE) \
+ voice->control |= CONTROL_IRQ; \
+ \
+ /* handle the different types of looping */ \
+ switch (voice->control & CONTROL_LOOPMASK) \
+ { \
+ /* non-looping */ \
+ case 0: \
+ voice->control |= CONTROL_STOP0; \
+ goto alldone; \
+ \
+ /* uni-directional looping */ \
+ case CONTROL_LPE: \
+ accum = (voice->start + (accum - voice->end)) & voice->accum_mask; \
+ break; \
+ \
+ /* trans-wave looping */ \
+ case CONTROL_BLE: \
+ accum = (voice->start + (accum - voice->end)) & voice->accum_mask; \
+ voice->control = (voice->control & ~CONTROL_LOOPMASK) | CONTROL_LEI;\
+ break; \
+ \
+ /* bi-directional looping */ \
+ case CONTROL_LPE | CONTROL_BLE: \
+ accum = (voice->end - (accum - voice->end)) & voice->accum_mask; \
+ voice->control ^= CONTROL_DIR; \
+ goto reverse; \
+ } \
+ } \
+} while (0)
+
+
+#define check_for_end_reverse(voice, accum) \
+do \
+{ \
+ /* are we past the end? */ \
+ if (accum < voice->start && !(voice->control & CONTROL_LEI)) \
+ { \
+ /* generate interrupt if required */ \
+ if (voice->control&CONTROL_IRQE) \
+ voice->control |= CONTROL_IRQ; \
+ \
+ /* handle the different types of looping */ \
+ switch (voice->control & CONTROL_LOOPMASK) \
+ { \
+ /* non-looping */ \
+ case 0: \
+ voice->control |= CONTROL_STOP0; \
+ goto alldone; \
+ \
+ /* uni-directional looping */ \
+ case CONTROL_LPE: \
+ accum = (voice->end - (voice->start - accum)) & voice->accum_mask; \
+ break; \
+ \
+ /* trans-wave looping */ \
+ case CONTROL_BLE: \
+ accum = (voice->end - (voice->start - accum)) & voice->accum_mask; \
+ voice->control = (voice->control & ~CONTROL_LOOPMASK) | CONTROL_LEI;\
+ break; \
+ \
+ /* bi-directional looping */ \
+ case CONTROL_LPE | CONTROL_BLE: \
+ accum = (voice->start + (voice->start - accum)) & voice->accum_mask;\
+ voice->control ^= CONTROL_DIR; \
+ goto reverse; \
+ } \
+ } \
+} while (0)
+
+
+
+/**********************************************************************************************
+
+ generate_dummy -- generate nothing, just apply envelopes
+
+***********************************************************************************************/
+
+void es550x_device::generate_dummy(es550x_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples)
+{
+ UINT32 freqcount = voice->freqcount;
+ UINT32 accum = voice->accum & voice->accum_mask;
+
+ /* outer loop, in case we switch directions */
+ while (samples > 0 && !(voice->control & CONTROL_STOPMASK))
+ {
+reverse:
+ /* two cases: first case is forward direction */
+ if (!(voice->control & CONTROL_DIR))
+ {
+ /* loop while we still have samples to generate */
+ while (samples--)
+ {
+ /* fetch two samples */
+ accum = (accum + freqcount) & voice->accum_mask;
+
+ /* update filters/volumes */
+ if (voice->ecount != 0)
+ update_envelopes(voice, 1);
+
+ /* check for loop end */
+ check_for_end_forward(voice, accum);
+ }
+ }
+
+ /* two cases: second case is backward direction */
+ else
+ {
+ /* loop while we still have samples to generate */
+ while (samples--)
+ {
+ /* fetch two samples */
+ accum = (accum - freqcount) & voice->accum_mask;
+
+ /* update filters/volumes */
+ if (voice->ecount != 0)
+ update_envelopes(voice, 1);
+
+ /* check for loop end */
+ check_for_end_reverse(voice, accum);
+ }
+ }
+ }
+
+ /* if we stopped, process any additional envelope */
+alldone:
+ voice->accum = accum;
+ if (samples > 0)
+ update_envelopes(voice, samples);
+}
+
+
+
+/**********************************************************************************************
+
+ generate_ulaw -- general u-law decoding routine
+
+***********************************************************************************************/
+
+void es550x_device::generate_ulaw(es550x_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples)
+{
+ UINT32 freqcount = voice->freqcount;
+ UINT32 accum = voice->accum & voice->accum_mask;
+ INT32 lvol = m_volume_lookup[voice->lvol >> 4];
+ INT32 rvol = m_volume_lookup[voice->rvol >> 4];
+
+ /* pre-add the bank offset */
+ base += voice->exbank;
+
+ /* outer loop, in case we switch directions */
+ while (samples > 0 && !(voice->control & CONTROL_STOPMASK))
+ {
+reverse:
+ /* two cases: first case is forward direction */
+ if (!(voice->control & CONTROL_DIR))
+ {
+ /* loop while we still have samples to generate */
+ while (samples--)
+ {
+ /* fetch two samples */
+ INT32 val1 = base[accum >> 11];
+ INT32 val2 = base[((accum + (1 << 11)) & voice->accum_mask) >> 11];
+
+ /* decompress u-law */
+ val1 = m_ulaw_lookup[val1 >> (16 - ULAW_MAXBITS)];
+ val2 = m_ulaw_lookup[val2 >> (16 - ULAW_MAXBITS)];
+
+ /* interpolate */
+ val1 = interpolate(val1, val2, accum);
+ accum = (accum + freqcount) & voice->accum_mask;
+
+ /* apply filters */
+ apply_filters(voice, val1);
+
+ /* update filters/volumes */
+ if (voice->ecount != 0)
+ {
+ update_envelopes(voice, 1);
+ lvol = m_volume_lookup[voice->lvol >> 4];
+ rvol = m_volume_lookup[voice->rvol >> 4];
+ }
+
+ /* apply volumes and add */
+ *lbuffer++ += (val1 * lvol) >> 11;
+ *rbuffer++ += (val1 * rvol) >> 11;
+
+ /* check for loop end */
+ check_for_end_forward(voice, accum);
+ }
+ }
+
+ /* two cases: second case is backward direction */
+ else
+ {
+ /* loop while we still have samples to generate */
+ while (samples--)
+ {
+ /* fetch two samples */
+ INT32 val1 = base[accum >> 11];
+ INT32 val2 = base[((accum + (1 << 11)) & voice->accum_mask) >> 11];
+
+ /* decompress u-law */
+ val1 = m_ulaw_lookup[val1 >> (16 - ULAW_MAXBITS)];
+ val2 = m_ulaw_lookup[val2 >> (16 - ULAW_MAXBITS)];
+
+ /* interpolate */
+ val1 = interpolate(val1, val2, accum);
+ accum = (accum - freqcount) & voice->accum_mask;
+
+ /* apply filters */
+ apply_filters(voice, val1);
+
+ /* update filters/volumes */
+ if (voice->ecount != 0)
+ {
+ update_envelopes(voice, 1);
+ lvol = m_volume_lookup[voice->lvol >> 4];
+ rvol = m_volume_lookup[voice->rvol >> 4];
+ }
+
+ /* apply volumes and add */
+ *lbuffer++ += (val1 * lvol) >> 11;
+ *rbuffer++ += (val1 * rvol) >> 11;
+
+ /* check for loop end */
+ check_for_end_reverse(voice, accum);
+ }
+ }
+ }
+
+ /* if we stopped, process any additional envelope */
+alldone:
+ voice->accum = accum;
+ if (samples > 0)
+ update_envelopes(voice, samples);
+}
+
+
+
+/**********************************************************************************************
+
+ generate_pcm -- general PCM decoding routine
+
+***********************************************************************************************/
+
+void es550x_device::generate_pcm(es550x_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples)
+{
+ UINT32 freqcount = voice->freqcount;
+ UINT32 accum = voice->accum & voice->accum_mask;
+ INT32 lvol = m_volume_lookup[voice->lvol >> 4];
+ INT32 rvol = m_volume_lookup[voice->rvol >> 4];
+
+ /* pre-add the bank offset */
+ base += voice->exbank;
+
+ /* outer loop, in case we switch directions */
+ while (samples > 0 && !(voice->control & CONTROL_STOPMASK))
+ {
+reverse:
+ /* two cases: first case is forward direction */
+ if (!(voice->control & CONTROL_DIR))
+ {
+ /* loop while we still have samples to generate */
+ while (samples--)
+ {
+ /* fetch two samples */
+ INT32 val1 = (INT16)base[accum >> 11];
+ INT32 val2 = (INT16)base[((accum + (1 << 11)) & voice->accum_mask) >> 11];
+
+ /* interpolate */
+ val1 = interpolate(val1, val2, accum);
+ accum = (accum + freqcount) & voice->accum_mask;
+
+ /* apply filters */
+ apply_filters(voice, val1);
+
+ /* update filters/volumes */
+ if (voice->ecount != 0)
+ {
+ update_envelopes(voice, 1);
+ lvol = m_volume_lookup[voice->lvol >> 4];
+ rvol = m_volume_lookup[voice->rvol >> 4];
+ }
+
+ /* apply volumes and add */
+ *lbuffer++ += (val1 * lvol) >> 11;
+ *rbuffer++ += (val1 * rvol) >> 11;
+
+ /* check for loop end */
+ check_for_end_forward(voice, accum);
+ }
+ }
+
+ /* two cases: second case is backward direction */
+ else
+ {
+ /* loop while we still have samples to generate */
+ while (samples--)
+ {
+ /* fetch two samples */
+ INT32 val1 = (INT16)base[accum >> 11];
+ INT32 val2 = (INT16)base[((accum + (1 << 11)) & voice->accum_mask) >> 11];
+
+ /* interpolate */
+ val1 = interpolate(val1, val2, accum);
+ accum = (accum - freqcount) & voice->accum_mask;
+
+ /* apply filters */
+ apply_filters(voice, val1);
+
+ /* update filters/volumes */
+ if (voice->ecount != 0)
+ {
+ update_envelopes(voice, 1);
+ lvol = m_volume_lookup[voice->lvol >> 4];
+ rvol = m_volume_lookup[voice->rvol >> 4];
+ }
+
+ /* apply volumes and add */
+ *lbuffer++ += (val1 * lvol) >> 11;
+ *rbuffer++ += (val1 * rvol) >> 11;
+
+ /* check for loop end */
+ check_for_end_reverse(voice, accum);
+ }
+ }
+ }
+
+ /* if we stopped, process any additional envelope */
+alldone:
+ voice->accum = accum;
+ if (samples > 0)
+ update_envelopes(voice, samples);
+}
+
+
+
+/**********************************************************************************************
+
+ generate_samples -- tell each voice to generate samples
+
+***********************************************************************************************/
+
+void es5506_device::generate_samples(INT32 **outputs, int offset, int samples)
+{
+ int v;
+
+ /* skip if nothing to do */
+ if (!samples)
+ return;
+
+ /* clear out the accumulators */
+ for (int i = 0; i < m_channels << 1; i++)
+ {
+ memset(outputs[i] + offset, 0, sizeof(INT32) * samples);
+ }
+
+ /* loop over voices */
+ for (v = 0; v <= m_active_voices; v++)
+ {
+ es550x_voice *voice = &m_voice[v];
+ UINT16 *base = m_region_base[voice->control >> 14];
+
+ /* special case: if end == start, stop the voice */
+ if (voice->start == voice->end)
+ voice->control |= CONTROL_STOP0;
+
+ int voice_channel = (voice->control & CONTROL_CAMASK) >> 10;
+ int channel = voice_channel % m_channels;
+ int l = channel << 1;
+ int r = l + 1;
+ INT32 *left = outputs[l] + offset;
+ INT32 *right = outputs[r] + offset;
+
+ /* generate from the appropriate source */
+ if (!base)
+ {
+ logerror("es5506: NULL region base %d\n",voice->control >> 14);
+ generate_dummy(voice, base, left, right, samples);
+ }
+ else if (voice->control & 0x2000)
+ generate_ulaw(voice, base, left, right, samples);
+ else
+ generate_pcm(voice, base, left, right, samples);
+
+ /* does this voice have it's IRQ bit raised? */
+ if (voice->control&CONTROL_IRQ)
+ {
+ logerror("es5506: IRQ raised on voice %d!!\n",v);
+
+ /* only update voice vector if existing IRQ is acked by host */
+ if (m_irqv&0x80)
+ {
+ /* latch voice number into vector, and set high bit low */
+ m_irqv=v&0x7f;
+
+ /* take down IRQ bit on voice */
+ voice->control&=~CONTROL_IRQ;
+
+ /* inform host of irq */
+ update_irq_state();
+ }
+ }
+ }
+}
+
+void es5505_device::generate_samples(INT32 **outputs, int offset, int samples)
+{
+ int v;
+
+ /* skip if nothing to do */
+ if (!samples)
+ return;
+
+ /* clear out the accumulators */
+ for (int i = 0; i < m_channels << 1; i++)
+ {
+ memset(outputs[i] + offset, 0, sizeof(INT32) * samples);
+ }
+
+ /* loop over voices */
+ for (v = 0; v <= m_active_voices; v++)
+ {
+ es550x_voice *voice = &m_voice[v];
+ UINT16 *base = m_region_base[voice->control >> 14];
+
+ /* special case: if end == start, stop the voice */
+ if (voice->start == voice->end)
+ voice->control |= CONTROL_STOP0;
+
+ int voice_channel = (voice->control & CONTROL_CAMASK) >> 10;
+ int channel = voice_channel % m_channels;
+ int l = channel << 1;
+ int r = l + 1;
+ INT32 *left = outputs[l] + offset;
+ INT32 *right = outputs[r] + offset;
+
+ /* generate from the appropriate source */
+ if (!base)
+ {
+ logerror("es5506: NULL region base %d\n",voice->control >> 14);
+ generate_dummy(voice, base, left, right, samples);
+ }
+ else if (voice->control & 0x2000)
+ generate_ulaw(voice, base, left, right, samples);
+ else
+ generate_pcm(voice, base, left, right, samples);
+
+ /* does this voice have it's IRQ bit raised? */
+ if (voice->control&CONTROL_IRQ)
+ {
+ logerror("es5506: IRQ raised on voice %d!!\n",v);
+
+ /* only update voice vector if existing IRQ is acked by host */
+ if (m_irqv&0x80)
+ {
+ /* latch voice number into vector, and set high bit low */
+ m_irqv=v&0x7f;
+
+ /* take down IRQ bit on voice */
+ voice->control&=~CONTROL_IRQ;
+
+ /* inform host of irq */
+ update_irq_state();
+ }
+ }
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ reg_write -- handle a write to the selected ES5506 register
+
+***********************************************************************************************/
+
+inline void es5506_device::reg_write_low(es550x_voice *voice, offs_t offset, UINT32 data)
+{
+ switch (offset)
+ {
+ case 0x00/8: /* CR */
+ voice->control = data & 0xffff;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, control=%04x\n", m_current_page & 0x1f, voice->control);
+ break;
+
+ case 0x08/8: /* FC */
+ voice->freqcount = data & 0x1ffff;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, freq count=%08x\n", m_current_page & 0x1f, voice->freqcount);
+ break;
+
+ case 0x10/8: /* LVOL */
+ voice->lvol = data & 0xffff;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, left vol=%04x\n", m_current_page & 0x1f, voice->lvol);
+ break;
+
+ case 0x18/8: /* LVRAMP */
+ voice->lvramp = (data & 0xff00) >> 8;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, left vol ramp=%04x\n", m_current_page & 0x1f, voice->lvramp);
+ break;
+
+ case 0x20/8: /* RVOL */
+ voice->rvol = data & 0xffff;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, right vol=%04x\n", m_current_page & 0x1f, voice->rvol);
+ break;
+
+ case 0x28/8: /* RVRAMP */
+ voice->rvramp = (data & 0xff00) >> 8;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, right vol ramp=%04x\n", m_current_page & 0x1f, voice->rvramp);
+ break;
+
+ case 0x30/8: /* ECOUNT */
+ voice->ecount = data & 0x1ff;
+ voice->filtcount = 0;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, envelope count=%04x\n", m_current_page & 0x1f, voice->ecount);
+ break;
+
+ case 0x38/8: /* K2 */
+ voice->k2 = data & 0xffff;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, K2=%04x\n", m_current_page & 0x1f, voice->k2);
+ break;
+
+ case 0x40/8: /* K2RAMP */
+ voice->k2ramp = ((data & 0xff00) >> 8) | ((data & 0x0001) << 31);
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, K2 ramp=%04x\n", m_current_page & 0x1f, voice->k2ramp);
+ break;
+
+ case 0x48/8: /* K1 */
+ voice->k1 = data & 0xffff;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, K1=%04x\n", m_current_page & 0x1f, voice->k1);
+ break;
+
+ case 0x50/8: /* K1RAMP */
+ voice->k1ramp = ((data & 0xff00) >> 8) | ((data & 0x0001) << 31);
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, K1 ramp=%04x\n", m_current_page & 0x1f, voice->k1ramp);
+ break;
+
+ case 0x58/8: /* ACTV */
+ {
+ m_active_voices = data & 0x1f;
+ m_sample_rate = m_master_clock / (16 * (m_active_voices + 1));
+ m_stream->set_sample_rate(m_sample_rate);
+
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "active voices=%d, sample_rate=%d\n", m_active_voices, m_sample_rate);
+ break;
+ }
+
+ case 0x60/8: /* MODE */
+ m_mode = data & 0x1f;
+ break;
+
+ case 0x68/8: /* PAR - read only */
+ case 0x70/8: /* IRQV - read only */
+ break;
+
+ case 0x78/8: /* PAGE */
+ m_current_page = data & 0x7f;
+ break;
+ }
+}
+
+inline void es5506_device::reg_write_high(es550x_voice *voice, offs_t offset, UINT32 data)
+{
+ switch (offset)
+ {
+ case 0x00/8: /* CR */
+ voice->control = data & 0xffff;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, control=%04x\n", m_current_page & 0x1f, voice->control);
+ break;
+
+ case 0x08/8: /* START */
+ voice->start = data & 0xfffff800;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, loop start=%08x\n", m_current_page & 0x1f, voice->start);
+ break;
+
+ case 0x10/8: /* END */
+ voice->end = data & 0xffffff80;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, loop end=%08x\n", m_current_page & 0x1f, voice->end);
+ break;
+
+ case 0x18/8: /* ACCUM */
+ voice->accum = data;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, accum=%08x\n", m_current_page & 0x1f, voice->accum);
+ break;
+
+ case 0x20/8: /* O4(n-1) */
+ voice->o4n1 = (INT32)(data << 14) >> 14;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, O4(n-1)=%05x\n", m_current_page & 0x1f, voice->o4n1 & 0x3ffff);
+ break;
+
+ case 0x28/8: /* O3(n-1) */
+ voice->o3n1 = (INT32)(data << 14) >> 14;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, O3(n-1)=%05x\n", m_current_page & 0x1f, voice->o3n1 & 0x3ffff);
+ break;
+
+ case 0x30/8: /* O3(n-2) */
+ voice->o3n2 = (INT32)(data << 14) >> 14;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, O3(n-2)=%05x\n", m_current_page & 0x1f, voice->o3n2 & 0x3ffff);
+ break;
+
+ case 0x38/8: /* O2(n-1) */
+ voice->o2n1 = (INT32)(data << 14) >> 14;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, O2(n-1)=%05x\n", m_current_page & 0x1f, voice->o2n1 & 0x3ffff);
+ break;
+
+ case 0x40/8: /* O2(n-2) */
+ voice->o2n2 = (INT32)(data << 14) >> 14;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, O2(n-2)=%05x\n", m_current_page & 0x1f, voice->o2n2 & 0x3ffff);
+ break;
+
+ case 0x48/8: /* O1(n-1) */
+ voice->o1n1 = (INT32)(data << 14) >> 14;
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "voice %d, O1(n-1)=%05x\n", m_current_page & 0x1f, voice->o1n1 & 0x3ffff);
+ break;
+
+ case 0x50/8: /* W_ST */
+ m_wst = data & 0x7f;
+ break;
+
+ case 0x58/8: /* W_END */
+ m_wend = data & 0x7f;
+ break;
+
+ case 0x60/8: /* LR_END */
+ m_lrend = data & 0x7f;
+ break;
+
+ case 0x68/8: /* PAR - read only */
+ case 0x70/8: /* IRQV - read only */
+ break;
+
+ case 0x78/8: /* PAGE */
+ m_current_page = data & 0x7f;
+ break;
+ }
+}
+
+inline void es5506_device::reg_write_test(es550x_voice *voice, offs_t offset, UINT32 data)
+{
+ switch (offset)
+ {
+ case 0x00/8: /* CHANNEL 0 LEFT */
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "Channel 0 left test write %08x\n", data);
+ break;
+
+ case 0x08/8: /* CHANNEL 0 RIGHT */
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "Channel 0 right test write %08x\n", data);
+ break;
+
+ case 0x10/8: /* CHANNEL 1 LEFT */
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "Channel 1 left test write %08x\n", data);
+ break;
+
+ case 0x18/8: /* CHANNEL 1 RIGHT */
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "Channel 1 right test write %08x\n", data);
+ break;
+
+ case 0x20/8: /* CHANNEL 2 LEFT */
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "Channel 2 left test write %08x\n", data);
+ break;
+
+ case 0x28/8: /* CHANNEL 2 RIGHT */
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "Channel 2 right test write %08x\n", data);
+ break;
+
+ case 0x30/8: /* CHANNEL 3 LEFT */
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "Channel 3 left test write %08x\n", data);
+ break;
+
+ case 0x38/8: /* CHANNEL 3 RIGHT */
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "Channel 3 right test write %08x\n", data);
+ break;
+
+ case 0x40/8: /* CHANNEL 4 LEFT */
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "Channel 4 left test write %08x\n", data);
+ break;
+
+ case 0x48/8: /* CHANNEL 4 RIGHT */
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "Channel 4 right test write %08x\n", data);
+ break;
+
+ case 0x50/8: /* CHANNEL 5 LEFT */
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "Channel 5 left test write %08x\n", data);
+ break;
+
+ case 0x58/8: /* CHANNEL 6 RIGHT */
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "Channel 5 right test write %08x\n", data);
+ break;
+
+ case 0x60/8: /* EMPTY */
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "Test write EMPTY %08x\n", data);
+ break;
+
+ case 0x68/8: /* PAR - read only */
+ case 0x70/8: /* IRQV - read only */
+ break;
+
+ case 0x78/8: /* PAGE */
+ m_current_page = data & 0x7f;
+ break;
+ }
+}
+
+WRITE8_MEMBER( es5506_device::write )
+{
+ es550x_voice *voice = &m_voice[m_current_page & 0x1f];
+ int shift = 8 * (offset & 3);
+
+ /* accumulate the data */
+ m_write_latch = (m_write_latch & ~(0xff000000 >> shift)) | (data << (24 - shift));
+
+ /* wait for a write to complete */
+ if (shift != 24)
+ return;
+
+ /* force an update */
+ m_stream->update();
+
+ /* switch off the page and register */
+ if (m_current_page < 0x20)
+ reg_write_low(voice, offset / 4, m_write_latch);
+ else if (m_current_page < 0x40)
+ reg_write_high(voice, offset / 4, m_write_latch);
+ else
+ reg_write_test(voice, offset / 4, m_write_latch);
+
+ /* clear the write latch when done */
+ m_write_latch = 0;
+}
+
+
+
+/**********************************************************************************************
+
+ reg_read -- read from the specified ES5506 register
+
+***********************************************************************************************/
+
+inline UINT32 es5506_device::reg_read_low(es550x_voice *voice, offs_t offset)
+{
+ UINT32 result = 0;
+
+ switch (offset)
+ {
+ case 0x00/8: /* CR */
+ result = voice->control;
+ break;
+
+ case 0x08/8: /* FC */
+ result = voice->freqcount;
+ break;
+
+ case 0x10/8: /* LVOL */
+ result = voice->lvol;
+ break;
+
+ case 0x18/8: /* LVRAMP */
+ result = voice->lvramp << 8;
+ break;
+
+ case 0x20/8: /* RVOL */
+ result = voice->rvol;
+ break;
+
+ case 0x28/8: /* RVRAMP */
+ result = voice->rvramp << 8;
+ break;
+
+ case 0x30/8: /* ECOUNT */
+ result = voice->ecount;
+ break;
+
+ case 0x38/8: /* K2 */
+ result = voice->k2;
+ break;
+
+ case 0x40/8: /* K2RAMP */
+ result = (voice->k2ramp << 8) | (voice->k2ramp >> 31);
+ break;
+
+ case 0x48/8: /* K1 */
+ result = voice->k1;
+ break;
+
+ case 0x50/8: /* K1RAMP */
+ result = (voice->k1ramp << 8) | (voice->k1ramp >> 31);
+ break;
+
+ case 0x58/8: /* ACTV */
+ result = m_active_voices;
+ break;
+
+ case 0x60/8: /* MODE */
+ result = m_mode;
+ break;
+
+ case 0x68/8: /* PAR */
+ if (!m_read_port_cb.isnull())
+ result = m_read_port_cb(0);
+ break;
+
+ case 0x70/8: /* IRQV */
+ result = m_irqv;
+ update_internal_irq_state();
+ break;
+
+ case 0x78/8: /* PAGE */
+ result = m_current_page;
+ break;
+ }
+ return result;
+}
+
+
+inline UINT32 es5506_device::reg_read_high(es550x_voice *voice, offs_t offset)
+{
+ UINT32 result = 0;
+
+ switch (offset)
+ {
+ case 0x00/8: /* CR */
+ result = voice->control;
+ break;
+
+ case 0x08/8: /* START */
+ result = voice->start;
+ break;
+
+ case 0x10/8: /* END */
+ result = voice->end;
+ break;
+
+ case 0x18/8: /* ACCUM */
+ result = voice->accum;
+ break;
+
+ case 0x20/8: /* O4(n-1) */
+ result = voice->o4n1 & 0x3ffff;
+ break;
+
+ case 0x28/8: /* O3(n-1) */
+ result = voice->o3n1 & 0x3ffff;
+ break;
+
+ case 0x30/8: /* O3(n-2) */
+ result = voice->o3n2 & 0x3ffff;
+ break;
+
+ case 0x38/8: /* O2(n-1) */
+ result = voice->o2n1 & 0x3ffff;
+ break;
+
+ case 0x40/8: /* O2(n-2) */
+ result = voice->o2n2 & 0x3ffff;
+ break;
+
+ case 0x48/8: /* O1(n-1) */
+ result = voice->o1n1 & 0x3ffff;
+ break;
+
+ case 0x50/8: /* W_ST */
+ result = m_wst;
+ break;
+
+ case 0x58/8: /* W_END */
+ result = m_wend;
+ break;
+
+ case 0x60/8: /* LR_END */
+ result = m_lrend;
+ break;
+
+ case 0x68/8: /* PAR */
+ if (!m_read_port_cb.isnull())
+ result = m_read_port_cb(0);
+ break;
+
+ case 0x70/8: /* IRQV */
+ result = m_irqv;
+ update_internal_irq_state();
+ break;
+
+ case 0x78/8: /* PAGE */
+ result = m_current_page;
+ break;
+ }
+ return result;
+}
+inline UINT32 es5506_device::reg_read_test(es550x_voice *voice, offs_t offset)
+{
+ UINT32 result = 0;
+
+ switch (offset)
+ {
+ case 0x68/8: /* PAR */
+ if (!m_read_port_cb.isnull())
+ result = m_read_port_cb(0);
+ break;
+
+ case 0x70/8: /* IRQV */
+ result = m_irqv;
+ break;
+
+ case 0x78/8: /* PAGE */
+ result = m_current_page;
+ break;
+ }
+ return result;
+}
+
+READ8_MEMBER( es5506_device::read )
+{
+ es550x_voice *voice = &m_voice[m_current_page & 0x1f];
+ int shift = 8 * (offset & 3);
+
+ /* only read on offset 0 */
+ if (shift != 0)
+ return m_read_latch >> (24 - shift);
+
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "read from %02x/%02x -> ", m_current_page, offset / 4 * 8);
+
+ /* force an update */
+ m_stream->update();
+
+ /* switch off the page and register */
+ if (m_current_page < 0x20)
+ m_read_latch = reg_read_low(voice, offset / 4);
+ else if (m_current_page < 0x40)
+ m_read_latch = reg_read_high(voice, offset / 4);
+ else
+ m_read_latch = reg_read_test(voice, offset / 4);
+
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%08x\n", m_read_latch);
+
+ /* return the high byte */
+ return m_read_latch >> 24;
+}
+
+
+
+void es5506_device::voice_bank_w(int voice, int bank)
+{
+ m_voice[voice].exbank=bank;
+}
+
+
+/**********************************************************************************************
+
+ reg_write -- handle a write to the selected ES5505 register
+
+***********************************************************************************************/
+
+inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, UINT16 data, UINT16 mem_mask)
+{
+ switch (offset)
+ {
+ case 0x00: /* CR */
+ if (ACCESSING_BITS_0_7)
+ {
+#if RAINE_CHECK
+ voice->control &= ~(CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_DIR);
+#else
+ voice->control &= ~(CONTROL_STOPMASK | CONTROL_BS0 | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ);
+#endif
+ voice->control |= (data & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) |
+ ((data << 12) & CONTROL_BS0);
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ voice->control &= ~(CONTROL_CA0 | CONTROL_CA1 | CONTROL_LPMASK);
+ voice->control |= ((data >> 2) & CONTROL_LPMASK) |
+ ((data << 2) & (CONTROL_CA0 | CONTROL_CA1));
+ }
+
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, control=%04x (raw=%04x & %04x)\n", machine().describe_context(), m_current_page & 0x1f, voice->control, data, mem_mask ^ 0xffff);
+ break;
+
+ case 0x01: /* FC */
+ if (ACCESSING_BITS_0_7)
+ voice->freqcount = (voice->freqcount & ~0x001fe) | ((data & 0x00ff) << 1);
+ if (ACCESSING_BITS_8_15)
+ voice->freqcount = (voice->freqcount & ~0x1fe00) | ((data & 0xff00) << 1);
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, freq count=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->freqcount);
+ break;
+
+ case 0x02: /* STRT (hi) */
+ if (ACCESSING_BITS_0_7)
+ voice->start = (voice->start & ~0x03fc0000) | ((data & 0x00ff) << 18);
+ if (ACCESSING_BITS_8_15)
+ voice->start = (voice->start & ~0x7c000000) | ((data & 0x1f00) << 18);
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, loop start=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->start);
+ break;
+
+ case 0x03: /* STRT (lo) */
+ if (ACCESSING_BITS_0_7)
+ voice->start = (voice->start & ~0x00000380) | ((data & 0x00e0) << 2);
+ if (ACCESSING_BITS_8_15)
+ voice->start = (voice->start & ~0x0003fc00) | ((data & 0xff00) << 2);
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, loop start=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->start);
+ break;
+
+ case 0x04: /* END (hi) */
+ if (ACCESSING_BITS_0_7)
+ voice->end = (voice->end & ~0x03fc0000) | ((data & 0x00ff) << 18);
+ if (ACCESSING_BITS_8_15)
+ voice->end = (voice->end & ~0x7c000000) | ((data & 0x1f00) << 18);
+#if RAINE_CHECK
+ voice->control |= CONTROL_STOP0;
+#endif
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, loop end=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->end);
+ break;
+
+ case 0x05: /* END (lo) */
+ if (ACCESSING_BITS_0_7)
+ voice->end = (voice->end & ~0x00000380) | ((data & 0x00e0) << 2);
+ if (ACCESSING_BITS_8_15)
+ voice->end = (voice->end & ~0x0003fc00) | ((data & 0xff00) << 2);
+#if RAINE_CHECK
+ voice->control |= CONTROL_STOP0;
+#endif
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, loop end=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->end);
+ break;
+
+ case 0x06: /* K2 */
+ if (ACCESSING_BITS_0_7)
+ voice->k2 = (voice->k2 & ~0x00f0) | (data & 0x00f0);
+ if (ACCESSING_BITS_8_15)
+ voice->k2 = (voice->k2 & ~0xff00) | (data & 0xff00);
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, K2=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->k2);
+ break;
+
+ case 0x07: /* K1 */
+ if (ACCESSING_BITS_0_7)
+ voice->k1 = (voice->k1 & ~0x00f0) | (data & 0x00f0);
+ if (ACCESSING_BITS_8_15)
+ voice->k1 = (voice->k1 & ~0xff00) | (data & 0xff00);
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, K1=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->k1);
+ break;
+
+ case 0x08: /* LVOL */
+ if (ACCESSING_BITS_8_15)
+ voice->lvol = (voice->lvol & ~0xff00) | (data & 0xff00);
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, left vol=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->lvol);
+ break;
+
+ case 0x09: /* RVOL */
+ if (ACCESSING_BITS_8_15)
+ voice->rvol = (voice->rvol & ~0xff00) | (data & 0xff00);
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, right vol=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->rvol);
+ break;
+
+ case 0x0a: /* ACC (hi) */
+ if (ACCESSING_BITS_0_7)
+ voice->accum = (voice->accum & ~0x03fc0000) | ((data & 0x00ff) << 18);
+ if (ACCESSING_BITS_8_15)
+ voice->accum = (voice->accum & ~0x7c000000) | ((data & 0x1f00) << 18);
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, accum=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->accum);
+ break;
+
+ case 0x0b: /* ACC (lo) */
+ if (ACCESSING_BITS_0_7)
+ voice->accum = (voice->accum & ~0x000003fc) | ((data & 0x00ff) << 2);
+ if (ACCESSING_BITS_8_15)
+ voice->accum = (voice->accum & ~0x0003fc00) | ((data & 0xff00) << 2);
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, accum=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->accum);
+ break;
+
+ case 0x0c: /* unused */
+ break;
+
+ case 0x0d: /* ACT */
+ if (ACCESSING_BITS_0_7)
+ {
+ m_active_voices = data & 0x1f;
+ m_sample_rate = m_master_clock / (16 * (m_active_voices + 1));
+ m_stream->set_sample_rate(m_sample_rate);
+
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "active voices=%d, sample_rate=%d\n", m_active_voices, m_sample_rate);
+ }
+ break;
+
+ case 0x0e: /* IRQV - read only */
+ break;
+
+ case 0x0f: /* PAGE */
+ if (ACCESSING_BITS_0_7)
+ m_current_page = data & 0x7f;
+ break;
+ }
+}
+
+
+inline void es5505_device::reg_write_high(es550x_voice *voice, offs_t offset, UINT16 data, UINT16 mem_mask)
+{
+ switch (offset)
+ {
+ case 0x00: /* CR */
+ if (ACCESSING_BITS_0_7)
+ {
+ voice->control &= ~(CONTROL_STOPMASK | CONTROL_BS0 | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ);
+ voice->control |= (data & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) |
+ ((data << 12) & CONTROL_BS0);
+ }
+ if (ACCESSING_BITS_8_15)
+ {
+ voice->control &= ~(CONTROL_CA0 | CONTROL_CA1 | CONTROL_LPMASK);
+ voice->control |= ((data >> 2) & CONTROL_LPMASK) |
+ ((data << 2) & (CONTROL_CA0 | CONTROL_CA1));
+ }
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, control=%04x (raw=%04x & %04x)\n", machine().describe_context(), m_current_page & 0x1f, voice->control, data, mem_mask);
+ break;
+
+ case 0x01: /* O4(n-1) */
+ if (ACCESSING_BITS_0_7)
+ voice->o4n1 = (voice->o4n1 & ~0x00ff) | (data & 0x00ff);
+ if (ACCESSING_BITS_8_15)
+ voice->o4n1 = (INT16)((voice->o4n1 & ~0xff00) | (data & 0xff00));
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, O4(n-1)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o4n1 & 0x3ffff);
+ break;
+
+ case 0x02: /* O3(n-1) */
+ if (ACCESSING_BITS_0_7)
+ voice->o3n1 = (voice->o3n1 & ~0x00ff) | (data & 0x00ff);
+ if (ACCESSING_BITS_8_15)
+ voice->o3n1 = (INT16)((voice->o3n1 & ~0xff00) | (data & 0xff00));
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, O3(n-1)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o3n1 & 0x3ffff);
+ break;
+
+ case 0x03: /* O3(n-2) */
+ if (ACCESSING_BITS_0_7)
+ voice->o3n2 = (voice->o3n2 & ~0x00ff) | (data & 0x00ff);
+ if (ACCESSING_BITS_8_15)
+ voice->o3n2 = (INT16)((voice->o3n2 & ~0xff00) | (data & 0xff00));
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, O3(n-2)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o3n2 & 0x3ffff);
+ break;
+
+ case 0x04: /* O2(n-1) */
+ if (ACCESSING_BITS_0_7)
+ voice->o2n1 = (voice->o2n1 & ~0x00ff) | (data & 0x00ff);
+ if (ACCESSING_BITS_8_15)
+ voice->o2n1 = (INT16)((voice->o2n1 & ~0xff00) | (data & 0xff00));
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, O2(n-1)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o2n1 & 0x3ffff);
+ break;
+
+ case 0x05: /* O2(n-2) */
+ if (ACCESSING_BITS_0_7)
+ voice->o2n2 = (voice->o2n2 & ~0x00ff) | (data & 0x00ff);
+ if (ACCESSING_BITS_8_15)
+ voice->o2n2 = (INT16)((voice->o2n2 & ~0xff00) | (data & 0xff00));
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, O2(n-2)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o2n2 & 0x3ffff);
+ break;
+
+ case 0x06: /* O1(n-1) */
+ if (ACCESSING_BITS_0_7)
+ voice->o1n1 = (voice->o1n1 & ~0x00ff) | (data & 0x00ff);
+ if (ACCESSING_BITS_8_15)
+ voice->o1n1 = (INT16)((voice->o1n1 & ~0xff00) | (data & 0xff00));
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%s:voice %d, O1(n-1)=%05x (accum=%08x)\n", machine().describe_context(), m_current_page & 0x1f, voice->o2n1 & 0x3ffff, voice->accum);
+ break;
+
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c: /* unused */
+ break;
+
+ case 0x0d: /* ACT */
+ if (ACCESSING_BITS_0_7)
+ {
+ m_active_voices = data & 0x1f;
+ m_sample_rate = m_master_clock / (16 * (m_active_voices + 1));
+ m_stream->set_sample_rate(m_sample_rate);
+
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "active voices=%d, sample_rate=%d\n", m_active_voices, m_sample_rate);
+ }
+ break;
+
+ case 0x0e: /* IRQV - read only */
+ break;
+
+ case 0x0f: /* PAGE */
+ if (ACCESSING_BITS_0_7)
+ m_current_page = data & 0x7f;
+ break;
+ }
+}
+
+
+inline void es5505_device::reg_write_test(es550x_voice *voice, offs_t offset, UINT16 data, UINT16 mem_mask)
+{
+ switch (offset)
+ {
+ case 0x00: /* CH0L */
+ case 0x01: /* CH0R */
+ case 0x02: /* CH1L */
+ case 0x03: /* CH1R */
+ case 0x04: /* CH2L */
+ case 0x05: /* CH2R */
+ case 0x06: /* CH3L */
+ case 0x07: /* CH3R */
+ break;
+
+ case 0x08: /* SERMODE */
+ m_mode = data & 0x0007;
+ break;
+
+ case 0x09: /* PAR */
+ break;
+
+ case 0x0d: /* ACT */
+ if (ACCESSING_BITS_0_7)
+ {
+ m_active_voices = data & 0x1f;
+ m_sample_rate = m_master_clock / (16 * (m_active_voices + 1));
+ m_stream->set_sample_rate(m_sample_rate);
+
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "active voices=%d, sample_rate=%d\n", m_active_voices, m_sample_rate);
+ }
+ break;
+
+ case 0x0e: /* IRQV - read only */
+ break;
+
+ case 0x0f: /* PAGE */
+ if (ACCESSING_BITS_0_7)
+ m_current_page = data & 0x7f;
+ break;
+ }
+}
+
+
+WRITE16_MEMBER( es5505_device::write )
+{
+ es550x_voice *voice = &m_voice[m_current_page & 0x1f];
+
+// logerror("%s:ES5505 write %02x/%02x = %04x & %04x\n", machine().describe_context(), m_current_page, offset, data, mem_mask);
+
+ /* force an update */
+ m_stream->update();
+
+ /* switch off the page and register */
+ if (m_current_page < 0x20)
+ reg_write_low(voice, offset, data, mem_mask);
+ else if (m_current_page < 0x40)
+ reg_write_high(voice, offset, data, mem_mask);
+ else
+ reg_write_test(voice, offset, data, mem_mask);
+}
+
+
+
+/**********************************************************************************************
+
+ reg_read -- read from the specified ES5505 register
+
+***********************************************************************************************/
+
+inline UINT16 es5505_device::reg_read_low(es550x_voice *voice, offs_t offset)
+{
+ UINT16 result = 0;
+
+ switch (offset)
+ {
+ case 0x00: /* CR */
+ result = (voice->control & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) |
+ ((voice->control & CONTROL_BS0) >> 12) |
+ ((voice->control & CONTROL_LPMASK) << 2) |
+ ((voice->control & (CONTROL_CA0 | CONTROL_CA1)) >> 2) |
+ 0xf000;
+ break;
+
+ case 0x01: /* FC */
+ result = voice->freqcount >> 1;
+ break;
+
+ case 0x02: /* STRT (hi) */
+ result = voice->start >> 18;
+ break;
+
+ case 0x03: /* STRT (lo) */
+ result = voice->start >> 2;
+ break;
+
+ case 0x04: /* END (hi) */
+ result = voice->end >> 18;
+ break;
+
+ case 0x05: /* END (lo) */
+ result = voice->end >> 2;
+ break;
+
+ case 0x06: /* K2 */
+ result = voice->k2;
+ break;
+
+ case 0x07: /* K1 */
+ result = voice->k1;
+ break;
+
+ case 0x08: /* LVOL */
+ result = voice->lvol;
+ break;
+
+ case 0x09: /* RVOL */
+ result = voice->rvol;
+ break;
+
+ case 0x0a: /* ACC (hi) */
+ result = voice->accum >> 18;
+ break;
+
+ case 0x0b: /* ACC (lo) */
+ result = voice->accum >> 2;
+ break;
+
+ case 0x0c: /* unused */
+ break;
+
+ case 0x0d: /* ACT */
+ result = m_active_voices;
+ break;
+
+ case 0x0e: /* IRQV */
+ result = m_irqv;
+ update_internal_irq_state();
+ break;
+
+ case 0x0f: /* PAGE */
+ result = m_current_page;
+ break;
+ }
+ return result;
+}
+
+
+inline UINT16 es5505_device::reg_read_high(es550x_voice *voice, offs_t offset)
+{
+ UINT16 result = 0;
+
+ switch (offset)
+ {
+ case 0x00: /* CR */
+ result = (voice->control & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) |
+ ((voice->control & CONTROL_BS0) >> 12) |
+ ((voice->control & CONTROL_LPMASK) << 2) |
+ ((voice->control & (CONTROL_CA0 | CONTROL_CA1)) >> 2) |
+ 0xf000;
+ break;
+
+ case 0x01: /* O4(n-1) */
+ result = voice->o4n1;
+ break;
+
+ case 0x02: /* O3(n-1) */
+ result = voice->o3n1;
+ break;
+
+ case 0x03: /* O3(n-2) */
+ result = voice->o3n2;
+ break;
+
+ case 0x04: /* O2(n-1) */
+ result = voice->o2n1;
+ break;
+
+ case 0x05: /* O2(n-2) */
+ result = voice->o2n2;
+ break;
+
+ case 0x06: /* O1(n-1) */
+ /* special case for the Taito F3 games: they set the accumulator on a stopped */
+ /* voice and assume the filters continue to process the data. They then read */
+ /* the O1(n-1) in order to extract raw data from the sound ROMs. Since we don't */
+ /* want to waste time filtering stopped channels, we just look for a read from */
+ /* this register on a stopped voice, and return the raw sample data at the */
+ /* accumulator */
+ if ((voice->control & CONTROL_STOPMASK) && m_region_base[voice->control >> 14])
+ {
+ voice->o1n1 = m_region_base[voice->control >> 14][voice->exbank + (voice->accum >> 11)];
+ // logerror("%02x %08x ==> %08x\n",voice->o1n1,voice->control >> 14,voice->exbank + (voice->accum >> 11));
+ }
+ result = voice->o1n1;
+ break;
+
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c: /* unused */
+ break;
+
+ case 0x0d: /* ACT */
+ result = m_active_voices;
+ break;
+
+ case 0x0e: /* IRQV */
+ result = m_irqv;
+ update_internal_irq_state();
+ break;
+
+ case 0x0f: /* PAGE */
+ result = m_current_page;
+ break;
+ }
+ return result;
+}
+
+
+inline UINT16 es5505_device::reg_read_test(es550x_voice *voice, offs_t offset)
+{
+ UINT16 result = 0;
+
+ switch (offset)
+ {
+ case 0x00: /* CH0L */
+ case 0x01: /* CH0R */
+ case 0x02: /* CH1L */
+ case 0x03: /* CH1R */
+ case 0x04: /* CH2L */
+ case 0x05: /* CH2R */
+ case 0x06: /* CH3L */
+ case 0x07: /* CH3R */
+ break;
+
+ case 0x08: /* SERMODE */
+ result = m_mode;
+ break;
+
+ case 0x09: /* PAR */
+ if (!m_read_port_cb.isnull())
+ result = m_read_port_cb(0);
+ break;
+
+ case 0x0f: /* PAGE */
+ result = m_current_page;
+ break;
+ }
+ return result;
+}
+
+
+READ16_MEMBER( es5505_device::read )
+{
+ es550x_voice *voice = &m_voice[m_current_page & 0x1f];
+ UINT16 result = 0;
+
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "read from %02x/%02x -> ", m_current_page, offset);
+
+ /* force an update */
+ m_stream->update();
+
+ /* switch off the page and register */
+ if (m_current_page < 0x20)
+ result = reg_read_low(voice, offset);
+ else if (m_current_page < 0x40)
+ result = reg_read_high(voice, offset);
+ else
+ result = reg_read_test(voice, offset);
+
+ if (LOG_COMMANDS && m_eslog)
+ fprintf(m_eslog, "%04x (accum=%08x)\n", result, voice->accum);
+
+ /* return the high byte */
+ return result;
+}
+
+
+
+void es5505_device::voice_bank_w(int voice, int bank)
+{
+#if RAINE_CHECK
+ m_voice[voice].control = CONTROL_STOPMASK;
+#endif
+ m_voice[voice].exbank=bank;
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void es550x_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+}
+
+void es5506_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+#if MAKE_WAVS
+ /* start the logging once we have a sample rate */
+ if (m_sample_rate)
+ {
+ if (!m_wavraw)
+ m_wavraw = wav_open("raw.wav", m_sample_rate, 2);
+ }
+#endif
+
+ /* loop until all samples are output */
+ int offset = 0;
+ while (samples)
+ {
+ int length = (samples > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : samples;
+
+ generate_samples(outputs, offset, length);
+
+#if MAKE_WAVS
+ /* log the raw data */
+ if (m_wavraw) {
+ /* determine left/right source data */
+ INT32 *lsrc = m_scratch, *rsrc = m_scratch + length;
+ int channel;
+ memset(lsrc, 0, sizeof(INT32) * length * 2);
+ /* loop over the output channels */
+ for (channel = 0; channel < m_channels; channel++) {
+ INT32 *l = outputs[(channel << 1)] + offset;
+ INT32 *r = outputs[(channel << 1) + 1] + offset;
+ /* add the current channel's samples to the WAV data */
+ for (samp = 0; samp < length; samp++) {
+ lsrc[samp] += l[samp];
+ rsrc[samp] += r[samp];
+ }
+ }
+ wav_add_data_32lr(m_wavraw, lsrc, rsrc, length, 4);
+ }
+#endif
+
+ /* account for these samples */
+ offset += length;
+ samples -= length;
+ }
+}
+
+void es5505_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+#if MAKE_WAVS
+ /* start the logging once we have a sample rate */
+ if (m_sample_rate)
+ {
+ if (!m_wavraw)
+ m_wavraw = wav_open("raw.wav", m_sample_rate, 2);
+ }
+#endif
+
+ /* loop until all samples are output */
+ int offset = 0;
+ while (samples)
+ {
+ int length = (samples > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : samples;
+
+ generate_samples(outputs, offset, length);
+
+#if MAKE_WAVS
+ /* log the raw data */
+ if (m_wavraw) {
+ /* determine left/right source data */
+ INT32 *lsrc = m_scratch, *rsrc = m_scratch + length;
+ int channel;
+ memset(lsrc, 0, sizeof(INT32) * length * 2);
+ /* loop over the output channels */
+ for (channel = 0; channel < m_channels; channel++) {
+ INT32 *l = outputs[(channel << 1)] + offset;
+ INT32 *r = outputs[(channel << 1) + 1] + offset;
+ /* add the current channel's samples to the WAV data */
+ for (samp = 0; samp < length; samp++) {
+ lsrc[samp] += l[samp];
+ rsrc[samp] += r[samp];
+ }
+ }
+ wav_add_data_32lr(m_wavraw, lsrc, rsrc, length, 4);
+ }
+#endif
+
+ /* account for these samples */
+ offset += length;
+ samples -= length;
+ }
+}
diff --git a/src/devices/sound/es5506.h b/src/devices/sound/es5506.h
new file mode 100644
index 00000000000..c434ec8b12e
--- /dev/null
+++ b/src/devices/sound/es5506.h
@@ -0,0 +1,248 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/**********************************************************************************************
+ *
+ * Ensoniq ES5505/6 driver
+ * by Aaron Giles
+ *
+ **********************************************************************************************/
+
+#pragma once
+
+#ifndef __ES5506_H__
+#define __ES5506_H__
+
+#define MAKE_WAVS 0
+
+#define MCFG_ES5506_REGION0(_region) \
+ es5506_device::set_region0(*device, _region);
+
+#define MCFG_ES5506_REGION1(_region) \
+ es5506_device::set_region1(*device, _region);
+
+#define MCFG_ES5506_REGION2(_region) \
+ es5506_device::set_region2(*device, _region);
+
+#define MCFG_ES5506_REGION3(_region) \
+ es5506_device::set_region3(*device, _region);
+
+#define MCFG_ES5506_CHANNELS(_chan) \
+ es5506_device::set_channels(*device, _chan);
+
+#define MCFG_ES5506_IRQ_CB(_devcb) \
+ devcb = &es5506_device::set_irq_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_ES5506_READ_PORT_CB(_devcb) \
+ devcb = &es5506_device::set_read_port_callback(*device, DEVCB_##_devcb);
+
+
+#define MCFG_ES5505_REGION0(_region) \
+ es5505_device::set_region0(*device, _region);
+
+#define MCFG_ES5505_REGION1(_region) \
+ es5505_device::set_region1(*device, _region);
+
+#define MCFG_ES5505_CHANNELS(_chan) \
+ es5505_device::set_channels(*device, _chan);
+
+#define MCFG_ES5505_IRQ_CB(_devcb) \
+ devcb = &es5505_device::set_irq_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_ES5505_READ_PORT_CB(_devcb) \
+ devcb = &es5505_device::set_read_port_callback(*device, DEVCB_##_devcb);
+
+
+/* struct describing a single playing voice */
+
+struct es550x_voice
+{
+ es550x_voice():
+ control(0),
+ freqcount(0),
+ start(0),
+ lvol(0),
+ end(0),
+ lvramp(0),
+ accum(0),
+ rvol(0),
+ rvramp(0),
+ ecount(0),
+ k2(0),
+ k2ramp(0),
+ k1(0),
+ k1ramp(0),
+ o4n1(0),
+ o3n1(0),
+ o3n2(0),
+ o2n1(0),
+ o2n2(0),
+ o1n1(0),
+ exbank(0),
+ index(0),
+ filtcount(0),
+ accum_mask(0) {}
+
+ /* external state */
+ UINT32 control; /* control register */
+ UINT32 freqcount; /* frequency count register */
+ UINT32 start; /* start register */
+ UINT32 lvol; /* left volume register */
+ UINT32 end; /* end register */
+ UINT32 lvramp; /* left volume ramp register */
+ UINT32 accum; /* accumulator register */
+ UINT32 rvol; /* right volume register */
+ UINT32 rvramp; /* right volume ramp register */
+ UINT32 ecount; /* envelope count register */
+ UINT32 k2; /* k2 register */
+ UINT32 k2ramp; /* k2 ramp register */
+ UINT32 k1; /* k1 register */
+ UINT32 k1ramp; /* k1 ramp register */
+ INT32 o4n1; /* filter storage O4(n-1) */
+ INT32 o3n1; /* filter storage O3(n-1) */
+ INT32 o3n2; /* filter storage O3(n-2) */
+ INT32 o2n1; /* filter storage O2(n-1) */
+ INT32 o2n2; /* filter storage O2(n-2) */
+ INT32 o1n1; /* filter storage O1(n-1) */
+ UINT32 exbank; /* external address bank */
+
+ /* internal state */
+ UINT8 index; /* index of this voice */
+ UINT8 filtcount; /* filter count */
+ UINT32 accum_mask;
+};
+
+class es550x_device : public device_t,
+ public device_sound_interface
+{
+public:
+ es550x_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ ~es550x_device() {}
+
+ static void set_region0(device_t &device, const char *region0) { downcast<es550x_device &>(device).m_region0 = region0; }
+ static void set_region1(device_t &device, const char *region1) { downcast<es550x_device &>(device).m_region1 = region1; }
+ static void set_region2(device_t &device, const char *region2) { downcast<es550x_device &>(device).m_region2 = region2; }
+ static void set_region3(device_t &device, const char *region3) { downcast<es550x_device &>(device).m_region3 = region3; }
+ static void set_channels(device_t &device, int channels) { downcast<es550x_device &>(device).m_channels = channels; }
+ template<class _Object> static devcb_base &set_irq_callback(device_t &device, _Object object) { return downcast<es550x_device &>(device).m_irq_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_read_port_callback(device_t &device, _Object object) { return downcast<es550x_device &>(device).m_read_port_cb.set_callback(object); }
+
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_stop();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ // internal state
+ sound_stream *m_stream; /* which stream are we using */
+ int m_sample_rate; /* current sample rate */
+ UINT16 * m_region_base[4]; /* pointer to the base of the region */
+ UINT32 m_write_latch; /* currently accumulated data for write */
+ UINT32 m_read_latch; /* currently accumulated data for read */
+ UINT32 m_master_clock; /* master clock frequency */
+
+ UINT8 m_current_page; /* current register page */
+ UINT8 m_active_voices; /* number of active voices */
+ UINT8 m_mode; /* MODE register */
+ UINT8 m_wst; /* W_ST register */
+ UINT8 m_wend; /* W_END register */
+ UINT8 m_lrend; /* LR_END register */
+ UINT8 m_irqv; /* IRQV register */
+
+ es550x_voice m_voice[32]; /* the 32 voices */
+
+ INT32 * m_scratch;
+
+ INT16 * m_ulaw_lookup;
+ UINT16 * m_volume_lookup;
+
+ #if MAKE_WAVS
+ void * m_wavraw; /* raw waveform */
+ #endif
+
+ FILE *m_eslog;
+
+ const char * m_region0; /* memory region where the sample ROM lives */
+ const char * m_region1; /* memory region where the sample ROM lives */
+ const char * m_region2; /* memory region where the sample ROM lives */
+ const char * m_region3; /* memory region where the sample ROM lives */
+ int m_channels; /* number of output channels: 1 .. 6 */
+ devcb_write_line m_irq_cb; /* irq callback */
+ devcb_read16 m_read_port_cb; /* input port read */
+
+ void update_irq_state();
+ void update_internal_irq_state();
+ void compute_tables();
+
+ void generate_dummy(es550x_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples);
+ void generate_ulaw(es550x_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples);
+ void generate_pcm(es550x_voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples);
+};
+
+
+class es5506_device : public es550x_device
+{
+public:
+ es5506_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~es5506_device() {}
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+ void voice_bank_w(int voice, int bank);
+
+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);
+
+
+ void generate_samples(INT32 **outputs, int offset, int samples);
+
+private:
+ inline void reg_write_low(es550x_voice *voice, offs_t offset, UINT32 data);
+ inline void reg_write_high(es550x_voice *voice, offs_t offset, UINT32 data);
+ inline void reg_write_test(es550x_voice *voice, offs_t offset, UINT32 data);
+ inline UINT32 reg_read_low(es550x_voice *voice, offs_t offset);
+ inline UINT32 reg_read_high(es550x_voice *voice, offs_t offset);
+ inline UINT32 reg_read_test(es550x_voice *voice, offs_t offset);
+};
+
+extern const device_type ES5506;
+
+
+class es5505_device : public es550x_device
+{
+public:
+ es5505_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_READ16_MEMBER( read );
+ DECLARE_WRITE16_MEMBER( write );
+ void voice_bank_w(int voice, int bank);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ void generate_samples(INT32 **outputs, int offset, int samples);
+
+private:
+ // internal state
+ inline void reg_write_low(es550x_voice *voice, offs_t offset, UINT16 data, UINT16 mem_mask);
+ inline void reg_write_high(es550x_voice *voice, offs_t offset, UINT16 data, UINT16 mem_mask);
+ inline void reg_write_test(es550x_voice *voice, offs_t offset, UINT16 data, UINT16 mem_mask);
+ inline UINT16 reg_read_low(es550x_voice *voice, offs_t offset);
+ inline UINT16 reg_read_high(es550x_voice *voice, offs_t offset);
+ inline UINT16 reg_read_test(es550x_voice *voice, offs_t offset);
+};
+
+extern const device_type ES5505;
+
+
+#endif /* __ES5506_H__ */
diff --git a/src/devices/sound/es8712.c b/src/devices/sound/es8712.c
new file mode 100644
index 00000000000..f6087f83dc1
--- /dev/null
+++ b/src/devices/sound/es8712.c
@@ -0,0 +1,349 @@
+// license:BSD-3-Clause
+// copyright-holders:Quench
+/**********************************************************************************************
+ *
+ * Streaming single channel ADPCM core for the ES8712 chip
+ * Chip is branded by Excellent Systems, probably OEM'd.
+ *
+ * Samples are currently looped, but whether they should and how, is unknown.
+ * Interface to the chip is also not 100% clear.
+ * Should there be any status signals signifying busy, end of sample - etc?
+ *
+ * Heavily borrowed from the OKI M6295 source
+ *
+ **********************************************************************************************/
+
+
+#include "emu.h"
+#include "es8712.h"
+
+#define MAX_SAMPLE_CHUNK 10000
+
+
+/* step size index shift table */
+static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
+
+/* lookup table for the precomputed difference */
+static int diff_lookup[49*16];
+
+
+// device type definition
+const device_type ES8712 = &device_creator<es8712_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// es8712_device - constructor
+//-------------------------------------------------
+
+es8712_device::es8712_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, ES8712, "ES8712", tag, owner, clock, "es8712", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_rom(*this, DEVICE_SELF),
+ m_playing(0),
+ m_base_offset(0),
+ m_sample(0),
+ m_count(0),
+ m_signal(0),
+ m_step(0),
+ m_start(0),
+ m_end(0),
+ m_repeat(0),
+ m_bank_offset(0),
+ m_stream(NULL)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - start emulation of an ES8712 chip
+//-------------------------------------------------
+
+void es8712_device::device_start()
+{
+ compute_tables();
+
+ m_start = 0;
+ m_end = 0;
+ m_repeat = 0;
+
+ m_bank_offset = 0;
+
+ /* generate the name and create the stream */
+ m_stream = stream_alloc(0, 1, clock());
+
+ /* initialize the rest of the structure */
+ m_signal = -2;
+
+ es8712_state_save_register();
+}
+
+
+//-------------------------------------------------
+// device_reset - stop emulation of an ES8712-compatible chip
+//-------------------------------------------------
+
+void es8712_device::device_reset()
+{
+ if (m_playing)
+ {
+ /* update the stream, then turn it off */
+ m_stream->update();
+ m_playing = 0;
+ m_repeat = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - update the sound chip so that it is in sync with CPU execution
+//-------------------------------------------------
+
+void es8712_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *buffer = outputs[0];
+
+ /* generate them into our buffer */
+ generate_adpcm(buffer, samples);
+}
+
+
+//-------------------------------------------------
+// compute_tables -- compute the difference tables
+//-------------------------------------------------
+
+void es8712_device::compute_tables()
+{
+ /* nibble to bit map */
+ static const int nbl2bit[16][4] =
+ {
+ { 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
+ { 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
+ {-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
+ {-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
+ };
+
+ int step, nib;
+
+ /* loop over all possible steps */
+ for (step = 0; step <= 48; step++)
+ {
+ /* compute the step value */
+ int stepval = floor(16.0 * pow(11.0 / 10.0, (double)step));
+
+ /* loop over all nibbles and compute the difference */
+ for (nib = 0; nib < 16; nib++)
+ {
+ diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
+ (stepval * nbl2bit[nib][1] +
+ stepval/2 * nbl2bit[nib][2] +
+ stepval/4 * nbl2bit[nib][3] +
+ stepval/8);
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// generate_adpcm -- general ADPCM decoding routine
+//-------------------------------------------------
+
+void es8712_device::generate_adpcm(stream_sample_t *buffer, int samples)
+{
+ /* if this chip is active */
+ if (m_playing)
+ {
+ UINT8 *base = &m_rom[m_bank_offset + m_base_offset];
+ int sample = m_sample;
+ int signal = m_signal;
+ int count = m_count;
+ int step = m_step;
+ int val;
+
+ /* loop while we still have samples to generate */
+ while (samples)
+ {
+ /* compute the new amplitude and update the current step */
+ val = base[sample / 2] >> (((sample & 1) << 2) ^ 4);
+ signal += diff_lookup[step * 16 + (val & 15)];
+
+ /* clamp to the maximum */
+ if (signal > 2047)
+ signal = 2047;
+ else if (signal < -2048)
+ signal = -2048;
+
+ /* adjust the step size and clamp */
+ step += index_shift[val & 7];
+ if (step > 48)
+ step = 48;
+ else if (step < 0)
+ step = 0;
+
+ /* output to the buffer */
+ *buffer++ = signal * 16;
+ samples--;
+
+ /* next! */
+ if (++sample >= count)
+ {
+ if (m_repeat)
+ {
+ sample = 0;
+ signal = -2;
+ step = 0;
+ }
+ else
+ {
+ m_playing = 0;
+ break;
+ }
+ }
+ }
+
+ /* update the parameters */
+ m_sample = sample;
+ m_signal = signal;
+ m_step = step;
+ }
+
+ /* fill the rest with silence */
+ while (samples--)
+ *buffer++ = 0;
+}
+
+
+
+//-------------------------------------------------
+// state save support for MAME
+//-------------------------------------------------
+
+void es8712_device::es8712_state_save_register()
+{
+ save_item(NAME(m_bank_offset));
+
+ save_item(NAME(m_playing));
+ save_item(NAME(m_sample));
+ save_item(NAME(m_count));
+ save_item(NAME(m_signal));
+ save_item(NAME(m_step));
+
+ save_item(NAME(m_base_offset));
+
+ save_item(NAME(m_start));
+ save_item(NAME(m_end));
+ save_item(NAME(m_repeat));
+}
+
+
+
+
+//-------------------------------------------------
+// es8712_set_bank_base -- set the base of the bank on a given chip
+//-------------------------------------------------
+
+void es8712_device::set_bank_base(int base)
+{
+ m_stream->update();
+ m_bank_offset = base;
+}
+
+
+//-------------------------------------------------
+// es8712_set_frequency -- dynamically adjusts the frequency of a given ADPCM chip
+//-------------------------------------------------
+
+void es8712_device::set_frequency(int frequency)
+{
+ /* update the stream and set the new base */
+ m_stream->update();
+ m_stream->set_sample_rate(frequency);
+}
+
+
+//-------------------------------------------------
+// play -- Begin playing the addressed sample
+//-------------------------------------------------
+
+void es8712_device::play()
+{
+ if (m_start < m_end)
+ {
+ if (!m_playing)
+ {
+ m_playing = 1;
+ m_base_offset = m_start;
+ m_sample = 0;
+ m_count = 2 * (m_end - m_start + 1);
+ m_repeat = 0;//1;
+
+ /* also reset the ADPCM parameters */
+ m_signal = -2;
+ m_step = 0;
+ }
+ }
+ /* invalid samples go here */
+ else
+ {
+ logerror("ES871295:'%s' requested to play invalid sample range %06x-%06x\n", tag(), m_start, m_end);
+
+ if (m_playing)
+ {
+ /* update the stream */
+ m_stream->update();
+ m_playing = 0;
+ }
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ es8712_data_0_w -- generic data write functions
+ es8712_data_1_w
+
+***********************************************************************************************/
+
+/**********************************************************************************************
+ *
+ * offset Start End
+ * 0hmmll - 0HMMLL
+ * 00 ----ll
+ * 01 --mm--
+ * 02 0h----
+ * 03 ----LL
+ * 04 --MM--
+ * 05 0H----
+ * 06 Go!
+ *
+ * Offsets are written in the order -> 00, 02, 01, 03, 05, 04, 06
+ * Offset 06 is written with the same value as offset 04.
+ *
+***********************************************************************************************/
+
+WRITE8_MEMBER( es8712_device::es8712_w )
+{
+ switch (offset)
+ {
+ case 00: m_start &= 0x000fff00;
+ m_start |= ((data & 0xff) << 0); break;
+ case 01: m_start &= 0x000f00ff;
+ m_start |= ((data & 0xff) << 8); break;
+ case 02: m_start &= 0x0000ffff;
+ m_start |= ((data & 0x0f) << 16); break;
+ case 03: m_end &= 0x000fff00;
+ m_end |= ((data & 0xff) << 0); break;
+ case 04: m_end &= 0x000f00ff;
+ m_end |= ((data & 0xff) << 8); break;
+ case 05: m_end &= 0x0000ffff;
+ m_end |= ((data & 0x0f) << 16); break;
+ case 06:
+ play(); break;
+ default: break;
+ }
+ m_start &= 0xfffff; m_end &= 0xfffff;
+}
diff --git a/src/devices/sound/es8712.h b/src/devices/sound/es8712.h
new file mode 100644
index 00000000000..ed05a4b3497
--- /dev/null
+++ b/src/devices/sound/es8712.h
@@ -0,0 +1,78 @@
+// license:BSD-3-Clause
+// copyright-holders:Quench
+/* An interface for the ES8712 ADPCM chip */
+
+#pragma once
+
+#ifndef __ES8712_H__
+#define __ES8712_H__
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_ES8712_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, ES8712, _clock)
+#define MCFG_ES8712_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, ES8712, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+
+// ======================> es8712_device
+
+class es8712_device : public device_t,
+ public device_sound_interface
+{
+public:
+ es8712_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~es8712_device() { }
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+public:
+ DECLARE_WRITE8_MEMBER( es8712_w );
+
+public:
+ void play();
+ void set_bank_base(int base);
+ void set_frequency(int frequency);
+
+private:
+ void compute_tables();
+ void generate_adpcm(stream_sample_t *buffer, int samples);
+ void es8712_state_save_register();
+
+private:
+ required_region_ptr<UINT8> m_rom;
+
+ UINT8 m_playing; /* 1 if we're actively playing */
+
+ UINT32 m_base_offset; /* pointer to the base memory location */
+ UINT32 m_sample; /* current sample number */
+ UINT32 m_count; /* total samples to play */
+
+ UINT32 m_signal; /* current ADPCM signal */
+ UINT32 m_step; /* current ADPCM step */
+
+ UINT32 m_start; /* starting address for the next loop */
+ UINT32 m_end; /* ending address for the next loop */
+ UINT8 m_repeat; /* Repeat current sample when 1 */
+
+ INT32 m_bank_offset;
+ sound_stream *m_stream; /* which stream are we playing on? */
+};
+
+extern const device_type ES8712;
+
+
+#endif /* __ES8712_H__ */
diff --git a/src/devices/sound/esqpump.c b/src/devices/sound/esqpump.c
new file mode 100644
index 00000000000..147f97786ca
--- /dev/null
+++ b/src/devices/sound/esqpump.c
@@ -0,0 +1,170 @@
+// license:BSD-3-Clause
+// copyright-holders:Christian Brunschen
+/***************************************************************************
+
+ esqpump.c - Ensoniq 5505/5506 to 5510 interface.
+
+ By Christian Brunschen
+
+***************************************************************************/
+
+#include "sound/esqpump.h"
+
+const device_type ESQ_5505_5510_PUMP = &device_creator<esq_5505_5510_pump>;
+
+esq_5505_5510_pump::esq_5505_5510_pump(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, ESQ_5505_5510_PUMP, "ESQ_5505_5510_PUMP", tag, owner, clock, "esq_5505_5510_pump", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_esp_halted(true)
+{
+}
+
+void esq_5505_5510_pump::device_start()
+{
+ logerror("Clock = %d\n", clock());
+
+ m_stream = machine().sound().stream_alloc(*this, 8, 2, clock());
+ m_timer = timer_alloc(0);
+ m_timer->enable(false);
+
+#if PUMP_DETECT_SILENCE
+ silent_for = 500;
+ was_silence = 1;
+#endif
+#if !PUMP_FAKE_ESP_PROCESSING
+ ticks_spent_processing = 0;
+ samples_processed = 0;
+#endif
+#if PUMP_TRACK_SAMPLES
+ last_samples = 0;
+ last_ticks = osd_ticks();
+ next_report_ticks = last_ticks + osd_ticks_per_second();
+#endif
+
+#if !PUMP_FAKE_ESP_PROCESSING && PUMP_REPLACE_ESP_PROGRAM
+ memset(e, 0, 0x4000 * sizeof(e[0]));
+ ei = 0;
+#endif
+}
+
+void esq_5505_5510_pump::device_stop()
+{
+ m_timer->enable(false);
+}
+
+void esq_5505_5510_pump::device_reset()
+{
+ INT64 nsec_per_sample = 100 * 16 * 21;
+ attotime sample_time(0, 1000000000 * nsec_per_sample);
+ attotime initial_delay(0, 0);
+
+ m_timer->adjust(initial_delay, 0, sample_time);
+ m_timer->enable(true);
+}
+
+void esq_5505_5510_pump::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ if (samples != 1) {
+ logerror("Pump: request for %d samples\n", samples);
+ }
+
+ stream_sample_t *left = outputs[0], *right = outputs[1];
+ for (int i = 0; i < samples; i++)
+ {
+#define SAMPLE_SHIFT 4
+ // anything for the 'aux' output?
+ INT16 l = inputs[0][i] >> SAMPLE_SHIFT;
+ INT16 r = inputs[1][i] >> SAMPLE_SHIFT;
+
+ // push the samples into the ESP
+ m_esp->ser_w(0, inputs[2][i] >> SAMPLE_SHIFT);
+ m_esp->ser_w(1, inputs[3][i] >> SAMPLE_SHIFT);
+ m_esp->ser_w(2, inputs[4][i] >> SAMPLE_SHIFT);
+ m_esp->ser_w(3, inputs[5][i] >> SAMPLE_SHIFT);
+ m_esp->ser_w(4, inputs[6][i] >> SAMPLE_SHIFT);
+ m_esp->ser_w(5, inputs[7][i] >> SAMPLE_SHIFT);
+
+#if PUMP_FAKE_ESP_PROCESSING
+ m_esp->ser_w(6, m_esp->ser_r(0) + m_esp->ser_r(2) + m_esp->ser_r(4));
+ m_esp->ser_w(7, m_esp->ser_r(1) + m_esp->ser_r(3) + m_esp->ser_r(5));
+#else
+ if (!m_esp_halted) {
+ logerror("passing one sample through ESP\n");
+ osd_ticks_t a = osd_ticks();
+ m_esp->run_once();
+ osd_ticks_t b = osd_ticks();
+ ticks_spent_processing += (b - a);
+ samples_processed++;
+ }
+#endif
+
+ // read the processed result from the ESP and add to the saved AUX data
+ INT16 ll = m_esp->ser_r(6);
+ INT16 rr = m_esp->ser_r(7);
+ l += ll;
+ r += rr;
+
+#if !PUMP_FAKE_ESP_PROCESSING && PUMP_REPLACE_ESP_PROGRAM
+ // if we're processing the fake program through the ESP, the result should just be that of adding the inputs
+ INT32 el = (inputs[2][i]) + (inputs[4][i]) + (inputs[6][i]);
+ INT32 er = (inputs[3][i]) + (inputs[5][i]) + (inputs[7][i]);
+ INT32 e_next = el + er;
+ e[(ei + 0x1d0f) % 0x4000] = e_next;
+
+ if (l != e[ei]) {
+ fprintf(stderr, "expected (%d) but have (%d)\n", e[ei], l);
+ }
+ ei = (ei + 1) % 0x4000;
+#endif
+
+ // write the combined data to the output
+ *left++ = l;
+ *right++ = r;
+ }
+
+#if PUMP_DETECT_SILENCE
+ for (int i = 0; i < samples; i++) {
+ if (outputs[0][i] == 0 && outputs[1][i] == 0) {
+ silent_for++;
+ } else {
+ silent_for = 0;
+ }
+ }
+ bool silence = silent_for >= 500;
+ if (was_silence != silence) {
+ if (!silence) {
+ fprintf(stderr, ".-*\n");
+ } else {
+ fprintf(stderr, "*-.\n");
+ }
+ was_silence = silence;
+ }
+#endif
+
+#if PUMP_TRACK_SAMPLES
+ last_samples += samples;
+ osd_ticks_t now = osd_ticks();
+ if (now >= next_report_ticks)
+ {
+ osd_ticks_t elapsed = now - last_ticks;
+ osd_ticks_t tps = osd_ticks_per_second();
+ fprintf(stderr, "Pump: %d samples in %" I64FMT "d ticks for %f Hz\n", last_samples, elapsed, last_samples * (double)tps / (double)elapsed);
+ last_ticks = now;
+ while (next_report_ticks <= now) {
+ next_report_ticks += tps;
+ }
+ last_samples = 0;
+
+#if !PUMP_FAKE_ESP_PROCESSING
+ fprintf(stderr, " ESP spent %" I64FMT "d ticks on %d samples, %f ticks per sample\n", ticks_spent_processing, samples_processed, (double)ticks_spent_processing / (double)samples_processed);
+ ticks_spent_processing = 0;
+ samples_processed = 0;
+#endif
+ }
+#endif
+}
+
+void esq_5505_5510_pump::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) {
+ // ecery time there's a new sample period, update the stream!
+ m_stream->update();
+}
diff --git a/src/devices/sound/esqpump.h b/src/devices/sound/esqpump.h
new file mode 100644
index 00000000000..bc2c72f8a72
--- /dev/null
+++ b/src/devices/sound/esqpump.h
@@ -0,0 +1,124 @@
+// license:BSD-3-Clause
+// copyright-holders:Christian Brunschen
+#pragma once
+
+#ifndef _ESQPUMP_H_
+#define _ESQPUMP_H_
+
+#include "emu.h"
+#include "sound/es5506.h"
+#include "cpu/es5510/es5510.h"
+
+#define PUMP_DETECT_SILENCE 0
+#define PUMP_TRACK_SAMPLES 0
+#define PUMP_FAKE_ESP_PROCESSING 0
+#define PUMP_REPLACE_ESP_PROGRAM 0
+
+class esq_5505_5510_pump : public device_t,
+ public device_sound_interface
+{
+public:
+ esq_5505_5510_pump(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ void set_otis(es5505_device *otis) { m_otis = otis; }
+ void set_esp(es5510_device *esp) { m_esp = esp; }
+ void set_esp_halted(bool esp_halted) {
+ m_esp_halted = esp_halted;
+ logerror("ESP-halted -> %d\n", m_esp_halted);
+ if (!esp_halted) {
+#if PUMP_REPLACE_ESP_PROGRAM
+ m_esp->write_reg(245, 0x1d0f << 8); // dlength = 0x3fff, 16-sample delay
+
+ int pc = 0;
+ for (pc = 0; pc < 0xc0; pc++) {
+ m_esp->write_reg(pc, 0);
+ }
+ pc = 0;
+ // replace the ESP program with a simple summing & single-sample delay
+ m_esp->_instr(pc++) = 0xffffeaa09000; // MOV SER0R > grp_a0
+ m_esp->_instr(pc++) = 0xffffeba00000; // ADD SER0L, gpr_a0 > gpr_a0
+ m_esp->_instr(pc++) = 0xffffeca00000; // ADD SER1R, gpr_a0 > gpr_a0
+ m_esp->_instr(pc++) = 0xffffeda00000; // ADD SER1L, gpr_a0 > gpr_a0
+ m_esp->_instr(pc++) = 0xffffeea00000; // ADD SER2R, gpr_a0 > gpr_a0
+
+ m_esp->_instr(pc ) = 0xffffefa00000; // ADD SER2L, gpr_a0 > gpr_a0; prepare to read from delay 2 instructions from now, offset = 0
+ m_esp->write_reg(pc++, 0); //offset into delay
+
+ m_esp->_instr(pc ) = 0xffffa0a09508; // MOV gpr_a0 > delay + offset
+ m_esp->write_reg(pc++, 1 << 8); // offset into delay - -1 samples
+
+ m_esp->_instr(pc++) = 0xffff00a19928; // MOV DIL > gpr_a1; read Delay and dump FIFO (so that the value gets written)
+
+ m_esp->_instr(pc++) = 0xffffa1f09000; // MOV gpr_a1 > SER3R
+ m_esp->_instr(pc++) = 0xffffa1f19000; // MOV gpr_a1 > SER3L
+
+ m_esp->_instr(pc++) = 0xffffffff0000; // NO-OP
+ m_esp->_instr(pc++) = 0xffffffff0000; // NO-OP
+ m_esp->_instr(pc++) = 0xfffffffff000; // END
+
+ while (pc < 160) {
+ m_esp->_instr(pc++) = 0xffffffffffff; // no-op
+ }
+#endif
+
+ // m_esp->list_program(print_to_stderr);
+ }
+ }
+ bool get_esp_halted() {
+ return m_esp_halted;
+ }
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_stop();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ // timer callback overrides
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+private:
+ // internal state:
+ // sound stream
+ sound_stream *m_stream;
+
+ // per-sample timer
+ emu_timer *m_timer;
+
+ // OTIS sound generator
+ es5505_device *m_otis;
+
+ // ESP signal processor
+ es5510_device *m_esp;
+
+ // Is the ESP halted by the CPU?
+ bool m_esp_halted;
+
+#if !PUMP_FAKE_ESP_PROCESSING
+ osd_ticks_t ticks_spent_processing;
+ int samples_processed;
+#endif
+
+#if PUMP_DETECT_SILENCE
+ int silent_for;
+ bool was_silence;
+#endif
+
+#if PUMP_TRACK_SAMPLES
+ int last_samples;
+ osd_ticks_t last_ticks;
+ osd_ticks_t next_report_ticks;
+#endif
+
+#if !PUMP_FAKE_ESP_PROCESSING && PUMP_REPLACE_ESP_PROGRAM
+ INT16 e[0x4000];
+ int ei;
+#endif
+};
+
+extern const device_type ESQ_5505_5510_PUMP;
+
+#endif
diff --git a/src/devices/sound/filter.c b/src/devices/sound/filter.c
new file mode 100644
index 00000000000..3d80966d029
--- /dev/null
+++ b/src/devices/sound/filter.c
@@ -0,0 +1,234 @@
+// license:???
+// copyright-holders:Derrick Renaud, Couriersud
+#include "emu.h"
+#include "filter.h"
+
+static filter* filter_alloc(void) {
+ filter* f = global_alloc(filter);
+ return f;
+}
+
+void filter_free(filter* f) {
+ global_free(f);
+}
+
+void filter_state_reset(filter* f, filter_state* s) {
+ int i;
+ s->prev_mac = 0;
+ for(i=0;i<f->order;++i) {
+ s->xprev[i] = 0;
+ }
+}
+
+filter_state* filter_state_alloc(void) {
+ int i;
+ filter_state* s = global_alloc(filter_state);
+ s->prev_mac = 0;
+ for(i=0;i<FILTER_ORDER_MAX;++i)
+ s->xprev[i] = 0;
+ return s;
+}
+
+void filter_state_free(filter_state* s) {
+ global_free(s);
+}
+
+/****************************************************************************/
+/* FIR */
+
+filter_real filter_compute(filter* f, filter_state* s) {
+ unsigned order = f->order;
+ unsigned midorder = f->order / 2;
+ filter_real y = 0;
+ unsigned i,j,k;
+
+ /* i == [0] */
+ /* j == [-2*midorder] */
+ i = s->prev_mac;
+ j = i + 1;
+ if (j == order)
+ j = 0;
+
+ /* x */
+ for(k=0;k<midorder;++k) {
+ y += f->xcoeffs[midorder-k] * (s->xprev[i] + s->xprev[j]);
+ ++j;
+ if (j == order)
+ j = 0;
+ if (i == 0)
+ i = order - 1;
+ else
+ --i;
+ }
+ y += f->xcoeffs[0] * s->xprev[i];
+
+#ifdef FILTER_USE_INT
+ return y >> FILTER_INT_FRACT;
+#else
+ return y;
+#endif
+}
+
+filter* filter_lp_fir_alloc(double freq, int order) {
+ filter* f = filter_alloc();
+ unsigned midorder = (order - 1) / 2;
+ unsigned i;
+ double gain;
+
+ assert( order <= FILTER_ORDER_MAX );
+ assert( order % 2 == 1 );
+ assert( 0 < freq && freq <= 0.5 );
+
+ /* Compute the antitrasform of the perfect low pass filter */
+ gain = 2*freq;
+#ifdef FILTER_USE_INT
+ f->xcoeffs[0] = gain * (1 << FILTER_INT_FRACT);
+#else
+ f->xcoeffs[0] = gain;
+#endif
+ for(i=1;i<=midorder;++i) {
+ /* number of the sample starting from 0 to (order-1) included */
+ unsigned n = i + midorder;
+
+ /* sample value */
+ double c = sin(2*M_PI*freq*i) / (M_PI*i);
+
+ /* apply only one window or none */
+ /* double w = 2 - 2*n/(order-1); */ /* Bartlett (triangular) */
+ /* double w = 0.5 * (1 - cos(2*M_PI*n/(order-1))); */ /* Hanning */
+ double w = 0.54 - 0.46 * cos(2*M_PI*n/(order-1)); /* Hamming */
+ /* double w = 0.42 - 0.5 * cos(2*M_PI*n/(order-1)) + 0.08 * cos(4*M_PI*n/(order-1)); */ /* Blackman */
+
+ /* apply the window */
+ c *= w;
+
+ /* update the gain */
+ gain += 2*c;
+
+ /* insert the coeff */
+#ifdef FILTER_USE_INT
+ f->xcoeffs[i] = c * (1 << FILTER_INT_FRACT);
+#else
+ f->xcoeffs[i] = c;
+#endif
+ }
+
+ /* adjust the gain to be exact 1.0 */
+ for(i=0;i<=midorder;++i) {
+#ifdef FILTER_USE_INT
+ f->xcoeffs[i] /= gain;
+#else
+ f->xcoeffs[i] = f->xcoeffs[i] * (double)(1 << FILTER_INT_FRAC) / gain;
+#endif
+ }
+
+ /* decrease the order if the last coeffs are 0 */
+ i = midorder;
+ while (i > 0 && f->xcoeffs[i] == 0.0)
+ --i;
+
+ f->order = i * 2 + 1;
+
+ return f;
+}
+
+
+void filter2_setup(device_t *device, int type, double fc, double d, double gain,
+ filter2_context *filter2)
+{
+ int sample_rate = device->machine().sample_rate();
+ double w; /* cutoff freq, in radians/sec */
+ double w_squared;
+ double den; /* temp variable */
+ double two_over_T = 2*sample_rate;
+ double two_over_T_squared = two_over_T * two_over_T;
+
+ /* calculate digital filter coefficents */
+ /*w = 2.0*M_PI*fc; no pre-warping */
+ w = sample_rate*2.0*tan(M_PI*fc/sample_rate); /* pre-warping */
+ w_squared = w*w;
+
+ den = two_over_T_squared + d*w*two_over_T + w_squared;
+
+ filter2->a1 = 2.0*(-two_over_T_squared + w_squared)/den;
+ filter2->a2 = (two_over_T_squared - d*w*two_over_T + w_squared)/den;
+
+ switch (type)
+ {
+ case FILTER_LOWPASS:
+ filter2->b0 = filter2->b2 = w_squared/den;
+ filter2->b1 = 2.0*(filter2->b0);
+ break;
+ case FILTER_BANDPASS:
+ filter2->b0 = d*w*two_over_T/den;
+ filter2->b1 = 0.0;
+ filter2->b2 = -(filter2->b0);
+ break;
+ case FILTER_HIGHPASS:
+ filter2->b0 = filter2->b2 = two_over_T_squared/den;
+ filter2->b1 = -2.0*(filter2->b0);
+ break;
+ default:
+ logerror("filter2_setup() - Invalid filter type for 2nd order filter.");
+ break;
+ }
+
+ filter2->b0 *= gain;
+ filter2->b1 *= gain;
+ filter2->b2 *= gain;
+}
+
+
+/* Reset the input/output voltages to 0. */
+void filter2_reset(filter2_context *filter2)
+{
+ filter2->x0 = 0;
+ filter2->x1 = 0;
+ filter2->x2 = 0;
+ filter2->y0 = 0;
+ filter2->y1 = 0;
+ filter2->y2 = 0;
+}
+
+
+/* Step the filter. */
+void filter2_step(filter2_context *filter2)
+{
+ filter2->y0 = -filter2->a1 * filter2->y1 - filter2->a2 * filter2->y2 +
+ filter2->b0 * filter2->x0 + filter2->b1 * filter2->x1 + filter2->b2 * filter2->x2;
+ filter2->x2 = filter2->x1;
+ filter2->x1 = filter2->x0;
+ filter2->y2 = filter2->y1;
+ filter2->y1 = filter2->y0;
+}
+
+
+/* Setup a filter2 structure based on an op-amp multipole bandpass circuit. */
+void filter_opamp_m_bandpass_setup(device_t *device, double r1, double r2, double r3, double c1, double c2,
+ filter2_context *filter2)
+{
+ double r_in, fc, d, gain;
+
+ if (r1 == 0)
+ {
+ logerror("filter_opamp_m_bandpass_setup() - r1 can not be 0");
+ return; /* Filter can not be setup. Undefined results. */
+ }
+
+ if (r2 == 0)
+ {
+ gain = 1;
+ r_in = r1;
+ }
+ else
+ {
+ gain = r2 / (r1 + r2);
+ r_in = 1.0 / (1.0/r1 + 1.0/r2);
+ }
+
+ fc = 1.0 / (2 * M_PI * sqrt(r_in * r3 * c1 * c2));
+ d = (c1 + c2) / sqrt(r3 / r_in * c1 * c2);
+ gain *= -r3 / r_in * c2 / (c1 + c2);
+
+ filter2_setup(device, FILTER_BANDPASS, fc, d, gain, filter2);
+}
diff --git a/src/devices/sound/filter.h b/src/devices/sound/filter.h
new file mode 100644
index 00000000000..5e671e85545
--- /dev/null
+++ b/src/devices/sound/filter.h
@@ -0,0 +1,133 @@
+// license:???
+// copyright-holders:Derrick Renaud, Couriersud
+#pragma once
+
+#ifndef __FILTER_H__
+#define __FILTER_H__
+
+/* Max filter order */
+#define FILTER_ORDER_MAX 51
+
+/* Define to use integer calculation */
+#define FILTER_USE_INT
+
+#ifdef FILTER_USE_INT
+typedef int filter_real;
+#define FILTER_INT_FRACT 15 /* fractional bits */
+#else
+typedef double filter_real;
+#endif
+
+struct filter
+{
+ filter_real xcoeffs[(FILTER_ORDER_MAX+1)/2];
+ unsigned order;
+};
+
+struct filter_state
+{
+ unsigned prev_mac;
+ filter_real xprev[FILTER_ORDER_MAX];
+};
+
+/* Allocate a FIR Low Pass filter */
+filter* filter_lp_fir_alloc(double freq, int order);
+void filter_free(filter* f);
+
+/* Allocate a filter state */
+filter_state* filter_state_alloc(void);
+
+/* Free the filter state */
+void filter_state_free(filter_state* s);
+
+/* Clear the filter state */
+void filter_state_reset(filter* f, filter_state* s);
+
+/* Insert a value in the filter state */
+INLINE void filter_insert(filter* f, filter_state* s, filter_real x) {
+ /* next state */
+ ++s->prev_mac;
+ if (s->prev_mac >= f->order)
+ s->prev_mac = 0;
+
+ /* set x[0] */
+ s->xprev[s->prev_mac] = x;
+}
+
+/* Compute the filter output */
+filter_real filter_compute(filter* f, filter_state* s);
+
+
+/* Filter types */
+#define FILTER_LOWPASS 0
+#define FILTER_HIGHPASS 1
+#define FILTER_BANDPASS 2
+
+#define Q_TO_DAMP(q) (1.0/q)
+
+struct filter2_context
+{
+ filter2_context() :
+ x0(0.0),
+ x1(0.0),
+ x2(0.0),
+ y0(0.0),
+ y1(0.0),
+ y2(0.0),
+ a1(0.0),
+ a2(0.0),
+ b0(0.0),
+ b1(0.0),
+ b2(0.0)
+ {}
+
+ double x0, x1, x2; /* x[k], x[k-1], x[k-2], current and previous 2 input values */
+ double y0, y1, y2; /* y[k], y[k-1], y[k-2], current and previous 2 output values */
+ double a1, a2; /* digital filter coefficients, denominator */
+ double b0, b1, b2; /* digital filter coefficients, numerator */
+};
+
+
+/* Setup the filter context based on the passed filter type info.
+ * type - 1 of the 3 defined filter types
+ * fc - center frequency
+ * d - damp = 1/Q
+ * gain - overall filter gain. Set to 1 if not needed.
+ */
+void filter2_setup(device_t *device, int type, double fc, double d, double gain,
+ filter2_context *filter2);
+
+
+/* Reset the input/output voltages to 0. */
+void filter2_reset(filter2_context *filter2);
+
+
+/* Step the filter.
+ * x0 is the new input, which needs to be set before stepping.
+ * y0 is the new filter output.
+ */
+void filter2_step(filter2_context *filter2);
+
+
+/* Setup a filter2 structure based on an op-amp multipole bandpass circuit.
+ * NOTE: If r2 is not used then set to 0.
+ * vRef is not needed to setup filter.
+ *
+ * .--------+---------.
+ * | | |
+ * --- c1 Z |
+ * --- Z r3 |
+ * | Z |
+ * r1 | c2 | |\ |
+ * In >----ZZZZ----+---------+--||----+ | \ |
+ * Z '--|- \ |
+ * Z r2 | >--+------> out
+ * Z .--|+ /
+ * | | | /
+ * gnd vRef >---' |/
+ *
+ */
+void filter_opamp_m_bandpass_setup(device_t *device, double r1, double r2, double r3, double c1, double c2,
+ filter2_context *filter2);
+
+#endif /* __FILTER_H__ */
diff --git a/src/devices/sound/flt_rc.c b/src/devices/sound/flt_rc.c
new file mode 100644
index 00000000000..5fc0a7df882
--- /dev/null
+++ b/src/devices/sound/flt_rc.c
@@ -0,0 +1,139 @@
+// license:???
+// copyright-holders:Derrick Renaud, Couriersud
+#include "emu.h"
+#include "flt_rc.h"
+
+
+// device type definition
+const device_type FILTER_RC = &device_creator<filter_rc_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// filter_rc_device - constructor
+//-------------------------------------------------
+
+filter_rc_device::filter_rc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, FILTER_RC, "RC Filter", tag, owner, clock, "filter_rc", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_k(0),
+ m_memory(0),
+ m_type(FLT_RC_LOWPASS),
+ m_R1(1),
+ m_R2(1),
+ m_R3(1),
+ m_C(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void filter_rc_device::device_start()
+{
+ m_stream = stream_alloc(1, 1, machine().sample_rate());
+ recalc();
+
+ save_item(NAME(m_k));
+ save_item(NAME(m_memory));
+ save_item(NAME(m_type));
+ save_item(NAME(m_R1));
+ save_item(NAME(m_R2));
+ save_item(NAME(m_R3));
+ save_item(NAME(m_C));
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void filter_rc_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *src = inputs[0];
+ stream_sample_t *dst = outputs[0];
+ int memory = m_memory;
+
+ switch (m_type)
+ {
+ case FLT_RC_LOWPASS:
+ while (samples--)
+ {
+ memory += ((*src++ - memory) * m_k) / 0x10000;
+ *dst++ = memory;
+ }
+ break;
+ case FLT_RC_HIGHPASS:
+ case FLT_RC_AC:
+ while (samples--)
+ {
+ *dst++ = *src - memory;
+ memory += ((*src++ - memory) * m_k) / 0x10000;
+ }
+ break;
+ }
+ m_memory = memory;
+}
+
+
+void filter_rc_device::recalc()
+{
+ double Req;
+
+ switch (m_type)
+ {
+ case FLT_RC_LOWPASS:
+ if (m_C == 0.0)
+ {
+ /* filter disabled */
+ m_k = 0x10000;
+ return;
+ }
+ Req = (m_R1 * (m_R2 + m_R3)) / (m_R1 + m_R2 + m_R3);
+ break;
+ case FLT_RC_HIGHPASS:
+ case FLT_RC_AC:
+ if (m_C == 0.0)
+ {
+ /* filter disabled */
+ m_k = 0x0;
+ m_memory = 0x0;
+ return;
+ }
+ Req = m_R1;
+ break;
+ default:
+ fatalerror("filter_rc_setRC: Wrong filter type %d\n", m_type);
+ }
+
+ /* Cut Frequency = 1/(2*Pi*Req*C) */
+ /* k = (1-(EXP(-TIMEDELTA/RC))) */
+ m_k = 0x10000 - 0x10000 * (exp(-1 / (Req * m_C) / machine().sample_rate()));
+}
+
+
+void filter_rc_device::filter_rc_set_RC(int type, double R1, double R2, double R3, double C)
+{
+ m_stream->update();
+ m_type = type;
+ m_R1 = R1;
+ m_R2 = R2;
+ m_R3 = R3;
+ m_C = C;
+ recalc();
+}
+
+void filter_rc_device::static_set_rc(device_t &device, int type, double R1, double R2, double R3, double C)
+{
+ downcast<filter_rc_device &>(device).m_type = type;
+ downcast<filter_rc_device &>(device).m_R1 = R1;
+ downcast<filter_rc_device &>(device).m_R2 = R2;
+ downcast<filter_rc_device &>(device).m_R3 = R3;
+ downcast<filter_rc_device &>(device).m_C = C;
+}
diff --git a/src/devices/sound/flt_rc.h b/src/devices/sound/flt_rc.h
new file mode 100644
index 00000000000..0ea52c6972a
--- /dev/null
+++ b/src/devices/sound/flt_rc.h
@@ -0,0 +1,104 @@
+// license:???
+// copyright-holders:Derrick Renaud, Couriersud
+#pragma once
+
+#ifndef __FLT_RC_H__
+#define __FLT_RC_H__
+
+#include "machine/rescap.h"
+
+/*
+ * FLT_RC_LOWPASS:
+ *
+ * signal >--R1--+--R2--+
+ * | |
+ * C R3---> amp
+ * | |
+ * GND GND
+ *
+ * Set C=0 to disable filter
+ *
+ * FLT_RC_HIGHPASS:
+ *
+ * signal >--C---+----> amp
+ * |
+ * R1
+ * |
+ * GND
+ *
+ * Set C = 0 to disable filter
+ *
+ * FLT_RC_AC:
+ *
+ * Same as FLT_RC_HIGHPASS, but with standard frequency of 16 HZ
+ * This filter may be setup just with
+ *
+ * MCFG_FILTER_RC_ADD("tag", 0)
+ * MCFG_FILTER_RC_AC(&flt_rc_ac_default)
+ *
+ * Default behaviour:
+ *
+ * Without MCFG_SOUND_CONFIG, a disabled FLT_RC_LOWPASS is created
+ *
+ */
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_FILTER_RC_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, FILTER_RC, _clock)
+
+#define MCFG_FILTER_RC_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, FILTER_RC, _clock)
+
+#define MCFG_FILTER_RC_AC() \
+ filter_rc_device::static_set_rc(*device, FLT_RC_AC, 10000, 0, 0, CAP_U(1));
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+#define FLT_RC_LOWPASS 0
+#define FLT_RC_HIGHPASS 1
+#define FLT_RC_AC 2
+
+// ======================> filter_rc_device
+
+class filter_rc_device : public device_t,
+ public device_sound_interface
+{
+public:
+ filter_rc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~filter_rc_device() { }
+
+ // static configuration
+ static void static_set_rc(device_t &device, int type, double R1, double R2, double R3, double C);
+
+ void filter_rc_set_RC(int type, double R1, double R2, double R3, double C);
+
+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:
+ void recalc();
+
+private:
+ sound_stream* m_stream;
+ int m_k;
+ int m_memory;
+ int m_type;
+ double m_R1;
+ double m_R2;
+ double m_R3;
+ double m_C;
+};
+
+extern const device_type FILTER_RC;
+
+#endif /* __FLT_RC_H__ */
diff --git a/src/devices/sound/flt_vol.c b/src/devices/sound/flt_vol.c
new file mode 100644
index 00000000000..0012f3fc4db
--- /dev/null
+++ b/src/devices/sound/flt_vol.c
@@ -0,0 +1,52 @@
+// license:???
+// copyright-holders:Derrick Renaud, Couriersud
+#include "emu.h"
+#include "flt_vol.h"
+
+
+// device type definition
+const device_type FILTER_VOLUME = &device_creator<filter_volume_device>;
+
+//-------------------------------------------------
+// filter_volume_device - constructor
+//-------------------------------------------------
+
+filter_volume_device::filter_volume_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, FILTER_VOLUME, "Volume Filter", tag, owner, clock, "filter_volume", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_gain(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void filter_volume_device::device_start()
+{
+ m_gain = 0x100;
+ m_stream = stream_alloc(1, 1, machine().sample_rate());
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void filter_volume_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *src = inputs[0];
+ stream_sample_t *dst = outputs[0];
+
+ while (samples--)
+ *dst++ = (*src++ * m_gain) >> 8;
+}
+
+
+
+void filter_volume_device::flt_volume_set_volume(float volume)
+{
+ m_gain = (int)(volume * 256);
+}
diff --git a/src/devices/sound/flt_vol.h b/src/devices/sound/flt_vol.h
new file mode 100644
index 00000000000..20ce17a58e9
--- /dev/null
+++ b/src/devices/sound/flt_vol.h
@@ -0,0 +1,50 @@
+// license:???
+// copyright-holders:Derrick Renaud, Couriersud
+#pragma once
+
+#ifndef __FLT_VOL_H__
+#define __FLT_VOL_H__
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_FILTER_VOLUME_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, FILTER_VOLUME, _clock)
+#define MCFG_FILTER_VOLUME_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, FILTER_VOLUME, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> filter_volume_device
+
+class filter_volume_device : public device_t,
+ public device_sound_interface
+{
+public:
+ filter_volume_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~filter_volume_device() { }
+
+ void flt_volume_set_volume(float volume);
+
+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:
+ sound_stream* m_stream;
+ int m_gain;
+};
+
+extern const device_type FILTER_VOLUME;
+
+
+#endif /* __FLT_VOL_H__ */
diff --git a/src/devices/sound/fm.c b/src/devices/sound/fm.c
new file mode 100644
index 00000000000..3782790409f
--- /dev/null
+++ b/src/devices/sound/fm.c
@@ -0,0 +1,3908 @@
+// license:???
+// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh
+#define YM2610B_WARNING
+
+/*
+**
+** File: fm.c -- software implementation of Yamaha FM sound generator
+**
+** Copyright Jarek Burczynski (bujar at mame dot net)
+** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development
+**
+** Version 1.4.2 (final beta)
+**
+*/
+
+/*
+** History:
+**
+** 2006-2008 Eke-Eke (Genesis Plus GX), MAME backport by R. Belmont.
+** - implemented PG overflow, aka "detune bug" (Ariel, Comix Zone, Shaq Fu, Spiderman,...), credits to Nemesis
+** - fixed SSG-EG support, credits to Nemesis and additional fixes from Alone Coder
+** - modified EG rates and frequency, tested by Nemesis on real hardware
+** - implemented LFO phase update for CH3 special mode (Warlock birds, Alladin bug sound)
+** - fixed Attack Rate update (Batman & Robin intro)
+** - fixed attenuation level at the start of Substain (Gynoug explosions)
+** - fixed EG decay->substain transition to handle special cases, like SL=0 and Decay rate is very slow (Mega Turrican tracks 03,09...)
+**
+** 06-23-2007 Zsolt Vasvari:
+** - changed the timing not to require the use of floating point calculations
+**
+** 03-08-2003 Jarek Burczynski:
+** - fixed YM2608 initial values (after the reset)
+** - fixed flag and irqmask handling (YM2608)
+** - fixed BUFRDY flag handling (YM2608)
+**
+** 14-06-2003 Jarek Burczynski:
+** - implemented all of the YM2608 status register flags
+** - implemented support for external memory read/write via YM2608
+** - implemented support for deltat memory limit register in YM2608 emulation
+**
+** 22-05-2003 Jarek Burczynski:
+** - fixed LFO PM calculations (copy&paste bugfix)
+**
+** 08-05-2003 Jarek Burczynski:
+** - fixed SSG support
+**
+** 22-04-2003 Jarek Burczynski:
+** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608)
+**
+** 15-04-2003 Jarek Burczynski:
+** - added support for YM2608's register 0x110 - status mask
+**
+** 01-12-2002 Jarek Burczynski:
+** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608)
+** The addressing patch used for early Neo-Geo games can be removed now.
+**
+** 26-11-2002 Jarek Burczynski, Nicola Salmoria:
+** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to:
+** - added emulation of YM2608 drums.
+** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608)
+**
+** 16-08-2002 Jarek Burczynski:
+** - binary exact Envelope Generator (verified on real YM2203);
+** identical to YM2151
+** - corrected 'off by one' error in feedback calculations (when feedback is off)
+** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610)
+**
+** 18-12-2001 Jarek Burczynski:
+** - added SSG-EG support (verified on real YM2203)
+**
+** 12-08-2001 Jarek Burczynski:
+** - corrected sin_tab and tl_tab data (verified on real chip)
+** - corrected feedback calculations (verified on real chip)
+** - corrected phase generator calculations (verified on real chip)
+** - corrected envelope generator calculations (verified on real chip)
+** - corrected FM volume level (YM2610 and YM2610B).
+** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) :
+** this was needed to calculate YM2610 FM channels output correctly.
+** (Each FM channel is calculated as in other chips, but the output of the channel
+** gets shifted right by one *before* sending to accumulator. That was impossible to do
+** with previous implementation).
+**
+** 23-07-2001 Jarek Burczynski, Nicola Salmoria:
+** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip)
+**
+** 11-06-2001 Jarek Burczynski:
+** - corrected end of sample bug in ADPCMA_calc_cha().
+** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits).
+**
+** 08-12-98 hiro-shi:
+** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA
+** move ROM limit check.(CALC_CH? -> 2610Write1/2)
+** test program (ADPCMB_TEST)
+** move ADPCM A/B end check.
+** ADPCMB repeat flag(no check)
+** change ADPCM volume rate (8->16) (32->48).
+**
+** 09-12-98 hiro-shi:
+** change ADPCM volume. (8->16, 48->64)
+** replace ym2610 ch0/3 (YM-2610B)
+** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff.
+** add ADPCM_SHIFT_MASK
+** change ADPCMA_DECODE_MIN/MAX.
+*/
+
+
+
+
+/************************************************************************/
+/* comment of hiro-shi(Hiromitsu Shioya) */
+/* YM2610(B) = OPN-B */
+/* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */
+/* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */
+/************************************************************************/
+
+#include "emu.h"
+#include "fm.h"
+
+
+/* include external DELTA-T unit (when needed) */
+#if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B)
+ #include "ymdeltat.h"
+#endif
+
+/* shared function building option */
+#define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B)
+#define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608)
+
+
+/* globals */
+#define TYPE_SSG 0x01 /* SSG support */
+#define TYPE_LFOPAN 0x02 /* OPN type LFO and PAN */
+#define TYPE_6CH 0x04 /* FM 6CH / 3CH */
+#define TYPE_DAC 0x08 /* YM2612's DAC device */
+#define TYPE_ADPCM 0x10 /* two ADPCM units */
+#define TYPE_2610 0x20 /* bogus flag to differentiate 2608 from 2610 */
+
+
+#define TYPE_YM2203 (TYPE_SSG)
+#define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM)
+#define TYPE_YM2610 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM |TYPE_2610)
+
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<<FREQ_SH)-1)
+
+#define ENV_BITS 10
+#define ENV_LEN (1<<ENV_BITS)
+#define ENV_STEP (128.0/ENV_LEN)
+
+#define MAX_ATT_INDEX (ENV_LEN-1) /* 1023 */
+#define MIN_ATT_INDEX (0) /* 0 */
+
+#define EG_ATT 4
+#define EG_DEC 3
+#define EG_SUS 2
+#define EG_REL 1
+#define EG_OFF 0
+
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
+
+
+#if (FM_SAMPLE_BITS==16)
+ #define FINAL_SH (0)
+ #define MAXOUT (+32767)
+ #define MINOUT (-32768)
+#else
+ #define FINAL_SH (8)
+ #define MAXOUT (+127)
+ #define MINOUT (-128)
+#endif
+
+
+/* TL_TAB_LEN is calculated as:
+* 13 - sinus amplitude bits (Y axis)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (13*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+#define ENV_QUIET (TL_TAB_LEN>>3)
+
+/* sin waveform table in 'decibel' scale */
+static unsigned int sin_tab[SIN_LEN];
+
+/* sustain level table (3dB per step) */
+/* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */
+/* 1, 2, 4, 8, 16, 32, 64 (value)*/
+/* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/
+
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) (UINT32) ( db * (4.0/ENV_STEP) )
+static const UINT32 sl_table[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+
+#define RATE_STEPS (8)
+static const UINT8 eg_inc[19*RATE_STEPS]={
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */
+/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */
+/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */
+/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */
+
+/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */
+/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */
+/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(17) in this table - it's directly in the code */
+static const UINT8 eg_rate_select[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */
+/* 32 infinite time rates */
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+
+/* rates 00-11 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 12 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 13 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 14 */
+O(12),O(13),O(14),O(15),
+
+/* rate 15 */
+O(16),O(16),O(16),O(16),
+
+/* 32 dummy rates (same as 15 3) */
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16)
+
+};
+
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/
+/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */
+/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */
+/* 32 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+/* rates 00-11 */
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 12 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 32 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0)
+
+};
+#undef O
+
+static const UINT8 dt_tab[4 * 32]={
+/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/
+/* FD=0 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* FD=1 */
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
+/* FD=2 */
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5,
+ 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16,
+/* FD=3 */
+ 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
+ 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22
+};
+
+
+/* OPN key frequency number -> key code follow table */
+/* fnum higher 4bit -> keycode lower 2bit */
+static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3};
+
+
+/* 8 LFO speed parameters */
+/* each value represents number of samples that one LFO level will last for */
+static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5};
+
+
+
+/*There are 4 different LFO AM depths available, they are:
+ 0 dB, 1.4 dB, 5.9 dB, 11.8 dB
+ Here is how it is generated (in EG steps):
+
+ 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0
+ 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0
+ 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0
+
+ (1.4 dB is losing precision as you can see)
+
+ It's implemented as generator from 0..126 with step 2 then a shift
+ right N times, where N is:
+ 8 for 0 dB
+ 3 for 1.4 dB
+ 1 for 5.9 dB
+ 0 for 11.8 dB
+*/
+static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0};
+
+
+
+/*There are 8 different LFO PM depths available, they are:
+ 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents)
+
+ Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10
+ (bits 8,9,10 = FNUM MSB from OCT/FNUM register)
+
+ Here we store only first quarter (positive one) of full waveform.
+ Full table (lfo_pm_table) containing all 128 waveforms is build
+ at run (init) time.
+
+ One value in table below represents 4 (four) basic LFO steps
+ (1 PM step = 4 AM steps).
+
+ For example:
+ at LFO SPEED=0 (which is 108 samples per basic LFO step)
+ one value from "lfo_pm_output" table lasts for 432 consecutive
+ samples (4*108=432) and one full LFO waveform cycle lasts for 13824
+ samples (32*432=13824; 32 because we store only a quarter of whole
+ waveform in the table below)
+*/
+static const UINT8 lfo_pm_output[7*8][8]={ /* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */
+/* FNUM BIT 4: 000 0001xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1},
+
+/* FNUM BIT 5: 000 0010xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3},
+
+/* FNUM BIT 6: 000 0100xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1},
+/* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3},
+/* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6},
+
+/* FNUM BIT 7: 000 1000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1},
+/* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2},
+/* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3},
+/* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6},
+/* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+
+/* FNUM BIT 8: 001 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2},
+/* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3},
+/* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4},
+/* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6},
+/* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+/* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+
+/* FNUM BIT 9: 010 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2},
+/* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4},
+/* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6},
+/* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8},
+/* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+/* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+/* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30},
+
+/* FNUM BIT10: 100 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4},
+/* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8},
+/* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc},
+/* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10},
+/* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+/* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30},
+/* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60},
+
+};
+
+/* all 128 LFO PM waveforms */
+static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */
+
+
+
+
+
+/* register number to channel number , slot offset */
+#define OPN_CHAN(N) (N&3)
+#define OPN_SLOT(N) ((N>>2)&3)
+
+/* slot number */
+#define SLOT1 0
+#define SLOT2 2
+#define SLOT3 1
+#define SLOT4 3
+
+/* bit0 = Right enable , bit1 = Left enable */
+#define OUTD_RIGHT 1
+#define OUTD_LEFT 2
+#define OUTD_CENTER 3
+
+
+/* save output as raw 16-bit sample */
+/* #define SAVE_SAMPLE */
+
+#ifdef SAVE_SAMPLE
+static FILE *sample[1];
+ #if 1 /*save to MONO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #else /*save to STEREO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ pom = rt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #endif
+#endif
+
+
+/* struct describing a single operator (SLOT) */
+struct FM_SLOT
+{
+ INT32 *DT; /* detune :dt_tab[DT] */
+ UINT8 KSR; /* key scale rate :3-KSR */
+ UINT32 ar; /* attack rate */
+ UINT32 d1r; /* decay rate */
+ UINT32 d2r; /* sustain rate */
+ UINT32 rr; /* release rate */
+ UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */
+ UINT32 mul; /* multiple :ML_TABLE[ML] */
+
+ /* Phase Generator */
+ UINT32 phase; /* phase counter */
+ INT32 Incr; /* phase step */
+
+ /* Envelope Generator */
+ UINT8 state; /* phase type */
+ UINT32 tl; /* total level: TL << 3 */
+ INT32 volume; /* envelope counter */
+ UINT32 sl; /* sustain level:sl_table[SL] */
+ UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */
+
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT8 eg_sh_d1r; /* (decay state) */
+ UINT8 eg_sel_d1r; /* (decay state) */
+ UINT8 eg_sh_d2r; /* (sustain state) */
+ UINT8 eg_sel_d2r; /* (sustain state) */
+ UINT8 eg_sh_rr; /* (release state) */
+ UINT8 eg_sel_rr; /* (release state) */
+
+ UINT8 ssg; /* SSG-EG waveform */
+ UINT8 ssgn; /* SSG-EG negated output */
+
+ UINT32 key; /* 0=last key was KEY OFF, 1=KEY ON */
+
+ /* LFO */
+ UINT32 AMmask; /* AM enable flag */
+
+};
+
+struct FM_CH
+{
+ FM_SLOT SLOT[4]; /* four SLOTs (operators) */
+
+ UINT8 ALGO; /* algorithm */
+ UINT8 FB; /* feedback shift */
+ INT32 op1_out[2]; /* op1 output for feedback */
+
+ INT32 *connect1; /* SLOT1 output pointer */
+ INT32 *connect3; /* SLOT3 output pointer */
+ INT32 *connect2; /* SLOT2 output pointer */
+ INT32 *connect4; /* SLOT4 output pointer */
+
+ INT32 *mem_connect;/* where to put the delayed sample (MEM) */
+ INT32 mem_value; /* delayed sample (MEM) value */
+
+ INT32 pms; /* channel PMS */
+ UINT8 ams; /* channel AMS */
+
+ UINT32 fc; /* fnum,blk:adjusted to sample rate */
+ UINT8 kcode; /* key code: */
+ UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
+};
+
+
+struct FM_ST
+{
+ device_t *device;
+ void * param; /* this chip parameter */
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ int timer_prescaler; /* timer prescaler */
+#if FM_BUSY_FLAG_SUPPORT
+ TIME_TYPE busy_expiry_time; /* expiry time of the busy status */
+#endif
+ UINT8 address; /* address register */
+ UINT8 irq; /* interrupt level */
+ UINT8 irqmask; /* irq mask */
+ UINT8 status; /* status flag */
+ UINT32 mode; /* mode CSM / 3SLOT */
+ UINT8 prescaler_sel; /* prescaler selector */
+ UINT8 fn_h; /* freq latch */
+ INT32 TA; /* timer a */
+ INT32 TAC; /* timer a counter */
+ UINT8 TB; /* timer b */
+ INT32 TBC; /* timer b counter */
+ /* local time tables */
+ INT32 dt_tab[8][32]; /* DeTune table */
+ /* Extention Timer and IRQ handler */
+ FM_TIMERHANDLER timer_handler;
+ FM_IRQHANDLER IRQ_Handler;
+ const ssg_callbacks *SSG;
+};
+
+
+
+/***********************************************************/
+/* OPN unit */
+/***********************************************************/
+
+/* OPN 3slot struct */
+struct FM_3SLOT
+{
+ UINT32 fc[3]; /* fnum3,blk3: calculated */
+ UINT8 fn_h; /* freq3 latch */
+ UINT8 kcode[3]; /* key code */
+ UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
+};
+
+/* OPN/A/B common state */
+struct FM_OPN
+{
+ UINT8 type; /* chip type */
+ FM_ST ST; /* general state */
+ FM_3SLOT SL3; /* 3 slot mode state */
+ FM_CH *P_CH; /* pointer of CH */
+ unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/64/3 */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow;/* envelope generator timer overlfows every 3 samples (on real chip) */
+
+
+ /* there are 2048 FNUMs that can be generated using FNUM/BLK registers
+ but LFO works with one more bit of a precision so we really need 4096 elements */
+
+ UINT32 fn_table[4096]; /* fnumber->increment counter */
+ UINT32 fn_max; /* maximal phase increment (used for phase overflow) */
+
+ /* LFO */
+ UINT32 LFO_AM; /* runtime LFO calculations helper */
+ INT32 LFO_PM; /* runtime LFO calculations helper */
+
+ UINT32 lfo_cnt;
+ UINT32 lfo_inc;
+
+ UINT32 lfo_freq[8]; /* LFO FREQ table */
+
+ INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */
+ INT32 mem; /* one sample delay memory */
+
+ INT32 out_fm[8]; /* outputs of working channels */
+
+#if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B)
+ INT32 out_adpcm[4]; /* channel output NONE,LEFT,RIGHT or CENTER for YM2608/YM2610 ADPCM */
+ INT32 out_delta[4]; /* channel output NONE,LEFT,RIGHT or CENTER for YM2608/YM2610 DELTAT*/
+#endif
+};
+
+
+
+/* current chip state */
+
+/* log output level */
+#define LOG_ERR 3 /* ERROR */
+#define LOG_WAR 2 /* WARNING */
+#define LOG_INF 1 /* INFORMATION */
+#define LOG_LEVEL LOG_INF
+
+#ifndef __RAINE__
+#define LOG(n,x) do { if( (n)>=LOG_LEVEL ) logerror x; } while (0)
+#endif
+
+/* limitter */
+#define Limit(val, max,min) { \
+ if ( val > max ) val = max; \
+ else if ( val < min ) val = min; \
+}
+
+
+/* status set and IRQ handling */
+INLINE void FM_STATUS_SET(FM_ST *ST,int flag)
+{
+ /* set status flag */
+ ST->status |= flag;
+ if ( !(ST->irq) && (ST->status & ST->irqmask) )
+ {
+ ST->irq = 1;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,1);
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void FM_STATUS_RESET(FM_ST *ST,int flag)
+{
+ /* reset status flag */
+ ST->status &=~flag;
+ if ( (ST->irq) && !(ST->status & ST->irqmask) )
+ {
+ ST->irq = 0;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,0);
+ }
+}
+
+/* IRQ mask set */
+INLINE void FM_IRQMASK_SET(FM_ST *ST,int flag)
+{
+ ST->irqmask = flag;
+ /* IRQ handling check */
+ FM_STATUS_SET(ST,0);
+ FM_STATUS_RESET(ST,0);
+}
+
+/* OPN Mode Register Write */
+INLINE void set_timers( FM_ST *ST, void *n, int v )
+{
+ /* b7 = CSM MODE */
+ /* b6 = 3 slot mode */
+ /* b5 = reset b */
+ /* b4 = reset a */
+ /* b3 = timer enable b */
+ /* b2 = timer enable a */
+ /* b1 = load b */
+ /* b0 = load a */
+ ST->mode = v;
+
+ /* reset Timer b flag */
+ if( v & 0x20 )
+ FM_STATUS_RESET(ST,0x02);
+ /* reset Timer a flag */
+ if( v & 0x10 )
+ FM_STATUS_RESET(ST,0x01);
+ /* load b */
+ if( v & 0x02 )
+ {
+ if( ST->TBC == 0 )
+ {
+ ST->TBC = ( 256-ST->TB)<<4;
+ /* External timer handler */
+ if (ST->timer_handler) (ST->timer_handler)(n,1,ST->TBC * ST->timer_prescaler,ST->clock);
+ }
+ }
+ else
+ { /* stop timer b */
+ if( ST->TBC != 0 )
+ {
+ ST->TBC = 0;
+ if (ST->timer_handler) (ST->timer_handler)(n,1,0,ST->clock);
+ }
+ }
+ /* load a */
+ if( v & 0x01 )
+ {
+ if( ST->TAC == 0 )
+ {
+ ST->TAC = (1024-ST->TA);
+ /* External timer handler */
+ if (ST->timer_handler) (ST->timer_handler)(n,0,ST->TAC * ST->timer_prescaler,ST->clock);
+ }
+ }
+ else
+ { /* stop timer a */
+ if( ST->TAC != 0 )
+ {
+ ST->TAC = 0;
+ if (ST->timer_handler) (ST->timer_handler)(n,0,0,ST->clock);
+ }
+ }
+}
+
+
+/* Timer A Overflow */
+INLINE void TimerAOver(FM_ST *ST)
+{
+ /* set status (if enabled) */
+ if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01);
+ /* clear or reload the counter */
+ ST->TAC = (1024-ST->TA);
+ if (ST->timer_handler) (ST->timer_handler)(ST->param,0,ST->TAC * ST->timer_prescaler,ST->clock);
+}
+/* Timer B Overflow */
+INLINE void TimerBOver(FM_ST *ST)
+{
+ /* set status (if enabled) */
+ if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02);
+ /* clear or reload the counter */
+ ST->TBC = ( 256-ST->TB)<<4;
+ if (ST->timer_handler) (ST->timer_handler)(ST->param,1,ST->TBC * ST->timer_prescaler,ST->clock);
+}
+
+
+#if FM_INTERNAL_TIMER
+/* ----- internal timer mode , update timer */
+
+/* ---------- calculate timer A ---------- */
+ #define INTERNAL_TIMER_A(ST,CSM_CH) \
+ { \
+ if( ST->TAC && (ST->timer_handler==0) ) \
+ if( (ST->TAC -= (int)(ST->freqbase*4096)) <= 0 ) \
+ { \
+ TimerAOver( ST ); \
+ /* CSM mode total level latch and auto key on */ \
+ if( ST->mode & 0x80 ) \
+ CSMKeyControll( CSM_CH ); \
+ } \
+ }
+/* ---------- calculate timer B ---------- */
+ #define INTERNAL_TIMER_B(ST,step) \
+ { \
+ if( ST->TBC && (ST->timer_handler==0) ) \
+ if( (ST->TBC -= (int)(ST->freqbase*4096*step)) <= 0 ) \
+ TimerBOver( ST ); \
+ }
+#else /* FM_INTERNAL_TIMER */
+/* external timer mode */
+#define INTERNAL_TIMER_A(ST,CSM_CH)
+#define INTERNAL_TIMER_B(ST,step)
+#endif /* FM_INTERNAL_TIMER */
+
+
+
+#if FM_BUSY_FLAG_SUPPORT
+#define FM_BUSY_CLEAR(ST) ((ST)->busy_expiry_time = UNDEFINED_TIME)
+INLINE UINT8 FM_STATUS_FLAG(FM_ST *ST)
+{
+ if( COMPARE_TIMES(ST->busy_expiry_time, UNDEFINED_TIME) != 0 )
+ {
+ if (COMPARE_TIMES(ST->busy_expiry_time, FM_GET_TIME_NOW(&ST->device->machine())) > 0)
+ return ST->status | 0x80; /* with busy */
+ /* expire */
+ FM_BUSY_CLEAR(ST);
+ }
+ return ST->status;
+}
+INLINE void FM_BUSY_SET(FM_ST *ST,int busyclock )
+{
+ TIME_TYPE expiry_period = MULTIPLY_TIME_BY_INT(attotime::from_hz(ST->clock), busyclock * ST->timer_prescaler);
+ ST->busy_expiry_time = ADD_TIMES(FM_GET_TIME_NOW(&ST->device->machine()), expiry_period);
+}
+#else
+#define FM_STATUS_FLAG(ST) ((ST)->status)
+#define FM_BUSY_SET(ST,bclock) {}
+#define FM_BUSY_CLEAR(ST) {}
+#endif
+
+
+
+
+INLINE void FM_KEYON(UINT8 type, FM_CH *CH , int s )
+{
+ FM_SLOT *SLOT = &CH->SLOT[s];
+ if( !SLOT->key )
+ {
+ SLOT->key = 1;
+ SLOT->phase = 0; /* restart Phase Generator */
+ SLOT->ssgn = (SLOT->ssg & 0x04) >> 1;
+ SLOT->state = EG_ATT;
+ }
+}
+
+INLINE void FM_KEYOFF(FM_CH *CH , int s )
+{
+ FM_SLOT *SLOT = &CH->SLOT[s];
+ if( SLOT->key )
+ {
+ SLOT->key = 0;
+ if (SLOT->state>EG_REL)
+ SLOT->state = EG_REL;/* phase -> Release */
+ }
+}
+
+/* set algorithm connection */
+static void setup_connection( FM_OPN *OPN, FM_CH *CH, int ch )
+{
+ INT32 *carrier = &OPN->out_fm[ch];
+
+ INT32 **om1 = &CH->connect1;
+ INT32 **om2 = &CH->connect3;
+ INT32 **oc1 = &CH->connect2;
+
+ INT32 **memc = &CH->mem_connect;
+
+ switch( CH->ALGO )
+ {
+ case 0:
+ /* M1---C1---MEM---M2---C2---OUT */
+ *om1 = &OPN->c1;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 1:
+ /* M1------+-MEM---M2---C2---OUT */
+ /* C1-+ */
+ *om1 = &OPN->mem;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 2:
+ /* M1-----------------+-C2---OUT */
+ /* C1---MEM---M2-+ */
+ *om1 = &OPN->c2;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 3:
+ /* M1---C1---MEM------+-C2---OUT */
+ /* M2-+ */
+ *om1 = &OPN->c1;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->c2;
+ break;
+ case 4:
+ /* M1---C1-+-OUT */
+ /* M2---C2-+ */
+ /* MEM: not used */
+ *om1 = &OPN->c1;
+ *oc1 = carrier;
+ *om2 = &OPN->c2;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ case 5:
+ /* +----C1----+ */
+ /* M1-+-MEM---M2-+-OUT */
+ /* +----C2----+ */
+ *om1 = 0; /* special mark */
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->m2;
+ break;
+ case 6:
+ /* M1---C1-+ */
+ /* M2-+-OUT */
+ /* C2-+ */
+ /* MEM: not used */
+ *om1 = &OPN->c1;
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ case 7:
+ /* M1-+ */
+ /* C1-+-OUT */
+ /* M2-+ */
+ /* C2-+ */
+ /* MEM: not used*/
+ *om1 = carrier;
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ }
+
+ CH->connect4 = carrier;
+}
+
+/* set detune & multiple */
+INLINE void set_det_mul(FM_ST *ST,FM_CH *CH,FM_SLOT *SLOT,int v)
+{
+ SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1;
+ SLOT->DT = ST->dt_tab[(v>>4)&7];
+ CH->SLOT[SLOT1].Incr=-1;
+}
+
+/* set total level */
+INLINE void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v)
+{
+ SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */
+}
+
+/* set attack rate & key scale */
+INLINE void set_ar_ksr(UINT8 type, FM_CH *CH,FM_SLOT *SLOT,int v)
+{
+ UINT8 old_KSR = SLOT->KSR;
+
+ SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->KSR = 3-(v>>6);
+ if (SLOT->KSR != old_KSR)
+ {
+ CH->SLOT[SLOT1].Incr=-1;
+ }
+
+ /* refresh Attack rate */
+ if ((SLOT->ar + SLOT->ksr) < 32+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 17*RATE_STEPS;
+ }
+}
+
+/* set decay rate */
+INLINE void set_dr(UINT8 type, FM_SLOT *SLOT,int v)
+{
+ SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr];
+ SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr];
+}
+
+/* set sustain rate */
+INLINE void set_sr(UINT8 type, FM_SLOT *SLOT,int v)
+{
+ SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr];
+ SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr];
+}
+
+/* set release rate */
+INLINE void set_sl_rr(UINT8 type, FM_SLOT *SLOT,int v)
+{
+ SLOT->sl = sl_table[ v>>4 ];
+
+ SLOT->rr = 34 + ((v&0x0f)<<2);
+
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr];
+}
+
+
+
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm)
+{
+ UINT32 p;
+
+ p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm)
+{
+ UINT32 p;
+
+ p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+/* advance LFO to next sample */
+INLINE void advance_lfo(FM_OPN *OPN)
+{
+ UINT8 pos;
+
+ if (OPN->lfo_inc) /* LFO enabled ? */
+ {
+ OPN->lfo_cnt += OPN->lfo_inc;
+
+ pos = (OPN->lfo_cnt >> LFO_SH) & 127;
+
+
+ /* update AM when LFO output changes */
+
+ /* actually I can't optimize is this way without rewriting chan_calc()
+ to use chip->lfo_am instead of global lfo_am */
+ {
+ /* triangle */
+ /* AM: 0 to 126 step +2, 126 to 0 step -2 */
+ if (pos<64)
+ OPN->LFO_AM = (pos&63) * 2;
+ else
+ OPN->LFO_AM = 126 - ((pos&63) * 2);
+ }
+
+ /* PM works with 4 times slower clock */
+ pos >>= 2;
+ /* update PM when LFO output changes */
+ /*if (prev_pos != pos)*/ /* can't use global lfo_pm for this optimization, must be chip->lfo_pm instead*/
+ {
+ OPN->LFO_PM = pos;
+ }
+
+ }
+ else
+ {
+ OPN->LFO_AM = 0;
+ OPN->LFO_PM = 0;
+ }
+}
+
+/* changed from INLINE to static here to work around gcc 4.2.1 codegen bug */
+static void advance_eg_channel(FM_OPN *OPN, FM_SLOT *SLOT)
+{
+ unsigned int out;
+ unsigned int swap_flag = 0;
+ unsigned int i;
+
+
+ i = 4; /* four operators per channel */
+ do
+ {
+ /* reset SSG-EG swap flag */
+ swap_flag = 0;
+
+ switch(SLOT->state)
+ {
+ case EG_ATT: /* attack phase */
+ if ( !(OPN->eg_cnt & ((1<<SLOT->eg_sh_ar)-1) ) )
+ {
+ SLOT->volume += (~SLOT->volume *
+ (eg_inc[SLOT->eg_sel_ar + ((OPN->eg_cnt>>SLOT->eg_sh_ar)&7)])
+ ) >>4;
+
+ if (SLOT->volume <= MIN_ATT_INDEX)
+ {
+ SLOT->volume = MIN_ATT_INDEX;
+ SLOT->state = EG_DEC;
+ }
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ {
+ if (SLOT->ssg&0x08) /* SSG EG type envelope selected */
+ {
+ if ( !(OPN->eg_cnt & ((1<<SLOT->eg_sh_d1r)-1) ) )
+ {
+ SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)];
+
+ if ( SLOT->volume >= (INT32)(SLOT->sl) )
+ SLOT->state = EG_SUS;
+ }
+ }
+ else
+ {
+ if ( !(OPN->eg_cnt & ((1<<SLOT->eg_sh_d1r)-1) ) )
+ {
+ SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)];
+
+ if ( SLOT->volume >= (INT32)(SLOT->sl) )
+ SLOT->state = EG_SUS;
+ }
+ }
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+ if (SLOT->ssg&0x08) /* SSG EG type envelope selected */
+ {
+ if ( !(OPN->eg_cnt & ((1<<SLOT->eg_sh_d2r)-1) ) )
+ {
+ SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)];
+
+ if ( SLOT->volume >= ENV_QUIET )
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+
+ if (SLOT->ssg&0x01) /* bit 0 = hold */
+ {
+ if (SLOT->ssgn&1) /* have we swapped once ??? */
+ {
+ /* yes, so do nothing, just hold current level */
+ }
+ else
+ swap_flag = (SLOT->ssg&0x02) | 1 ; /* bit 1 = alternate */
+
+ }
+ else
+ {
+ /* same as KEY-ON operation */
+
+ /* restart of the Phase Generator should be here */
+ SLOT->phase = 0;
+
+ {
+ /* phase -> Attack */
+ SLOT->volume = 511;
+ SLOT->state = EG_ATT;
+ }
+
+ swap_flag = (SLOT->ssg&0x02); /* bit 1 = alternate */
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( !(OPN->eg_cnt & ((1<<SLOT->eg_sh_d2r)-1) ) )
+ {
+ SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)];
+
+ if ( SLOT->volume >= MAX_ATT_INDEX )
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ /* do not change SLOT->state (verified on real chip) */
+ }
+ }
+
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ if ( !(OPN->eg_cnt & ((1<<SLOT->eg_sh_rr)-1) ) )
+ {
+ /* SSG-EG affects Release phase also (Nemesis) */
+ SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)];
+
+ if ( SLOT->volume >= MAX_ATT_INDEX )
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ SLOT->state = EG_OFF;
+ }
+ }
+ break;
+
+ }
+
+
+ out = ((UINT32)SLOT->volume);
+
+ /* negate output (changes come from alternate bit, init comes from attack bit) */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn&2) && (SLOT->state > EG_REL))
+ out ^= MAX_ATT_INDEX;
+
+ /* we need to store the result here because we are going to change ssgn
+ in next instruction */
+ SLOT->vol_out = out + SLOT->tl;
+
+ /* reverse SLOT inversion flag */
+ SLOT->ssgn ^= swap_flag;
+
+ SLOT++;
+ i--;
+ }while (i);
+
+}
+
+
+
+#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask))
+
+INLINE void update_phase_lfo_slot(FM_OPN *OPN, FM_SLOT *SLOT, INT32 pms, UINT32 block_fnum)
+{
+ UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
+ INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + OPN->LFO_PM ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ UINT8 blk;
+ UINT32 fn;
+ int kc, fc;
+
+ block_fnum = block_fnum*2 + lfo_fn_table_index_offset;
+
+ blk = (block_fnum&0x7000) >> 12;
+ fn = block_fnum & 0xfff;
+
+ /* keyscale code */
+ kc = (blk<<2) | opn_fktable[fn >> 8];
+
+ /* phase increment counter */
+ fc = (OPN->fn_table[fn]>>(7-blk)) + SLOT->DT[kc];
+
+ /* detects frequency overflow (credits to Nemesis) */
+ if (fc < 0) fc += OPN->fn_max;
+
+ /* update phase */
+ SLOT->phase += (fc * SLOT->mul) >> 1;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ SLOT->phase += SLOT->Incr;
+ }
+}
+
+INLINE void update_phase_lfo_channel(FM_OPN *OPN, FM_CH *CH)
+{
+ UINT32 block_fnum = CH->block_fnum;
+
+ UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
+ INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + OPN->LFO_PM ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ UINT8 blk;
+ UINT32 fn;
+ int kc, fc, finc;
+
+ block_fnum = block_fnum*2 + lfo_fn_table_index_offset;
+
+ blk = (block_fnum&0x7000) >> 12;
+ fn = block_fnum & 0xfff;
+
+ /* keyscale code */
+ kc = (blk<<2) | opn_fktable[fn >> 8];
+
+ /* phase increment counter */
+ fc = (OPN->fn_table[fn]>>(7-blk));
+
+ /* detects frequency overflow (credits to Nemesis) */
+ finc = fc + CH->SLOT[SLOT1].DT[kc];
+
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT1].phase += (finc*CH->SLOT[SLOT1].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT2].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT2].phase += (finc*CH->SLOT[SLOT2].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT3].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT3].phase += (finc*CH->SLOT[SLOT3].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT4].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT4].phase += (finc*CH->SLOT[SLOT4].mul) >> 1;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr;
+ CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr;
+ CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr;
+ CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr;
+ }
+}
+
+INLINE void chan_calc(FM_OPN *OPN, FM_CH *CH, int chnum)
+{
+ unsigned int eg_out;
+
+ UINT32 AM = OPN->LFO_AM >> CH->ams;
+
+
+ OPN->m2 = OPN->c1 = OPN->c2 = OPN->mem = 0;
+
+ *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */
+
+ eg_out = volume_calc(&CH->SLOT[SLOT1]);
+ {
+ INT32 out = CH->op1_out[0] + CH->op1_out[1];
+ CH->op1_out[0] = CH->op1_out[1];
+
+ if( !CH->connect1 )
+ {
+ /* algorithm 5 */
+ OPN->mem = OPN->c1 = OPN->c2 = CH->op1_out[0];
+ }
+ else
+ {
+ /* other algorithms */
+ *CH->connect1 += CH->op1_out[0];
+ }
+
+ CH->op1_out[1] = 0;
+ if( eg_out < ENV_QUIET ) /* SLOT 1 */
+ {
+ if (!CH->FB)
+ out=0;
+
+ CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<<CH->FB) );
+ }
+ }
+
+ eg_out = volume_calc(&CH->SLOT[SLOT3]);
+ if( eg_out < ENV_QUIET ) /* SLOT 3 */
+ *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, OPN->m2);
+
+ eg_out = volume_calc(&CH->SLOT[SLOT2]);
+ if( eg_out < ENV_QUIET ) /* SLOT 2 */
+ *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, OPN->c1);
+
+ eg_out = volume_calc(&CH->SLOT[SLOT4]);
+ if( eg_out < ENV_QUIET ) /* SLOT 4 */
+ *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, OPN->c2);
+
+
+ /* store current MEM */
+ CH->mem_value = OPN->mem;
+
+ /* update phase counters AFTER output calculations */
+ if(CH->pms)
+ {
+ /* add support for 3 slot mode */
+ if ((OPN->ST.mode & 0xC0) && (chnum == 2))
+ {
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT1], CH->pms, OPN->SL3.block_fnum[1]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT2], CH->pms, OPN->SL3.block_fnum[2]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT3], CH->pms, OPN->SL3.block_fnum[0]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT4], CH->pms, CH->block_fnum);
+ }
+ else update_phase_lfo_channel(OPN, CH);
+ }
+ else /* no LFO phase modulation */
+ {
+ CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr;
+ CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr;
+ CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr;
+ CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr;
+ }
+}
+
+/* update phase increment and envelope generator */
+INLINE void refresh_fc_eg_slot(FM_OPN *OPN, FM_SLOT *SLOT , int fc , int kc )
+{
+ int ksr = kc >> SLOT->KSR;
+
+ fc += SLOT->DT[kc];
+
+ /* detects frequency overflow (credits to Nemesis) */
+ if (fc < 0) fc += OPN->fn_max;
+
+ /* (frequency) phase increment counter */
+ SLOT->Incr = (fc * SLOT->mul) >> 1;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT->ar + SLOT->ksr) < 32+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 17*RATE_STEPS;
+ }
+
+ SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr];
+ SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr];
+
+ SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr];
+ SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr];
+ }
+}
+
+/* update phase increment counters */
+/* Changed from INLINE to static to work around gcc 4.2.1 codegen bug */
+static void refresh_fc_eg_chan(FM_OPN *OPN, FM_CH *CH )
+{
+ if( CH->SLOT[SLOT1].Incr==-1)
+ {
+ int fc = CH->fc;
+ int kc = CH->kcode;
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT1] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT2] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT3] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT4] , fc , kc );
+ }
+}
+
+/* initialize time tables */
+static void init_timetables( FM_ST *ST , const UINT8 *dttable )
+{
+ int i,d;
+ double rate;
+
+#if 0
+ logerror("FM.C: samplerate=%8i chip clock=%8i freqbase=%f \n",
+ ST->rate, ST->clock, ST->freqbase );
+#endif
+
+ /* DeTune table */
+ for (d = 0;d <= 3;d++)
+ {
+ for (i = 0;i <= 31;i++)
+ {
+ rate = ((double)dttable[d*32 + i]) * SIN_LEN * ST->freqbase * (1<<FREQ_SH) / ((double)(1<<20));
+ ST->dt_tab[d][i] = (INT32) rate;
+ ST->dt_tab[d+4][i] = -ST->dt_tab[d][i];
+#if 0
+ logerror("FM.C: DT [%2i %2i] = %8x \n", d, i, ST->dt_tab[d][i] );
+#endif
+ }
+ }
+
+}
+
+
+static void reset_channels( FM_ST *ST , FM_CH *CH , int num )
+{
+ int c,s;
+
+ ST->mode = 0; /* normal mode */
+ ST->TA = 0;
+ ST->TAC = 0;
+ ST->TB = 0;
+ ST->TBC = 0;
+
+ for( c = 0 ; c < num ; c++ )
+ {
+ CH[c].fc = 0;
+ for(s = 0 ; s < 4 ; s++ )
+ {
+ CH[c].SLOT[s].ssg = 0;
+ CH[c].SLOT[s].ssgn = 0;
+ CH[c].SLOT[s].state= EG_OFF;
+ CH[c].SLOT[s].volume = MAX_ATT_INDEX;
+ CH[c].SLOT[s].vol_out= MAX_ATT_INDEX;
+ }
+ }
+}
+
+/* initialize generic tables */
+static int init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+ for (x=0; x<TL_RES_LEN; x++)
+ {
+ m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);
+ m = floor(m);
+
+ /* we never reach (1<<16) here due to the (x+1) */
+ /* result fits within 16 bits at maximum */
+
+ n = (int)m; /* 16 bits here */
+ n >>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 2; /* 13 bits here (as in real chip) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<13; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ #if 0
+ logerror("tl %04i", x);
+ for (i=0; i<13; i++)
+ logerror(", [%02i] %4x", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ]);
+ logerror("\n");
+ #endif
+ }
+ /*logerror("FM.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/
+
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* non-standard sinus */
+ m = sin( ((i*2)+1) * M_PI / SIN_LEN ); /* checked against the real chip */
+
+ /* we never reach zero here due to ((i*2)+1) */
+
+ if (m>0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+ /*logerror("FM.C: sin [%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[i],tl_tab[sin_tab[i]]);*/
+ }
+
+ /*logerror("FM.C: ENV_QUIET= %08x\n",ENV_QUIET );*/
+
+
+ /* build LFO PM modulation table */
+ for(i = 0; i < 8; i++) /* 8 PM depths */
+ {
+ UINT8 fnum;
+ for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */
+ {
+ UINT8 value;
+ UINT8 step;
+ UINT32 offset_depth = i;
+ UINT32 offset_fnum_bit;
+ UINT32 bit_tmp;
+
+ for (step=0; step<8; step++)
+ {
+ value = 0;
+ for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */
+ {
+ if (fnum & (1<<bit_tmp)) /* only if bit "bit_tmp" is set */
+ {
+ offset_fnum_bit = bit_tmp * 8;
+ value += lfo_pm_output[offset_fnum_bit + offset_depth][step];
+ }
+ }
+ lfo_pm_table[(fnum*32*8) + (i*32) + step + 0] = value;
+ lfo_pm_table[(fnum*32*8) + (i*32) +(step^7)+ 8] = value;
+ lfo_pm_table[(fnum*32*8) + (i*32) + step +16] = -value;
+ lfo_pm_table[(fnum*32*8) + (i*32) +(step^7)+24] = -value;
+ }
+#if 0
+ logerror("LFO depth=%1x FNUM=%04x (<<4=%4x): ", i, fnum, fnum<<4);
+ for (step=0; step<16; step++) /* dump only positive part of waveforms */
+ logerror("%02x ", lfo_pm_table[(fnum*32*8) + (i*32) + step] );
+ logerror("\n");
+#endif
+
+ }
+ }
+
+
+
+#ifdef SAVE_SAMPLE
+ sample[0]=fopen("sampsum.pcm","wb");
+#endif
+
+ return 1;
+
+}
+
+
+
+static void FMCloseTable( void )
+{
+#ifdef SAVE_SAMPLE
+ fclose(sample[0]);
+#endif
+ return;
+}
+
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(UINT8 type, FM_CH *CH)
+{
+ /* all key on then off (only for operators which were OFF!) */
+ if (!CH->SLOT[SLOT1].key)
+ {
+ FM_KEYON(type, CH,SLOT1);
+ FM_KEYOFF(CH, SLOT1);
+ }
+ if (!CH->SLOT[SLOT2].key)
+ {
+ FM_KEYON(type, CH,SLOT2);
+ FM_KEYOFF(CH, SLOT2);
+ }
+ if (!CH->SLOT[SLOT3].key)
+ {
+ FM_KEYON(type, CH,SLOT3);
+ FM_KEYOFF(CH, SLOT3);
+ }
+ if (!CH->SLOT[SLOT4].key)
+ {
+ FM_KEYON(type, CH,SLOT4);
+ FM_KEYOFF(CH, SLOT4);
+ }
+}
+
+#ifdef __SAVE_H__
+/* FM channel save , internal state only */
+static void FMsave_state_channel(device_t *device,FM_CH *CH,int num_ch)
+{
+ int slot , ch;
+
+ for(ch=0;ch<num_ch;ch++,CH++)
+ {
+ /* channel */
+ device->save_item(NAME(CH->op1_out), ch);
+ device->save_item(NAME(CH->fc), ch);
+ /* slots */
+ for(slot=0;slot<4;slot++)
+ {
+ FM_SLOT *SLOT = &CH->SLOT[slot];
+ device->save_item(NAME(SLOT->phase), ch * 4 + slot);
+ device->save_item(NAME(SLOT->state), ch * 4 + slot);
+ device->save_item(NAME(SLOT->volume), ch * 4 + slot);
+ }
+ }
+}
+
+static void FMsave_state_st(device_t *device,FM_ST *ST)
+{
+#if FM_BUSY_FLAG_SUPPORT
+ device->save_item(NAME(ST->busy_expiry_time) );
+#endif
+ device->save_item(NAME(ST->address) );
+ device->save_item(NAME(ST->irq) );
+ device->save_item(NAME(ST->irqmask) );
+ device->save_item(NAME(ST->status) );
+ device->save_item(NAME(ST->mode) );
+ device->save_item(NAME(ST->prescaler_sel) );
+ device->save_item(NAME(ST->fn_h) );
+ device->save_item(NAME(ST->TA) );
+ device->save_item(NAME(ST->TAC) );
+ device->save_item(NAME(ST->TB) );
+ device->save_item(NAME(ST->TBC) );
+}
+#endif /* _STATE_H */
+
+#if BUILD_OPN
+
+
+
+/* prescaler set (and make time tables) */
+static void OPNSetPres(FM_OPN *OPN, int pres, int timer_prescaler, int SSGpres)
+{
+ int i;
+
+ /* frequency base */
+ OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pres : 0;
+
+#if 0
+ OPN->ST.rate = (double)OPN->ST.clock / pres;
+ OPN->ST.freqbase = 1.0;
+#endif
+
+ OPN->eg_timer_add = (1<<EG_SH) * OPN->ST.freqbase;
+ OPN->eg_timer_overflow = ( 3 ) * (1<<EG_SH);
+
+
+ /* Timer base time */
+ OPN->ST.timer_prescaler = timer_prescaler;
+
+ /* SSG part prescaler set */
+ if( SSGpres ) (*OPN->ST.SSG->set_clock)( OPN->ST.param, OPN->ST.clock * 2 / SSGpres );
+
+ /* make time tables */
+ init_timetables( &OPN->ST, dt_tab );
+
+ /* there are 2048 FNUMs that can be generated using FNUM/BLK registers
+ but LFO works with one more bit of a precision so we really need 4096 elements */
+ /* calculate fnumber -> increment counter table */
+ for(i = 0; i < 4096; i++)
+ {
+ /* freq table for octave 7 */
+ /* OPN phase increment counter = 20bit */
+ OPN->fn_table[i] = (UINT32)( (double)i * 32 * OPN->ST.freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+#if 0
+ logerror("FM.C: fn_table[%4i] = %08x (dec=%8i)\n",
+ i, OPN->fn_table[i]>>6,OPN->fn_table[i]>>6 );
+#endif
+ }
+
+ /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */
+ OPN->fn_max = (UINT32)( (double)0x20000 * OPN->ST.freqbase * (1<<(FREQ_SH-10)) );
+
+ /* LFO freq. table */
+ for(i = 0; i < 8; i++)
+ {
+ /* Amplitude modulation: 64 output levels (triangle waveform); 1 level lasts for one of "lfo_samples_per_step" samples */
+ /* Phase modulation: one entry from lfo_pm_output lasts for one of 4 * "lfo_samples_per_step" samples */
+ OPN->lfo_freq[i] = (1.0 / lfo_samples_per_step[i]) * (1<<LFO_SH) * OPN->ST.freqbase;
+#if 0
+ logerror("FM.C: lfo_freq[%i] = %08x (dec=%8i)\n",
+ i, OPN->lfo_freq[i],OPN->lfo_freq[i] );
+#endif
+ }
+}
+
+
+
+/* write a OPN mode register 0x20-0x2f */
+static void OPNWriteMode(FM_OPN *OPN, int r, int v)
+{
+ UINT8 c;
+ FM_CH *CH;
+
+ switch(r)
+ {
+ case 0x21: /* Test */
+ break;
+ case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/YM2612) */
+ if( OPN->type & TYPE_LFOPAN )
+ {
+ if (v&0x08) /* LFO enabled ? */
+ {
+ OPN->lfo_inc = OPN->lfo_freq[v&7];
+ }
+ else
+ {
+ OPN->lfo_inc = 0;
+ }
+ }
+ break;
+ case 0x24: /* timer A High 8*/
+ OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2);
+ break;
+ case 0x25: /* timer A Low 2*/
+ OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3);
+ break;
+ case 0x26: /* timer B */
+ OPN->ST.TB = v;
+ break;
+ case 0x27: /* mode, timer control */
+ set_timers( &(OPN->ST),OPN->ST.param,v );
+ break;
+ case 0x28: /* key on / off */
+ c = v & 0x03;
+ if( c == 3 ) break;
+ if( (v&0x04) && (OPN->type & TYPE_6CH) ) c+=3;
+ CH = OPN->P_CH;
+ CH = &CH[c];
+ if(v&0x10) FM_KEYON(OPN->type,CH,SLOT1); else FM_KEYOFF(CH,SLOT1);
+ if(v&0x20) FM_KEYON(OPN->type,CH,SLOT2); else FM_KEYOFF(CH,SLOT2);
+ if(v&0x40) FM_KEYON(OPN->type,CH,SLOT3); else FM_KEYOFF(CH,SLOT3);
+ if(v&0x80) FM_KEYON(OPN->type,CH,SLOT4); else FM_KEYOFF(CH,SLOT4);
+ break;
+ }
+}
+
+/* write a OPN register (0x30-0xff) */
+static void OPNWriteReg(FM_OPN *OPN, int r, int v)
+{
+ FM_CH *CH;
+ FM_SLOT *SLOT;
+
+ UINT8 c = OPN_CHAN(r);
+
+ if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */
+
+ if (r >= 0x100) c+=3;
+
+ CH = OPN->P_CH;
+ CH = &CH[c];
+
+ SLOT = &(CH->SLOT[OPN_SLOT(r)]);
+
+ switch( r & 0xf0 )
+ {
+ case 0x30: /* DET , MUL */
+ set_det_mul(&OPN->ST,CH,SLOT,v);
+ break;
+
+ case 0x40: /* TL */
+ set_tl(CH,SLOT,v);
+ break;
+
+ case 0x50: /* KS, AR */
+ set_ar_ksr(OPN->type,CH,SLOT,v);
+ break;
+
+ case 0x60: /* bit7 = AM ENABLE, DR */
+ set_dr(OPN->type, SLOT,v);
+
+ if(OPN->type & TYPE_LFOPAN) /* YM2608/2610/2610B/2612 */
+ {
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;
+ }
+ break;
+
+ case 0x70: /* SR */
+ set_sr(OPN->type,SLOT,v);
+ break;
+
+ case 0x80: /* SL, RR */
+ set_sl_rr(OPN->type,SLOT,v);
+ break;
+
+ case 0x90: /* SSG-EG */
+ SLOT->ssg = v&0x0f;
+ SLOT->ssgn = (v&0x04)>>1; /* bit 1 in ssgn = attack */
+
+ /* SSG-EG envelope shapes :
+
+ E AtAlH
+ 1 0 0 0 \\\\
+
+ 1 0 0 1 \___
+
+ 1 0 1 0 \/\/
+ ___
+ 1 0 1 1 \
+
+ 1 1 0 0 ////
+ ___
+ 1 1 0 1 /
+
+ 1 1 1 0 /\/\
+
+ 1 1 1 1 /___
+
+
+ E = SSG-EG enable
+
+
+ The shapes are generated using Attack, Decay and Sustain phases.
+
+ Each single character in the diagrams above represents this whole
+ sequence:
+
+ - when KEY-ON = 1, normal Attack phase is generated (*without* any
+ difference when compared to normal mode),
+
+ - later, when envelope level reaches minimum level (max volume),
+ the EG switches to Decay phase (which works with bigger steps
+ when compared to normal mode - see below),
+
+ - later when envelope level passes the SL level,
+ the EG swithes to Sustain phase (which works with bigger steps
+ when compared to normal mode - see below),
+
+ - finally when envelope level reaches maximum level (min volume),
+ the EG switches to Attack phase again (depends on actual waveform).
+
+ Important is that when switch to Attack phase occurs, the phase counter
+ of that operator will be zeroed-out (as in normal KEY-ON) but not always.
+ (I havent found the rule for that - perhaps only when the output level is low)
+
+ The difference (when compared to normal Envelope Generator mode) is
+ that the resolution in Decay and Sustain phases is 4 times lower;
+ this results in only 256 steps instead of normal 1024.
+ In other words:
+ when SSG-EG is disabled, the step inside of the EG is one,
+ when SSG-EG is enabled, the step is four (in Decay and Sustain phases).
+
+ Times between the level changes are the same in both modes.
+
+
+ Important:
+ Decay 1 Level (so called SL) is compared to actual SSG-EG output, so
+ it is the same in both SSG and no-SSG modes, with this exception:
+
+ when the SSG-EG is enabled and is generating raising levels
+ (when the EG output is inverted) the SL will be found at wrong level !!!
+ For example, when SL=02:
+ 0 -6 = -6dB in non-inverted EG output
+ 96-6 = -90dB in inverted EG output
+ Which means that EG compares its level to SL as usual, and that the
+ output is simply inverted afterall.
+
+
+ The Yamaha's manuals say that AR should be set to 0x1f (max speed).
+ That is not necessary, but then EG will be generating Attack phase.
+
+ */
+
+
+ break;
+
+ case 0xa0:
+ switch( OPN_SLOT(r) )
+ {
+ case 0: /* 0xa0-0xa2 : FNUM1 */
+ {
+ UINT32 fn = (((UINT32)( (OPN->ST.fn_h)&7))<<8) + v;
+ UINT8 blk = OPN->ST.fn_h>>3;
+ /* keyscale code */
+ CH->kcode = (blk<<2) | opn_fktable[fn >> 7];
+ /* phase increment counter */
+ CH->fc = OPN->fn_table[fn*2]>>(7-blk);
+
+ /* store fnum in clear form for LFO PM calculations */
+ CH->block_fnum = (blk<<11) | fn;
+
+ CH->SLOT[SLOT1].Incr=-1;
+ }
+ break;
+ case 1: /* 0xa4-0xa6 : FNUM2,BLK */
+ OPN->ST.fn_h = v&0x3f;
+ break;
+ case 2: /* 0xa8-0xaa : 3CH FNUM1 */
+ if(r < 0x100)
+ {
+ UINT32 fn = (((UINT32)(OPN->SL3.fn_h&7))<<8) + v;
+ UINT8 blk = OPN->SL3.fn_h>>3;
+ /* keyscale code */
+ OPN->SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7];
+ /* phase increment counter */
+ OPN->SL3.fc[c] = OPN->fn_table[fn*2]>>(7-blk);
+ OPN->SL3.block_fnum[c] = (blk<<11) | fn;
+ (OPN->P_CH)[2].SLOT[SLOT1].Incr=-1;
+ }
+ break;
+ case 3: /* 0xac-0xae : 3CH FNUM2,BLK */
+ if(r < 0x100)
+ OPN->SL3.fn_h = v&0x3f;
+ break;
+ }
+ break;
+
+ case 0xb0:
+ switch( OPN_SLOT(r) )
+ {
+ case 0: /* 0xb0-0xb2 : FB,ALGO */
+ {
+ int feedback = (v>>3)&7;
+ CH->ALGO = v&7;
+ CH->FB = feedback ? feedback+6 : 0;
+ setup_connection( OPN, CH, c );
+ }
+ break;
+ case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2610B/YM2610/YM2608) */
+ if( OPN->type & TYPE_LFOPAN)
+ {
+ /* b0-2 PMS */
+ CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */
+
+ /* b4-5 AMS */
+ CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03];
+
+ /* PAN : b7 = L, b6 = R */
+ OPN->pan[ c*2 ] = (v & 0x80) ? ~0 : 0;
+ OPN->pan[ c*2+1 ] = (v & 0x40) ? ~0 : 0;
+
+ }
+ break;
+ }
+ break;
+ }
+}
+
+#endif /* BUILD_OPN */
+
+#if BUILD_OPN_PRESCALER
+/*
+ prescaler circuit (best guess to verified chip behaviour)
+
+ +--------------+ +-sel2-+
+ | +--|in20 |
+ +---+ | +-sel1-+ | |
+M-CLK -+-|1/2|-+--|in10 | +---+ | out|--INT_CLOCK
+ | +---+ | out|-|1/3|-|in21 |
+ +----------|in11 | +---+ +------+
+ +------+
+
+reg.2d : sel2 = in21 (select sel2)
+reg.2e : sel1 = in11 (select sel1)
+reg.2f : sel1 = in10 , sel2 = in20 (clear selector)
+reset : sel1 = in11 , sel2 = in21 (clear both)
+
+*/
+static void OPNPrescaler_w(FM_OPN *OPN , int addr, int pre_divider)
+{
+ static const int opn_pres[4] = { 2*12 , 2*12 , 6*12 , 3*12 };
+ static const int ssg_pres[4] = { 1 , 1 , 4 , 2 };
+ int sel;
+
+ switch(addr)
+ {
+ case 0: /* when reset */
+ OPN->ST.prescaler_sel = 2;
+ break;
+ case 1: /* when postload */
+ break;
+ case 0x2d: /* divider sel : select 1/1 for 1/3line */
+ OPN->ST.prescaler_sel |= 0x02;
+ break;
+ case 0x2e: /* divider sel , select 1/3line for output */
+ OPN->ST.prescaler_sel |= 0x01;
+ break;
+ case 0x2f: /* divider sel , clear both selector to 1/2,1/2 */
+ OPN->ST.prescaler_sel = 0;
+ break;
+ }
+ sel = OPN->ST.prescaler_sel & 3;
+ /* update prescaler */
+ OPNSetPres( OPN, opn_pres[sel]*pre_divider,
+ opn_pres[sel]*pre_divider,
+ ssg_pres[sel]*pre_divider );
+}
+#endif /* BUILD_OPN_PRESCALER */
+
+#if BUILD_YM2203
+/*****************************************************************************/
+/* YM2203 local section */
+/*****************************************************************************/
+
+/* here's the virtual YM2203(OPN) */
+struct YM2203
+{
+ UINT8 REGS[256]; /* registers */
+ FM_OPN OPN; /* OPN state */
+ FM_CH CH[3]; /* channel state */
+};
+
+/* Generate samples for one of the YM2203s */
+void ym2203_update_one(void *chip, FMSAMPLE *buffer, int length)
+{
+ YM2203 *F2203 = (YM2203 *)chip;
+ FM_OPN *OPN = &F2203->OPN;
+ int i;
+ FMSAMPLE *buf = buffer;
+ FM_CH *cch[3];
+
+ cch[0] = &F2203->CH[0];
+ cch[1] = &F2203->CH[1];
+ cch[2] = &F2203->CH[2];
+
+
+ /* refresh PG and EG */
+ refresh_fc_eg_chan( OPN, cch[0] );
+ refresh_fc_eg_chan( OPN, cch[1] );
+ if( (F2203->OPN.ST.mode & 0xc0) )
+ {
+ /* 3SLOT MODE */
+ if( cch[2]->SLOT[SLOT1].Incr==-1)
+ {
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode );
+ }
+ }
+ else
+ refresh_fc_eg_chan( OPN, cch[2] );
+
+
+ /* YM2203 doesn't have LFO so we must keep these globals at 0 level */
+ OPN->LFO_AM = 0;
+ OPN->LFO_PM = 0;
+
+ /* buffering */
+ for (i=0; i < length ; i++)
+ {
+ /* clear outputs */
+ OPN->out_fm[0] = 0;
+ OPN->out_fm[1] = 0;
+ OPN->out_fm[2] = 0;
+
+ /* advance envelope generator */
+ OPN->eg_timer += OPN->eg_timer_add;
+ while (OPN->eg_timer >= OPN->eg_timer_overflow)
+ {
+ OPN->eg_timer -= OPN->eg_timer_overflow;
+ OPN->eg_cnt++;
+
+ advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]);
+ }
+
+ /* calculate FM */
+ chan_calc(OPN, cch[0], 0 );
+ chan_calc(OPN, cch[1], 1 );
+ chan_calc(OPN, cch[2], 2 );
+
+ /* buffering */
+ {
+ int lt;
+
+ lt = OPN->out_fm[0] + OPN->out_fm[1] + OPN->out_fm[2];
+
+ lt >>= FINAL_SH;
+
+ Limit( lt , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ SAVE_ALL_CHANNELS
+ #endif
+
+ /* buffering */
+ buf[i] = lt;
+ }
+
+ /* timer A control */
+ INTERNAL_TIMER_A( &F2203->OPN.ST , cch[2] )
+ }
+ INTERNAL_TIMER_B(&F2203->OPN.ST,length)
+}
+
+/* ---------- reset one of chip ---------- */
+void ym2203_reset_chip(void *chip)
+{
+ int i;
+ YM2203 *F2203 = (YM2203 *)chip;
+ FM_OPN *OPN = &F2203->OPN;
+
+ /* Reset Prescaler */
+ OPNPrescaler_w(OPN, 0 , 1 );
+ /* reset SSG section */
+ (*OPN->ST.SSG->reset)(OPN->ST.param);
+ /* status clear */
+ FM_IRQMASK_SET(&OPN->ST,0x03);
+ FM_BUSY_CLEAR(&OPN->ST);
+ OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */
+
+ OPN->eg_timer = 0;
+ OPN->eg_cnt = 0;
+
+ FM_STATUS_RESET(&OPN->ST, 0xff);
+
+ reset_channels( &OPN->ST , F2203->CH , 3 );
+ /* reset OPerator paramater */
+ for(i = 0xb2 ; i >= 0x30 ; i-- ) OPNWriteReg(OPN,i,0);
+ for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0);
+}
+
+#ifdef __SAVE_H__
+void ym2203_postload(void *chip)
+{
+ if (chip)
+ {
+ YM2203 *F2203 = (YM2203 *)chip;
+ int r;
+
+ /* prescaler */
+ OPNPrescaler_w(&F2203->OPN,1,1);
+
+ /* SSG registers */
+ for(r=0;r<16;r++)
+ {
+ (*F2203->OPN.ST.SSG->write)(F2203->OPN.ST.param,0,r);
+ (*F2203->OPN.ST.SSG->write)(F2203->OPN.ST.param,1,F2203->REGS[r]);
+ }
+
+ /* OPN registers */
+ /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */
+ for(r=0x30;r<0x9e;r++)
+ if((r&3) != 3)
+ OPNWriteReg(&F2203->OPN,r,F2203->REGS[r]);
+ /* FB / CONNECT , L / R / AMS / PMS */
+ for(r=0xb0;r<0xb6;r++)
+ if((r&3) != 3)
+ OPNWriteReg(&F2203->OPN,r,F2203->REGS[r]);
+
+ /* channels */
+ /*FM_channel_postload(F2203->CH,3);*/
+ }
+}
+
+static void YM2203_save_state(YM2203 *F2203, device_t *device)
+{
+ device->save_item(NAME(F2203->REGS));
+ FMsave_state_st(device,&F2203->OPN.ST);
+ FMsave_state_channel(device,F2203->CH,3);
+ /* 3slots */
+ device->save_item (NAME(F2203->OPN.SL3.fc));
+ device->save_item (NAME(F2203->OPN.SL3.fn_h));
+ device->save_item (NAME(F2203->OPN.SL3.kcode));
+}
+#endif /* _STATE_H */
+
+/* ---------- Initialize YM2203 emulator(s) ----------
+ 'num' is the number of virtual YM2203s to allocate
+ 'clock' is the chip clock in Hz
+ 'rate' is sampling rate
+*/
+void * ym2203_init(void *param, device_t *device, int clock, int rate,
+ FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg)
+{
+ YM2203 *F2203;
+
+ /* allocate ym2203 state space */
+ F2203 = auto_alloc_clear(device->machine(), YM2203);
+
+ if( !init_tables() )
+ {
+ auto_free( device->machine(), F2203 );
+ return NULL;
+ }
+
+ F2203->OPN.ST.param = param;
+ F2203->OPN.type = TYPE_YM2203;
+ F2203->OPN.P_CH = F2203->CH;
+ F2203->OPN.ST.device = device;
+ F2203->OPN.ST.clock = clock;
+ F2203->OPN.ST.rate = rate;
+
+ F2203->OPN.ST.timer_handler = timer_handler;
+ F2203->OPN.ST.IRQ_Handler = IRQHandler;
+ F2203->OPN.ST.SSG = ssg;
+
+#ifdef __SAVE_H__
+ YM2203_save_state(F2203, device);
+#endif
+ return F2203;
+}
+
+/* shut down emulator */
+void ym2203_shutdown(void *chip)
+{
+ YM2203 *FM2203 = (YM2203 *)chip;
+
+ FMCloseTable();
+ auto_free(FM2203->OPN.ST.device->machine(), FM2203);
+}
+
+/* YM2203 I/O interface */
+int ym2203_write(void *chip,int a,UINT8 v)
+{
+ YM2203 *F2203 = (YM2203 *)chip;
+ FM_OPN *OPN = &F2203->OPN;
+
+ if( !(a&1) )
+ { /* address port */
+ OPN->ST.address = (v &= 0xff);
+
+ /* Write register to SSG emulator */
+ if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.param,0,v);
+
+ /* prescaler select : 2d,2e,2f */
+ if( v >= 0x2d && v <= 0x2f )
+ OPNPrescaler_w(OPN , v , 1);
+ }
+ else
+ { /* data port */
+ int addr = OPN->ST.address;
+ F2203->REGS[addr] = v;
+ switch( addr & 0xf0 )
+ {
+ case 0x00: /* 0x00-0x0f : SSG section */
+ /* Write data to SSG emulator */
+ (*OPN->ST.SSG->write)(OPN->ST.param,a,v);
+ break;
+ case 0x20: /* 0x20-0x2f : Mode section */
+ ym2203_update_req(OPN->ST.param);
+ /* write register */
+ OPNWriteMode(OPN,addr,v);
+ break;
+ default: /* 0x30-0xff : OPN section */
+ ym2203_update_req(OPN->ST.param);
+ /* write register */
+ OPNWriteReg(OPN,addr,v);
+ }
+ FM_BUSY_SET(&OPN->ST,1);
+ }
+ return OPN->ST.irq;
+}
+
+UINT8 ym2203_read(void *chip,int a)
+{
+ YM2203 *F2203 = (YM2203 *)chip;
+ int addr = F2203->OPN.ST.address;
+ UINT8 ret = 0;
+
+ if( !(a&1) )
+ { /* status port */
+ ret = FM_STATUS_FLAG(&F2203->OPN.ST);
+ }
+ else
+ { /* data port (only SSG) */
+ if( addr < 16 ) ret = (*F2203->OPN.ST.SSG->read)(F2203->OPN.ST.param);
+ }
+ return ret;
+}
+
+int ym2203_timer_over(void *chip,int c)
+{
+ YM2203 *F2203 = (YM2203 *)chip;
+
+ if( c )
+ { /* Timer B */
+ TimerBOver( &(F2203->OPN.ST) );
+ }
+ else
+ { /* Timer A */
+ ym2203_update_req(F2203->OPN.ST.param);
+ /* timer update */
+ TimerAOver( &(F2203->OPN.ST) );
+ /* CSM mode key,TL control */
+ if( F2203->OPN.ST.mode & 0x80 )
+ { /* CSM mode auto key on */
+ CSMKeyControll( F2203->OPN.type, &(F2203->CH[2]) );
+ }
+ }
+ return F2203->OPN.ST.irq;
+}
+#endif /* BUILD_YM2203 */
+
+
+
+#if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B)
+
+/* ADPCM type A channel struct */
+struct ADPCM_CH
+{
+ UINT8 flag; /* port state */
+ UINT8 flagMask; /* arrived flag mask */
+ UINT8 now_data; /* current ROM data */
+ UINT32 now_addr; /* current ROM address */
+ UINT32 now_step;
+ UINT32 step;
+ UINT32 start; /* sample data start address*/
+ UINT32 end; /* sample data end address */
+ UINT8 IL; /* Instrument Level */
+ INT32 adpcm_acc; /* accumulator */
+ INT32 adpcm_step; /* step */
+ INT32 adpcm_out; /* (speedup) hiro-shi!! */
+ INT8 vol_mul; /* volume in "0.75dB" steps */
+ UINT8 vol_shift; /* volume in "-6dB" steps */
+ INT32 *pan; /* &out_adpcm[OPN_xxxx] */
+};
+
+/* here's the virtual YM2610 */
+struct YM2610
+{
+ UINT8 REGS[512]; /* registers */
+ FM_OPN OPN; /* OPN state */
+ FM_CH CH[6]; /* channel state */
+ UINT8 addr_A1; /* address line A1 */
+
+ /* ADPCM-A unit */
+ const UINT8 *pcmbuf; /* pcm rom buffer */
+ UINT32 pcm_size; /* size of pcm rom */
+ UINT8 adpcmTL; /* adpcmA total level */
+ ADPCM_CH adpcm[6]; /* adpcm channels */
+ UINT32 adpcmreg[0x30]; /* registers */
+ UINT8 adpcm_arrivedEndAddress;
+ YM_DELTAT deltaT; /* Delta-T ADPCM unit */
+
+ UINT8 flagmask; /* YM2608 only */
+ UINT8 irqmask; /* YM2608 only */
+};
+
+/* here is the virtual YM2608 */
+typedef YM2610 YM2608;
+
+
+/**** YM2610 ADPCM defines ****/
+#define ADPCM_SHIFT (16) /* frequency step rate */
+#define ADPCMA_ADDRESS_SHIFT 8 /* adpcm A address shift */
+
+/* Algorithm and tables verified on real YM2608 and YM2610 */
+
+/* usual ADPCM table (16 * 1.1^N) */
+static const int steps[49] =
+{
+ 16, 17, 19, 21, 23, 25, 28,
+ 31, 34, 37, 41, 45, 50, 55,
+ 60, 66, 73, 80, 88, 97, 107,
+ 118, 130, 143, 157, 173, 190, 209,
+ 230, 253, 279, 307, 337, 371, 408,
+ 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552
+};
+
+/* different from the usual ADPCM table */
+static const int step_inc[8] = { -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16 };
+
+/* speedup purposes only */
+static int jedi_table[ 49*16 ];
+
+
+static void Init_ADPCMATable(void)
+{
+ int step, nib;
+
+ for (step = 0; step < 49; step++)
+ {
+ /* loop over all nibbles and compute the difference */
+ for (nib = 0; nib < 16; nib++)
+ {
+ int value = (2*(nib & 0x07) + 1) * steps[step] / 8;
+ jedi_table[step*16 + nib] = (nib&0x08) ? -value : value;
+ }
+ }
+}
+
+/* ADPCM A (Non control type) : calculate one channel output */
+INLINE void ADPCMA_calc_chan( YM2610 *F2610, ADPCM_CH *ch )
+{
+ UINT32 step;
+ UINT8 data;
+
+
+ ch->now_step += ch->step;
+ if ( ch->now_step >= (1<<ADPCM_SHIFT) )
+ {
+ step = ch->now_step >> ADPCM_SHIFT;
+ ch->now_step &= (1<<ADPCM_SHIFT)-1;
+ do{
+ /* end check */
+ /* 11-06-2001 JB: corrected comparison. Was > instead of == */
+ /* YM2610 checks lower 20 bits only, the 4 MSB bits are sample bank */
+ /* Here we use 1<<21 to compensate for nibble calculations */
+
+ if ( (ch->now_addr & ((1<<21)-1)) == ((ch->end<<1) & ((1<<21)-1)) )
+ {
+ ch->flag = 0;
+ F2610->adpcm_arrivedEndAddress |= ch->flagMask;
+ return;
+ }
+#if 0
+ if ( ch->now_addr > (F2610->pcmsizeA<<1) )
+ {
+ LOG(LOG_WAR,("YM2610: Attempting to play past adpcm rom size!\n" ));
+ return;
+ }
+#endif
+ if ( ch->now_addr&1 )
+ data = ch->now_data & 0x0f;
+ else
+ {
+ ch->now_data = *(F2610->pcmbuf+(ch->now_addr>>1));
+ data = (ch->now_data >> 4) & 0x0f;
+ }
+
+ ch->now_addr++;
+
+ ch->adpcm_acc += jedi_table[ch->adpcm_step + data];
+
+ /* extend 12-bit signed int */
+ if (ch->adpcm_acc & ~0x7ff)
+ ch->adpcm_acc |= ~0xfff;
+ else
+ ch->adpcm_acc &= 0xfff;
+
+ ch->adpcm_step += step_inc[data & 7];
+ Limit( ch->adpcm_step, 48*16, 0*16 );
+
+ }while(--step);
+
+ /* calc pcm * volume data */
+ ch->adpcm_out = ((ch->adpcm_acc * ch->vol_mul) >> ch->vol_shift) & ~3; /* multiply, shift and mask out 2 LSB bits */
+ }
+
+ /* output for work of output channels (out_adpcm[OPNxxxx])*/
+ *(ch->pan) += ch->adpcm_out;
+}
+
+/* ADPCM type A Write */
+static void FM_ADPCMAWrite(YM2610 *F2610,int r,int v)
+{
+ ADPCM_CH *adpcm = F2610->adpcm;
+ UINT8 c = r&0x07;
+
+ F2610->adpcmreg[r] = v&0xff; /* stock data */
+ switch( r )
+ {
+ case 0x00: /* DM,--,C5,C4,C3,C2,C1,C0 */
+ if( !(v&0x80) )
+ {
+ /* KEY ON */
+ for( c = 0; c < 6; c++ )
+ {
+ if( (v>>c)&1 )
+ {
+ /**** start adpcm ****/
+ adpcm[c].step = (UINT32)((float)(1<<ADPCM_SHIFT)*((float)F2610->OPN.ST.freqbase)/3.0f);
+ adpcm[c].now_addr = adpcm[c].start<<1;
+ adpcm[c].now_step = 0;
+ adpcm[c].adpcm_acc = 0;
+ adpcm[c].adpcm_step= 0;
+ adpcm[c].adpcm_out = 0;
+ adpcm[c].flag = 1;
+
+ if(F2610->pcmbuf==NULL)
+ { /* Check ROM Mapped */
+ logerror("YM2608-YM2610: ADPCM-A rom not mapped\n");
+ adpcm[c].flag = 0;
+ }
+ else
+ {
+ if(adpcm[c].end >= F2610->pcm_size)
+ { /* Check End in Range */
+ logerror("YM2610: ADPCM-A end out of range: $%08x\n",adpcm[c].end);
+ /*adpcm[c].end = F2610->pcm_size-1;*/ /* JB: DO NOT uncomment this, otherwise you will break the comparison in the ADPCM_CALC_CHA() */
+ }
+ if(adpcm[c].start >= F2610->pcm_size) /* Check Start in Range */
+ {
+ logerror("YM2608-YM2610: ADPCM-A start out of range: $%08x\n",adpcm[c].start);
+ adpcm[c].flag = 0;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /* KEY OFF */
+ for( c = 0; c < 6; c++ )
+ if( (v>>c)&1 )
+ adpcm[c].flag = 0;
+ }
+ break;
+ case 0x01: /* B0-5 = TL */
+ F2610->adpcmTL = (v & 0x3f) ^ 0x3f;
+ for( c = 0; c < 6; c++ )
+ {
+ int volume = F2610->adpcmTL + adpcm[c].IL;
+
+ if ( volume >= 63 ) /* This is correct, 63 = quiet */
+ {
+ adpcm[c].vol_mul = 0;
+ adpcm[c].vol_shift = 0;
+ }
+ else
+ {
+ adpcm[c].vol_mul = 15 - (volume & 7); /* so called 0.75 dB */
+ adpcm[c].vol_shift = 1 + (volume >> 3); /* Yamaha engineers used the approximation: each -6 dB is close to divide by two (shift right) */
+ }
+
+ /* calc pcm * volume data */
+ adpcm[c].adpcm_out = ((adpcm[c].adpcm_acc * adpcm[c].vol_mul) >> adpcm[c].vol_shift) & ~3; /* multiply, shift and mask out low 2 bits */
+ }
+ break;
+ default:
+ c = r&0x07;
+ if( c >= 0x06 ) return;
+ switch( r&0x38 )
+ {
+ case 0x08: /* B7=L,B6=R, B4-0=IL */
+ {
+ int volume;
+
+ adpcm[c].IL = (v & 0x1f) ^ 0x1f;
+
+ volume = F2610->adpcmTL + adpcm[c].IL;
+
+ if ( volume >= 63 ) /* This is correct, 63 = quiet */
+ {
+ adpcm[c].vol_mul = 0;
+ adpcm[c].vol_shift = 0;
+ }
+ else
+ {
+ adpcm[c].vol_mul = 15 - (volume & 7); /* so called 0.75 dB */
+ adpcm[c].vol_shift = 1 + (volume >> 3); /* Yamaha engineers used the approximation: each -6 dB is close to divide by two (shift right) */
+ }
+
+ adpcm[c].pan = &F2610->OPN.out_adpcm[(v>>6)&0x03];
+
+ /* calc pcm * volume data */
+ adpcm[c].adpcm_out = ((adpcm[c].adpcm_acc * adpcm[c].vol_mul) >> adpcm[c].vol_shift) & ~3; /* multiply, shift and mask out low 2 bits */
+ }
+ break;
+ case 0x10:
+ case 0x18:
+ adpcm[c].start = ( (F2610->adpcmreg[0x18 + c]*0x0100 | F2610->adpcmreg[0x10 + c]) << ADPCMA_ADDRESS_SHIFT);
+ break;
+ case 0x20:
+ case 0x28:
+ adpcm[c].end = ( (F2610->adpcmreg[0x28 + c]*0x0100 | F2610->adpcmreg[0x20 + c]) << ADPCMA_ADDRESS_SHIFT);
+ adpcm[c].end += (1<<ADPCMA_ADDRESS_SHIFT) - 1;
+ break;
+ }
+ }
+}
+
+#ifdef __SAVE_H__
+/* FM channel save , internal state only */
+static void FMsave_state_adpcma(device_t *device,ADPCM_CH *adpcm)
+{
+ int ch;
+
+ for(ch=0;ch<6;ch++,adpcm++)
+ {
+ device->save_item(NAME(adpcm->flag), ch);
+ device->save_item(NAME(adpcm->now_data), ch);
+ device->save_item(NAME(adpcm->now_addr), ch);
+ device->save_item(NAME(adpcm->now_step), ch);
+ device->save_item(NAME(adpcm->adpcm_acc), ch);
+ device->save_item(NAME(adpcm->adpcm_step), ch);
+ device->save_item(NAME(adpcm->adpcm_out), ch);
+ }
+}
+#endif /* _STATE_H */
+
+#endif /* (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) */
+
+
+#if BUILD_YM2608
+/*****************************************************************************/
+/* YM2608 local section */
+/*****************************************************************************/
+
+
+
+static const unsigned int YM2608_ADPCM_ROM_addr[2*6] = {
+0x0000, 0x01bf, /* bass drum */
+0x01c0, 0x043f, /* snare drum */
+0x0440, 0x1b7f, /* top cymbal */
+0x1b80, 0x1cff, /* high hat */
+0x1d00, 0x1f7f, /* tom tom */
+0x1f80, 0x1fff /* rim shot */
+};
+
+
+/* flag enable control 0x110 */
+INLINE void YM2608IRQFlagWrite(FM_OPN *OPN, YM2608 *F2608, int v)
+{
+ if( v & 0x80 )
+ { /* Reset IRQ flag */
+ FM_STATUS_RESET(&OPN->ST, 0xf7); /* don't touch BUFRDY flag otherwise we'd have to call ymdeltat module to set the flag back */
+ }
+ else
+ { /* Set status flag mask */
+ F2608->flagmask = (~(v&0x1f));
+ FM_IRQMASK_SET(&OPN->ST, (F2608->irqmask & F2608->flagmask) );
+ }
+}
+
+/* compatible mode & IRQ enable control 0x29 */
+INLINE void YM2608IRQMaskWrite(FM_OPN *OPN, YM2608 *F2608, int v)
+{
+ /* SCH,xx,xxx,EN_ZERO,EN_BRDY,EN_EOS,EN_TB,EN_TA */
+
+ /* extend 3ch. enable/disable */
+ if(v&0x80)
+ OPN->type |= TYPE_6CH; /* OPNA mode - 6 FM channels */
+ else
+ OPN->type &= ~TYPE_6CH; /* OPN mode - 3 FM channels */
+
+ /* IRQ MASK store and set */
+ F2608->irqmask = v&0x1f;
+ FM_IRQMASK_SET(&OPN->ST, (F2608->irqmask & F2608->flagmask) );
+}
+
+/* Generate samples for one of the YM2608s */
+void ym2608_update_one(void *chip, FMSAMPLE **buffer, int length)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+ FM_OPN *OPN = &F2608->OPN;
+ YM_DELTAT *DELTAT = &F2608->deltaT;
+ int i,j;
+ FMSAMPLE *bufL,*bufR;
+ FM_CH *cch[6];
+ INT32 *out_fm = OPN->out_fm;
+
+ /* set bufer */
+ bufL = buffer[0];
+ bufR = buffer[1];
+
+ cch[0] = &F2608->CH[0];
+ cch[1] = &F2608->CH[1];
+ cch[2] = &F2608->CH[2];
+ cch[3] = &F2608->CH[3];
+ cch[4] = &F2608->CH[4];
+ cch[5] = &F2608->CH[5];
+
+ /* refresh PG and EG */
+ refresh_fc_eg_chan( OPN, cch[0] );
+ refresh_fc_eg_chan( OPN, cch[1] );
+ if( (OPN->ST.mode & 0xc0) )
+ {
+ /* 3SLOT MODE */
+ if( cch[2]->SLOT[SLOT1].Incr==-1)
+ {
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode );
+ }
+ }
+ else
+ refresh_fc_eg_chan( OPN, cch[2] );
+ refresh_fc_eg_chan( OPN, cch[3] );
+ refresh_fc_eg_chan( OPN, cch[4] );
+ refresh_fc_eg_chan( OPN, cch[5] );
+
+
+ /* buffering */
+ for(i=0; i < length ; i++)
+ {
+ advance_lfo(OPN);
+
+ /* clear output acc. */
+ OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0;
+ OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0;
+ /* clear outputs */
+ out_fm[0] = 0;
+ out_fm[1] = 0;
+ out_fm[2] = 0;
+ out_fm[3] = 0;
+ out_fm[4] = 0;
+ out_fm[5] = 0;
+
+ /* calculate FM */
+ chan_calc(OPN, cch[0], 0 );
+ chan_calc(OPN, cch[1], 1 );
+ chan_calc(OPN, cch[2], 2 );
+ chan_calc(OPN, cch[3], 3 );
+ chan_calc(OPN, cch[4], 4 );
+ chan_calc(OPN, cch[5], 5 );
+
+ /* deltaT ADPCM */
+ if( DELTAT->portstate&0x80 )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+
+ /* ADPCMA */
+ for( j = 0; j < 6; j++ )
+ {
+ if( F2608->adpcm[j].flag )
+ ADPCMA_calc_chan( F2608, &F2608->adpcm[j]);
+ }
+
+ /* advance envelope generator */
+ OPN->eg_timer += OPN->eg_timer_add;
+ while (OPN->eg_timer >= OPN->eg_timer_overflow)
+ {
+ OPN->eg_timer -= OPN->eg_timer_overflow;
+ OPN->eg_cnt++;
+
+ advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[4]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[5]->SLOT[SLOT1]);
+ }
+
+ /* buffering */
+ {
+ int lt,rt;
+
+ lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER];
+ rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER];
+ lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9;
+ rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9;
+ lt += ((out_fm[0]>>1) & OPN->pan[0]); /* shift right verified on real YM2608 */
+ rt += ((out_fm[0]>>1) & OPN->pan[1]);
+ lt += ((out_fm[1]>>1) & OPN->pan[2]);
+ rt += ((out_fm[1]>>1) & OPN->pan[3]);
+ lt += ((out_fm[2]>>1) & OPN->pan[4]);
+ rt += ((out_fm[2]>>1) & OPN->pan[5]);
+ lt += ((out_fm[3]>>1) & OPN->pan[6]);
+ rt += ((out_fm[3]>>1) & OPN->pan[7]);
+ lt += ((out_fm[4]>>1) & OPN->pan[8]);
+ rt += ((out_fm[4]>>1) & OPN->pan[9]);
+ lt += ((out_fm[5]>>1) & OPN->pan[10]);
+ rt += ((out_fm[5]>>1) & OPN->pan[11]);
+
+ lt >>= FINAL_SH;
+ rt >>= FINAL_SH;
+
+ Limit( lt, MAXOUT, MINOUT );
+ Limit( rt, MAXOUT, MINOUT );
+ /* buffering */
+ bufL[i] = lt;
+ bufR[i] = rt;
+
+ #ifdef SAVE_SAMPLE
+ SAVE_ALL_CHANNELS
+ #endif
+
+ }
+
+ /* timer A control */
+ INTERNAL_TIMER_A( &OPN->ST , cch[2] )
+ }
+ INTERNAL_TIMER_B(&OPN->ST,length)
+
+
+ /* check IRQ for DELTA-T EOS */
+ FM_STATUS_SET(&OPN->ST, 0);
+
+}
+#ifdef __SAVE_H__
+void ym2608_postload(void *chip)
+{
+ if (chip)
+ {
+ YM2608 *F2608 = (YM2608 *)chip;
+ int r;
+
+ /* prescaler */
+ OPNPrescaler_w(&F2608->OPN,1,2);
+ F2608->deltaT.freqbase = F2608->OPN.ST.freqbase;
+ /* IRQ mask / mode */
+ YM2608IRQMaskWrite(&F2608->OPN, F2608, F2608->REGS[0x29]);
+ /* SSG registers */
+ for(r=0;r<16;r++)
+ {
+ (*F2608->OPN.ST.SSG->write)(F2608->OPN.ST.param,0,r);
+ (*F2608->OPN.ST.SSG->write)(F2608->OPN.ST.param,1,F2608->REGS[r]);
+ }
+
+ /* OPN registers */
+ /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */
+ for(r=0x30;r<0x9e;r++)
+ if((r&3) != 3)
+ {
+ OPNWriteReg(&F2608->OPN,r,F2608->REGS[r]);
+ OPNWriteReg(&F2608->OPN,r|0x100,F2608->REGS[r|0x100]);
+ }
+ /* FB / CONNECT , L / R / AMS / PMS */
+ for(r=0xb0;r<0xb6;r++)
+ if((r&3) != 3)
+ {
+ OPNWriteReg(&F2608->OPN,r,F2608->REGS[r]);
+ OPNWriteReg(&F2608->OPN,r|0x100,F2608->REGS[r|0x100]);
+ }
+ /* FM channels */
+ /*FM_channel_postload(F2608->CH,6);*/
+ /* rhythm(ADPCMA) */
+ FM_ADPCMAWrite(F2608,1,F2608->REGS[0x111]);
+ for( r=0x08 ; r<0x0c ; r++)
+ FM_ADPCMAWrite(F2608,r,F2608->REGS[r+0x110]);
+ /* Delta-T ADPCM unit */
+ YM_DELTAT_postload(&F2608->deltaT , &F2608->REGS[0x100] );
+ }
+}
+
+static void YM2608_save_state(YM2608 *F2608, device_t *device)
+{
+ device->save_item(NAME(F2608->REGS));
+ FMsave_state_st(device,&F2608->OPN.ST);
+ FMsave_state_channel(device,F2608->CH,6);
+ /* 3slots */
+ device->save_item(NAME(F2608->OPN.SL3.fc));
+ device->save_item(NAME(F2608->OPN.SL3.fn_h));
+ device->save_item(NAME(F2608->OPN.SL3.kcode));
+ /* address register1 */
+ device->save_item(NAME(F2608->addr_A1));
+ /* rhythm(ADPCMA) */
+ FMsave_state_adpcma(device,F2608->adpcm);
+ /* Delta-T ADPCM unit */
+ YM_DELTAT_savestate(device,&F2608->deltaT);
+}
+#endif /* _STATE_H */
+
+static void YM2608_deltat_status_set(void *chip, UINT8 changebits)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+ FM_STATUS_SET(&(F2608->OPN.ST), changebits);
+}
+static void YM2608_deltat_status_reset(void *chip, UINT8 changebits)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+ FM_STATUS_RESET(&(F2608->OPN.ST), changebits);
+}
+/* YM2608(OPNA) */
+void * ym2608_init(void *param, device_t *device, int clock, int rate,
+ void *pcmrom,int pcmsize,
+ FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg)
+{
+ YM2608 *F2608;
+
+ /* allocate extend state space */
+ F2608 = auto_alloc_clear(device->machine(), YM2608);
+ /* allocate total level table (128kb space) */
+ if( !init_tables() )
+ {
+ auto_free( device->machine(), F2608 );
+ return NULL;
+ }
+
+ F2608->OPN.ST.param = param;
+ F2608->OPN.type = TYPE_YM2608;
+ F2608->OPN.P_CH = F2608->CH;
+ F2608->OPN.ST.device = device;
+ F2608->OPN.ST.clock = clock;
+ F2608->OPN.ST.rate = rate;
+
+ /* External handlers */
+ F2608->OPN.ST.timer_handler = timer_handler;
+ F2608->OPN.ST.IRQ_Handler = IRQHandler;
+ F2608->OPN.ST.SSG = ssg;
+
+ /* DELTA-T */
+ F2608->deltaT.memory = (UINT8 *)pcmrom;
+ F2608->deltaT.memory_size = pcmsize;
+
+ /*F2608->deltaT.write_time = 20.0 / clock;*/ /* a single byte write takes 20 cycles of main clock */
+ /*F2608->deltaT.read_time = 18.0 / clock;*/ /* a single byte read takes 18 cycles of main clock */
+
+ F2608->deltaT.status_set_handler = YM2608_deltat_status_set;
+ F2608->deltaT.status_reset_handler = YM2608_deltat_status_reset;
+ F2608->deltaT.status_change_which_chip = F2608;
+ F2608->deltaT.status_change_EOS_bit = 0x04; /* status flag: set bit2 on End Of Sample */
+ F2608->deltaT.status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY */
+ F2608->deltaT.status_change_ZERO_bit = 0x10; /* status flag: set bit4 if silence continues for more than 290 miliseconds while recording the ADPCM */
+
+ /* ADPCM Rhythm */
+ F2608->pcmbuf = device->memregion("ym2608")->base();
+ F2608->pcm_size = 0x2000;
+
+ Init_ADPCMATable();
+
+#ifdef __SAVE_H__
+ YM2608_save_state(F2608, device);
+#endif
+ return F2608;
+}
+
+/* shut down emulator */
+void ym2608_shutdown(void *chip)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+
+ FMCloseTable();
+ auto_free(F2608->OPN.ST.device->machine(), F2608);
+}
+
+/* reset one of chips */
+void ym2608_reset_chip(void *chip)
+{
+ int i;
+ YM2608 *F2608 = (YM2608 *)chip;
+ FM_OPN *OPN = &F2608->OPN;
+ YM_DELTAT *DELTAT = &F2608->deltaT;
+
+ /* Reset Prescaler */
+ OPNPrescaler_w(OPN , 0 , 2);
+ F2608->deltaT.freqbase = OPN->ST.freqbase;
+ /* reset SSG section */
+ (*OPN->ST.SSG->reset)(OPN->ST.param);
+
+ /* status clear */
+ FM_BUSY_CLEAR(&OPN->ST);
+
+ /* register 0x29 - default value after reset is:
+ enable only 3 FM channels and enable all the status flags */
+ YM2608IRQMaskWrite(OPN, F2608, 0x1f ); /* default value for D4-D0 is 1 */
+
+ /* register 0x10, A1=1 - default value is 1 for D4, D3, D2, 0 for the rest */
+ YM2608IRQFlagWrite(OPN, F2608, 0x1c ); /* default: enable timer A and B, disable EOS, BRDY and ZERO */
+
+ OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */
+
+ OPN->eg_timer = 0;
+ OPN->eg_cnt = 0;
+
+ FM_STATUS_RESET(&OPN->ST, 0xff);
+
+ reset_channels( &OPN->ST , F2608->CH , 6 );
+ /* reset OPerator paramater */
+ for(i = 0xb6 ; i >= 0xb4 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0xc0);
+ OPNWriteReg(OPN,i|0x100,0xc0);
+ }
+ for(i = 0xb2 ; i >= 0x30 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0);
+ OPNWriteReg(OPN,i|0x100,0);
+ }
+ for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0);
+
+ /* ADPCM - percussion sounds */
+ for( i = 0; i < 6; i++ )
+ {
+ if (i<=3) /* channels 0,1,2,3 */
+ F2608->adpcm[i].step = (UINT32)((float)(1<<ADPCM_SHIFT)*((float)F2608->OPN.ST.freqbase)/3.0f);
+ else /* channels 4 and 5 work with slower clock */
+ F2608->adpcm[i].step = (UINT32)((float)(1<<ADPCM_SHIFT)*((float)F2608->OPN.ST.freqbase)/6.0f);
+
+ F2608->adpcm[i].start = YM2608_ADPCM_ROM_addr[i*2];
+ F2608->adpcm[i].end = YM2608_ADPCM_ROM_addr[i*2+1];
+
+ F2608->adpcm[i].now_addr = 0;
+ F2608->adpcm[i].now_step = 0;
+ /* F2608->adpcm[i].delta = 21866; */
+ F2608->adpcm[i].vol_mul = 0;
+ F2608->adpcm[i].pan = &OPN->out_adpcm[OUTD_CENTER]; /* default center */
+ F2608->adpcm[i].flagMask = 0;
+ F2608->adpcm[i].flag = 0;
+ F2608->adpcm[i].adpcm_acc = 0;
+ F2608->adpcm[i].adpcm_step= 0;
+ F2608->adpcm[i].adpcm_out = 0;
+ }
+ F2608->adpcmTL = 0x3f;
+
+ F2608->adpcm_arrivedEndAddress = 0; /* not used */
+
+ /* DELTA-T unit */
+ DELTAT->freqbase = OPN->ST.freqbase;
+ DELTAT->output_pointer = OPN->out_delta;
+ DELTAT->portshift = 5; /* always 5bits shift */ /* ASG */
+ DELTAT->output_range = 1<<23;
+ YM_DELTAT_ADPCM_Reset(DELTAT,OUTD_CENTER,YM_DELTAT_EMULATION_MODE_NORMAL);
+}
+
+/* YM2608 write */
+/* n = number */
+/* a = address */
+/* v = value */
+int ym2608_write(void *chip, int a,UINT8 v)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+ FM_OPN *OPN = &F2608->OPN;
+ int addr;
+
+ v &= 0xff; /*adjust to 8 bit bus */
+
+
+ switch(a&3)
+ {
+ case 0: /* address port 0 */
+ OPN->ST.address = v;
+ F2608->addr_A1 = 0;
+
+ /* Write register to SSG emulator */
+ if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.param,0,v);
+ /* prescaler selecter : 2d,2e,2f */
+ if( v >= 0x2d && v <= 0x2f )
+ {
+ OPNPrescaler_w(OPN , v , 2);
+ F2608->deltaT.freqbase = OPN->ST.freqbase;
+ }
+ break;
+
+ case 1: /* data port 0 */
+ if (F2608->addr_A1 != 0)
+ break; /* verified on real YM2608 */
+
+ addr = OPN->ST.address;
+ F2608->REGS[addr] = v;
+ switch(addr & 0xf0)
+ {
+ case 0x00: /* SSG section */
+ /* Write data to SSG emulator */
+ (*OPN->ST.SSG->write)(OPN->ST.param,a,v);
+ break;
+ case 0x10: /* 0x10-0x1f : Rhythm section */
+ ym2608_update_req(OPN->ST.param);
+ FM_ADPCMAWrite(F2608,addr-0x10,v);
+ break;
+ case 0x20: /* Mode Register */
+ switch(addr)
+ {
+ case 0x29: /* SCH,xx,xxx,EN_ZERO,EN_BRDY,EN_EOS,EN_TB,EN_TA */
+ YM2608IRQMaskWrite(OPN, F2608, v);
+ break;
+ default:
+ ym2608_update_req(OPN->ST.param);
+ OPNWriteMode(OPN,addr,v);
+ }
+ break;
+ default: /* OPN section */
+ ym2608_update_req(OPN->ST.param);
+ OPNWriteReg(OPN,addr,v);
+ }
+ break;
+
+ case 2: /* address port 1 */
+ OPN->ST.address = v;
+ F2608->addr_A1 = 1;
+ break;
+
+ case 3: /* data port 1 */
+ if (F2608->addr_A1 != 1)
+ break; /* verified on real YM2608 */
+
+ addr = OPN->ST.address;
+ F2608->REGS[addr | 0x100] = v;
+ ym2608_update_req(OPN->ST.param);
+ switch( addr & 0xf0 )
+ {
+ case 0x00: /* DELTAT PORT */
+ switch( addr )
+ {
+ case 0x0e: /* DAC data */
+ logerror("YM2608: write to DAC data (unimplemented) value=%02x\n",v);
+ break;
+ default:
+ /* 0x00-0x0d */
+ YM_DELTAT_ADPCM_Write(&F2608->deltaT,addr,v);
+ }
+ break;
+ case 0x10: /* IRQ Flag control */
+ if( addr == 0x10 )
+ {
+ YM2608IRQFlagWrite(OPN, F2608, v);
+ }
+ break;
+ default:
+ OPNWriteReg(OPN,addr | 0x100,v);
+ }
+ }
+ return OPN->ST.irq;
+}
+
+UINT8 ym2608_read(void *chip,int a)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+ int addr = F2608->OPN.ST.address;
+ UINT8 ret = 0;
+
+ switch( a&3 )
+ {
+ case 0: /* status 0 : YM2203 compatible */
+ /* BUSY:x:x:x:x:x:FLAGB:FLAGA */
+ ret = FM_STATUS_FLAG(&F2608->OPN.ST) & 0x83;
+ break;
+
+ case 1: /* status 0, ID */
+ if( addr < 16 ) ret = (*F2608->OPN.ST.SSG->read)(F2608->OPN.ST.param);
+ else if(addr == 0xff) ret = 0x01; /* ID code */
+ break;
+
+ case 2: /* status 1 : status 0 + ADPCM status */
+ /* BUSY : x : PCMBUSY : ZERO : BRDY : EOS : FLAGB : FLAGA */
+ ret = (FM_STATUS_FLAG(&F2608->OPN.ST) & (F2608->flagmask|0x80)) | ((F2608->deltaT.PCM_BSY & 1)<<5) ;
+ break;
+
+ case 3:
+ if(addr == 0x08)
+ {
+ ret = YM_DELTAT_ADPCM_Read(&F2608->deltaT);
+ }
+ else
+ {
+ if(addr == 0x0f)
+ {
+ logerror("YM2608 A/D convertion is accessed but not implemented !\n");
+ ret = 0x80; /* 2's complement PCM data - result from A/D convertion */
+ }
+ }
+ break;
+ }
+ return ret;
+}
+
+int ym2608_timer_over(void *chip,int c)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+
+ switch(c)
+ {
+#if 0
+ case 2:
+ { /* BUFRDY flag */
+ YM_DELTAT_BRDY_callback( &F2608->deltaT );
+ }
+ break;
+#endif
+ case 1:
+ { /* Timer B */
+ TimerBOver( &(F2608->OPN.ST) );
+ }
+ break;
+ case 0:
+ { /* Timer A */
+ ym2608_update_req(F2608->OPN.ST.param);
+ /* timer update */
+ TimerAOver( &(F2608->OPN.ST) );
+ /* CSM mode key,TL controll */
+ if( F2608->OPN.ST.mode & 0x80 )
+ { /* CSM mode total level latch and auto key on */
+ CSMKeyControll( F2608->OPN.type, &(F2608->CH[2]) );
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return F2608->OPN.ST.irq;
+}
+
+#endif /* BUILD_YM2608 */
+
+
+
+#if (BUILD_YM2610||BUILD_YM2610B)
+/* YM2610(OPNB) */
+
+/* Generate samples for one of the YM2610s */
+void ym2610_update_one(void *chip, FMSAMPLE **buffer, int length)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+ FM_OPN *OPN = &F2610->OPN;
+ YM_DELTAT *DELTAT = &F2610->deltaT;
+ int i,j;
+ FMSAMPLE *bufL,*bufR;
+ FM_CH *cch[4];
+ INT32 *out_fm = OPN->out_fm;
+
+ /* buffer setup */
+ bufL = buffer[0];
+ bufR = buffer[1];
+
+ cch[0] = &F2610->CH[1];
+ cch[1] = &F2610->CH[2];
+ cch[2] = &F2610->CH[4];
+ cch[3] = &F2610->CH[5];
+
+#ifdef YM2610B_WARNING
+#define FM_KEY_IS(SLOT) ((SLOT)->key)
+#define FM_MSG_YM2610B "YM2610-%p.CH%d is playing,Check whether the type of the chip is YM2610B\n"
+ /* Check YM2610B warning message */
+ if( FM_KEY_IS(&F2610->CH[0].SLOT[3]) )
+ LOG(LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.param,0));
+ if( FM_KEY_IS(&F2610->CH[3].SLOT[3]) )
+ LOG(LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.param,3));
+#endif
+
+ /* refresh PG and EG */
+ refresh_fc_eg_chan( OPN, cch[0] );
+ if( (OPN->ST.mode & 0xc0) )
+ {
+ /* 3SLOT MODE */
+ if( cch[1]->SLOT[SLOT1].Incr==-1)
+ {
+ refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] );
+ refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] );
+ refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] );
+ refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT4] , cch[1]->fc , cch[1]->kcode );
+ }
+ }
+ else
+ refresh_fc_eg_chan( OPN, cch[1] );
+ refresh_fc_eg_chan( OPN, cch[2] );
+ refresh_fc_eg_chan( OPN, cch[3] );
+
+ /* buffering */
+ for(i=0; i < length ; i++)
+ {
+ advance_lfo(OPN);
+
+ /* clear output acc. */
+ OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0;
+ OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0;
+ /* clear outputs */
+ out_fm[1] = 0;
+ out_fm[2] = 0;
+ out_fm[4] = 0;
+ out_fm[5] = 0;
+
+ /* advance envelope generator */
+ OPN->eg_timer += OPN->eg_timer_add;
+ while (OPN->eg_timer >= OPN->eg_timer_overflow)
+ {
+ OPN->eg_timer -= OPN->eg_timer_overflow;
+ OPN->eg_cnt++;
+
+ advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]);
+ }
+
+ /* calculate FM */
+ chan_calc(OPN, cch[0], 1 ); /*remapped to 1*/
+ chan_calc(OPN, cch[1], 2 ); /*remapped to 2*/
+ chan_calc(OPN, cch[2], 4 ); /*remapped to 4*/
+ chan_calc(OPN, cch[3], 5 ); /*remapped to 5*/
+
+ /* deltaT ADPCM */
+ if( DELTAT->portstate&0x80 )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+
+ /* ADPCMA */
+ for( j = 0; j < 6; j++ )
+ {
+ if( F2610->adpcm[j].flag )
+ ADPCMA_calc_chan( F2610, &F2610->adpcm[j]);
+ }
+
+ /* buffering */
+ {
+ int lt,rt;
+
+ lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER];
+ rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER];
+ lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9;
+ rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9;
+
+
+ lt += ((out_fm[1]>>1) & OPN->pan[2]); /* the shift right was verified on real chip */
+ rt += ((out_fm[1]>>1) & OPN->pan[3]);
+ lt += ((out_fm[2]>>1) & OPN->pan[4]);
+ rt += ((out_fm[2]>>1) & OPN->pan[5]);
+
+ lt += ((out_fm[4]>>1) & OPN->pan[8]);
+ rt += ((out_fm[4]>>1) & OPN->pan[9]);
+ lt += ((out_fm[5]>>1) & OPN->pan[10]);
+ rt += ((out_fm[5]>>1) & OPN->pan[11]);
+
+
+ lt >>= FINAL_SH;
+ rt >>= FINAL_SH;
+
+ Limit( lt, MAXOUT, MINOUT );
+ Limit( rt, MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ SAVE_ALL_CHANNELS
+ #endif
+
+ /* buffering */
+ bufL[i] = lt;
+ bufR[i] = rt;
+ }
+
+ /* timer A control */
+ INTERNAL_TIMER_A( &OPN->ST , cch[1] )
+ }
+ INTERNAL_TIMER_B(&OPN->ST,length)
+
+}
+
+#if BUILD_YM2610B
+/* Generate samples for one of the YM2610Bs */
+void ym2610b_update_one(void *chip, FMSAMPLE **buffer, int length)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+ FM_OPN *OPN = &F2610->OPN;
+ YM_DELTAT *DELTAT = &F2610->deltaT;
+ int i,j;
+ FMSAMPLE *bufL,*bufR;
+ FM_CH *cch[6];
+ INT32 *out_fm = OPN->out_fm;
+
+ /* buffer setup */
+ bufL = buffer[0];
+ bufR = buffer[1];
+
+ cch[0] = &F2610->CH[0];
+ cch[1] = &F2610->CH[1];
+ cch[2] = &F2610->CH[2];
+ cch[3] = &F2610->CH[3];
+ cch[4] = &F2610->CH[4];
+ cch[5] = &F2610->CH[5];
+
+ /* refresh PG and EG */
+ refresh_fc_eg_chan( OPN, cch[0] );
+ refresh_fc_eg_chan( OPN, cch[1] );
+ if( (OPN->ST.mode & 0xc0) )
+ {
+ /* 3SLOT MODE */
+ if( cch[2]->SLOT[SLOT1].Incr==-1)
+ {
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode );
+ }
+ }
+ else
+ refresh_fc_eg_chan( OPN, cch[2] );
+ refresh_fc_eg_chan( OPN, cch[3] );
+ refresh_fc_eg_chan( OPN, cch[4] );
+ refresh_fc_eg_chan( OPN, cch[5] );
+
+ /* buffering */
+ for(i=0; i < length ; i++)
+ {
+ advance_lfo(OPN);
+
+ /* clear output acc. */
+ OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0;
+ OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0;
+ /* clear outputs */
+ out_fm[0] = 0;
+ out_fm[1] = 0;
+ out_fm[2] = 0;
+ out_fm[3] = 0;
+ out_fm[4] = 0;
+ out_fm[5] = 0;
+
+ /* advance envelope generator */
+ OPN->eg_timer += OPN->eg_timer_add;
+ while (OPN->eg_timer >= OPN->eg_timer_overflow)
+ {
+ OPN->eg_timer -= OPN->eg_timer_overflow;
+ OPN->eg_cnt++;
+
+ advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[4]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[5]->SLOT[SLOT1]);
+ }
+
+ /* calculate FM */
+ chan_calc(OPN, cch[0], 0 );
+ chan_calc(OPN, cch[1], 1 );
+ chan_calc(OPN, cch[2], 2 );
+ chan_calc(OPN, cch[3], 3 );
+ chan_calc(OPN, cch[4], 4 );
+ chan_calc(OPN, cch[5], 5 );
+
+ /* deltaT ADPCM */
+ if( DELTAT->portstate&0x80 )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+
+ /* ADPCMA */
+ for( j = 0; j < 6; j++ )
+ {
+ if( F2610->adpcm[j].flag )
+ ADPCMA_calc_chan( F2610, &F2610->adpcm[j]);
+ }
+
+ /* buffering */
+ {
+ int lt,rt;
+
+ lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER];
+ rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER];
+ lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9;
+ rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9;
+
+ lt += ((out_fm[0]>>1) & OPN->pan[0]); /* the shift right is verified on YM2610 */
+ rt += ((out_fm[0]>>1) & OPN->pan[1]);
+ lt += ((out_fm[1]>>1) & OPN->pan[2]);
+ rt += ((out_fm[1]>>1) & OPN->pan[3]);
+ lt += ((out_fm[2]>>1) & OPN->pan[4]);
+ rt += ((out_fm[2]>>1) & OPN->pan[5]);
+ lt += ((out_fm[3]>>1) & OPN->pan[6]);
+ rt += ((out_fm[3]>>1) & OPN->pan[7]);
+ lt += ((out_fm[4]>>1) & OPN->pan[8]);
+ rt += ((out_fm[4]>>1) & OPN->pan[9]);
+ lt += ((out_fm[5]>>1) & OPN->pan[10]);
+ rt += ((out_fm[5]>>1) & OPN->pan[11]);
+
+
+ lt >>= FINAL_SH;
+ rt >>= FINAL_SH;
+
+ Limit( lt, MAXOUT, MINOUT );
+ Limit( rt, MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ SAVE_ALL_CHANNELS
+ #endif
+
+ /* buffering */
+ bufL[i] = lt;
+ bufR[i] = rt;
+ }
+
+ /* timer A control */
+ INTERNAL_TIMER_A( &OPN->ST , cch[2] )
+ }
+ INTERNAL_TIMER_B(&OPN->ST,length)
+
+}
+#endif /* BUILD_YM2610B */
+
+
+#ifdef __SAVE_H__
+void ym2610_postload(void *chip)
+{
+ if (chip)
+ {
+ YM2610 *F2610 = (YM2610 *)chip;
+ int r;
+
+ /* SSG registers */
+ for(r=0;r<16;r++)
+ {
+ (*F2610->OPN.ST.SSG->write)(F2610->OPN.ST.param,0,r);
+ (*F2610->OPN.ST.SSG->write)(F2610->OPN.ST.param,1,F2610->REGS[r]);
+ }
+
+ /* OPN registers */
+ /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */
+ for(r=0x30;r<0x9e;r++)
+ if((r&3) != 3)
+ {
+ OPNWriteReg(&F2610->OPN,r,F2610->REGS[r]);
+ OPNWriteReg(&F2610->OPN,r|0x100,F2610->REGS[r|0x100]);
+ }
+ /* FB / CONNECT , L / R / AMS / PMS */
+ for(r=0xb0;r<0xb6;r++)
+ if((r&3) != 3)
+ {
+ OPNWriteReg(&F2610->OPN,r,F2610->REGS[r]);
+ OPNWriteReg(&F2610->OPN,r|0x100,F2610->REGS[r|0x100]);
+ }
+ /* FM channels */
+ /*FM_channel_postload(F2610->CH,6);*/
+
+ /* rhythm(ADPCMA) */
+ FM_ADPCMAWrite(F2610,1,F2610->REGS[0x101]);
+ for( r=0 ; r<6 ; r++)
+ {
+ FM_ADPCMAWrite(F2610,r+0x08,F2610->REGS[r+0x108]);
+ FM_ADPCMAWrite(F2610,r+0x10,F2610->REGS[r+0x110]);
+ FM_ADPCMAWrite(F2610,r+0x18,F2610->REGS[r+0x118]);
+ FM_ADPCMAWrite(F2610,r+0x20,F2610->REGS[r+0x120]);
+ FM_ADPCMAWrite(F2610,r+0x28,F2610->REGS[r+0x128]);
+ }
+ /* Delta-T ADPCM unit */
+ YM_DELTAT_postload(&F2610->deltaT , &F2610->REGS[0x010] );
+ }
+}
+
+static void YM2610_save_state(YM2610 *F2610, device_t *device)
+{
+ device->save_item(NAME(F2610->REGS));
+ FMsave_state_st(device,&F2610->OPN.ST);
+ FMsave_state_channel(device,F2610->CH,6);
+ /* 3slots */
+ device->save_item(NAME(F2610->OPN.SL3.fc));
+ device->save_item(NAME(F2610->OPN.SL3.fn_h));
+ device->save_item(NAME(F2610->OPN.SL3.kcode));
+ /* address register1 */
+ device->save_item(NAME(F2610->addr_A1));
+
+ device->save_item(NAME(F2610->adpcm_arrivedEndAddress));
+ /* rhythm(ADPCMA) */
+ FMsave_state_adpcma(device,F2610->adpcm);
+ /* Delta-T ADPCM unit */
+ YM_DELTAT_savestate(device,&F2610->deltaT);
+}
+#endif /* _STATE_H */
+
+static void YM2610_deltat_status_set(void *chip, UINT8 changebits)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+ F2610->adpcm_arrivedEndAddress |= changebits;
+}
+static void YM2610_deltat_status_reset(void *chip, UINT8 changebits)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+ F2610->adpcm_arrivedEndAddress &= (~changebits);
+}
+
+void *ym2610_init(void *param, device_t *device, int clock, int rate,
+ void *pcmroma,int pcmsizea,void *pcmromb,int pcmsizeb,
+ FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg)
+
+{
+ YM2610 *F2610;
+
+ /* allocate extend state space */
+ F2610 = auto_alloc_clear(device->machine(), YM2610);
+ /* allocate total level table (128kb space) */
+ if( !init_tables() )
+ {
+ auto_free( device->machine(), F2610 );
+ return NULL;
+ }
+
+ /* FM */
+ F2610->OPN.ST.param = param;
+ F2610->OPN.type = TYPE_YM2610;
+ F2610->OPN.P_CH = F2610->CH;
+ F2610->OPN.ST.device = device;
+ F2610->OPN.ST.clock = clock;
+ F2610->OPN.ST.rate = rate;
+ /* Extend handler */
+ F2610->OPN.ST.timer_handler = timer_handler;
+ F2610->OPN.ST.IRQ_Handler = IRQHandler;
+ F2610->OPN.ST.SSG = ssg;
+ /* ADPCM */
+ F2610->pcmbuf = (const UINT8 *)pcmroma;
+ F2610->pcm_size = pcmsizea;
+ /* DELTA-T */
+ F2610->deltaT.memory = (UINT8 *)pcmromb;
+ F2610->deltaT.memory_size = pcmsizeb;
+
+ F2610->deltaT.status_set_handler = YM2610_deltat_status_set;
+ F2610->deltaT.status_reset_handler = YM2610_deltat_status_reset;
+ F2610->deltaT.status_change_which_chip = F2610;
+ F2610->deltaT.status_change_EOS_bit = 0x80; /* status flag: set bit7 on End Of Sample */
+
+ Init_ADPCMATable();
+#ifdef __SAVE_H__
+ YM2610_save_state(F2610, device);
+#endif
+ return F2610;
+}
+
+/* shut down emulator */
+void ym2610_shutdown(void *chip)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+
+ FMCloseTable();
+ auto_free(F2610->OPN.ST.device->machine(), F2610);
+}
+
+/* reset one of chip */
+void ym2610_reset_chip(void *chip)
+{
+ int i;
+ YM2610 *F2610 = (YM2610 *)chip;
+ FM_OPN *OPN = &F2610->OPN;
+ YM_DELTAT *DELTAT = &F2610->deltaT;
+
+ device_t* dev = F2610->OPN.ST.device;
+ std::string name(dev->tag());
+
+ /* setup PCM buffers again */
+ F2610->pcmbuf = (const UINT8 *)dev->machine().root_device().memregion(name.c_str())->base();
+ F2610->pcm_size = dev->machine().root_device().memregion(name.c_str())->bytes();
+ name.append(".deltat");
+ F2610->deltaT.memory = (UINT8 *)dev->machine().root_device().memregion(name.c_str())->base();
+ if(F2610->deltaT.memory == NULL)
+ {
+ F2610->deltaT.memory = (UINT8*)F2610->pcmbuf;
+ F2610->deltaT.memory_size = F2610->pcm_size;
+ }
+ else
+ F2610->deltaT.memory_size = dev->machine().root_device().memregion(name.c_str())->bytes();
+
+ /* Reset Prescaler */
+ OPNSetPres( OPN, 6*24, 6*24, 4*2); /* OPN 1/6 , SSG 1/4 */
+ /* reset SSG section */
+ (*OPN->ST.SSG->reset)(OPN->ST.param);
+ /* status clear */
+ FM_IRQMASK_SET(&OPN->ST,0x03);
+ FM_BUSY_CLEAR(&OPN->ST);
+ OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */
+
+ OPN->eg_timer = 0;
+ OPN->eg_cnt = 0;
+
+ FM_STATUS_RESET(&OPN->ST, 0xff);
+
+ reset_channels( &OPN->ST , F2610->CH , 6 );
+ /* reset OPerator paramater */
+ for(i = 0xb6 ; i >= 0xb4 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0xc0);
+ OPNWriteReg(OPN,i|0x100,0xc0);
+ }
+ for(i = 0xb2 ; i >= 0x30 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0);
+ OPNWriteReg(OPN,i|0x100,0);
+ }
+ for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0);
+ /**** ADPCM work initial ****/
+ for( i = 0; i < 6 ; i++ )
+ {
+ F2610->adpcm[i].step = (UINT32)((float)(1<<ADPCM_SHIFT)*((float)F2610->OPN.ST.freqbase)/3.0f);
+ F2610->adpcm[i].now_addr = 0;
+ F2610->adpcm[i].now_step = 0;
+ F2610->adpcm[i].start = 0;
+ F2610->adpcm[i].end = 0;
+ /* F2610->adpcm[i].delta = 21866; */
+ F2610->adpcm[i].vol_mul = 0;
+ F2610->adpcm[i].pan = &OPN->out_adpcm[OUTD_CENTER]; /* default center */
+ F2610->adpcm[i].flagMask = 1<<i;
+ F2610->adpcm[i].flag = 0;
+ F2610->adpcm[i].adpcm_acc = 0;
+ F2610->adpcm[i].adpcm_step= 0;
+ F2610->adpcm[i].adpcm_out = 0;
+ }
+ F2610->adpcmTL = 0x3f;
+
+ F2610->adpcm_arrivedEndAddress = 0;
+
+ /* DELTA-T unit */
+ DELTAT->freqbase = OPN->ST.freqbase;
+ DELTAT->output_pointer = OPN->out_delta;
+ DELTAT->portshift = 8; /* allways 8bits shift */
+ DELTAT->output_range = 1<<23;
+ YM_DELTAT_ADPCM_Reset(DELTAT,OUTD_CENTER,YM_DELTAT_EMULATION_MODE_YM2610);
+}
+
+/* YM2610 write */
+/* n = number */
+/* a = address */
+/* v = value */
+int ym2610_write(void *chip, int a, UINT8 v)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+ FM_OPN *OPN = &F2610->OPN;
+ int addr;
+ int ch;
+
+ v &= 0xff; /* adjust to 8 bit bus */
+
+ switch( a&3 )
+ {
+ case 0: /* address port 0 */
+ OPN->ST.address = v;
+ F2610->addr_A1 = 0;
+
+ /* Write register to SSG emulator */
+ if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.param,0,v);
+ break;
+
+ case 1: /* data port 0 */
+ if (F2610->addr_A1 != 0)
+ break; /* verified on real YM2608 */
+
+ addr = OPN->ST.address;
+ F2610->REGS[addr] = v;
+ switch(addr & 0xf0)
+ {
+ case 0x00: /* SSG section */
+ /* Write data to SSG emulator */
+ (*OPN->ST.SSG->write)(OPN->ST.param,a,v);
+ break;
+ case 0x10: /* DeltaT ADPCM */
+ ym2610_update_req(OPN->ST.param);
+
+ switch(addr)
+ {
+ case 0x10: /* control 1 */
+ case 0x11: /* control 2 */
+ case 0x12: /* start address L */
+ case 0x13: /* start address H */
+ case 0x14: /* stop address L */
+ case 0x15: /* stop address H */
+
+ case 0x19: /* delta-n L */
+ case 0x1a: /* delta-n H */
+ case 0x1b: /* volume */
+ {
+ YM_DELTAT_ADPCM_Write(&F2610->deltaT,addr-0x10,v);
+ }
+ break;
+
+ case 0x1c: /* FLAG CONTROL : Extend Status Clear/Mask */
+ {
+ UINT8 statusmask = ~v;
+ /* set arrived flag mask */
+ for(ch=0;ch<6;ch++)
+ F2610->adpcm[ch].flagMask = statusmask&(1<<ch);
+
+ F2610->deltaT.status_change_EOS_bit = statusmask & 0x80; /* status flag: set bit7 on End Of Sample */
+
+ /* clear arrived flag */
+ F2610->adpcm_arrivedEndAddress &= statusmask;
+ }
+ break;
+
+ default:
+ logerror("YM2610: write to unknown deltat register %02x val=%02x\n",addr,v);
+ break;
+ }
+
+ break;
+ case 0x20: /* Mode Register */
+ ym2610_update_req(OPN->ST.param);
+ OPNWriteMode(OPN,addr,v);
+ break;
+ default: /* OPN section */
+ ym2610_update_req(OPN->ST.param);
+ /* write register */
+ OPNWriteReg(OPN,addr,v);
+ }
+ break;
+
+ case 2: /* address port 1 */
+ OPN->ST.address = v;
+ F2610->addr_A1 = 1;
+ break;
+
+ case 3: /* data port 1 */
+ if (F2610->addr_A1 != 1)
+ break; /* verified on real YM2608 */
+
+ ym2610_update_req(OPN->ST.param);
+ addr = OPN->ST.address;
+ F2610->REGS[addr | 0x100] = v;
+ if( addr < 0x30 )
+ /* 100-12f : ADPCM A section */
+ FM_ADPCMAWrite(F2610,addr,v);
+ else
+ OPNWriteReg(OPN,addr | 0x100,v);
+ }
+ return OPN->ST.irq;
+}
+
+UINT8 ym2610_read(void *chip,int a)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+ int addr = F2610->OPN.ST.address;
+ UINT8 ret = 0;
+
+ switch( a&3)
+ {
+ case 0: /* status 0 : YM2203 compatible */
+ ret = FM_STATUS_FLAG(&F2610->OPN.ST) & 0x83;
+ break;
+ case 1: /* data 0 */
+ if( addr < 16 ) ret = (*F2610->OPN.ST.SSG->read)(F2610->OPN.ST.param);
+ if( addr == 0xff ) ret = 0x01;
+ break;
+ case 2: /* status 1 : ADPCM status */
+ /* ADPCM STATUS (arrived End Address) */
+ /* B,--,A5,A4,A3,A2,A1,A0 */
+ /* B = ADPCM-B(DELTA-T) arrived end address */
+ /* A0-A5 = ADPCM-A arrived end address */
+ ret = F2610->adpcm_arrivedEndAddress;
+ break;
+ case 3:
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+int ym2610_timer_over(void *chip,int c)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+
+ if( c )
+ { /* Timer B */
+ TimerBOver( &(F2610->OPN.ST) );
+ }
+ else
+ { /* Timer A */
+ ym2610_update_req(F2610->OPN.ST.param);
+ /* timer update */
+ TimerAOver( &(F2610->OPN.ST) );
+ /* CSM mode key,TL controll */
+ if( F2610->OPN.ST.mode & 0x80 )
+ { /* CSM mode total level latch and auto key on */
+ CSMKeyControll( F2610->OPN.type, &(F2610->CH[2]) );
+ }
+ }
+ return F2610->OPN.ST.irq;
+}
+
+#endif /* (BUILD_YM2610||BUILD_YM2610B) */
diff --git a/src/devices/sound/fm.h b/src/devices/sound/fm.h
new file mode 100644
index 00000000000..878f753b599
--- /dev/null
+++ b/src/devices/sound/fm.h
@@ -0,0 +1,201 @@
+// license:???
+// copyright-holders:Jarek Burczynski
+/*
+ File: fm.h -- header file for software emulation for FM sound generator
+
+*/
+
+#pragma once
+
+#ifndef __FM_H__
+#define __FM_H__
+
+/* --- select emulation chips --- */
+#define BUILD_YM2203 (1) /* build YM2203(OPN) emulator */
+#define BUILD_YM2608 (1) /* build YM2608(OPNA) emulator */
+#define BUILD_YM2610 (1) /* build YM2610(OPNB) emulator */
+#define BUILD_YM2610B (1) /* build YM2610B(OPNB?)emulator */
+#define BUILD_YM2612 (1) /* build YM2612(OPN2) emulator */
+#define BUILD_YM3438 (1) /* build YM3438(OPN) emulator */
+
+/* select bit size of output : 8 or 16 */
+#define FM_SAMPLE_BITS 16
+
+/* select timer system internal or external */
+#define FM_INTERNAL_TIMER 0
+
+/* --- speedup optimize --- */
+/* busy flag enulation , The definition of FM_GET_TIME_NOW() is necessary. */
+#define FM_BUSY_FLAG_SUPPORT 1
+
+/* --- external SSG(YM2149/AY-3-8910)emulator interface port */
+/* used by YM2203,YM2608,and YM2610 */
+struct ssg_callbacks
+{
+ void (*set_clock)(void *param, int clock);
+ void (*write)(void *param, int address, int data);
+ int (*read)(void *param);
+ void (*reset)(void *param);
+};
+
+/* --- external callback funstions for realtime update --- */
+
+#if FM_BUSY_FLAG_SUPPORT
+#define TIME_TYPE attotime
+#define UNDEFINED_TIME attotime::zero
+#define FM_GET_TIME_NOW(machine) (machine)->time()
+#define ADD_TIMES(t1, t2) ((t1) + (t2))
+#define COMPARE_TIMES(t1, t2) (((t1) == (t2)) ? 0 : ((t1) < (t2)) ? -1 : 1)
+#define MULTIPLY_TIME_BY_INT(t,i) ((t) * (i))
+#endif
+
+#if BUILD_YM2203
+ /* in 2203intf.c */
+ void ym2203_update_request(void *param);
+ #define ym2203_update_req(chip) ym2203_update_request(chip)
+#endif /* BUILD_YM2203 */
+
+#if BUILD_YM2608
+ /* in 2608intf.c */
+ void ym2608_update_request(void *param);
+ #define ym2608_update_req(chip) ym2608_update_request(chip);
+#endif /* BUILD_YM2608 */
+
+#if (BUILD_YM2610||BUILD_YM2610B)
+ /* in 2610intf.c */
+ void ym2610_update_request(void *param);
+ #define ym2610_update_req(chip) ym2610_update_request(chip);
+#endif /* (BUILD_YM2610||BUILD_YM2610B) */
+
+#if (BUILD_YM2612||BUILD_YM3438)
+ /* in 2612intf.c */
+ void ym2612_update_request(void *param);
+ #define ym2612_update_req(chip) ym2612_update_request(chip);
+#endif /* (BUILD_YM2612||BUILD_YM3438) */
+
+
+typedef stream_sample_t FMSAMPLE;
+/*
+#if (FM_SAMPLE_BITS==16)
+typedef INT16 FMSAMPLE;
+#endif
+#if (FM_SAMPLE_BITS==8)
+typedef unsigned char FMSAMPLE;
+#endif
+*/
+
+typedef void (*FM_TIMERHANDLER)(void *param,int c,int cnt,int clock);
+typedef void (*FM_IRQHANDLER)(void *param,int irq);
+/* FM_TIMERHANDLER : Stop or Start timer */
+/* int n = chip number */
+/* int c = Channel 0=TimerA,1=TimerB */
+/* int count = timer count (0=stop) */
+/* doube stepTime = step time of one count (sec.)*/
+
+/* FM_IRQHHANDLER : IRQ level changing sense */
+/* int n = chip number */
+/* int irq = IRQ level 0=OFF,1=ON */
+
+#if BUILD_YM2203
+/* -------------------- YM2203(OPN) Interface -------------------- */
+
+/*
+** Initialize YM2203 emulator(s).
+**
+** 'num' is the number of virtual YM2203's to allocate
+** 'baseclock'
+** 'rate' is sampling rate
+** 'TimerHandler' timer callback handler when timer start and clear
+** 'IRQHandler' IRQ callback handler when changed IRQ level
+** return 0 = success
+*/
+void * ym2203_init(void *param, device_t *device, int baseclock, int rate,
+ FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg);
+
+/*
+** shutdown the YM2203 emulators
+*/
+void ym2203_shutdown(void *chip);
+
+/*
+** reset all chip registers for YM2203 number 'num'
+*/
+void ym2203_reset_chip(void *chip);
+
+/*
+** update one of chip
+*/
+void ym2203_update_one(void *chip, FMSAMPLE *buffer, int length);
+
+/*
+** Write
+** return : InterruptLevel
+*/
+int ym2203_write(void *chip,int a,unsigned char v);
+
+/*
+** Read
+** return : InterruptLevel
+*/
+unsigned char ym2203_read(void *chip,int a);
+
+/*
+** Timer OverFlow
+*/
+int ym2203_timer_over(void *chip, int c);
+
+/*
+** State Save
+*/
+void ym2203_postload(void *chip);
+#endif /* BUILD_YM2203 */
+
+#if BUILD_YM2608
+/* -------------------- YM2608(OPNA) Interface -------------------- */
+void * ym2608_init(void *param, device_t *device, int baseclock, int rate,
+ void *pcmroma,int pcmsizea,
+ FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg);
+void ym2608_shutdown(void *chip);
+void ym2608_reset_chip(void *chip);
+void ym2608_update_one(void *chip, FMSAMPLE **buffer, int length);
+
+int ym2608_write(void *chip, int a,unsigned char v);
+unsigned char ym2608_read(void *chip,int a);
+int ym2608_timer_over(void *chip, int c );
+void ym2608_postload(void *chip);
+#endif /* BUILD_YM2608 */
+
+#if (BUILD_YM2610||BUILD_YM2610B)
+/* -------------------- YM2610(OPNB) Interface -------------------- */
+void * ym2610_init(void *param, device_t *device, int baseclock, int rate,
+ void *pcmroma,int pcmasize,void *pcmromb,int pcmbsize,
+ FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg);
+void ym2610_shutdown(void *chip);
+void ym2610_reset_chip(void *chip);
+void ym2610_update_one(void *chip, FMSAMPLE **buffer, int length);
+
+#if BUILD_YM2610B
+void ym2610b_update_one(void *chip, FMSAMPLE **buffer, int length);
+#endif /* BUILD_YM2610B */
+
+int ym2610_write(void *chip, int a,unsigned char v);
+unsigned char ym2610_read(void *chip,int a);
+int ym2610_timer_over(void *chip, int c );
+void ym2610_postload(void *chip);
+#endif /* (BUILD_YM2610||BUILD_YM2610B) */
+
+#if (BUILD_YM2612||BUILD_YM3438)
+void * ym2612_init(void *param, device_t *device, int baseclock, int rate,
+ FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler);
+void ym2612_shutdown(void *chip);
+void ym2612_reset_chip(void *chip);
+void ym2612_update_one(void *chip, FMSAMPLE **buffer, int length);
+
+int ym2612_write(void *chip, int a,unsigned char v);
+unsigned char ym2612_read(void *chip,int a);
+int ym2612_timer_over(void *chip, int c );
+void ym2612_postload(void *chip);
+#endif /* (BUILD_YM2612||BUILD_YM3438) */
+
+
+#endif /* __FM_H__ */
diff --git a/src/devices/sound/fm2612.c b/src/devices/sound/fm2612.c
new file mode 100644
index 00000000000..034b07a8595
--- /dev/null
+++ b/src/devices/sound/fm2612.c
@@ -0,0 +1,2559 @@
+// license:???
+// copyright-holders:Jarek Burczynski,Tatsuyuki Satoh
+/*
+**
+** File: fm2612.c -- software implementation of Yamaha YM2612 FM sound generator
+** Split from fm.c to keep 2612 fixes from infecting other OPN chips
+**
+** Copyright Jarek Burczynski (bujar at mame dot net)
+** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development
+**
+** Version 1.5.1 (Genesis Plus GX ym2612.c rev. 368)
+**
+*/
+
+/*
+** History:
+**
+** 2006~2009 Eke-Eke (Genesis Plus GX):
+** Huge thanks to Nemesis, lot of those fixes came from his tests on Sega Genesis hardware
+** More informations at http://gendev.spritesmind.net/forum/viewtopic.php?t=386
+**
+** TODO:
+**
+** - core documentation
+** - BUSY flag support
+**
+** CHANGELOG:
+**
+** 07-30-2014 dink (FB Alpha project):
+** - fixed missing dac channel on savestate load
+**
+** xx-xx-xxxx
+** - fixed LFO implementation:
+** .added support for CH3 special mode: fixes various sound effects (birds in Warlock, bug sound in Aladdin...)
+** .inverted LFO AM waveform: fixes Spider-Man & Venom : Separation Anxiety (intro), California Games (surfing event)
+** .improved LFO timing accuracy: now updated AFTER sample output, like EG/PG updates, and without any precision loss anymore.
+** - improved internal timers emulation
+** - adjusted lowest EG rates increment values
+** - fixed Attack Rate not being updated in some specific cases (Batman & Robin intro)
+** - fixed EG behavior when Attack Rate is maximal
+** - fixed EG behavior when SL=0 (Mega Turrican tracks 03,09...) or/and Key ON occurs at minimal attenuation
+** - implemented EG output immediate changes on register writes
+** - fixed YM2612 initial values (after the reset): fixes missing intro in B.O.B
+** - implemented Detune overflow (Ariel, Comix Zone, Shaq Fu, Spiderman & many other games using GEMS sound engine)
+** - implemented accurate CSM mode emulation
+** - implemented accurate SSG-EG emulation (Asterix, Beavis&Butthead, Bubba'n Stix & many other games)
+** - implemented accurate address/data ports behavior
+**
+** 06-23-2007 Zsolt Vasvari:
+** - changed the timing not to require the use of floating point calculations
+**
+** 03-08-2003 Jarek Burczynski:
+** - fixed YM2608 initial values (after the reset)
+** - fixed flag and irqmask handling (YM2608)
+** - fixed BUFRDY flag handling (YM2608)
+**
+** 14-06-2003 Jarek Burczynski:
+** - implemented all of the YM2608 status register flags
+** - implemented support for external memory read/write via YM2608
+** - implemented support for deltat memory limit register in YM2608 emulation
+**
+** 22-05-2003 Jarek Burczynski:
+** - fixed LFO PM calculations (copy&paste bugfix)
+**
+** 08-05-2003 Jarek Burczynski:
+** - fixed SSG support
+**
+** 22-04-2003 Jarek Burczynski:
+** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608)
+**
+** 15-04-2003 Jarek Burczynski:
+** - added support for YM2608's register 0x110 - status mask
+**
+** 01-12-2002 Jarek Burczynski:
+** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608)
+** The addressing patch used for early Neo-Geo games can be removed now.
+**
+** 26-11-2002 Jarek Burczynski, Nicola Salmoria:
+** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to:
+** - added emulation of YM2608 drums.
+** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608)
+**
+** 16-08-2002 Jarek Burczynski:
+** - binary exact Envelope Generator (verified on real YM2203);
+** identical to YM2151
+** - corrected 'off by one' error in feedback calculations (when feedback is off)
+** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610)
+**
+** 18-12-2001 Jarek Burczynski:
+** - added SSG-EG support (verified on real YM2203)
+**
+** 12-08-2001 Jarek Burczynski:
+** - corrected sin_tab and tl_tab data (verified on real chip)
+** - corrected feedback calculations (verified on real chip)
+** - corrected phase generator calculations (verified on real chip)
+** - corrected envelope generator calculations (verified on real chip)
+** - corrected FM volume level (YM2610 and YM2610B).
+** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) :
+** this was needed to calculate YM2610 FM channels output correctly.
+** (Each FM channel is calculated as in other chips, but the output of the channel
+** gets shifted right by one *before* sending to accumulator. That was impossible to do
+** with previous implementation).
+**
+** 23-07-2001 Jarek Burczynski, Nicola Salmoria:
+** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip)
+**
+** 11-06-2001 Jarek Burczynski:
+** - corrected end of sample bug in ADPCMA_calc_cha().
+** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits).
+**
+** 08-12-98 hiro-shi:
+** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA
+** move ROM limit check.(CALC_CH? -> 2610Write1/2)
+** test program (ADPCMB_TEST)
+** move ADPCM A/B end check.
+** ADPCMB repeat flag(no check)
+** change ADPCM volume rate (8->16) (32->48).
+**
+** 09-12-98 hiro-shi:
+** change ADPCM volume. (8->16, 48->64)
+** replace ym2610 ch0/3 (YM-2610B)
+** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff.
+** add ADPCM_SHIFT_MASK
+** change ADPCMA_DECODE_MIN/MAX.
+*/
+
+
+
+
+/************************************************************************/
+/* comment of hiro-shi(Hiromitsu Shioya) */
+/* YM2610(B) = OPN-B */
+/* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */
+/* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */
+/************************************************************************/
+
+#include "emu.h"
+#include "fm.h"
+
+/* shared function building option */
+#define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B||BUILD_YM2612||BUILD_YM3438)
+#define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608)
+
+
+/* globals */
+#define TYPE_SSG 0x01 /* SSG support */
+#define TYPE_LFOPAN 0x02 /* OPN type LFO and PAN */
+#define TYPE_6CH 0x04 /* FM 6CH / 3CH */
+#define TYPE_DAC 0x08 /* YM2612's DAC device */
+#define TYPE_ADPCM 0x10 /* two ADPCM units */
+#define TYPE_2610 0x20 /* bogus flag to differentiate 2608 from 2610 */
+
+
+#define TYPE_YM2203 (TYPE_SSG)
+#define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM)
+#define TYPE_YM2610 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM |TYPE_2610)
+#define TYPE_YM2612 (TYPE_DAC |TYPE_LFOPAN |TYPE_6CH)
+
+
+/* globals */
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<<FREQ_SH)-1)
+
+#define MAXOUT (+32767)
+#define MINOUT (-32768)
+
+/* envelope generator */
+#define ENV_BITS 10
+#define ENV_LEN (1<<ENV_BITS)
+#define ENV_STEP (128.0/ENV_LEN)
+
+#define MAX_ATT_INDEX (ENV_LEN-1) /* 1023 */
+#define MIN_ATT_INDEX (0) /* 0 */
+
+#define EG_ATT 4
+#define EG_DEC 3
+#define EG_SUS 2
+#define EG_REL 1
+#define EG_OFF 0
+
+/* operator unit */
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
+
+/* TL_TAB_LEN is calculated as:
+* 13 - sinus amplitude bits (Y axis)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (13*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+#define ENV_QUIET (TL_TAB_LEN>>3)
+
+/* sin waveform table in 'decibel' scale */
+static unsigned int sin_tab[SIN_LEN];
+
+/* sustain level table (3dB per step) */
+/* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */
+/* 1, 2, 4, 8, 16, 32, 64 (value)*/
+/* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/
+
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+/* attenuation value (10 bits) = (SL << 2) << 3 */
+#define SC(db) (UINT32) ( db * (4.0/ENV_STEP) )
+static const UINT32 sl_table[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+
+#define RATE_STEPS (8)
+static const UINT8 eg_inc[19*RATE_STEPS]={
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */
+/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */
+/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */
+/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */
+
+/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */
+/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */
+/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(17) in this table - it's directly in the code */
+static const UINT8 eg_rate_select2612[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */
+/* 32 infinite time rates (same as Rate 0) */
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+
+/* rates 00-11 */
+/*
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+*/
+O(18),O(18),O( 0),O( 0),
+O( 0),O( 0),O( 2),O( 2), // Nemesis's tests
+
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 12 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 13 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 14 */
+O(12),O(13),O(14),O(15),
+
+/* rate 15 */
+O(16),O(16),O(16),O(16),
+
+/* 32 dummy rates (same as 15 3) */
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16)
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/
+/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */
+/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */
+/* 32 infinite time rates */
+/* O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), */
+
+/* fixed (should be the same as rate 0, even if it makes no difference since increment value is 0 for these rates) */
+O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11),
+O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11),
+O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11),
+O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11),
+
+/* rates 00-11 */
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 12 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 32 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0)
+
+};
+#undef O
+
+static const UINT8 dt_tab[4 * 32]={
+/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/
+/* FD=0 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* FD=1 */
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
+/* FD=2 */
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5,
+ 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16,
+/* FD=3 */
+ 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
+ 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22
+};
+
+
+/* OPN key frequency number -> key code follow table */
+/* fnum higher 4bit -> keycode lower 2bit */
+static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3};
+
+
+/* 8 LFO speed parameters */
+/* each value represents number of samples that one LFO level will last for */
+static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5};
+
+
+
+/*There are 4 different LFO AM depths available, they are:
+ 0 dB, 1.4 dB, 5.9 dB, 11.8 dB
+ Here is how it is generated (in EG steps):
+
+ 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0
+ 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0
+ 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0
+
+ (1.4 dB is losing precision as you can see)
+
+ It's implemented as generator from 0..126 with step 2 then a shift
+ right N times, where N is:
+ 8 for 0 dB
+ 3 for 1.4 dB
+ 1 for 5.9 dB
+ 0 for 11.8 dB
+*/
+static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0};
+
+
+
+/*There are 8 different LFO PM depths available, they are:
+ 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents)
+
+ Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10
+ (bits 8,9,10 = FNUM MSB from OCT/FNUM register)
+
+ Here we store only first quarter (positive one) of full waveform.
+ Full table (lfo_pm_table) containing all 128 waveforms is build
+ at run (init) time.
+
+ One value in table below represents 4 (four) basic LFO steps
+ (1 PM step = 4 AM steps).
+
+ For example:
+ at LFO SPEED=0 (which is 108 samples per basic LFO step)
+ one value from "lfo_pm_output" table lasts for 432 consecutive
+ samples (4*108=432) and one full LFO waveform cycle lasts for 13824
+ samples (32*432=13824; 32 because we store only a quarter of whole
+ waveform in the table below)
+*/
+static const UINT8 lfo_pm_output[7*8][8]={ /* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */
+/* FNUM BIT 4: 000 0001xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1},
+
+/* FNUM BIT 5: 000 0010xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3},
+
+/* FNUM BIT 6: 000 0100xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1},
+/* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3},
+/* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6},
+
+/* FNUM BIT 7: 000 1000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1},
+/* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2},
+/* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3},
+/* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6},
+/* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+
+/* FNUM BIT 8: 001 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2},
+/* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3},
+/* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4},
+/* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6},
+/* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+/* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+
+/* FNUM BIT 9: 010 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2},
+/* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4},
+/* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6},
+/* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8},
+/* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+/* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+/* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30},
+
+/* FNUM BIT10: 100 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4},
+/* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8},
+/* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc},
+/* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10},
+/* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+/* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30},
+/* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60},
+
+};
+
+/* all 128 LFO PM waveforms */
+static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */
+
+/* register number to channel number , slot offset */
+#define OPN_CHAN(N) (N&3)
+#define OPN_SLOT(N) ((N>>2)&3)
+
+/* slot number */
+#define SLOT1 0
+#define SLOT2 2
+#define SLOT3 1
+#define SLOT4 3
+
+/* bit0 = Right enable , bit1 = Left enable */
+#define OUTD_RIGHT 1
+#define OUTD_LEFT 2
+#define OUTD_CENTER 3
+
+
+/* save output as raw 16-bit sample */
+/* #define SAVE_SAMPLE */
+
+#ifdef SAVE_SAMPLE
+static FILE *sample[1];
+ #if 1 /*save to MONO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #else /*save to STEREO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ pom = rt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #endif
+#endif
+
+
+/* struct describing a single operator (SLOT) */
+struct fm2612_FM_SLOT
+{
+ INT32 *DT; /* detune :dt_tab[DT] */
+ UINT8 KSR; /* key scale rate :3-KSR */
+ UINT32 ar; /* attack rate */
+ UINT32 d1r; /* decay rate */
+ UINT32 d2r; /* sustain rate */
+ UINT32 rr; /* release rate */
+ UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */
+ UINT32 mul; /* multiple :ML_TABLE[ML] */
+
+ /* Phase Generator */
+ UINT32 phase; /* phase counter */
+ INT32 Incr; /* phase step */
+
+ /* Envelope Generator */
+ UINT8 state; /* phase type */
+ UINT32 tl; /* total level: TL << 3 */
+ INT32 volume; /* envelope counter */
+ UINT32 sl; /* sustain level:sl_table[SL] */
+ UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */
+
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT8 eg_sh_d1r; /* (decay state) */
+ UINT8 eg_sel_d1r; /* (decay state) */
+ UINT8 eg_sh_d2r; /* (sustain state) */
+ UINT8 eg_sel_d2r; /* (sustain state) */
+ UINT8 eg_sh_rr; /* (release state) */
+ UINT8 eg_sel_rr; /* (release state) */
+
+ UINT8 ssg; /* SSG-EG waveform */
+ UINT8 ssgn; /* SSG-EG negated output */
+
+ UINT8 key; /* 0=last key was KEY OFF, 1=KEY ON */
+
+ /* LFO */
+ UINT32 AMmask; /* AM enable flag */
+
+};
+
+struct fm2612_FM_CH
+{
+ fm2612_FM_SLOT SLOT[4]; /* four SLOTs (operators) */
+
+ UINT8 ALGO; /* algorithm */
+ UINT8 FB; /* feedback shift */
+ INT32 op1_out[2]; /* op1 output for feedback */
+
+ INT32 *connect1; /* SLOT1 output pointer */
+ INT32 *connect3; /* SLOT3 output pointer */
+ INT32 *connect2; /* SLOT2 output pointer */
+ INT32 *connect4; /* SLOT4 output pointer */
+
+ INT32 *mem_connect;/* where to put the delayed sample (MEM) */
+ INT32 mem_value; /* delayed sample (MEM) value */
+
+ INT32 pms; /* channel PMS */
+ UINT8 ams; /* channel AMS */
+
+ UINT32 fc; /* fnum,blk:adjusted to sample rate */
+ UINT8 kcode; /* key code: */
+ UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
+};
+
+
+struct fm2612_FM_ST
+{
+ device_t *device;
+ void * param; /* this chip parameter */
+ double freqbase; /* frequency base */
+ int timer_prescaler; /* timer prescaler */
+ UINT8 irq; /* interrupt level */
+ UINT8 irqmask; /* irq mask */
+#if FM_BUSY_FLAG_SUPPORT
+ TIME_TYPE busy_expiry_time; /* expiry time of the busy status */
+#endif
+ UINT32 clock; /* master clock (Hz) */
+ UINT32 rate; /* sampling rate (Hz) */
+ UINT16 address; /* address register */
+ UINT8 status; /* status flag */
+ UINT32 mode; /* mode CSM / 3SLOT */
+ UINT8 fn_h; /* freq latch */
+ UINT8 prescaler_sel; /* prescaler selector */
+ INT32 TA; /* timer a */
+ INT32 TAC; /* timer a counter */
+ UINT8 TB; /* timer b */
+ INT32 TBC; /* timer b counter */
+ /* local time tables */
+ INT32 dt_tab[8][32]; /* DeTune table */
+ /* Extention Timer and IRQ handler */
+ FM_TIMERHANDLER timer_handler;
+ FM_IRQHANDLER IRQ_Handler;
+ const ssg_callbacks *SSG;
+};
+
+
+
+/***********************************************************/
+/* OPN unit */
+/***********************************************************/
+
+/* OPN 3slot struct */
+struct fm2612_FM_3SLOT
+{
+ UINT32 fc[3]; /* fnum3,blk3: calculated */
+ UINT8 fn_h; /* freq3 latch */
+ UINT8 kcode[3]; /* key code */
+ UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
+ UINT8 key_csm; /* CSM mode Key-ON flag */
+};
+
+/* OPN/A/B common state */
+struct fm2612_FM_OPN
+{
+ UINT8 type; /* chip type */
+ fm2612_FM_ST ST; /* general state */
+ fm2612_FM_3SLOT SL3; /* 3 slot mode state */
+ fm2612_FM_CH *P_CH; /* pointer of CH */
+ unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/144/3 */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow;/* envelope generator timer overlfows every 3 samples (on real chip) */
+
+
+ /* there are 2048 FNUMs that can be generated using FNUM/BLK registers
+ but LFO works with one more bit of a precision so we really need 4096 elements */
+ UINT32 fn_table[4096]; /* fnumber->increment counter */
+ UINT32 fn_max; /* maximal phase increment (used for phase overflow) */
+
+ /* LFO */
+ UINT8 lfo_cnt; /* current LFO phase (out of 128) */
+ UINT32 lfo_timer; /* current LFO phase runs at LFO frequency */
+ UINT32 lfo_timer_add; /* step of lfo_timer */
+ UINT32 lfo_timer_overflow; /* LFO timer overflows every N samples (depends on LFO frequency) */
+ UINT32 LFO_AM; /* current LFO AM step */
+ UINT32 LFO_PM; /* current LFO PM step */
+
+ INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */
+ INT32 mem; /* one sample delay memory */
+ INT32 out_fm[8]; /* outputs of working channels */
+
+};
+
+/* here's the virtual YM2612 */
+struct YM2612
+{
+ UINT8 REGS[512]; /* registers */
+ fm2612_FM_OPN OPN; /* OPN state */
+ fm2612_FM_CH CH[6]; /* channel state */
+ UINT8 addr_A1; /* address line A1 */
+
+ /* dac output (YM2612) */
+ int dacen;
+ INT32 dacout;
+};
+
+/* log output level */
+#define LOG_ERR 3 /* ERROR */
+#define LOG_WAR 2 /* WARNING */
+#define LOG_INF 1 /* INFORMATION */
+#define LOG_LEVEL LOG_INF
+
+#ifndef __RAINE__
+#define LOG(n,x) do { if( (n)>=LOG_LEVEL ) logerror x; } while (0)
+#endif
+
+/* limitter */
+#define Limit(val, max,min) { \
+ if ( val > max ) val = max; \
+ else if ( val < min ) val = min; \
+}
+
+
+/* status set and IRQ handling */
+INLINE void FM_STATUS_SET(fm2612_FM_ST *ST,int flag)
+{
+ /* set status flag */
+ ST->status |= flag;
+ if ( !(ST->irq) && (ST->status & ST->irqmask) )
+ {
+ ST->irq = 1;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,1);
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void FM_STATUS_RESET(fm2612_FM_ST *ST,int flag)
+{
+ /* reset status flag */
+ ST->status &=~flag;
+ if ( (ST->irq) && !(ST->status & ST->irqmask) )
+ {
+ ST->irq = 0;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,0);
+ }
+}
+
+/* IRQ mask set */
+INLINE void FM_IRQMASK_SET(fm2612_FM_ST *ST,int flag)
+{
+ ST->irqmask = flag;
+ /* IRQ handling check */
+ FM_STATUS_SET(ST,0);
+ FM_STATUS_RESET(ST,0);
+}
+
+INLINE void FM_KEYON(fm2612_FM_OPN *OPN, fm2612_FM_CH *CH , int s )
+{
+ fm2612_FM_SLOT *SLOT = &CH->SLOT[s];
+
+ if( !SLOT->key && !OPN->SL3.key_csm)
+ {
+ /* restart Phase Generator */
+ SLOT->phase = 0;
+
+ /* reset SSG-EG inversion flag */
+ SLOT->ssgn = 0;
+
+ if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
+ {
+ SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT;
+ }
+ else
+ {
+ /* force attenuation level to 0 */
+ SLOT->volume = MIN_ATT_INDEX;
+
+ /* directly switch to Decay (or Sustain) */
+ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
+ }
+
+ /* recalculate EG output */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+
+ SLOT->key = 1;
+}
+
+INLINE void FM_KEYOFF(fm2612_FM_OPN *OPN, fm2612_FM_CH *CH , int s )
+{
+ fm2612_FM_SLOT *SLOT = &CH->SLOT[s];
+
+ if (SLOT->key && !OPN->SL3.key_csm)
+ {
+ if (SLOT->state>EG_REL)
+ {
+ SLOT->state = EG_REL; /* phase -> Release */
+
+ /* SSG-EG specific update */
+ if (SLOT->ssg&0x08)
+ {
+ /* convert EG attenuation level */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04))
+ SLOT->volume = (0x200 - SLOT->volume);
+
+ /* force EG attenuation level */
+ if (SLOT->volume >= 0x200)
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ SLOT->state = EG_OFF;
+ }
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ }
+ }
+
+ SLOT->key = 0;
+}
+
+INLINE void FM_KEYON_CSM(fm2612_FM_OPN *OPN, fm2612_FM_CH *CH , int s )
+{
+ fm2612_FM_SLOT *SLOT = &CH->SLOT[s];
+
+ if( !SLOT->key && !OPN->SL3.key_csm)
+ {
+ /* restart Phase Generator */
+ SLOT->phase = 0;
+
+ /* reset SSG-EG inversion flag */
+ SLOT->ssgn = 0;
+
+ if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
+ {
+ SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT;
+ }
+ else
+ {
+ /* force attenuation level to 0 */
+ SLOT->volume = MIN_ATT_INDEX;
+
+ /* directly switch to Decay (or Sustain) */
+ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
+ }
+
+ /* recalculate EG output */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+}
+
+INLINE void FM_KEYOFF_CSM(fm2612_FM_CH *CH , int s )
+{
+ fm2612_FM_SLOT *SLOT = &CH->SLOT[s];
+ if (!SLOT->key)
+ {
+ if (SLOT->state>EG_REL)
+ {
+ SLOT->state = EG_REL; /* phase -> Release */
+
+ /* SSG-EG specific update */
+ if (SLOT->ssg&0x08)
+ {
+ /* convert EG attenuation level */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04))
+ SLOT->volume = (0x200 - SLOT->volume);
+
+ /* force EG attenuation level */
+ if (SLOT->volume >= 0x200)
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ SLOT->state = EG_OFF;
+ }
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ }
+ }
+}
+
+/* OPN Mode Register Write */
+INLINE void set_timers(fm2612_FM_OPN *OPN, fm2612_FM_ST *ST, void *n, int v)
+{
+ /* b7 = CSM MODE */
+ /* b6 = 3 slot mode */
+ /* b5 = reset b */
+ /* b4 = reset a */
+ /* b3 = timer enable b */
+ /* b2 = timer enable a */
+ /* b1 = load b */
+ /* b0 = load a */
+
+ if ((OPN->ST.mode ^ v) & 0xC0)
+ {
+ /* phase increment need to be recalculated */
+ OPN->P_CH[2].SLOT[SLOT1].Incr=-1;
+
+ /* CSM mode disabled and CSM key ON active*/
+ if (((v & 0xC0) != 0x80) && OPN->SL3.key_csm)
+ {
+ /* CSM Mode Key OFF (verified by Nemesis on real hardware) */
+ FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT1);
+ FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT2);
+ FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT3);
+ FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT4);
+ OPN->SL3.key_csm = 0;
+ }
+ }
+
+ /* reload Timers */
+ if ((v&1) && !(ST->mode&1))
+ {
+ ST->TAC = (1024-ST->TA);
+ /* External timer handler */
+ if (ST->timer_handler) (ST->timer_handler)(n,0,ST->TAC * ST->timer_prescaler,ST->clock);
+ }
+ else if (!(v & 1))
+ {
+ if( ST->TAC != 0 )
+ {
+ ST->TAC = 0;
+ if (ST->timer_handler) (ST->timer_handler)(n,0,0,ST->clock);
+ }
+ }
+
+ if ((v&2) && !(ST->mode&2))
+ {
+ ST->TBC = ( 256-ST->TB)<<4;
+ /* External timer handler */
+ if (ST->timer_handler) (ST->timer_handler)(n,1,ST->TBC * ST->timer_prescaler,ST->clock);
+ }
+ else if (!(v & 2))
+ {
+ if( ST->TBC != 0 )
+ {
+ ST->TBC = 0;
+ if (ST->timer_handler) (ST->timer_handler)(n,1,0,ST->clock);
+ }
+ }
+
+ /* reset Timers flags */
+ ST->status &= (~v >> 4);
+
+ /* if IRQ should be lowered now, do so */
+ if ( (ST->irq) && !(ST->status & ST->irqmask) )
+ {
+ ST->irq = 0;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param, 0);
+ }
+ ST->mode = v;
+}
+
+
+/* Timer A Overflow */
+INLINE void TimerAOver(fm2612_FM_ST *ST)
+{
+ /* set status (if enabled) */
+ if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01);
+ /* clear or reload the counter */
+ ST->TAC = (1024-ST->TA);
+ if (ST->timer_handler) (ST->timer_handler)(ST->param,0,ST->TAC * ST->timer_prescaler,ST->clock);
+}
+/* Timer B Overflow */
+INLINE void TimerBOver(fm2612_FM_ST *ST)
+{
+ /* set status (if enabled) */
+ if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02);
+ /* clear or reload the counter */
+ ST->TBC = ( 256-ST->TB)<<4;
+ if (ST->timer_handler) (ST->timer_handler)(ST->param,1,ST->TBC * ST->timer_prescaler,ST->clock);
+}
+
+
+#if FM_INTERNAL_TIMER
+/* ----- internal timer mode , update timer */
+
+/* ---------- calculate timer A ---------- */
+ #define INTERNAL_TIMER_A(ST,CSM_CH) \
+ { \
+ if( ST->TAC && (ST->timer_handler==0) ) \
+ if( (ST->TAC -= (int)(ST->freqbase*4096)) <= 0 ) \
+ { \
+ TimerAOver( ST ); \
+ /* CSM mode total level latch and auto key on */ \
+ if( ST->mode & 0x80 ) \
+ CSMKeyControll( CSM_CH ); \
+ } \
+ }
+/* ---------- calculate timer B ---------- */
+ #define INTERNAL_TIMER_B(ST,step) \
+ { \
+ if( ST->TBC && (ST->timer_handler==0) ) \
+ if( (ST->TBC -= (int)(ST->freqbase*4096*step)) <= 0 ) \
+ TimerBOver( ST ); \
+ }
+#else /* FM_INTERNAL_TIMER */
+/* external timer mode */
+#define INTERNAL_TIMER_A(ST,CSM_CH)
+#define INTERNAL_TIMER_B(ST,step)
+#endif /* FM_INTERNAL_TIMER */
+
+
+
+#if FM_BUSY_FLAG_SUPPORT
+#define FM_BUSY_CLEAR(ST) ((ST)->busy_expiry_time = UNDEFINED_TIME)
+INLINE UINT8 FM_STATUS_FLAG(fm2612_FM_ST *ST)
+{
+ if( COMPARE_TIMES(ST->busy_expiry_time, UNDEFINED_TIME) != 0 )
+ {
+ if (COMPARE_TIMES(ST->busy_expiry_time, FM_GET_TIME_NOW(&ST->device->machine())) > 0)
+ return ST->status | 0x80; /* with busy */
+ /* expire */
+ FM_BUSY_CLEAR(ST);
+ }
+ return ST->status;
+}
+#if 0
+INLINE void FM_BUSY_SET(fm2612_FM_ST *ST,int busyclock )
+{
+ TIME_TYPE expiry_period = MULTIPLY_TIME_BY_INT(attotime::from_hz(ST->clock), busyclock * ST->timer_prescaler);
+ ST->busy_expiry_time = ADD_TIMES(FM_GET_TIME_NOW(&ST->device->machine()), expiry_period);
+}
+#endif
+#else
+#define FM_STATUS_FLAG(ST) ((ST)->status)
+#define FM_BUSY_SET(ST,bclock) {}
+#define FM_BUSY_CLEAR(ST) {}
+#endif
+
+
+/* set algorithm connection */
+static void setup_connection(fm2612_FM_OPN *OPN, fm2612_FM_CH *CH, int ch)
+{
+ INT32 *carrier = &OPN->out_fm[ch];
+
+ INT32 **om1 = &CH->connect1;
+ INT32 **om2 = &CH->connect3;
+ INT32 **oc1 = &CH->connect2;
+
+ INT32 **memc = &CH->mem_connect;
+
+ switch( CH->ALGO )
+ {
+ case 0:
+ /* M1---C1---MEM---M2---C2---OUT */
+ *om1 = &OPN->c1;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 1:
+ /* M1------+-MEM---M2---C2---OUT */
+ /* C1-+ */
+ *om1 = &OPN->mem;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 2:
+ /* M1-----------------+-C2---OUT */
+ /* C1---MEM---M2-+ */
+ *om1 = &OPN->c2;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 3:
+ /* M1---C1---MEM------+-C2---OUT */
+ /* M2-+ */
+ *om1 = &OPN->c1;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->c2;
+ break;
+ case 4:
+ /* M1---C1-+-OUT */
+ /* M2---C2-+ */
+ /* MEM: not used */
+ *om1 = &OPN->c1;
+ *oc1 = carrier;
+ *om2 = &OPN->c2;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ case 5:
+ /* +----C1----+ */
+ /* M1-+-MEM---M2-+-OUT */
+ /* +----C2----+ */
+ *om1 = 0; /* special mark */
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->m2;
+ break;
+ case 6:
+ /* M1---C1-+ */
+ /* M2-+-OUT */
+ /* C2-+ */
+ /* MEM: not used */
+ *om1 = &OPN->c1;
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ case 7:
+ /* M1-+ */
+ /* C1-+-OUT */
+ /* M2-+ */
+ /* C2-+ */
+ /* MEM: not used*/
+ *om1 = carrier;
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ }
+
+ CH->connect4 = carrier;
+}
+
+/* set detune & multiple */
+INLINE void set_det_mul(fm2612_FM_ST *ST,fm2612_FM_CH *CH,fm2612_FM_SLOT *SLOT,int v)
+{
+ SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1;
+ SLOT->DT = ST->dt_tab[(v>>4)&7];
+ CH->SLOT[SLOT1].Incr=-1;
+}
+
+/* set total level */
+INLINE void set_tl(fm2612_FM_CH *CH,fm2612_FM_SLOT *SLOT , int v)
+{
+ SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */
+
+ /* recalculate EG output */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+}
+
+/* set attack rate & key scale */
+INLINE void set_ar_ksr(UINT8 type, fm2612_FM_CH *CH,fm2612_FM_SLOT *SLOT,int v)
+{
+ UINT8 old_KSR = SLOT->KSR;
+
+ SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->KSR = 3-(v>>6);
+ if (SLOT->KSR != old_KSR)
+ {
+ CH->SLOT[SLOT1].Incr=-1;
+ }
+
+ /* Even if it seems unnecessary, in some odd case, KSR and KC are modified */
+ /* and could result in SLOT->kc remaining unchanged. */
+ /* In such case, AR values would not be recalculated despite SLOT->ar has changed */
+ /* This actually fixes the intro of "The Adventures of Batman & Robin" (Eke-Eke) */
+ if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select2612[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware */
+ }
+}
+
+/* set decay rate */
+INLINE void set_dr(UINT8 type, fm2612_FM_SLOT *SLOT,int v)
+{
+ SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr];
+ SLOT->eg_sel_d1r= eg_rate_select2612[SLOT->d1r + SLOT->ksr];
+}
+
+/* set sustain rate */
+INLINE void set_sr(UINT8 type, fm2612_FM_SLOT *SLOT,int v)
+{
+ SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr];
+ SLOT->eg_sel_d2r= eg_rate_select2612[SLOT->d2r + SLOT->ksr];
+}
+
+/* set release rate */
+INLINE void set_sl_rr(UINT8 type, fm2612_FM_SLOT *SLOT,int v)
+{
+ SLOT->sl = sl_table[ v>>4 ];
+
+ /* check EG state changes */
+ if ((SLOT->state == EG_DEC) && (SLOT->volume >= (INT32)(SLOT->sl)))
+ SLOT->state = EG_SUS;
+
+ SLOT->rr = 34 + ((v&0x0f)<<2);
+
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr];
+ SLOT->eg_sel_rr = eg_rate_select2612[SLOT->rr + SLOT->ksr];
+}
+
+/* advance LFO to next sample */
+INLINE void advance_lfo(fm2612_FM_OPN *OPN)
+{
+ if (OPN->lfo_timer_overflow) /* LFO enabled ? */
+ {
+ /* increment LFO timer */
+ OPN->lfo_timer += OPN->lfo_timer_add;
+
+ /* when LFO is enabled, one level will last for 108, 77, 71, 67, 62, 44, 8 or 5 samples */
+ while (OPN->lfo_timer >= OPN->lfo_timer_overflow)
+ {
+ OPN->lfo_timer -= OPN->lfo_timer_overflow;
+
+ /* There are 128 LFO steps */
+ OPN->lfo_cnt = ( OPN->lfo_cnt + 1 ) & 127;
+
+ /* triangle (inverted) */
+ /* AM: from 126 to 0 step -2, 0 to 126 step +2 */
+ if (OPN->lfo_cnt<64)
+ OPN->LFO_AM = (OPN->lfo_cnt ^ 63) << 1;
+ else
+ OPN->LFO_AM = (OPN->lfo_cnt & 63) << 1;
+
+ /* PM works with 4 times slower clock */
+ OPN->LFO_PM = OPN->lfo_cnt >> 2;
+ }
+ }
+}
+
+/* changed from INLINE to static here to work around gcc 4.2.1 codegen bug */
+static void advance_eg_channel(fm2612_FM_OPN *OPN, fm2612_FM_SLOT *SLOT)
+{
+ unsigned int out;
+ unsigned int i = 4; /* four operators per channel */
+
+ do
+ {
+ switch(SLOT->state)
+ {
+ case EG_ATT: /* attack phase */
+ if (!(OPN->eg_cnt & ((1<<SLOT->eg_sh_ar)-1)))
+ {
+ /* update attenuation level */
+ SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((OPN->eg_cnt>>SLOT->eg_sh_ar)&7)]))>>4;
+
+ /* check phase transition*/
+ if (SLOT->volume <= MIN_ATT_INDEX)
+ {
+ SLOT->volume = MIN_ATT_INDEX;
+ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; /* special case where SL=0 */
+ }
+
+ /* recalculate EG output */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) /* SSG-EG Output Inversion */
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ if (!(OPN->eg_cnt & ((1<<SLOT->eg_sh_d1r)-1)))
+ {
+ /* SSG EG type */
+ if (SLOT->ssg&0x08)
+ {
+ /* update attenuation level */
+ if (SLOT->volume < 0x200)
+ {
+ SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)];
+
+ /* recalculate EG output */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+
+ }
+ else
+ {
+ /* update attenuation level */
+ SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)];
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+
+ /* check phase transition*/
+ if (SLOT->volume >= (INT32)(SLOT->sl))
+ SLOT->state = EG_SUS;
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+ if (!(OPN->eg_cnt & ((1<<SLOT->eg_sh_d2r)-1)))
+ {
+ /* SSG EG type */
+ if (SLOT->ssg&0x08)
+ {
+ /* update attenuation level */
+ if (SLOT->volume < 0x200)
+ {
+ SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)];
+
+ /* recalculate EG output */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ }
+ else
+ {
+ /* update attenuation level */
+ SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)];
+
+ /* check phase transition*/
+ if ( SLOT->volume >= MAX_ATT_INDEX )
+ SLOT->volume = MAX_ATT_INDEX;
+ /* do not change SLOT->state (verified on real chip) */
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ if (!(OPN->eg_cnt & ((1<<SLOT->eg_sh_rr)-1)))
+ {
+ /* SSG EG type */
+ if (SLOT->ssg&0x08)
+ {
+ /* update attenuation level */
+ if (SLOT->volume < 0x200)
+ SLOT->volume += 4 * eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)];
+ /* check phase transition */
+ if (SLOT->volume >= 0x200)
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ SLOT->state = EG_OFF;
+ }
+ }
+ else
+ {
+ /* update attenuation level */
+ SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)];
+
+ /* check phase transition*/
+ if (SLOT->volume >= MAX_ATT_INDEX)
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ SLOT->state = EG_OFF;
+ }
+ }
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+
+ }
+ break;
+ }
+
+ out = ((UINT32)SLOT->volume);
+
+ /* negate output (changes come from alternate bit, init comes from attack bit) */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn&2) && (SLOT->state > EG_REL))
+ out ^= MAX_ATT_INDEX;
+
+ /* we need to store the result here because we are going to change ssgn
+ in next instruction */
+ SLOT->vol_out = out + SLOT->tl;
+
+ SLOT++;
+ i--;
+ }while (i);
+
+}
+
+/* SSG-EG update process */
+/* The behavior is based upon Nemesis tests on real hardware */
+/* This is actually executed before each samples */
+static void update_ssg_eg_channel(fm2612_FM_SLOT *SLOT)
+{
+ unsigned int i = 4; /* four operators per channel */
+
+ do
+ {
+ /* detect SSG-EG transition */
+ /* this is not required during release phase as the attenuation has been forced to MAX and output invert flag is not used */
+ /* if an Attack Phase is programmed, inversion can occur on each sample */
+ if ((SLOT->ssg & 0x08) && (SLOT->volume >= 0x200) && (SLOT->state > EG_REL))
+ {
+ if (SLOT->ssg & 0x01) /* bit 0 = hold SSG-EG */
+ {
+ /* set inversion flag */
+ if (SLOT->ssg & 0x02)
+ SLOT->ssgn = 4;
+
+ /* force attenuation level during decay phases */
+ if ((SLOT->state != EG_ATT) && !(SLOT->ssgn ^ (SLOT->ssg & 0x04)))
+ SLOT->volume = MAX_ATT_INDEX;
+ }
+ else /* loop SSG-EG */
+ {
+ /* toggle output inversion flag or reset Phase Generator */
+ if (SLOT->ssg & 0x02)
+ SLOT->ssgn ^= 4;
+ else
+ SLOT->phase = 0;
+
+ /* same as Key ON */
+ if (SLOT->state != EG_ATT)
+ {
+ if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
+ {
+ SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT;
+ }
+ else
+ {
+ /* Attack Rate is maximal: directly switch to Decay or Substain */
+ SLOT->volume = MIN_ATT_INDEX;
+ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
+ }
+ }
+ }
+
+ /* recalculate EG output */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+
+ /* next slot */
+ SLOT++;
+ i--;
+ } while (i);
+}
+
+
+INLINE void update_phase_lfo_slot(fm2612_FM_OPN *OPN, fm2612_FM_SLOT *SLOT, INT32 pms, UINT32 block_fnum)
+{
+ UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
+ INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + OPN->LFO_PM ];
+
+ block_fnum = block_fnum*2 + lfo_fn_table_index_offset;
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ UINT8 blk = (block_fnum&0x7000) >> 12;
+ UINT32 fn = block_fnum & 0xfff;
+
+ /* recalculate keyscale code */
+ int kc = (blk<<2) | opn_fktable[(fn >> 7) & 0xf];
+
+ /* recalculate (frequency) phase increment counter */
+ int fc = (OPN->fn_table[fn]>>(7-blk)) + SLOT->DT[kc];
+
+ /* (frequency) phase overflow (credits to Nemesis) */
+ if (fc < 0) fc += OPN->fn_max;
+
+ /* update phase */
+ SLOT->phase += (fc * SLOT->mul) >> 1;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ SLOT->phase += SLOT->Incr;
+ }
+}
+
+INLINE void update_phase_lfo_channel(fm2612_FM_OPN *OPN, fm2612_FM_CH *CH)
+{
+ UINT32 block_fnum = CH->block_fnum;
+
+ UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
+ INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + OPN->LFO_PM ];
+
+ block_fnum = block_fnum*2 + lfo_fn_table_index_offset;
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ UINT8 blk = (block_fnum&0x7000) >> 12;
+ UINT32 fn = block_fnum & 0xfff;
+
+ /* recalculate keyscale code */
+ int kc = (blk<<2) | opn_fktable[(fn >> 7) & 0xf];
+
+ /* recalculate (frequency) phase increment counter */
+ int fc = (OPN->fn_table[fn]>>(7-blk));
+
+ /* (frequency) phase overflow (credits to Nemesis) */
+ int finc = fc + CH->SLOT[SLOT1].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT1].phase += (finc*CH->SLOT[SLOT1].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT2].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT2].phase += (finc*CH->SLOT[SLOT2].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT3].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT3].phase += (finc*CH->SLOT[SLOT3].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT4].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT4].phase += (finc*CH->SLOT[SLOT4].mul) >> 1;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr;
+ CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr;
+ CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr;
+ CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr;
+ }
+}
+
+/* update phase increment and envelope generator */
+INLINE void refresh_fc_eg_slot(fm2612_FM_OPN *OPN, fm2612_FM_SLOT *SLOT , int fc , int kc )
+{
+ int ksr = kc >> SLOT->KSR;
+
+ fc += SLOT->DT[kc];
+
+ /* detects frequency overflow (credits to Nemesis) */
+ if (fc < 0) fc += OPN->fn_max;
+
+ /* (frequency) phase increment counter */
+ SLOT->Incr = (fc * SLOT->mul) >> 1;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT->ar + SLOT->ksr) < 32+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select2612[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware (Attack phase is blocked) */
+ }
+
+ SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr];
+ SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr];
+
+ SLOT->eg_sel_d1r= eg_rate_select2612[SLOT->d1r + SLOT->ksr];
+ SLOT->eg_sel_d2r= eg_rate_select2612[SLOT->d2r + SLOT->ksr];
+ SLOT->eg_sel_rr = eg_rate_select2612[SLOT->rr + SLOT->ksr];
+ }
+}
+
+/* update phase increment counters */
+/* Changed from INLINE to static to work around gcc 4.2.1 codegen bug */
+static void refresh_fc_eg_chan(fm2612_FM_OPN *OPN, fm2612_FM_CH *CH )
+{
+ if( CH->SLOT[SLOT1].Incr==-1)
+ {
+ int fc = CH->fc;
+ int kc = CH->kcode;
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT1] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT2] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT3] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT4] , fc , kc );
+ }
+}
+
+#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask))
+
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm)
+{
+ UINT32 p;
+
+ p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm)
+{
+ UINT32 p;
+
+ p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE void chan_calc(YM2612 *F2612, fm2612_FM_OPN *OPN, fm2612_FM_CH *CH)
+{
+ UINT32 AM = OPN->LFO_AM >> CH->ams;
+ unsigned int eg_out = volume_calc(&CH->SLOT[SLOT1]);
+
+ OPN->m2 = OPN->c1 = OPN->c2 = OPN->mem = 0;
+
+ *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */
+
+ {
+ INT32 out = CH->op1_out[0] + CH->op1_out[1];
+ CH->op1_out[0] = CH->op1_out[1];
+
+ if( !CH->connect1 )
+ {
+ /* algorithm 5 */
+ OPN->mem = OPN->c1 = OPN->c2 = CH->op1_out[0];
+ }
+ else
+ {
+ /* other algorithms */
+ *CH->connect1 += CH->op1_out[0];
+ }
+
+
+ CH->op1_out[1] = 0;
+ if( eg_out < ENV_QUIET ) /* SLOT 1 */
+ {
+ if (!CH->FB)
+ out=0;
+
+ CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<<CH->FB) );
+ }
+ }
+
+ eg_out = volume_calc(&CH->SLOT[SLOT3]);
+ if( eg_out < ENV_QUIET ) /* SLOT 3 */
+ *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, OPN->m2);
+
+ eg_out = volume_calc(&CH->SLOT[SLOT2]);
+ if( eg_out < ENV_QUIET ) /* SLOT 2 */
+ *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, OPN->c1);
+
+ eg_out = volume_calc(&CH->SLOT[SLOT4]);
+ if( eg_out < ENV_QUIET ) /* SLOT 4 */
+ *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, OPN->c2);
+
+
+ /* store current MEM */
+ CH->mem_value = OPN->mem;
+
+ /* update phase counters AFTER output calculations */
+ if(CH->pms)
+ {
+ /* add support for 3 slot mode */
+ if ((OPN->ST.mode & 0xC0) && (CH == &F2612->CH[2]))
+ {
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT1], CH->pms, OPN->SL3.block_fnum[1]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT2], CH->pms, OPN->SL3.block_fnum[2]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT3], CH->pms, OPN->SL3.block_fnum[0]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT4], CH->pms, CH->block_fnum);
+ }
+ else update_phase_lfo_channel(OPN, CH);
+ }
+ else /* no LFO phase modulation */
+ {
+ CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr;
+ CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr;
+ CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr;
+ CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr;
+ }
+}
+
+static void FMCloseTable( void )
+{
+#ifdef SAVE_SAMPLE
+ fclose(sample[0]);
+#endif
+ return;
+}
+
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(fm2612_FM_OPN *OPN, fm2612_FM_CH *CH)
+{
+ /* all key ON (verified by Nemesis on real hardware) */
+ FM_KEYON_CSM(OPN,CH,SLOT1);
+ FM_KEYON_CSM(OPN,CH,SLOT2);
+ FM_KEYON_CSM(OPN,CH,SLOT3);
+ FM_KEYON_CSM(OPN,CH,SLOT4);
+ OPN->SL3.key_csm = 1;
+}
+
+#ifdef __SAVE_H__
+/* FM channel save , internal state only */
+static void FMsave_state_channel(device_t *device,fm2612_FM_CH *CH,int num_ch)
+{
+ int slot , ch;
+
+ for(ch=0;ch<num_ch;ch++,CH++)
+ {
+ /* channel */
+ device->save_item(NAME(CH->op1_out), ch);
+ device->save_item(NAME(CH->fc), ch);
+ /* slots */
+ for(slot=0;slot<4;slot++)
+ {
+ fm2612_FM_SLOT *SLOT = &CH->SLOT[slot];
+ device->save_item(NAME(SLOT->phase), ch * 4 + slot);
+ device->save_item(NAME(SLOT->state), ch * 4 + slot);
+ device->save_item(NAME(SLOT->volume), ch * 4 + slot);
+ }
+ }
+}
+
+static void FMsave_state_st(device_t *device,fm2612_FM_ST *ST)
+{
+#if FM_BUSY_FLAG_SUPPORT
+ device->save_item(NAME(ST->busy_expiry_time) );
+#endif
+ device->save_item(NAME(ST->address) );
+ device->save_item(NAME(ST->irq) );
+ device->save_item(NAME(ST->irqmask) );
+ device->save_item(NAME(ST->status) );
+ device->save_item(NAME(ST->mode) );
+ device->save_item(NAME(ST->prescaler_sel) );
+ device->save_item(NAME(ST->fn_h) );
+ device->save_item(NAME(ST->TA) );
+ device->save_item(NAME(ST->TAC) );
+ device->save_item(NAME(ST->TB) );
+ device->save_item(NAME(ST->TBC) );
+}
+#endif /* _STATE_H */
+
+#if BUILD_OPN
+/* write a OPN mode register 0x20-0x2f */
+static void OPNWriteMode(fm2612_FM_OPN *OPN, int r, int v)
+{
+ UINT8 c;
+ fm2612_FM_CH *CH;
+
+ switch(r)
+ {
+ case 0x21: /* Test */
+ break;
+ case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/YM2612) */
+ if (v&8) /* LFO enabled ? */
+ {
+ OPN->lfo_timer_overflow = lfo_samples_per_step[v&7] << LFO_SH;
+ }
+ else
+ {
+ /* hold LFO waveform in reset state */
+ OPN->lfo_timer_overflow = 0;
+ OPN->lfo_timer = 0;
+ OPN->lfo_cnt = 0;
+ OPN->LFO_PM = 0;
+ OPN->LFO_AM = 126;
+ }
+ break;
+ case 0x24: /* timer A High 8*/
+ OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2);
+ break;
+ case 0x25: /* timer A Low 2*/
+ OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3);
+ break;
+ case 0x26: /* timer B */
+ OPN->ST.TB = v;
+ break;
+ case 0x27: /* mode, timer control */
+ set_timers( OPN, &(OPN->ST),OPN->ST.param,v );
+ break;
+ case 0x28: /* key on / off */
+ c = v & 0x03;
+ if( c == 3 ) break;
+ if( (v&0x04) && (OPN->type & TYPE_6CH) ) c+=3;
+ CH = OPN->P_CH;
+ CH = &CH[c];
+ if(v&0x10) FM_KEYON(OPN,CH,SLOT1); else FM_KEYOFF(OPN,CH,SLOT1);
+ if(v&0x20) FM_KEYON(OPN,CH,SLOT2); else FM_KEYOFF(OPN,CH,SLOT2);
+ if(v&0x40) FM_KEYON(OPN,CH,SLOT3); else FM_KEYOFF(OPN,CH,SLOT3);
+ if(v&0x80) FM_KEYON(OPN,CH,SLOT4); else FM_KEYOFF(OPN,CH,SLOT4);
+ break;
+ }
+}
+
+/* write a OPN register (0x30-0xff) */
+static void OPNWriteReg(fm2612_FM_OPN *OPN, int r, int v)
+{
+ fm2612_FM_CH *CH;
+ fm2612_FM_SLOT *SLOT;
+
+ UINT8 c = OPN_CHAN(r);
+
+ if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */
+
+ if (r >= 0x100) c+=3;
+
+ CH = OPN->P_CH;
+ CH = &CH[c];
+
+ SLOT = &(CH->SLOT[OPN_SLOT(r)]);
+
+ switch( r & 0xf0 ) {
+ case 0x30: /* DET , MUL */
+ set_det_mul(&OPN->ST,CH,SLOT,v);
+ break;
+
+ case 0x40: /* TL */
+ set_tl(CH,SLOT,v);
+ break;
+
+ case 0x50: /* KS, AR */
+ set_ar_ksr(OPN->type,CH,SLOT,v);
+ break;
+
+ case 0x60: /* bit7 = AM ENABLE, DR */
+ set_dr(OPN->type, SLOT,v);
+
+ if(OPN->type & TYPE_LFOPAN) /* YM2608/2610/2610B/2612 */
+ {
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;
+ }
+ break;
+
+ case 0x70: /* SR */
+ set_sr(OPN->type,SLOT,v);
+ break;
+
+ case 0x80: /* SL, RR */
+ set_sl_rr(OPN->type,SLOT,v);
+ break;
+
+ case 0x90: /* SSG-EG */
+ SLOT->ssg = v&0x0f;
+
+ /* recalculate EG output */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+
+ /* SSG-EG envelope shapes :
+
+ E AtAlH
+ 1 0 0 0 \\\\
+
+ 1 0 0 1 \___
+
+ 1 0 1 0 \/\/
+ ___
+ 1 0 1 1 \
+
+ 1 1 0 0 ////
+ ___
+ 1 1 0 1 /
+
+ 1 1 1 0 /\/\
+
+ 1 1 1 1 /___
+
+
+ E = SSG-EG enable
+
+
+ The shapes are generated using Attack, Decay and Sustain phases.
+
+ Each single character in the diagrams above represents this whole
+ sequence:
+
+ - when KEY-ON = 1, normal Attack phase is generated (*without* any
+ difference when compared to normal mode),
+
+ - later, when envelope level reaches minimum level (max volume),
+ the EG switches to Decay phase (which works with bigger steps
+ when compared to normal mode - see below),
+
+ - later when envelope level passes the SL level,
+ the EG swithes to Sustain phase (which works with bigger steps
+ when compared to normal mode - see below),
+
+ - finally when envelope level reaches maximum level (min volume),
+ the EG switches to Attack phase again (depends on actual waveform).
+
+ Important is that when switch to Attack phase occurs, the phase counter
+ of that operator will be zeroed-out (as in normal KEY-ON) but not always.
+ (I havent found the rule for that - perhaps only when the output level is low)
+
+ The difference (when compared to normal Envelope Generator mode) is
+ that the resolution in Decay and Sustain phases is 4 times lower;
+ this results in only 256 steps instead of normal 1024.
+ In other words:
+ when SSG-EG is disabled, the step inside of the EG is one,
+ when SSG-EG is enabled, the step is four (in Decay and Sustain phases).
+
+ Times between the level changes are the same in both modes.
+
+
+ Important:
+ Decay 1 Level (so called SL) is compared to actual SSG-EG output, so
+ it is the same in both SSG and no-SSG modes, with this exception:
+
+ when the SSG-EG is enabled and is generating raising levels
+ (when the EG output is inverted) the SL will be found at wrong level !!!
+ For example, when SL=02:
+ 0 -6 = -6dB in non-inverted EG output
+ 96-6 = -90dB in inverted EG output
+ Which means that EG compares its level to SL as usual, and that the
+ output is simply inverted afterall.
+
+
+ The Yamaha's manuals say that AR should be set to 0x1f (max speed).
+ That is not necessary, but then EG will be generating Attack phase.
+
+ */
+
+
+ break;
+
+ case 0xa0:
+ switch( OPN_SLOT(r) )
+ {
+ case 0: /* 0xa0-0xa2 : FNUM1 */
+ {
+ UINT32 fn = (((UINT32)( (OPN->ST.fn_h)&7))<<8) + v;
+ UINT8 blk = OPN->ST.fn_h>>3;
+ /* keyscale code */
+ CH->kcode = (blk<<2) | opn_fktable[(fn >> 7) & 0xf];
+ /* phase increment counter */
+ CH->fc = OPN->fn_table[fn*2]>>(7-blk);
+
+ /* store fnum in clear form for LFO PM calculations */
+ CH->block_fnum = (blk<<11) | fn;
+
+ CH->SLOT[SLOT1].Incr=-1;
+ }
+ break;
+ case 1: /* 0xa4-0xa6 : FNUM2,BLK */
+ OPN->ST.fn_h = v&0x3f;
+ break;
+ case 2: /* 0xa8-0xaa : 3CH FNUM1 */
+ if(r < 0x100)
+ {
+ UINT32 fn = (((UINT32)(OPN->SL3.fn_h&7))<<8) + v;
+ UINT8 blk = OPN->SL3.fn_h>>3;
+ /* keyscale code */
+ OPN->SL3.kcode[c]= (blk<<2) | opn_fktable[(fn >> 7) & 0xf];
+ /* phase increment counter */
+ OPN->SL3.fc[c] = OPN->fn_table[fn*2]>>(7-blk);
+ OPN->SL3.block_fnum[c] = (blk<<11) | fn;
+ (OPN->P_CH)[2].SLOT[SLOT1].Incr=-1;
+ }
+ break;
+ case 3: /* 0xac-0xae : 3CH FNUM2,BLK */
+ if(r < 0x100)
+ OPN->SL3.fn_h = v&0x3f;
+ break;
+ }
+ break;
+
+ case 0xb0:
+ switch( OPN_SLOT(r) )
+ {
+ case 0: /* 0xb0-0xb2 : FB,ALGO */
+ {
+ int feedback = (v>>3)&7;
+ CH->ALGO = v&7;
+ CH->FB = feedback ? feedback+6 : 0;
+ setup_connection( OPN, CH, c );
+ }
+ break;
+ case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2610B/YM2610/YM2608) */
+ if( OPN->type & TYPE_LFOPAN)
+ {
+ /* b0-2 PMS */
+ CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */
+
+ /* b4-5 AMS */
+ CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03];
+
+ /* PAN : b7 = L, b6 = R */
+ OPN->pan[ c*2 ] = (v & 0x80) ? ~0 : 0;
+ OPN->pan[ c*2+1 ] = (v & 0x40) ? ~0 : 0;
+
+ }
+ break;
+ }
+ break;
+ }
+}
+
+/* initialize time tables */
+static void init_timetables(fm2612_FM_OPN *OPN, double freqbase)
+{
+ int i,d;
+ double rate;
+
+ /* DeTune table */
+ for (d = 0;d <= 3;d++)
+ {
+ for (i = 0;i <= 31;i++)
+ {
+ rate = ((double)dt_tab[d*32 + i]) * freqbase * (1<<(FREQ_SH-10)); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+ OPN->ST.dt_tab[d][i] = (INT32) rate;
+ OPN->ST.dt_tab[d+4][i] = -OPN->ST.dt_tab[d][i];
+ }
+ }
+
+ /* there are 2048 FNUMs that can be generated using FNUM/BLK registers
+ but LFO works with one more bit of a precision so we really need 4096 elements */
+ /* calculate fnumber -> increment counter table */
+ for(i = 0; i < 4096; i++)
+ {
+ /* freq table for octave 7 */
+ /* OPN phase increment counter = 20bit */
+ /* the correct formula is : F-Number = (144 * fnote * 2^20 / M) / 2^(B-1) */
+ /* where sample clock is M/144 */
+ /* this means the increment value for one clock sample is FNUM * 2^(B-1) = FNUM * 64 for octave 7 */
+ /* we also need to handle the ratio between the chip frequency and the emulated frequency (can be 1.0) */
+ OPN->fn_table[i] = (UINT32)( (double)i * 32 * freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+ }
+
+ /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */
+ OPN->fn_max = (UINT32)( (double)0x20000 * freqbase * (1<<(FREQ_SH-10)) );
+}
+
+/* prescaler set (and make time tables) */
+static void OPNSetPres(fm2612_FM_OPN *OPN, int pres, int timer_prescaler, int SSGpres)
+{
+ /* frequency base */
+ OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pres : 0;
+
+ /* EG is updated every 3 samples */
+ OPN->eg_timer_add = (UINT32)((1<<EG_SH) * OPN->ST.freqbase);
+ OPN->eg_timer_overflow = ( 3 ) * (1<<EG_SH);
+
+ /* LFO timer increment (every samples) */
+ OPN->lfo_timer_add = (UINT32)((1<<LFO_SH) * OPN->ST.freqbase);
+
+ /* Timer base time */
+ OPN->ST.timer_prescaler = timer_prescaler;
+
+ /* SSG part prescaler set */
+ if( SSGpres ) (*OPN->ST.SSG->set_clock)( OPN->ST.param, OPN->ST.clock * 2 / SSGpres );
+
+ /* make time tables */
+ init_timetables(OPN, OPN->ST.freqbase);
+}
+
+static void reset_channels(fm2612_FM_ST *ST , fm2612_FM_CH *CH , int num)
+{
+ int c,s;
+
+ for( c = 0 ; c < num ; c++ )
+ {
+ CH[c].fc = 0;
+ for(s = 0 ; s < 4 ; s++ )
+ {
+ CH[c].SLOT[s].ssg = 0;
+ CH[c].SLOT[s].ssgn = 0;
+ CH[c].SLOT[s].state= EG_OFF;
+ CH[c].SLOT[s].volume = MAX_ATT_INDEX;
+ CH[c].SLOT[s].vol_out= MAX_ATT_INDEX;
+ }
+ }
+}
+
+/* initialize generic tables */
+static void init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+ /* build Linear Power Table */
+ for (x=0; x<TL_RES_LEN; x++)
+ {
+ m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);
+ m = floor(m);
+
+ /* we never reach (1<<16) here due to the (x+1) */
+ /* result fits within 16 bits at maximum */
+
+ n = (int)m; /* 16 bits here */
+ n >>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 2; /* 13 bits here (as in real chip) */
+
+
+ /* 14 bits (with sign bit) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ /* one entry in the 'Power' table use the following format, xxxxxyyyyyyyys with: */
+ /* s = sign bit */
+ /* yyyyyyyy = 8-bits decimal part (0-TL_RES_LEN) */
+ /* xxxxx = 5-bits integer 'shift' value (0-31) but, since Power table output is 13 bits, */
+ /* any value above 13 (included) would be discarded. */
+ for (i=1; i<13; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ }
+
+ /* build Logarithmic Sinus table */
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* non-standard sinus */
+ m = sin( ((i*2)+1) * M_PI / SIN_LEN ); /* checked against the real chip */
+ /* we never reach zero here due to ((i*2)+1) */
+
+ if (m>0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ /* 13-bits (8.5) value is formatted for above 'Power' table */
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+ }
+
+ /* build LFO PM modulation table */
+ for(i = 0; i < 8; i++) /* 8 PM depths */
+ {
+ UINT8 fnum;
+ for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */
+ {
+ UINT8 value;
+ UINT8 step;
+ UINT32 offset_depth = i;
+ UINT32 offset_fnum_bit;
+ UINT32 bit_tmp;
+
+ for (step=0; step<8; step++)
+ {
+ value = 0;
+ for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */
+ {
+ if (fnum & (1<<bit_tmp)) /* only if bit "bit_tmp" is set */
+ {
+ offset_fnum_bit = bit_tmp * 8;
+ value += lfo_pm_output[offset_fnum_bit + offset_depth][step];
+ }
+ }
+ /* 32 steps for LFO PM (sinus) */
+ lfo_pm_table[(fnum*32*8) + (i*32) + step + 0] = value;
+ lfo_pm_table[(fnum*32*8) + (i*32) +(step^7)+ 8] = value;
+ lfo_pm_table[(fnum*32*8) + (i*32) + step +16] = -value;
+ lfo_pm_table[(fnum*32*8) + (i*32) +(step^7)+24] = -value;
+ }
+
+ }
+ }
+
+#ifdef SAVE_SAMPLE
+ sample[0]=fopen("sampsum.pcm","wb");
+#endif
+}
+
+#endif /* BUILD_OPN */
+
+#if (BUILD_YM2612||BUILD_YM3438)
+/*******************************************************************************/
+/* YM2612 local section */
+/*******************************************************************************/
+
+/* Generate samples for one of the YM2612s */
+void ym2612_update_one(void *chip, FMSAMPLE **buffer, int length)
+{
+ YM2612 *F2612 = (YM2612 *)chip;
+ fm2612_FM_OPN *OPN = &F2612->OPN;
+ INT32 *out_fm = OPN->out_fm;
+ int i;
+ FMSAMPLE *bufL,*bufR;
+ fm2612_FM_CH *cch[6];
+ int lt,rt;
+
+ /* set bufer */
+ bufL = buffer[0];
+ bufR = buffer[1];
+
+ cch[0] = &F2612->CH[0];
+ cch[1] = &F2612->CH[1];
+ cch[2] = &F2612->CH[2];
+ cch[3] = &F2612->CH[3];
+ cch[4] = &F2612->CH[4];
+ cch[5] = &F2612->CH[5];
+
+ /* refresh PG and EG */
+ refresh_fc_eg_chan( OPN, cch[0] );
+ refresh_fc_eg_chan( OPN, cch[1] );
+ if( (OPN->ST.mode & 0xc0) )
+ {
+ /* 3SLOT MODE */
+ if( cch[2]->SLOT[SLOT1].Incr==-1)
+ {
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode );
+ }
+ }else refresh_fc_eg_chan( OPN, cch[2] );
+ refresh_fc_eg_chan( OPN, cch[3] );
+ refresh_fc_eg_chan( OPN, cch[4] );
+ refresh_fc_eg_chan( OPN, cch[5] );
+
+ /* buffering */
+ for(i=0; i < length ; i++)
+ {
+ /* clear outputs */
+ out_fm[0] = 0;
+ out_fm[1] = 0;
+ out_fm[2] = 0;
+ out_fm[3] = 0;
+ out_fm[4] = 0;
+ out_fm[5] = 0;
+
+ /* update SSG-EG output */
+ update_ssg_eg_channel(&cch[0]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[1]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[2]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[3]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[4]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[5]->SLOT[SLOT1]);
+
+ /* calculate FM */
+ chan_calc(F2612, OPN, cch[0]);
+ chan_calc(F2612, OPN, cch[1]);
+ chan_calc(F2612, OPN, cch[2]);
+ chan_calc(F2612, OPN, cch[3]);
+ chan_calc(F2612, OPN, cch[4]);
+ if( F2612->dacen )
+ *cch[5]->connect4 += F2612->dacout;
+ else
+ chan_calc(F2612, OPN, cch[5]);
+
+ /* advance LFO */
+ advance_lfo(OPN);
+
+ /* advance envelope generator */
+ OPN->eg_timer += OPN->eg_timer_add;
+ while (OPN->eg_timer >= OPN->eg_timer_overflow)
+ {
+ OPN->eg_timer -= OPN->eg_timer_overflow;
+ OPN->eg_cnt++;
+
+ advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[4]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[5]->SLOT[SLOT1]);
+ }
+
+ if (out_fm[0] > 8191) out_fm[0] = 8191;
+ else if (out_fm[0] < -8192) out_fm[0] = -8192;
+ if (out_fm[1] > 8191) out_fm[1] = 8191;
+ else if (out_fm[1] < -8192) out_fm[1] = -8192;
+ if (out_fm[2] > 8191) out_fm[2] = 8191;
+ else if (out_fm[2] < -8192) out_fm[2] = -8192;
+ if (out_fm[3] > 8191) out_fm[3] = 8191;
+ else if (out_fm[3] < -8192) out_fm[3] = -8192;
+ if (out_fm[4] > 8191) out_fm[4] = 8191;
+ else if (out_fm[4] < -8192) out_fm[4] = -8192;
+ if (out_fm[5] > 8191) out_fm[5] = 8191;
+ else if (out_fm[5] < -8192) out_fm[5] = -8192;
+
+ /* 6-channels mixing */
+ lt = ((out_fm[0]>>0) & OPN->pan[0]);
+ rt = ((out_fm[0]>>0) & OPN->pan[1]);
+ lt += ((out_fm[1]>>0) & OPN->pan[2]);
+ rt += ((out_fm[1]>>0) & OPN->pan[3]);
+ lt += ((out_fm[2]>>0) & OPN->pan[4]);
+ rt += ((out_fm[2]>>0) & OPN->pan[5]);
+ lt += ((out_fm[3]>>0) & OPN->pan[6]);
+ rt += ((out_fm[3]>>0) & OPN->pan[7]);
+ lt += ((out_fm[4]>>0) & OPN->pan[8]);
+ rt += ((out_fm[4]>>0) & OPN->pan[9]);
+ lt += ((out_fm[5]>>0) & OPN->pan[10]);
+ rt += ((out_fm[5]>>0) & OPN->pan[11]);
+
+// Limit( lt, MAXOUT, MINOUT );
+// Limit( rt, MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ SAVE_ALL_CHANNELS
+ #endif
+
+ /* buffering */
+ bufL[i] = lt;
+ bufR[i] = rt;
+
+ /* CSM mode: if CSM Key ON has occurred, CSM Key OFF need to be sent */
+ /* only if Timer A does not overflow again (i.e CSM Key ON not set again) */
+ OPN->SL3.key_csm <<= 1;
+
+ /* timer A control */
+ INTERNAL_TIMER_A( &OPN->ST , cch[2] )
+
+ /* CSM Mode Key ON still disabled */
+ /* CSM Mode Key OFF (verified by Nemesis on real hardware) */
+ FM_KEYOFF_CSM(cch[2],SLOT1);
+ FM_KEYOFF_CSM(cch[2],SLOT2);
+ FM_KEYOFF_CSM(cch[2],SLOT3);
+ FM_KEYOFF_CSM(cch[2],SLOT4);
+ OPN->SL3.key_csm = 0;
+ }
+
+ /* timer B control */
+ INTERNAL_TIMER_B(&OPN->ST,length)
+}
+
+#ifdef __SAVE_H__
+void ym2612_postload(void *chip)
+{
+ if (chip)
+ {
+ YM2612 *F2612 = (YM2612 *)chip;
+ int r;
+
+ /* DAC data & port */
+ F2612->dacout = ((int)F2612->REGS[0x2a] - 0x80) << 6; /* level unknown */
+ F2612->dacen = F2612->REGS[0x2b] & 0x80;
+ /* OPN registers */
+ /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */
+ for(r=0x30;r<0x9e;r++)
+ if((r&3) != 3)
+ {
+ OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]);
+ OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]);
+ }
+ /* FB / CONNECT , L / R / AMS / PMS */
+ for(r=0xb0;r<0xb6;r++)
+ if((r&3) != 3)
+ {
+ OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]);
+ OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]);
+ }
+ /* channels */
+ /*FM_channel_postload(F2612->CH,6);*/
+ }
+}
+
+static void YM2612_save_state(YM2612 *F2612, device_t *device)
+{
+ device->save_item(NAME(F2612->REGS));
+ FMsave_state_st(device,&F2612->OPN.ST);
+ FMsave_state_channel(device,F2612->CH,6);
+ /* 3slots */
+ device->save_item(NAME(F2612->OPN.SL3.fc));
+ device->save_item(NAME(F2612->OPN.SL3.fn_h));
+ device->save_item(NAME(F2612->OPN.SL3.kcode));
+ /* address register1 */
+ device->save_item(NAME(F2612->addr_A1));
+}
+#endif /* _STATE_H */
+
+/* initialize YM2612 emulator(s) */
+void * ym2612_init(void *param, device_t *device, int clock, int rate,
+ FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler)
+{
+ YM2612 *F2612;
+
+ /* allocate extend state space */
+ F2612 = auto_alloc_clear(device->machine(), YM2612);
+ /* allocate total level table (128kb space) */
+ init_tables();
+
+ F2612->OPN.ST.param = param;
+ F2612->OPN.type = TYPE_YM2612;
+ F2612->OPN.P_CH = F2612->CH;
+ F2612->OPN.ST.device = device;
+ F2612->OPN.ST.clock = clock;
+ F2612->OPN.ST.rate = rate;
+ /* F2612->OPN.ST.irq = 0; */
+ /* F2612->OPN.ST.status = 0; */
+ /* Extend handler */
+ F2612->OPN.ST.timer_handler = timer_handler;
+ F2612->OPN.ST.IRQ_Handler = IRQHandler;
+
+#ifdef __SAVE_H__
+ YM2612_save_state(F2612, device);
+#endif
+ return F2612;
+}
+
+/* shut down emulator */
+void ym2612_shutdown(void *chip)
+{
+ YM2612 *F2612 = (YM2612 *)chip;
+
+ FMCloseTable();
+ auto_free(F2612->OPN.ST.device->machine(), F2612);
+}
+
+/* reset one of chip */
+void ym2612_reset_chip(void *chip)
+{
+ int i;
+ YM2612 *F2612 = (YM2612 *)chip;
+ fm2612_FM_OPN *OPN = &F2612->OPN;
+
+ OPNSetPres( OPN, 6*24, 6*24, 0);
+ /* status clear */
+ FM_IRQMASK_SET(&OPN->ST,0x03);
+ FM_BUSY_CLEAR(&OPN->ST);
+ OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */
+
+ OPN->eg_timer = 0;
+ OPN->eg_cnt = 0;
+
+ OPN->lfo_timer = 0;
+ OPN->lfo_cnt = 0;
+ OPN->LFO_AM = 126;
+ OPN->LFO_PM = 0;
+
+ OPN->ST.status = 0;
+ OPN->ST.mode = 0;
+
+ OPNWriteMode(OPN,0x27,0x30);
+ OPNWriteMode(OPN,0x26,0x00);
+ OPNWriteMode(OPN,0x25,0x00);
+ OPNWriteMode(OPN,0x24,0x00);
+
+ reset_channels( &OPN->ST , &F2612->CH[0] , 6 );
+
+ for(i = 0xb6 ; i >= 0xb4 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0xc0);
+ OPNWriteReg(OPN,i|0x100,0xc0);
+ }
+ for(i = 0xb2 ; i >= 0x30 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0);
+ OPNWriteReg(OPN,i|0x100,0);
+ }
+
+ /* DAC mode clear */
+ F2612->dacen = 0;
+ F2612->dacout = 0;
+}
+
+/* YM2612 write */
+/* n = number */
+/* a = address */
+/* v = value */
+int ym2612_write(void *chip, int a, UINT8 v)
+{
+ YM2612 *F2612 = (YM2612 *)chip;
+ int addr;
+
+ v &= 0xff; /* adjust to 8 bit bus */
+
+ switch( a&3)
+ {
+ case 0: /* address port 0 */
+ F2612->OPN.ST.address = v;
+ F2612->addr_A1 = 0;
+ break;
+
+ case 1: /* data port 0 */
+ if (F2612->addr_A1 != 0)
+ break; /* verified on real YM2608 */
+
+ addr = F2612->OPN.ST.address;
+ F2612->REGS[addr] = v;
+ switch( addr & 0xf0 )
+ {
+ case 0x20: /* 0x20-0x2f Mode */
+ switch( addr )
+ {
+ case 0x2a: /* DAC data (YM2612) */
+ ym2612_update_req(F2612->OPN.ST.param);
+ F2612->dacout = ((int)v - 0x80) << 6; /* level unknown */
+ break;
+ case 0x2b: /* DAC Sel (YM2612) */
+ /* b7 = dac enable */
+ F2612->dacen = v & 0x80;
+ break;
+ default: /* OPN section */
+ ym2612_update_req(F2612->OPN.ST.param);
+ /* write register */
+ OPNWriteMode(&(F2612->OPN),addr,v);
+ }
+ break;
+ default: /* 0x30-0xff OPN section */
+ ym2612_update_req(F2612->OPN.ST.param);
+ /* write register */
+ OPNWriteReg(&(F2612->OPN),addr,v);
+ }
+ break;
+
+ case 2: /* address port 1 */
+ F2612->OPN.ST.address = v;
+ F2612->addr_A1 = 1;
+ break;
+
+ case 3: /* data port 1 */
+ if (F2612->addr_A1 != 1)
+ break; /* verified on real YM2608 */
+
+ addr = F2612->OPN.ST.address;
+ F2612->REGS[addr | 0x100] = v;
+ ym2612_update_req(F2612->OPN.ST.param);
+ OPNWriteReg(&(F2612->OPN),addr | 0x100,v);
+ break;
+ }
+ return F2612->OPN.ST.irq;
+}
+
+UINT8 ym2612_read(void *chip,int a)
+{
+ YM2612 *F2612 = (YM2612 *)chip;
+
+ switch( a&3)
+ {
+ case 0: /* status 0 */
+ return FM_STATUS_FLAG(&F2612->OPN.ST);
+ case 1:
+ case 2:
+ case 3:
+ LOG(LOG_WAR,("YM2612 #%p:A=%d read unmapped area\n",F2612->OPN.ST.param,a));
+ return FM_STATUS_FLAG(&F2612->OPN.ST);
+ }
+ return 0;
+}
+
+int ym2612_timer_over(void *chip,int c)
+{
+ YM2612 *F2612 = (YM2612 *)chip;
+
+ if( c )
+ { /* Timer B */
+ TimerBOver( &(F2612->OPN.ST) );
+ }
+ else
+ { /* Timer A */
+ ym2612_update_req(F2612->OPN.ST.param);
+ /* timer update */
+ TimerAOver( &(F2612->OPN.ST) );
+ /* CSM mode key,TL controll */
+ if ((F2612->OPN.ST.mode & 0xc0) == 0x80)
+ { /* CSM mode total level latch and auto key on */
+ CSMKeyControll( &F2612->OPN, &(F2612->CH[2]) );
+ }
+ }
+ return F2612->OPN.ST.irq;
+}
+
+#endif /* (BUILD_YM2612||BUILD_YM3238) */
diff --git a/src/devices/sound/fmopl.c b/src/devices/sound/fmopl.c
new file mode 100644
index 00000000000..4932cde31fa
--- /dev/null
+++ b/src/devices/sound/fmopl.c
@@ -0,0 +1,2582 @@
+// license:???
+// copyright-holders:Jarek Burczynski
+/*
+**
+** File: fmopl.c - software implementation of FM sound generator
+** types OPL and OPL2
+**
+** Copyright Jarek Burczynski (bujar at mame dot net)
+** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development
+**
+** Version 0.72
+**
+
+Revision History:
+
+04-08-2003 Jarek Burczynski:
+ - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip
+ handles memory read/write or during the adpcm synthesis when the chip
+ requests another byte of ADPCM data.
+
+24-07-2003 Jarek Burczynski:
+ - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after
+ some (unknown) delay). Right now it's always set.
+
+14-06-2003 Jarek Burczynski:
+ - implemented all of the status register flags in Y8950 emulation
+ - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since
+ they can be either RAM or ROM
+
+08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip)
+ - corrected ym3526_read() to always set bit 2 and bit 1
+ to HIGH state - identical to ym3812_read (verified on real YM3526)
+
+04-28-2002 Jarek Burczynski:
+ - binary exact Envelope Generator (verified on real YM3812);
+ compared to YM2151: the EG clock is equal to internal_clock,
+ rates are 2 times slower and volume resolution is one bit less
+ - modified interface functions (they no longer return pointer -
+ that's internal to the emulator now):
+ - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init()
+ - corrected 'off by one' error in feedback calculations (when feedback is off)
+ - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22)
+ - speeded up noise generator calculations (Nicola Salmoria)
+
+03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip)
+ Complete rewrite (all verified on real YM3812):
+ - corrected sin_tab and tl_tab data
+ - corrected operator output calculations
+ - corrected waveform_select_enable register;
+ simply: ignore all writes to waveform_select register when
+ waveform_select_enable == 0 and do not change the waveform previously selected.
+ - corrected KSR handling
+ - corrected Envelope Generator: attack shape, Sustain mode and
+ Percussive/Non-percussive modes handling
+ - Envelope Generator rates are two times slower now
+ - LFO amplitude (tremolo) and phase modulation (vibrato)
+ - rhythm sounds phase generation
+ - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm)
+ - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM)
+ - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1)
+
+12-28-2001 Acho A. Tang
+ - reflected Delta-T EOS status on Y8950 status port.
+ - fixed subscription range of attack/decay tables
+
+
+ To do:
+ add delay before key off in CSM mode (see CSMKeyControll)
+ verify volume of the FM part on the Y8950
+*/
+
+#include "emu.h"
+#include "ymdeltat.h"
+#include "fmopl.h"
+
+
+
+/* output final shift */
+#if (OPL_SAMPLE_BITS==16)
+ #define FINAL_SH (0)
+ #define MAXOUT (+32767)
+ #define MINOUT (-32768)
+#else
+ #define FINAL_SH (8)
+ #define MAXOUT (+127)
+ #define MINOUT (-128)
+#endif
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (EG timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<<FREQ_SH)-1)
+
+/* envelope output entries */
+#define ENV_BITS 10
+#define ENV_LEN (1<<ENV_BITS)
+#define ENV_STEP (128.0/ENV_LEN)
+
+#define MAX_ATT_INDEX ((1<<(ENV_BITS-1))-1) /*511*/
+#define MIN_ATT_INDEX (0)
+
+/* sinwave entries */
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
+
+
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* Envelope Generator phases */
+
+#define EG_ATT 4
+#define EG_DEC 3
+#define EG_SUS 2
+#define EG_REL 1
+#define EG_OFF 0
+
+
+/* save output as raw 16-bit sample */
+
+/*#define SAVE_SAMPLE*/
+
+#ifdef SAVE_SAMPLE
+INLINE signed int acc_calc(signed int value)
+{
+ if (value>=0)
+ {
+ if (value < 0x0200)
+ return (value & ~0);
+ if (value < 0x0400)
+ return (value & ~1);
+ if (value < 0x0800)
+ return (value & ~3);
+ if (value < 0x1000)
+ return (value & ~7);
+ if (value < 0x2000)
+ return (value & ~15);
+ if (value < 0x4000)
+ return (value & ~31);
+ return (value & ~63);
+ }
+ /*else value < 0*/
+ if (value > -0x0200)
+ return (~abs(value) & ~0);
+ if (value > -0x0400)
+ return (~abs(value) & ~1);
+ if (value > -0x0800)
+ return (~abs(value) & ~3);
+ if (value > -0x1000)
+ return (~abs(value) & ~7);
+ if (value > -0x2000)
+ return (~abs(value) & ~15);
+ if (value > -0x4000)
+ return (~abs(value) & ~31);
+ return (~abs(value) & ~63);
+}
+
+
+static FILE *sample[1];
+ #if 1 /*save to MONO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = acc_calc(lt); \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #else /*save to STEREO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ pom = rt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #endif
+#endif
+
+#define LOG_CYM_FILE 0
+static FILE * cymfile = NULL;
+
+
+
+#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
+#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */
+#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */
+#define OPL_TYPE_IO 0x08 /* I/O port */
+
+/* ---------- Generic interface section ---------- */
+#define OPL_TYPE_YM3526 (0)
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
+#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
+
+
+
+struct OPL_SLOT
+{
+ UINT32 ar; /* attack rate: AR<<2 */
+ UINT32 dr; /* decay rate: DR<<2 */
+ UINT32 rr; /* release rate:RR<<2 */
+ UINT8 KSR; /* key scale rate */
+ UINT8 ksl; /* keyscale level */
+ UINT8 ksr; /* key scale rate: kcode>>KSR */
+ UINT8 mul; /* multiple: mul_tab[ML] */
+
+ /* Phase Generator */
+ UINT32 Cnt; /* frequency counter */
+ UINT32 Incr; /* frequency counter step */
+ UINT8 FB; /* feedback shift value */
+ INT32 *connect1; /* slot1 output pointer */
+ INT32 op1_out[2]; /* slot1 output for feedback */
+ UINT8 CON; /* connection (algorithm) type */
+
+ /* Envelope Generator */
+ UINT8 eg_type; /* percussive/non-percussive mode */
+ UINT8 state; /* phase type */
+ UINT32 TL; /* total level: TL << 2 */
+ INT32 TLL; /* adjusted now TL */
+ INT32 volume; /* envelope counter */
+ UINT32 sl; /* sustain level: sl_tab[SL] */
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT8 eg_sh_dr; /* (decay state) */
+ UINT8 eg_sel_dr; /* (decay state) */
+ UINT8 eg_sh_rr; /* (release state) */
+ UINT8 eg_sel_rr; /* (release state) */
+ UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */
+
+ /* LFO */
+ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */
+ UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/
+
+ /* waveform select */
+ UINT16 wavetable;
+};
+
+struct OPL_CH
+{
+ OPL_SLOT SLOT[2];
+ /* phase generator state */
+ UINT32 block_fnum; /* block+fnum */
+ UINT32 fc; /* Freq. Increment base */
+ UINT32 ksl_base; /* KeyScaleLevel Base step */
+ UINT8 kcode; /* key code (for key scaling) */
+};
+
+/* OPL state */
+struct FM_OPL
+{
+ /* FM channel slots */
+ OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */
+
+ UINT8 rhythm; /* Rhythm mode */
+
+ UINT32 fn_tab[1024]; /* fnumber->increment counter */
+
+ /* LFO */
+ UINT32 LFO_AM;
+ INT32 LFO_PM;
+
+ UINT8 lfo_am_depth;
+ UINT8 lfo_pm_depth_range;
+ UINT32 lfo_am_cnt;
+ UINT32 lfo_am_inc;
+ UINT32 lfo_pm_cnt;
+ UINT32 lfo_pm_inc;
+
+ UINT32 noise_rng; /* 23 bit noise shift register */
+ UINT32 noise_p; /* current noise 'phase' */
+ UINT32 noise_f; /* current noise period */
+
+ UINT8 wavesel; /* waveform select enable flag */
+
+ UINT32 T[2]; /* timer counters */
+ UINT8 st[2]; /* timer enable */
+
+#if BUILD_Y8950
+ /* Delta-T ADPCM unit (Y8950) */
+
+ YM_DELTAT *deltat;
+
+ /* Keyboard and I/O ports interface */
+ UINT8 portDirection;
+ UINT8 portLatch;
+ OPL_PORTHANDLER_R porthandler_r;
+ OPL_PORTHANDLER_W porthandler_w;
+ void * port_param;
+ OPL_PORTHANDLER_R keyboardhandler_r;
+ OPL_PORTHANDLER_W keyboardhandler_w;
+ void * keyboard_param;
+#endif
+
+ /* external event callback handlers */
+ OPL_TIMERHANDLER timer_handler; /* TIMER handler */
+ void *TimerParam; /* TIMER parameter */
+ OPL_IRQHANDLER IRQHandler; /* IRQ handler */
+ void *IRQParam; /* IRQ parameter */
+ OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */
+ void *UpdateParam; /* stream update parameter */
+
+ UINT8 type; /* chip type */
+ UINT8 address; /* address register */
+ UINT8 status; /* status flag */
+ UINT8 statusmask; /* status mask */
+ UINT8 mode; /* Reg.08 : CSM,notesel,etc. */
+
+ UINT32 clock; /* master clock (Hz) */
+ UINT32 rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ attotime TimerBase; /* Timer base time (==sampling time)*/
+ device_t *device;
+
+ signed int phase_modulation; /* phase modulation input (SLOT 2) */
+ signed int output[1];
+#if BUILD_Y8950
+ INT32 output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */
+#endif
+};
+
+
+
+/* mapping of register number (offset) to slot number used by the emulator */
+static const int slot_array[32]=
+{
+ 0, 2, 4, 1, 3, 5,-1,-1,
+ 6, 8,10, 7, 9,11,-1,-1,
+ 12,14,16,13,15,17,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1
+};
+
+/* key scale level */
+/* table is 3dB/octave , DV converts this into 6dB/octave */
+/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */
+#define DV (0.1875/2.0)
+static const UINT32 ksl_tab[8*16]=
+{
+ /* OCT 0 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ /* OCT 1 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
+ 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
+ /* OCT 2 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
+ 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
+ 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
+ /* OCT 3 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
+ 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
+ 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
+ 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
+ /* OCT 4 */
+ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
+ 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
+ 9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
+ 10.875/DV,11.250/DV,11.625/DV,12.000/DV,
+ /* OCT 5 */
+ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
+ 9.000/DV,10.125/DV,10.875/DV,11.625/DV,
+ 12.000/DV,12.750/DV,13.125/DV,13.500/DV,
+ 13.875/DV,14.250/DV,14.625/DV,15.000/DV,
+ /* OCT 6 */
+ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
+ 12.000/DV,13.125/DV,13.875/DV,14.625/DV,
+ 15.000/DV,15.750/DV,16.125/DV,16.500/DV,
+ 16.875/DV,17.250/DV,17.625/DV,18.000/DV,
+ /* OCT 7 */
+ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
+ 15.000/DV,16.125/DV,16.875/DV,17.625/DV,
+ 18.000/DV,18.750/DV,19.125/DV,19.500/DV,
+ 19.875/DV,20.250/DV,20.625/DV,21.000/DV
+};
+#undef DV
+
+/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */
+static const UINT32 ksl_shift[4] = { 31, 1, 2, 0 };
+
+
+/* sustain level table (3dB per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) (UINT32) ( db * (2.0/ENV_STEP) )
+static const UINT32 sl_tab[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+
+#define RATE_STEPS (8)
+static const unsigned char eg_inc[15*RATE_STEPS]={
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */
+/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */
+/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(13) in this table - it's directly in the code */
+static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+
+/* rates 00-12 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 13 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 14 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 15 */
+O(12),O(12),O(12),O(12),
+
+/* 16 dummy rates (same as 15 3) */
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */
+/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */
+/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+/* rates 00-12 */
+O(12),O(12),O(12),O(12),
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 16 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+
+};
+#undef O
+
+
+/* multiple table */
+#define ML 2
+static const UINT8 mul_tab[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */
+ ML/2, 1*ML, 2*ML, 3*ML, 4*ML, 5*ML, 6*ML, 7*ML,
+ 8*ML, 9*ML,10*ML,10*ML,12*ML,12*ML,15*ML,15*ML
+};
+#undef ML
+
+/* TL_TAB_LEN is calculated as:
+* 12 - sinus amplitude bits (Y axis)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (12*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+#define ENV_QUIET (TL_TAB_LEN>>4)
+
+/* sin waveform table in 'decibel' scale */
+/* four waveforms on OPL2 type chips */
+static unsigned int sin_tab[SIN_LEN * 4];
+
+
+/* LFO Amplitude Modulation table (verified on real YM3812)
+ 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples
+
+ Length: 210 elements.
+
+ Each of the elements has to be repeated
+ exactly 64 times (on 64 consecutive samples).
+ The whole table takes: 64 * 210 = 13440 samples.
+
+ When AM = 1 data is used directly
+ When AM = 0 data is divided by 4 before being used (losing precision is important)
+*/
+
+#define LFO_AM_TAB_ELEMENTS 210
+
+static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = {
+0,0,0,0,0,0,0,
+1,1,1,1,
+2,2,2,2,
+3,3,3,3,
+4,4,4,4,
+5,5,5,5,
+6,6,6,6,
+7,7,7,7,
+8,8,8,8,
+9,9,9,9,
+10,10,10,10,
+11,11,11,11,
+12,12,12,12,
+13,13,13,13,
+14,14,14,14,
+15,15,15,15,
+16,16,16,16,
+17,17,17,17,
+18,18,18,18,
+19,19,19,19,
+20,20,20,20,
+21,21,21,21,
+22,22,22,22,
+23,23,23,23,
+24,24,24,24,
+25,25,25,25,
+26,26,26,
+25,25,25,25,
+24,24,24,24,
+23,23,23,23,
+22,22,22,22,
+21,21,21,21,
+20,20,20,20,
+19,19,19,19,
+18,18,18,18,
+17,17,17,17,
+16,16,16,16,
+15,15,15,15,
+14,14,14,14,
+13,13,13,13,
+12,12,12,12,
+11,11,11,11,
+10,10,10,10,
+9,9,9,9,
+8,8,8,8,
+7,7,7,7,
+6,6,6,6,
+5,5,5,5,
+4,4,4,4,
+3,3,3,3,
+2,2,2,2,
+1,1,1,1
+};
+
+/* LFO Phase Modulation table (verified on real YM3812) */
+static const INT8 lfo_pm_table[8*8*2] = {
+/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/
+};
+
+
+/* lock level of common table */
+static int num_lock = 0;
+
+
+#define SLOT7_1 (&OPL->P_CH[7].SLOT[SLOT1])
+#define SLOT7_2 (&OPL->P_CH[7].SLOT[SLOT2])
+#define SLOT8_1 (&OPL->P_CH[8].SLOT[SLOT1])
+#define SLOT8_2 (&OPL->P_CH[8].SLOT[SLOT2])
+
+
+
+
+INLINE int limit( int val, int max, int min ) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+
+/* status set and IRQ handling */
+INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag)
+{
+ /* set status flag */
+ OPL->status |= flag;
+ if(!(OPL->status & 0x80))
+ {
+ if(OPL->status & OPL->statusmask)
+ { /* IRQ on */
+ OPL->status |= 0x80;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);
+ }
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag)
+{
+ /* reset status flag */
+ OPL->status &=~flag;
+ if((OPL->status & 0x80))
+ {
+ if (!(OPL->status & OPL->statusmask) )
+ {
+ OPL->status &= 0x7f;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
+ }
+ }
+}
+
+/* IRQ mask set */
+INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag)
+{
+ OPL->statusmask = flag;
+ /* IRQ handling check */
+ OPL_STATUS_SET(OPL,0);
+ OPL_STATUS_RESET(OPL,0);
+}
+
+
+/* advance LFO to next sample */
+INLINE void advance_lfo(FM_OPL *OPL)
+{
+ UINT8 tmp;
+
+ /* LFO */
+ OPL->lfo_am_cnt += OPL->lfo_am_inc;
+ if (OPL->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH) ) /* lfo_am_table is 210 elements long */
+ OPL->lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH);
+
+ tmp = lfo_am_table[ OPL->lfo_am_cnt >> LFO_SH ];
+
+ if (OPL->lfo_am_depth)
+ OPL->LFO_AM = tmp;
+ else
+ OPL->LFO_AM = tmp>>2;
+
+ OPL->lfo_pm_cnt += OPL->lfo_pm_inc;
+ OPL->LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range;
+}
+
+/* advance to next sample */
+INLINE void advance(FM_OPL *OPL)
+{
+ OPL_CH *CH;
+ OPL_SLOT *op;
+ int i;
+
+ OPL->eg_timer += OPL->eg_timer_add;
+
+ while (OPL->eg_timer >= OPL->eg_timer_overflow)
+ {
+ OPL->eg_timer -= OPL->eg_timer_overflow;
+
+ OPL->eg_cnt++;
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &OPL->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Envelope Generator */
+ switch(op->state)
+ {
+ case EG_ATT: /* attack phase */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_ar)-1) ) )
+ {
+ op->volume += (~op->volume *
+ (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)])
+ ) >>3;
+
+ if (op->volume <= MIN_ATT_INDEX)
+ {
+ op->volume = MIN_ATT_INDEX;
+ op->state = EG_DEC;
+ }
+
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_dr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)];
+
+ if ( op->volume >= op->sl )
+ op->state = EG_SUS;
+
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+
+ /* this is important behaviour:
+ one can change percusive/non-percussive modes on the fly and
+ the chip will remain in sustain phase - verified on real YM3812 */
+
+ if(op->eg_type) /* non-percussive mode */
+ {
+ /* do nothing */
+ }
+ else /* percussive mode */
+ {
+ /* during sustain phase chip adds Release Rate (in percussive mode) */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ op->volume = MAX_ATT_INDEX;
+ }
+ /* else do nothing in sustain phase */
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &OPL->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Phase Generator */
+ if(op->vib)
+ {
+ UINT8 block;
+ unsigned int block_fnum = CH->block_fnum;
+
+ unsigned int fnum_lfo = (block_fnum&0x0380) >> 7;
+
+ signed int lfo_fn_table_index_offset = lfo_pm_table[OPL->LFO_PM + 16*fnum_lfo ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ block_fnum += lfo_fn_table_index_offset;
+ block = (block_fnum&0x1c00) >> 10;
+ op->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ op->Cnt += op->Incr;
+ }
+ }
+ else /* LFO phase modulation disabled for this operator */
+ {
+ op->Cnt += op->Incr;
+ }
+ }
+
+ /* The Noise Generator of the YM3812 is 23-bit shift register.
+ * Period is equal to 2^23-2 samples.
+ * Register works at sampling frequency of the chip, so output
+ * can change on every sample.
+ *
+ * Output of the register and input to the bit 22 is:
+ * bit0 XOR bit14 XOR bit15 XOR bit22
+ *
+ * Simply use bit 22 as the noise output.
+ */
+
+ OPL->noise_p += OPL->noise_f;
+ i = OPL->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */
+ OPL->noise_p &= FREQ_MASK;
+ while (i)
+ {
+ /*
+ UINT32 j;
+ j = ( (OPL->noise_rng) ^ (OPL->noise_rng>>14) ^ (OPL->noise_rng>>15) ^ (OPL->noise_rng>>22) ) & 1;
+ OPL->noise_rng = (j<<22) | (OPL->noise_rng>>1);
+ */
+
+ /*
+ Instead of doing all the logic operations above, we
+ use a trick here (and use bit 0 as the noise output).
+ The difference is only that the noise bit changes one
+ step ahead. This doesn't matter since we don't know
+ what is real state of the noise_rng after the reset.
+ */
+
+ if (OPL->noise_rng & 1) OPL->noise_rng ^= 0x800302;
+ OPL->noise_rng >>= 1;
+
+ i--;
+ }
+}
+
+
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+
+#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (OPL->LFO_AM & (OP)->AMmask))
+
+/* calculate output */
+INLINE void OPL_CALC_CH( FM_OPL *OPL, OPL_CH *CH )
+{
+ OPL_SLOT *SLOT;
+ unsigned int env;
+ signed int out;
+
+ OPL->phase_modulation = 0;
+
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env = volume_calc(SLOT);
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+ *SLOT->connect1 += SLOT->op1_out[0];
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable);
+}
+
+/*
+ operators used in the rhythm sounds generation process:
+
+ Envelope Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal
+ 6 / 0 12 50 70 90 f0 +
+ 6 / 1 15 53 73 93 f3 +
+ 7 / 0 13 51 71 91 f1 +
+ 7 / 1 16 54 74 94 f4 +
+ 8 / 0 14 52 72 92 f2 +
+ 8 / 1 17 55 75 95 f5 +
+
+ Phase Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number MULTIPLE Drum Hat Drum Tom Cymbal
+ 6 / 0 12 30 +
+ 6 / 1 15 33 +
+ 7 / 0 13 31 + + +
+ 7 / 1 16 34 ----- n o t u s e d -----
+ 8 / 0 14 32 +
+ 8 / 1 17 35 + +
+
+channel operator register number Bass High Snare Tom Top
+number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal
+ 6 12,15 B6 A6 +
+
+ 7 13,16 B7 A7 + + +
+
+ 8 14,17 B8 A8 + + +
+
+*/
+
+/* calculate rhythm */
+
+INLINE void OPL_CALC_RH( FM_OPL *OPL, OPL_CH *CH, unsigned int noise )
+{
+ OPL_SLOT *SLOT;
+ signed int out;
+ unsigned int env;
+
+
+ /* Bass Drum (verified on real YM3812):
+ - depends on the channel 6 'connect' register:
+ when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out)
+ when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored
+ - output sample always is multiplied by 2
+ */
+
+ OPL->phase_modulation = 0;
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env = volume_calc(SLOT);
+
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+
+ if (!SLOT->CON)
+ OPL->phase_modulation = SLOT->op1_out[0];
+ /* else ignore output of operator 1 */
+
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable) * 2;
+
+
+ /* Phase generation is based on: */
+ /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */
+ /* SD (16) channel 7->slot 1 */
+ /* TOM (14) channel 8->slot 1 */
+ /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */
+
+ /* Envelope generation based on: */
+ /* HH channel 7->slot1 */
+ /* SD channel 7->slot2 */
+ /* TOM channel 8->slot1 */
+ /* TOP channel 8->slot2 */
+
+
+ /* The following formulas can be well optimized.
+ I leave them in direct form for now (in case I've missed something).
+ */
+
+ /* High Hat (verified on real YM3812) */
+ env = volume_calc(SLOT7_1);
+ if( env < ENV_QUIET )
+ {
+ /* high hat phase generation:
+ phase = d0 or 234 (based on frequency only)
+ phase = 34 or 2d0 (based on noise)
+ */
+
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0xd0; */
+ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */
+ UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e ^ bit5e);
+
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */
+ if (res2)
+ phase = (0x200|(0xd0>>2));
+
+
+ /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */
+ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */
+ if (phase&0x200)
+ {
+ if (noise)
+ phase = 0x200|0xd0;
+ }
+ else
+ /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */
+ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */
+ {
+ if (noise)
+ phase = 0xd0>>2;
+ }
+
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_1->wavetable) * 2;
+ }
+
+ /* Snare Drum (verified on real YM3812) */
+ env = volume_calc(SLOT7_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1;
+
+ /* when bit8 = 0 phase = 0x100; */
+ /* when bit8 = 1 phase = 0x200; */
+ UINT32 phase = bit8 ? 0x200 : 0x100;
+
+ /* Noise bit XOR'es phase by 0x100 */
+ /* when noisebit = 0 pass the phase from calculation above */
+ /* when noisebit = 1 phase ^= 0x100; */
+ /* in other words: phase ^= (noisebit<<8); */
+ if (noise)
+ phase ^= 0x100;
+
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_2->wavetable) * 2;
+ }
+
+ /* Tom Tom (verified on real YM3812) */
+ env = volume_calc(SLOT8_1);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2;
+
+ /* Top Cymbal (verified on real YM3812) */
+ env = volume_calc(SLOT8_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0x100; */
+ /* when res1 = 1 phase = 0x200 | 0x100; */
+ UINT32 phase = res1 ? 0x300 : 0x100;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e ^ bit5e);
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | 0x100; */
+ if (res2)
+ phase = 0x300;
+
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT8_2->wavetable) * 2;
+ }
+}
+
+
+/* generic table initialize */
+static int init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+
+ for (x=0; x<TL_RES_LEN; x++)
+ {
+ m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);
+ m = floor(m);
+
+ /* we never reach (1<<16) here due to the (x+1) */
+ /* result fits within 16 bits at maximum */
+
+ n = (int)m; /* 16 bits here */
+ n >>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 1; /* 12 bits here (as in real chip) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<12; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ #if 0
+ logerror("tl %04i", x*2);
+ for (i=0; i<12; i++)
+ logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] );
+ logerror("\n");
+ #endif
+ }
+ /*logerror("FMOPL.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/
+
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* non-standard sinus */
+ m = sin( ((i*2)+1) * M_PI / SIN_LEN ); /* checked against the real chip */
+
+ /* we never reach zero here due to ((i*2)+1) */
+
+ if (m>0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+
+ /*logerror("FMOPL.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/
+ }
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* waveform 1: __ __ */
+ /* / \____/ \____*/
+ /* output only first half of the sinus waveform (positive one) */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[1*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[1*SIN_LEN+i] = sin_tab[i];
+
+ /* waveform 2: __ __ __ __ */
+ /* / \/ \/ \/ \*/
+ /* abs(sin) */
+
+ sin_tab[2*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>1) ];
+
+ /* waveform 3: _ _ _ _ */
+ /* / |_/ |_/ |_/ |_*/
+ /* abs(output only first quarter of the sinus waveform) */
+
+ if (i & (1<<(SIN_BITS-2)) )
+ sin_tab[3*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)];
+
+ /*logerror("FMOPL.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );
+ logerror("FMOPL.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] );
+ logerror("FMOPL.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );*/
+ }
+ /*logerror("FMOPL.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/
+
+
+#ifdef SAVE_SAMPLE
+ sample[0]=fopen("sampsum.pcm","wb");
+#endif
+
+ return 1;
+}
+
+static void OPLCloseTable( void )
+{
+#ifdef SAVE_SAMPLE
+ fclose(sample[0]);
+#endif
+}
+
+
+
+static void OPL_initalize(FM_OPL *OPL)
+{
+ int i;
+
+ /* frequency base */
+ OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / 72.0) / OPL->rate : 0;
+#if 0
+ OPL->rate = (double)OPL->clock / 72.0;
+ OPL->freqbase = 1.0;
+#endif
+
+ /*logerror("freqbase=%f\n", OPL->freqbase);*/
+
+ /* Timer base time */
+ OPL->TimerBase = attotime::from_hz(OPL->clock) * 72;
+
+ /* make fnumber -> increment counter table */
+ for( i=0 ; i < 1024 ; i++ )
+ {
+ /* opn phase increment counter = 20bit */
+ OPL->fn_tab[i] = (UINT32)( (double)i * 64 * OPL->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+#if 0
+ logerror("FMOPL.C: fn_tab[%4i] = %08x (dec=%8i)\n",
+ i, OPL->fn_tab[i]>>6, OPL->fn_tab[i]>>6 );
+#endif
+ }
+
+#if 0
+ for( i=0 ; i < 16 ; i++ )
+ {
+ logerror("FMOPL.C: sl_tab[%i] = %08x\n",
+ i, sl_tab[i] );
+ }
+ for( i=0 ; i < 8 ; i++ )
+ {
+ int j;
+ logerror("FMOPL.C: ksl_tab[oct=%2i] =",i);
+ for (j=0; j<16; j++)
+ {
+ logerror("%08x ", ksl_tab[i*16+j] );
+ }
+ logerror("\n");
+ }
+#endif
+
+
+ /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */
+ /* One entry from LFO_AM_TABLE lasts for 64 samples */
+ OPL->lfo_am_inc = (1.0 / 64.0 ) * (1<<LFO_SH) * OPL->freqbase;
+
+ /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */
+ OPL->lfo_pm_inc = (1.0 / 1024.0) * (1<<LFO_SH) * OPL->freqbase;
+
+ /*logerror ("OPL->lfo_am_inc = %8x ; OPL->lfo_pm_inc = %8x\n", OPL->lfo_am_inc, OPL->lfo_pm_inc);*/
+
+ /* Noise generator: a step takes 1 sample */
+ OPL->noise_f = (1.0 / 1.0) * (1<<FREQ_SH) * OPL->freqbase;
+
+ OPL->eg_timer_add = (1<<EG_SH) * OPL->freqbase;
+ OPL->eg_timer_overflow = ( 1 ) * (1<<EG_SH);
+ /*logerror("OPLinit eg_timer_add=%8x eg_timer_overflow=%8x\n", OPL->eg_timer_add, OPL->eg_timer_overflow);*/
+
+}
+
+INLINE void FM_KEYON(OPL_SLOT *SLOT, UINT32 key_set)
+{
+ if( !SLOT->key )
+ {
+ /* restart Phase Generator */
+ SLOT->Cnt = 0;
+ /* phase -> Attack */
+ SLOT->state = EG_ATT;
+ }
+ SLOT->key |= key_set;
+}
+
+INLINE void FM_KEYOFF(OPL_SLOT *SLOT, UINT32 key_clr)
+{
+ if( SLOT->key )
+ {
+ SLOT->key &= key_clr;
+
+ if( !SLOT->key )
+ {
+ /* phase -> Release */
+ if (SLOT->state>EG_REL)
+ SLOT->state = EG_REL;
+ }
+ }
+}
+
+/* update phase increment counter of operator (also update the EG rates if necessary) */
+INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)
+{
+ int ksr;
+
+ /* (frequency) phase increment counter */
+ SLOT->Incr = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+ }
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->mul = mul_tab[v&0x0f];
+ SLOT->KSR = (v&0x10) ? 0 : 2;
+ SLOT->eg_type = (v&0x20);
+ SLOT->vib = (v&0x40);
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;
+ CALC_FCSLOT(CH,SLOT);
+}
+
+/* set ksl & tl */
+INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->ksl = ksl_shift[v >> 6];
+ SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */
+
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set attack rate & decay rate */
+INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0;
+
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+
+ SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->sl = sl_tab[ v>>4 ];
+
+ SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+}
+
+
+/* write a value v to register r on OPL chip */
+static void OPLWriteReg(FM_OPL *OPL, int r, int v)
+{
+ OPL_CH *CH;
+ int slot;
+ int block_fnum;
+
+
+ /* adjust bus to 8 bits */
+ r &= 0xff;
+ v &= 0xff;
+
+ if (LOG_CYM_FILE && (cymfile) && (r!=0) )
+ {
+ fputc( (unsigned char)r, cymfile );
+ fputc( (unsigned char)v, cymfile );
+ }
+
+
+ switch(r&0xe0)
+ {
+ case 0x00: /* 00-1f:control */
+ switch(r&0x1f)
+ {
+ case 0x01: /* waveform select enable */
+ if(OPL->type&OPL_TYPE_WAVESEL)
+ {
+ OPL->wavesel = v&0x20;
+ /* do not change the waveform previously selected */
+ }
+ break;
+ case 0x02: /* Timer 1 */
+ OPL->T[0] = (256-v)*4;
+ break;
+ case 0x03: /* Timer 2 */
+ OPL->T[1] = (256-v)*16;
+ break;
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v&0x80)
+ { /* IRQ flag clear */
+ OPL_STATUS_RESET(OPL,0x7f-0x08); /* don't reset BFRDY flag or we will have to call deltat module to set the flag */
+ }
+ else
+ { /* set IRQ mask ,timer enable*/
+ UINT8 st1 = v&1;
+ UINT8 st2 = (v>>1)&1;
+
+ /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
+ OPL_STATUS_RESET(OPL, v & (0x78-0x08) );
+ OPL_STATUSMASK_SET(OPL, (~v) & 0x78 );
+
+ /* timer 2 */
+ if(OPL->st[1] != st2)
+ {
+ attotime period = st2 ? (OPL->TimerBase * OPL->T[1]) : attotime::zero;
+ OPL->st[1] = st2;
+ if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,1,period);
+ }
+ /* timer 1 */
+ if(OPL->st[0] != st1)
+ {
+ attotime period = st1 ? (OPL->TimerBase * OPL->T[0]) : attotime::zero;
+ OPL->st[0] = st1;
+ if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,0,period);
+ }
+ }
+ break;
+#if BUILD_Y8950
+ case 0x06: /* Key Board OUT */
+ if(OPL->type&OPL_TYPE_KEYBOARD)
+ {
+ if(OPL->keyboardhandler_w)
+ OPL->keyboardhandler_w(OPL->keyboard_param,v);
+ else
+ logerror("Y8950: write unmapped KEYBOARD port\n");
+ }
+ break;
+ case 0x07: /* DELTA-T control 1 : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ break;
+#endif
+ case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */
+ OPL->mode = v;
+#if BUILD_Y8950
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v&0x0f); /* mask 4 LSBs in register 08 for DELTA-T unit */
+#endif
+ break;
+
+#if BUILD_Y8950
+ case 0x09: /* START ADD */
+ case 0x0a:
+ case 0x0b: /* STOP ADD */
+ case 0x0c:
+ case 0x0d: /* PRESCALE */
+ case 0x0e:
+ case 0x0f: /* ADPCM data write */
+ case 0x10: /* DELTA-N */
+ case 0x11: /* DELTA-N */
+ case 0x12: /* ADPCM volume */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ break;
+
+ case 0x15: /* DAC data high 8 bits (F7,F6...F2) */
+ case 0x16: /* DAC data low 2 bits (F1, F0 in bits 7,6) */
+ case 0x17: /* DAC data shift (S2,S1,S0 in bits 2,1,0) */
+ logerror("FMOPL.C: DAC data register written, but not implemented reg=%02x val=%02x\n",r,v);
+ break;
+
+ case 0x18: /* I/O CTRL (Direction) */
+ if(OPL->type&OPL_TYPE_IO)
+ OPL->portDirection = v&0x0f;
+ break;
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ OPL->portLatch = v;
+ if(OPL->porthandler_w)
+ OPL->porthandler_w(OPL->port_param,v&OPL->portDirection);
+ }
+ break;
+#endif
+ default:
+ logerror("FMOPL.C: write to unknown register: %02x\n",r);
+ break;
+ }
+ break;
+ case 0x20: /* am ON, vib ON, ksr, eg_type, mul */
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_mul(OPL,slot,v);
+ break;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ksl_tl(OPL,slot,v);
+ break;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ar_dr(OPL,slot,v);
+ break;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_sl_rr(OPL,slot,v);
+ break;
+ case 0xa0:
+ if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */
+ {
+ OPL->lfo_am_depth = v & 0x80;
+ OPL->lfo_pm_depth_range = (v&0x40) ? 8 : 0;
+
+ OPL->rhythm = v&0x3f;
+
+ if(OPL->rhythm&0x20)
+ {
+ /* BD key on/off */
+ if(v&0x10)
+ {
+ FM_KEYON (&OPL->P_CH[6].SLOT[SLOT1], 2);
+ FM_KEYON (&OPL->P_CH[6].SLOT[SLOT2], 2);
+ }
+ else
+ {
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2);
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2);
+ }
+ /* HH key on/off */
+ if(v&0x01) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT1], 2);
+ else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key on/off */
+ if(v&0x08) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT2], 2);
+ else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key on/off */
+ if(v&0x04) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT1], 2);
+ else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY key on/off */
+ if(v&0x02) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT2], 2);
+ else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2);
+ }
+ else
+ {
+ /* BD key off */
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2);
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2);
+ /* HH key off */
+ FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key off */
+ FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key off */
+ FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY off */
+ FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2);
+ }
+ return;
+ }
+ /* keyon,block,fnum */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ if(!(r&0x10))
+ { /* a0-a8 */
+ block_fnum = (CH->block_fnum&0x1f00) | v;
+ }
+ else
+ { /* b0-b8 */
+ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ UINT8 block = block_fnum >> 10;
+
+ CH->block_fnum = block_fnum;
+
+ CH->ksl_base = ksl_tab[block_fnum>>6];
+ CH->fc = OPL->fn_tab[block_fnum&0x03ff] >> (7-block);
+
+ /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */
+ CH->kcode = (CH->block_fnum&0x1c00)>>9;
+
+ /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */
+ /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */
+ /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */
+ if (OPL->mode&0x40)
+ CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */
+ else
+ CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */
+
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ break;
+ case 0xc0:
+ /* FB,C */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0;
+ CH->SLOT[SLOT1].CON = v&1;
+ CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &OPL->output[0] : &OPL->phase_modulation;
+ break;
+ case 0xe0: /* waveform select */
+ /* simply ignore write to the waveform select register if selecting not enabled in test register */
+ if(OPL->wavesel)
+ {
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ CH = &OPL->P_CH[slot/2];
+
+ CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN;
+ }
+ break;
+ }
+}
+
+static TIMER_CALLBACK( cymfile_callback )
+{
+ if (cymfile)
+ {
+ fputc( (unsigned char)0, cymfile );
+ }
+}
+
+/* lock/unlock for common table */
+static int OPL_LockTable(device_t *device)
+{
+ num_lock++;
+ if(num_lock>1) return 0;
+
+ /* first time */
+
+ /* allocate total level table (128kb space) */
+ if( !init_tables() )
+ {
+ num_lock--;
+ return -1;
+ }
+
+ if (LOG_CYM_FILE)
+ {
+ cymfile = fopen("3812_.cym","wb");
+ if (cymfile)
+ device->machine().scheduler().timer_pulse ( attotime::from_hz(110), FUNC(cymfile_callback)); /*110 Hz pulse timer*/
+ else
+ logerror("Could not create file 3812_.cym\n");
+ }
+
+ return 0;
+}
+
+static void OPL_UnLockTable(void)
+{
+ if(num_lock) num_lock--;
+ if(num_lock) return;
+
+ /* last time */
+
+ OPLCloseTable();
+
+ if (cymfile)
+ fclose (cymfile);
+ cymfile = NULL;
+}
+
+static void OPLResetChip(FM_OPL *OPL)
+{
+ int c,s;
+ int i;
+
+ OPL->eg_timer = 0;
+ OPL->eg_cnt = 0;
+
+ OPL->noise_rng = 1; /* noise shift register */
+ OPL->mode = 0; /* normal mode */
+ OPL_STATUS_RESET(OPL,0x7f);
+
+ /* reset with register write */
+ OPLWriteReg(OPL,0x01,0); /* wavesel disable */
+ OPLWriteReg(OPL,0x02,0); /* Timer1 */
+ OPLWriteReg(OPL,0x03,0); /* Timer2 */
+ OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */
+ for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);
+
+ /* reset operator parameters */
+ for( c = 0 ; c < 9 ; c++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[c];
+ for(s = 0 ; s < 2 ; s++ )
+ {
+ /* wave table */
+ CH->SLOT[s].wavetable = 0;
+ CH->SLOT[s].state = EG_OFF;
+ CH->SLOT[s].volume = MAX_ATT_INDEX;
+ }
+ }
+#if BUILD_Y8950
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ YM_DELTAT *DELTAT = OPL->deltat;
+
+ DELTAT->freqbase = OPL->freqbase;
+ DELTAT->output_pointer = &OPL->output_deltat[0];
+ DELTAT->portshift = 5;
+ DELTAT->output_range = 1<<23;
+ YM_DELTAT_ADPCM_Reset(DELTAT,0,YM_DELTAT_EMULATION_MODE_NORMAL);
+ }
+#endif
+}
+
+
+static void OPL_postload(FM_OPL *OPL)
+{
+ int slot, ch;
+
+ for( ch=0 ; ch < 9 ; ch++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[ch];
+
+ /* Look up key scale level */
+ UINT32 block_fnum = CH->block_fnum;
+ CH->ksl_base = ksl_tab[block_fnum >> 6];
+ CH->fc = OPL->fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10));
+
+ for( slot=0 ; slot < 2 ; slot++ )
+ {
+ OPL_SLOT *SLOT = &CH->SLOT[slot];
+
+ /* Calculate key scale rate */
+ SLOT->ksr = CH->kcode >> SLOT->KSR;
+
+ /* Calculate attack, decay and release rates */
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+
+ /* Calculate phase increment */
+ SLOT->Incr = CH->fc * SLOT->mul;
+
+ /* Total level */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl);
+
+ /* Connect output */
+ SLOT->connect1 = SLOT->CON ? &OPL->output[0] : &OPL->phase_modulation;
+ }
+ }
+#if BUILD_Y8950
+ if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) )
+ {
+ // We really should call the postlod function for the YM_DELTAT, but it's hard without registers
+ // (see the way the YM2610 does it)
+ //YM_DELTAT_postload(OPL->deltat, REGS);
+ }
+#endif
+}
+
+
+static void OPLsave_state_channel(device_t *device, OPL_CH *CH)
+{
+ int slot, ch;
+
+ for( ch=0 ; ch < 9 ; ch++, CH++ )
+ {
+ /* channel */
+ device->save_item(NAME(CH->block_fnum), ch);
+ device->save_item(NAME(CH->kcode), ch);
+ /* slots */
+ for( slot=0 ; slot < 2 ; slot++ )
+ {
+ OPL_SLOT *SLOT = &CH->SLOT[slot];
+
+ device->save_item(NAME(SLOT->ar), ch * 2 + slot);
+ device->save_item(NAME(SLOT->dr), ch * 2 + slot);
+ device->save_item(NAME(SLOT->rr), ch * 2 + slot);
+ device->save_item(NAME(SLOT->KSR), ch * 2 + slot);
+ device->save_item(NAME(SLOT->ksl), ch * 2 + slot);
+ device->save_item(NAME(SLOT->mul), ch * 2 + slot);
+
+ device->save_item(NAME(SLOT->Cnt), ch * 2 + slot);
+ device->save_item(NAME(SLOT->FB), ch * 2 + slot);
+ device->save_item(NAME(SLOT->op1_out), ch * 2 + slot);
+ device->save_item(NAME(SLOT->CON), ch * 2 + slot);
+
+ device->save_item(NAME(SLOT->eg_type), ch * 2 + slot);
+ device->save_item(NAME(SLOT->state), ch * 2 + slot);
+ device->save_item(NAME(SLOT->TL), ch * 2 + slot);
+ device->save_item(NAME(SLOT->volume), ch * 2 + slot);
+ device->save_item(NAME(SLOT->sl), ch * 2 + slot);
+ device->save_item(NAME(SLOT->key), ch * 2 + slot);
+
+ device->save_item(NAME(SLOT->AMmask), ch * 2 + slot);
+ device->save_item(NAME(SLOT->vib), ch * 2 + slot);
+
+ device->save_item(NAME(SLOT->wavetable), ch * 2 + slot);
+ }
+ }
+}
+
+
+/* Register savestate for a virtual YM3812/YM3526Y8950 */
+
+static void OPL_save_state(FM_OPL *OPL, device_t *device)
+{
+ OPLsave_state_channel(device, OPL->P_CH);
+
+ device->save_item(NAME(OPL->eg_cnt));
+ device->save_item(NAME(OPL->eg_timer));
+
+ device->save_item(NAME(OPL->rhythm));
+
+ device->save_item(NAME(OPL->lfo_am_depth));
+ device->save_item(NAME(OPL->lfo_pm_depth_range));
+ device->save_item(NAME(OPL->lfo_am_cnt));
+ device->save_item(NAME(OPL->lfo_pm_cnt));
+
+ device->save_item(NAME(OPL->noise_rng));
+ device->save_item(NAME(OPL->noise_p));
+
+ if( OPL->type & OPL_TYPE_WAVESEL )
+ {
+ device->save_item(NAME(OPL->wavesel));
+ }
+
+ device->save_item(NAME(OPL->T));
+ device->save_item(NAME(OPL->st));
+
+#if BUILD_Y8950
+ if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) )
+ {
+ YM_DELTAT_savestate(device, OPL->deltat);
+ }
+
+ if ( OPL->type & OPL_TYPE_IO )
+ {
+ device->save_item(NAME(OPL->portDirection));
+ device->save_item(NAME(OPL->portLatch));
+ }
+#endif
+
+ device->save_item(NAME(OPL->address));
+ device->save_item(NAME(OPL->status));
+ device->save_item(NAME(OPL->statusmask));
+ device->save_item(NAME(OPL->mode));
+
+ device->machine().save().register_postload(save_prepost_delegate(FUNC(OPL_postload), OPL));
+}
+
+
+/* Create one of virtual YM3812/YM3526/Y8950 */
+/* 'clock' is chip clock in Hz */
+/* 'rate' is sampling rate */
+static FM_OPL *OPLCreate(device_t *device, UINT32 clock, UINT32 rate, int type)
+{
+ char *ptr;
+ FM_OPL *OPL;
+ int state_size;
+
+ if (OPL_LockTable(device) == -1) return NULL;
+
+ /* calculate OPL state size */
+ state_size = sizeof(FM_OPL);
+
+#if BUILD_Y8950
+ if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);
+#endif
+
+ /* allocate memory block */
+ ptr = (char *)auto_alloc_array_clear(device->machine(), UINT8, state_size);
+
+ OPL = (FM_OPL *)ptr;
+
+ ptr += sizeof(FM_OPL);
+
+#if BUILD_Y8950
+ if (type&OPL_TYPE_ADPCM)
+ {
+ OPL->deltat = (YM_DELTAT *)ptr;
+ }
+ ptr += sizeof(YM_DELTAT);
+#endif
+
+ OPL->device = device;
+ OPL->type = type;
+ OPL->clock = clock;
+ OPL->rate = rate;
+
+ /* init global tables */
+ OPL_initalize(OPL);
+
+ return OPL;
+}
+
+/* Destroy one of virtual YM3812 */
+static void OPLDestroy(FM_OPL *OPL)
+{
+ OPL_UnLockTable();
+ auto_free(OPL->device->machine(), OPL);
+}
+
+/* Optional handlers */
+
+static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER timer_handler,void *param)
+{
+ OPL->timer_handler = timer_handler;
+ OPL->TimerParam = param;
+}
+static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ OPL->IRQHandler = IRQHandler;
+ OPL->IRQParam = param;
+}
+static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ OPL->UpdateHandler = UpdateHandler;
+ OPL->UpdateParam = param;
+}
+
+static int OPLWrite(FM_OPL *OPL,int a,int v)
+{
+ if( !(a&1) )
+ { /* address port */
+ OPL->address = v & 0xff;
+ }
+ else
+ { /* data port */
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+ OPLWriteReg(OPL,OPL->address,v);
+ }
+ return OPL->status>>7;
+}
+
+static unsigned char OPLRead(FM_OPL *OPL,int a)
+{
+ if( !(a&1) )
+ {
+ /* status port */
+
+ #if BUILD_Y8950
+
+ if(OPL->type&OPL_TYPE_ADPCM) /* Y8950 */
+ {
+ return (OPL->status & (OPL->statusmask|0x80)) | (OPL->deltat->PCM_BSY&1);
+ }
+
+ #endif
+
+ /* OPL and OPL2 */
+ return OPL->status & (OPL->statusmask|0x80);
+ }
+
+#if BUILD_Y8950
+ /* data port */
+ switch(OPL->address)
+ {
+ case 0x05: /* KeyBoard IN */
+ if(OPL->type&OPL_TYPE_KEYBOARD)
+ {
+ if(OPL->keyboardhandler_r)
+ return OPL->keyboardhandler_r(OPL->keyboard_param);
+ else
+ logerror("Y8950: read unmapped KEYBOARD port\n");
+ }
+ return 0;
+
+ case 0x0f: /* ADPCM-DATA */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ UINT8 val;
+
+ val = YM_DELTAT_ADPCM_Read(OPL->deltat);
+ /*logerror("Y8950: read ADPCM value read=%02x\n",val);*/
+ return val;
+ }
+ return 0;
+
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ if(OPL->porthandler_r)
+ return OPL->porthandler_r(OPL->port_param);
+ else
+ logerror("Y8950:read unmapped I/O port\n");
+ }
+ return 0;
+ case 0x1a: /* PCM-DATA */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ logerror("Y8950 A/D convertion is accessed but not implemented !\n");
+ return 0x80; /* 2's complement PCM data - result from A/D convertion */
+ }
+ return 0;
+ }
+#endif
+
+ return 0xff;
+}
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(OPL_CH *CH)
+{
+ FM_KEYON (&CH->SLOT[SLOT1], 4);
+ FM_KEYON (&CH->SLOT[SLOT2], 4);
+
+ /* The key off should happen exactly one sample later - not implemented correctly yet */
+
+ FM_KEYOFF(&CH->SLOT[SLOT1], ~4);
+ FM_KEYOFF(&CH->SLOT[SLOT2], ~4);
+}
+
+
+static int OPLTimerOver(FM_OPL *OPL,int c)
+{
+ if( c )
+ { /* Timer B */
+ OPL_STATUS_SET(OPL,0x20);
+ }
+ else
+ { /* Timer A */
+ OPL_STATUS_SET(OPL,0x40);
+ /* CSM mode key,TL controll */
+ if( OPL->mode & 0x80 )
+ { /* CSM mode total level latch and auto key on */
+ int ch;
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+ for(ch=0; ch<9; ch++)
+ CSMKeyControll( &OPL->P_CH[ch] );
+ }
+ }
+ /* reload timer */
+ if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,c,OPL->TimerBase * OPL->T[c]);
+ return OPL->status>>7;
+}
+
+
+#define MAX_OPL_CHIPS 2
+
+
+#if (BUILD_YM3812)
+
+void * ym3812_init(device_t *device, UINT32 clock, UINT32 rate)
+{
+ /* emulator create */
+ FM_OPL *YM3812 = OPLCreate(device,clock,rate,OPL_TYPE_YM3812);
+ if (YM3812)
+ {
+ OPL_save_state(YM3812, device);
+ ym3812_reset_chip(YM3812);
+ }
+ return YM3812;
+}
+
+void ym3812_shutdown(void *chip)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+
+ /* emulator shutdown */
+ OPLDestroy(YM3812);
+}
+void ym3812_reset_chip(void *chip)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLResetChip(YM3812);
+}
+
+int ym3812_write(void *chip, int a, int v)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ return OPLWrite(YM3812, a, v);
+}
+
+unsigned char ym3812_read(void *chip, int a)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ /* YM3812 always returns bit2 and bit1 in HIGH state */
+ return OPLRead(YM3812, a) | 0x06 ;
+}
+int ym3812_timer_over(void *chip, int c)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ return OPLTimerOver(YM3812, c);
+}
+
+void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetTimerHandler(YM3812, timer_handler, param);
+}
+void ym3812_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetIRQHandler(YM3812, IRQHandler, param);
+}
+void ym3812_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(YM3812, UpdateHandler, param);
+}
+
+
+/*
+** Generate samples for one of the YM3812's
+**
+** 'which' is the virtual YM3812 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ UINT8 rhythm = OPL->rhythm&0x20;
+ OPLSAMPLE *buf = buffer;
+ int i;
+
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+
+ advance_lfo(OPL);
+
+ /* FM part */
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
+ }
+
+ lt = OPL->output[0];
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ advance(OPL);
+ }
+
+}
+#endif /* BUILD_YM3812 */
+
+
+
+#if (BUILD_YM3526)
+
+void *ym3526_init(device_t *device, UINT32 clock, UINT32 rate)
+{
+ /* emulator create */
+ FM_OPL *YM3526 = OPLCreate(device,clock,rate,OPL_TYPE_YM3526);
+ if (YM3526)
+ {
+ OPL_save_state(YM3526, device);
+ ym3526_reset_chip(YM3526);
+ }
+ return YM3526;
+}
+
+void ym3526_shutdown(void *chip)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ /* emulator shutdown */
+ OPLDestroy(YM3526);
+}
+void ym3526_reset_chip(void *chip)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLResetChip(YM3526);
+}
+
+int ym3526_write(void *chip, int a, int v)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ return OPLWrite(YM3526, a, v);
+}
+
+unsigned char ym3526_read(void *chip, int a)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ /* YM3526 always returns bit2 and bit1 in HIGH state */
+ return OPLRead(YM3526, a) | 0x06 ;
+}
+int ym3526_timer_over(void *chip, int c)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ return OPLTimerOver(YM3526, c);
+}
+
+void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetTimerHandler(YM3526, timer_handler, param);
+}
+void ym3526_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetIRQHandler(YM3526, IRQHandler, param);
+}
+void ym3526_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(YM3526, UpdateHandler, param);
+}
+
+
+/*
+** 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)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ UINT8 rhythm = OPL->rhythm&0x20;
+ OPLSAMPLE *buf = buffer;
+ int i;
+
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+
+ advance_lfo(OPL);
+
+ /* FM part */
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
+ }
+
+ lt = OPL->output[0];
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ advance(OPL);
+ }
+
+}
+#endif /* BUILD_YM3526 */
+
+
+
+
+#if BUILD_Y8950
+
+static void Y8950_deltat_status_set(void *chip, UINT8 changebits)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPL_STATUS_SET(Y8950, changebits);
+}
+static void Y8950_deltat_status_reset(void *chip, UINT8 changebits)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPL_STATUS_RESET(Y8950, changebits);
+}
+
+void *y8950_init(device_t *device, UINT32 clock, UINT32 rate)
+{
+ /* emulator create */
+ FM_OPL *Y8950 = OPLCreate(device,clock,rate,OPL_TYPE_Y8950);
+ if (Y8950)
+ {
+ Y8950->deltat->status_set_handler = Y8950_deltat_status_set;
+ Y8950->deltat->status_reset_handler = Y8950_deltat_status_reset;
+ Y8950->deltat->status_change_which_chip = Y8950;
+ Y8950->deltat->status_change_EOS_bit = 0x10; /* status flag: set bit4 on End Of Sample */
+ Y8950->deltat->status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY (End Of: ADPCM analysis/synthesis, memory reading/writing) */
+
+ /*Y8950->deltat->write_time = 10.0 / clock;*/ /* a single byte write takes 10 cycles of main clock */
+ /*Y8950->deltat->read_time = 8.0 / clock;*/ /* a single byte read takes 8 cycles of main clock */
+ /* reset */
+ OPL_save_state(Y8950, device);
+ y8950_reset_chip(Y8950);
+ }
+
+ return Y8950;
+}
+
+void y8950_shutdown(void *chip)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ /* emulator shutdown */
+ OPLDestroy(Y8950);
+}
+void y8950_reset_chip(void *chip)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLResetChip(Y8950);
+}
+
+int y8950_write(void *chip, int a, int v)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLWrite(Y8950, a, v);
+}
+
+unsigned char y8950_read(void *chip, int a)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLRead(Y8950, a);
+}
+int y8950_timer_over(void *chip, int c)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLTimerOver(Y8950, c);
+}
+
+void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetTimerHandler(Y8950, timer_handler, param);
+}
+void y8950_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetIRQHandler(Y8950, IRQHandler, param);
+}
+void y8950_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(Y8950, UpdateHandler, param);
+}
+
+void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size )
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->deltat->memory = (UINT8 *)(deltat_mem_ptr);
+ OPL->deltat->memory_size = deltat_mem_size;
+}
+
+/*
+** Generate samples for one of the Y8950's
+**
+** 'which' is the virtual Y8950 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length)
+{
+ int i;
+ FM_OPL *OPL = (FM_OPL *)chip;
+ UINT8 rhythm = OPL->rhythm&0x20;
+ YM_DELTAT *DELTAT = OPL->deltat;
+ OPLSAMPLE *buf = buffer;
+
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+ OPL->output_deltat[0] = 0;
+
+ advance_lfo(OPL);
+
+ /* deltaT ADPCM */
+ if( DELTAT->portstate&0x80 )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+
+ /* FM part */
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
+ }
+
+ lt = OPL->output[0] + (OPL->output_deltat[0]>>11);
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ advance(OPL);
+ }
+
+}
+
+void y8950_set_port_handler(void *chip,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,void * param)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->porthandler_w = PortHandler_w;
+ OPL->porthandler_r = PortHandler_r;
+ OPL->port_param = param;
+}
+
+void y8950_set_keyboard_handler(void *chip,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,void * param)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->keyboardhandler_w = KeyboardHandler_w;
+ OPL->keyboardhandler_r = KeyboardHandler_r;
+ OPL->keyboard_param = param;
+}
+
+#endif
diff --git a/src/devices/sound/fmopl.h b/src/devices/sound/fmopl.h
new file mode 100644
index 00000000000..3b0cda28c9f
--- /dev/null
+++ b/src/devices/sound/fmopl.h
@@ -0,0 +1,115 @@
+// license:???
+// copyright-holders:Jarek Burczynski
+#pragma once
+
+#ifndef __FMOPL_H__
+#define __FMOPL_H__
+
+/* --- select emulation chips --- */
+#define BUILD_YM3812 (1)
+#define BUILD_YM3526 (1)
+#define BUILD_Y8950 (1)
+
+/* select output bits size of output : 8 or 16 */
+#define OPL_SAMPLE_BITS 16
+
+/* compiler dependence */
+#ifndef __OSDCOMM_H__
+#define __OSDCOMM_H__
+typedef unsigned char UINT8; /* unsigned 8bit */
+typedef unsigned short UINT16; /* unsigned 16bit */
+typedef unsigned int UINT32; /* unsigned 32bit */
+typedef signed char INT8; /* signed 8bit */
+typedef signed short INT16; /* signed 16bit */
+typedef signed int INT32; /* signed 32bit */
+#endif /* __OSDCOMM_H__ */
+
+typedef stream_sample_t OPLSAMPLE;
+/*
+#if (OPL_SAMPLE_BITS==16)
+typedef INT16 OPLSAMPLE;
+#endif
+#if (OPL_SAMPLE_BITS==8)
+typedef INT8 OPLSAMPLE;
+#endif
+*/
+
+typedef void (*OPL_TIMERHANDLER)(void *param,int timer,const attotime &period);
+typedef void (*OPL_IRQHANDLER)(void *param,int irq);
+typedef void (*OPL_UPDATEHANDLER)(void *param,int min_interval_us);
+typedef void (*OPL_PORTHANDLER_W)(void *param,unsigned char data);
+typedef unsigned char (*OPL_PORTHANDLER_R)(void *param);
+
+
+#if BUILD_YM3812
+
+void *ym3812_init(device_t *device, UINT32 clock, UINT32 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, void *param);
+void ym3812_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);
+void ym3812_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);
+
+#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 clock, UINT32 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, void *param);
+void ym3526_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);
+void ym3526_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);
+
+#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, void *param);
+void y8950_set_keyboard_handler(void *chip, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, void *param);
+void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size );
+
+void * y8950_init(device_t *device, UINT32 clock, UINT32 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, void *param);
+void y8950_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);
+void y8950_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);
+
+#endif /* BUILD_Y8950 */
+
+
+#endif /* __FMOPL_H__ */
diff --git a/src/devices/sound/gaelco.c b/src/devices/sound/gaelco.c
new file mode 100644
index 00000000000..9a40796a1ac
--- /dev/null
+++ b/src/devices/sound/gaelco.c
@@ -0,0 +1,276 @@
+// license:BSD-3-Clause
+// copyright-holders:Manuel Abadia
+/***************************************************************************
+ Gaelco Sound Hardware
+
+ By Manuel Abadia <emumanu+mame@gmail.com>
+
+CG-1V/GAE1 (Gaelco custom GFX & Sound chip):
+ The CG-1V/GAE1 can handle up to 7 stereo channels.
+ The chip output is connected to a TDA1543 (16 bit DAC).
+
+Registers per channel:
+======================
+ Word | Bit(s) | Description
+ -----+-FEDCBA98-76543210-+--------------------------
+ 0 | xxxxxxxx xxxxxxxx | not used?
+ 1 | xxxx---- -------- | left channel volume (0x00..0x0f)
+ 1 | ----xxxx -------- | right channel volume (0x00..0x0f)
+ 1 | -------- xxxx---- | sample type (0x0c = PCM 8 bits mono, 0x08 = PCM 8 bits stereo)
+ 1 | -------- ----xxxx | ROM Bank
+ 2 | xxxxxxxx xxxxxxxx | sample end position
+ 3 | xxxxxxxx xxxxxxxx | remaining bytes to play
+
+ the following are used only when looping (usually used for music)
+
+ 4 | xxxxxxxx xxxxxxxx | not used?
+ 5 | xxxx---- -------- | left channel volume (0x00..0x0f)
+ 5 | ----xxxx -------- | right channel volume (0x00..0x0f)
+ 5 | -------- xxxx---- | sample type (0x0c = PCM 8 bits mono, 0x08 = PCM 8 bits stereo)
+ 5 | -------- ----xxxx | ROM Bank
+ 6 | xxxxxxxx xxxxxxxx | sample end position
+ 7 | xxxxxxxx xxxxxxxx | remaining bytes to play
+
+ The samples are played from (end position + length) to (end position)!
+
+***************************************************************************/
+
+#include "emu.h"
+#include "gaelco.h"
+#include "wavwrite.h"
+
+#define VERBOSE_SOUND 0
+#define VERBOSE_READ_WRITES 0
+#define LOG_SOUND(x) do { if (VERBOSE_SOUND) logerror x; } while (0)
+#define LOG_READ_WRITES(x) do { if (VERBOSE_READ_WRITES) logerror x; } while (0)
+
+//#define ALT_MIX
+
+#define LOG_WAVE 0
+static wav_file* wavraw; // Raw waveform
+
+
+/*============================================================================
+ Gaelco GAE1 sound device
+ ============================================================================*/
+
+const device_type GAELCO_GAE1 = &device_creator<gaelco_gae1_device>;
+
+gaelco_gae1_device::gaelco_gae1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, GAELCO_GAE1, "Gaelco GAE1", tag, owner, clock, "gaelco_gae1", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_snd_data(NULL),
+ m_data_tag(NULL)
+{
+}
+
+gaelco_gae1_device::gaelco_gae1_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_snd_data(NULL),
+ m_data_tag(NULL)
+{
+}
+
+
+/*============================================================================
+ CG-1V/GAE1 Sound Update
+
+ Writes length bytes to the sound buffer
+ ============================================================================*/
+
+void gaelco_gae1_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int j, ch;
+
+ /* fill all data needed */
+ for(j = 0; j < samples; j++){
+ int output_l = 0, output_r = 0;
+
+ /* for each channel */
+ for (ch = 0; ch < GAELCO_NUM_CHANNELS; ch ++){
+ int ch_data_l = 0, ch_data_r = 0;
+ gaelco_sound_channel *channel = &m_channel[ch];
+
+ /* if the channel is playing */
+ if (channel->active == 1){
+ int data, chunkNum = 0;
+ int base_offset, type, bank, vol_r, vol_l, end_pos;
+
+ /* if the channel is looping, get current chunk to play */
+ if (channel->loop == 1){
+ chunkNum = channel->chunkNum;
+ }
+
+ base_offset = ch*8 + chunkNum*4;
+
+ /* get channel parameters */
+ type = ((m_sndregs[base_offset + 1] >> 4) & 0x0f);
+ bank = m_banks[((m_sndregs[base_offset + 1] >> 0) & 0x03)];
+ vol_l = ((m_sndregs[base_offset + 1] >> 12) & 0x0f);
+ vol_r = ((m_sndregs[base_offset + 1] >> 8) & 0x0f);
+ end_pos = m_sndregs[base_offset + 2] << 8;
+
+ /* generates output data (range 0x00000..0xffff) */
+ if (type == 0x08){
+ /* PCM, 8 bits mono */
+ data = m_snd_data[bank + end_pos + m_sndregs[base_offset + 3]];
+ ch_data_l = m_volume_table[vol_l][data];
+ ch_data_r = m_volume_table[vol_r][data];
+
+ m_sndregs[base_offset + 3]--;
+ } else if (type == 0x0c){
+ /* PCM, 8 bits stereo */
+ data = m_snd_data[bank + end_pos + m_sndregs[base_offset + 3]];
+ ch_data_l = m_volume_table[vol_l][data];
+
+ m_sndregs[base_offset + 3]--;
+
+ if (m_sndregs[base_offset + 3] > 0){
+ data = m_snd_data[bank + end_pos + m_sndregs[base_offset + 3]];
+ ch_data_r = m_volume_table[vol_r][data];
+
+ m_sndregs[base_offset + 3]--;
+ }
+ } else {
+ LOG_SOUND(("(GAE1) Playing unknown sample format in channel: %02d, type: %02x, bank: %02x, end: %08x, Length: %04x\n", ch, type, bank, end_pos, m_sndregs[base_offset + 3]));
+ channel->active = 0;
+ }
+
+ /* check if the current sample has finished playing */
+ if (m_sndregs[base_offset + 3] == 0){
+ if (channel->loop == 0){ /* if no looping, we're done */
+ channel->active = 0;
+ } else { /* if we're looping, swap chunks */
+ channel->chunkNum = (channel->chunkNum + 1) & 0x01;
+
+ /* if the length of the next chunk is 0, we're done */
+ if (m_sndregs[ch*8 + channel->chunkNum*4 + 3] == 0){
+ channel->active = 0;
+ }
+ }
+ }
+ }
+
+ /* add the contribution of this channel to the current data output */
+ output_l += ch_data_l;
+ output_r += ch_data_r;
+ }
+
+#ifndef ALT_MIX
+ /* clip to max or min value */
+ if (output_l > 32767) output_l = 32767;
+ if (output_r > 32767) output_r = 32767;
+ if (output_l < -32768) output_l = -32768;
+ if (output_r < -32768) output_r = -32768;
+#else
+ /* ponderate channels */
+ output_l /= GAELCO_NUM_CHANNELS;
+ output_r /= GAELCO_NUM_CHANNELS;
+#endif
+
+ /* now that we have computed all channels, save current data to the output buffer */
+ outputs[0][j] = output_l;
+ outputs[1][j] = output_r;
+ }
+
+ if (wavraw)
+ wav_add_data_32lr(wavraw, outputs[0], outputs[1], samples, 0);
+}
+
+/*============================================================================
+ CG-1V/GAE1 Read Handler
+ ============================================================================*/
+
+READ16_MEMBER( gaelco_gae1_device::gaelcosnd_r )
+{
+ LOG_READ_WRITES(("%s: (GAE1): read from %04x\n", machine().describe_context(), offset));
+
+ return m_sndregs[offset];
+}
+
+/*============================================================================
+ CG-1V/GAE1 Write Handler
+ ============================================================================*/
+
+WRITE16_MEMBER( gaelco_gae1_device::gaelcosnd_w )
+{
+ gaelco_sound_channel *channel = &m_channel[offset >> 3];
+
+ LOG_READ_WRITES(("%s: (GAE1): write %04x to %04x\n", machine().describe_context(), data, offset));
+
+ /* first update the stream to this point in time */
+ m_stream->update();
+
+ COMBINE_DATA(&m_sndregs[offset]);
+
+ switch(offset & 0x07){
+ case 0x03:
+ /* trigger sound */
+ if ((m_sndregs[offset - 1] != 0) && (data != 0)){
+ if (!channel->active){
+ channel->active = 1;
+ channel->chunkNum = 0;
+ channel->loop = 0;
+ LOG_SOUND(("(GAE1) Playing sample channel: %02d, type: %02x, bank: %02x, end: %08x, Length: %04x\n", offset >> 3, (m_sndregs[offset - 2] >> 4) & 0x0f, m_sndregs[offset - 2] & 0x03, m_sndregs[offset - 1] << 8, data));
+ }
+ } else {
+ channel->active = 0;
+ }
+
+ break;
+
+ case 0x07: /* enable/disable looping */
+ if ((m_sndregs[offset - 1] != 0) && (data != 0)){
+ LOG_SOUND(("(GAE1) Looping in channel: %02d, type: %02x, bank: %02x, end: %08x, Length: %04x\n", offset >> 3, (m_sndregs[offset - 2] >> 4) & 0x0f, m_sndregs[offset - 2] & 0x03, m_sndregs[offset - 1] << 8, data));
+ channel->loop = 1;
+ } else {
+ channel->loop = 0;
+ }
+
+ break;
+ }
+}
+
+/*============================================================================
+ CG-1V/GAE1 Init / Close
+ ============================================================================*/
+
+void gaelco_gae1_device::device_start()
+{
+ m_stream = stream_alloc(0, 2, 8000);
+
+ m_snd_data = owner()->memregion(m_data_tag)->base();
+
+ /* init volume table */
+ for (int vol = 0; vol < GAELCO_VOLUME_LEVELS; vol++){
+ for (int j = -128; j <= 127; j++){
+ m_volume_table[vol][(j ^ 0x80) & 0xff] = (vol*j*256)/(GAELCO_VOLUME_LEVELS - 1);
+ }
+ }
+
+ if (LOG_WAVE)
+ wavraw = wav_open("gae1_snd.wav", 8000, 2);
+}
+
+
+void gaelco_gae1_device::device_stop()
+{
+ if (wavraw)
+ wav_close(wavraw);
+ wavraw = NULL;
+}
+
+
+/*============================================================================
+ Gaelco CG-1V sound device
+ ============================================================================*/
+
+const device_type GAELCO_CG1V = &device_creator<gaelco_cg1v_device>;
+
+gaelco_cg1v_device::gaelco_cg1v_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : gaelco_gae1_device(mconfig, GAELCO_CG1V, "Gaelco CG1V", tag, owner, clock, "gaelco_cg1v", __FILE__)
+{
+}
diff --git a/src/devices/sound/gaelco.h b/src/devices/sound/gaelco.h
new file mode 100644
index 00000000000..25b8cb129d2
--- /dev/null
+++ b/src/devices/sound/gaelco.h
@@ -0,0 +1,98 @@
+// license:BSD-3-Clause
+// copyright-holders:Manuel Abadia
+#pragma once
+
+#ifndef __GAELCO_SND_H__
+#define __GAELCO_SND_H__
+
+#define GAELCO_NUM_CHANNELS 0x07
+#define GAELCO_VOLUME_LEVELS 0x10
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_GAELCO_SND_DATA(_tag) \
+ gaelco_gae1_device::set_snd_data_tag(*device, _tag);
+
+#define MCFG_GAELCO_BANKS(_offs1, _offs2, _offs3, _offs4) \
+ gaelco_gae1_device::set_bank_offsets(*device, _offs1, _offs2, _offs3, _offs4);
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> Sound Channel
+
+struct gaelco_sound_channel
+{
+ int active; // is it playing?
+ int loop; // = 0 no looping, = 1 looping
+ int chunkNum; // current chunk if looping
+};
+
+
+// ======================> gaelco_gae1_device
+
+class gaelco_gae1_device : public device_t,
+ public device_sound_interface
+{
+public:
+ gaelco_gae1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ gaelco_gae1_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ ~gaelco_gae1_device() { }
+
+ static void set_snd_data_tag(device_t &device, const char *tag) { downcast<gaelco_gae1_device &>(device).m_data_tag = tag; }
+ static void set_bank_offsets(device_t &device, int offs1, int offs2, int offs3, int offs4)
+ {
+ gaelco_gae1_device &dev = downcast<gaelco_gae1_device &>(device);
+ dev.m_banks[0] = offs1;
+ dev.m_banks[1] = offs2;
+ dev.m_banks[2] = offs3;
+ dev.m_banks[3] = offs4;
+ }
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_stop();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+public:
+ DECLARE_WRITE16_MEMBER( gaelcosnd_w );
+ DECLARE_READ16_MEMBER( gaelcosnd_r );
+
+private:
+ sound_stream *m_stream; /* our stream */
+ UINT8 *m_snd_data; /* PCM data */
+ int m_banks[4]; /* start of each ROM bank */
+ gaelco_sound_channel m_channel[GAELCO_NUM_CHANNELS]; /* 7 stereo channels */
+
+ const char *m_data_tag;
+
+ UINT16 m_sndregs[0x38];
+
+ // Table for converting from 8 to 16 bits with volume control
+ INT16 m_volume_table[GAELCO_VOLUME_LEVELS][256];
+};
+
+extern const device_type GAELCO_GAE1;
+
+
+
+// ======================> gaelco_cg1v_device
+
+class gaelco_cg1v_device : public gaelco_gae1_device
+{
+public:
+ gaelco_cg1v_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+extern const device_type GAELCO_CG1V;
+
+
+#endif /* __GAELCO_SND_H__ */
diff --git a/src/devices/sound/hc55516.c b/src/devices/sound/hc55516.c
new file mode 100644
index 00000000000..9d8a191bdba
--- /dev/null
+++ b/src/devices/sound/hc55516.c
@@ -0,0 +1,337 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/*****************************************************************************
+
+ Harris HC-55516 (and related) emulator
+
+*****************************************************************************/
+
+#include "emu.h"
+#include "hc55516.h"
+
+
+/* 4x oversampling */
+#define SAMPLE_RATE (48000 * 4)
+
+#define INTEGRATOR_LEAK_TC 0.001
+#define FILTER_DECAY_TC 0.004
+#define FILTER_CHARGE_TC 0.004
+#define FILTER_MIN 0.0416
+#define FILTER_MAX 1.0954
+#define SAMPLE_GAIN 10000.0
+
+
+
+
+const device_type HC55516 = &device_creator<hc55516_device>;
+
+hc55516_device::hc55516_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, HC55516, "HC-55516", tag, owner, clock, "hc55516", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_channel(NULL),
+ m_active_clock_hi(0),
+ m_shiftreg_mask(0),
+ m_last_clock_state(0),
+ m_digit(0),
+ m_new_digit(0),
+ m_shiftreg(0),
+ m_curr_sample(0),
+ m_next_sample(0),
+ m_update_count(0),
+ m_filter(0),
+ m_integrator(0),
+ m_charge(0),
+ m_decay(0),
+ m_leak(0)
+{
+}
+hc55516_device::hc55516_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_sound_interface(mconfig, *this),
+ m_channel(NULL),
+ m_active_clock_hi(0),
+ m_shiftreg_mask(0),
+ m_last_clock_state(0),
+ m_digit(0),
+ m_new_digit(0),
+ m_shiftreg(0),
+ m_curr_sample(0),
+ m_next_sample(0),
+ m_update_count(0),
+ m_filter(0),
+ m_integrator(0),
+ m_charge(0),
+ m_decay(0),
+ m_leak(0)
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void hc55516_device::device_config_complete()
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void hc55516_device::device_start()
+{
+ start_common(0x07, TRUE);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void hc55516_device::device_reset()
+{
+ m_last_clock_state = 0;
+}
+
+const device_type MC3417 = &device_creator<mc3417_device>;
+
+mc3417_device::mc3417_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : hc55516_device(mconfig, MC3417, "MC3417", tag, owner, clock, "mc3417", __FILE__)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mc3417_device::device_start()
+{
+ start_common(0x07, FALSE);
+}
+
+
+const device_type MC3418 = &device_creator<mc3418_device>;
+
+mc3418_device::mc3418_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : hc55516_device(mconfig, MC3418, "MC3418", tag, owner, clock, "mc3418", __FILE__)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mc3418_device::device_start()
+{
+ start_common(0x0f, FALSE);
+}
+
+
+void hc55516_device::start_common(UINT8 _shiftreg_mask, int _active_clock_hi)
+{
+ /* compute the fixed charge, decay, and leak time constants */
+ m_charge = pow(exp(-1.0), 1.0 / (FILTER_CHARGE_TC * 16000.0));
+ m_decay = pow(exp(-1.0), 1.0 / (FILTER_DECAY_TC * 16000.0));
+ m_leak = pow(exp(-1.0), 1.0 / (INTEGRATOR_LEAK_TC * 16000.0));
+
+ m_shiftreg_mask = _shiftreg_mask;
+ m_active_clock_hi = _active_clock_hi;
+ m_last_clock_state = 0;
+
+ /* create the stream */
+ m_channel = machine().sound().stream_alloc(*this, 0, 1, SAMPLE_RATE);
+
+ save_item(NAME(m_last_clock_state));
+ save_item(NAME(m_digit));
+ save_item(NAME(m_new_digit));
+ save_item(NAME(m_shiftreg));
+ save_item(NAME(m_curr_sample));
+ save_item(NAME(m_next_sample));
+ save_item(NAME(m_update_count));
+ save_item(NAME(m_filter));
+ save_item(NAME(m_integrator));
+}
+
+inline int hc55516_device::is_external_oscillator()
+{
+ return clock() != 0;
+}
+
+
+inline int hc55516_device::is_active_clock_transition(int clock_state)
+{
+ return (( m_active_clock_hi && !m_last_clock_state && clock_state) ||
+ (!m_active_clock_hi && m_last_clock_state && !clock_state));
+}
+
+
+inline int hc55516_device::current_clock_state()
+{
+ return ((UINT64)m_update_count * clock() * 2 / SAMPLE_RATE) & 0x01;
+}
+
+
+void hc55516_device::process_digit()
+{
+ double integrator = m_integrator, temp;
+
+ /* shift the bit into the shift register */
+ m_shiftreg = (m_shiftreg << 1) | m_digit;
+
+ /* move the estimator up or down a step based on the bit */
+ if (m_digit)
+ integrator += m_filter;
+ else
+ integrator -= m_filter;
+
+ /* simulate leakage */
+ integrator *= m_leak;
+
+ /* if we got all 0's or all 1's in the last n bits, bump the step up */
+ if (((m_shiftreg & m_shiftreg_mask) == 0) ||
+ ((m_shiftreg & m_shiftreg_mask) == m_shiftreg_mask))
+ {
+ m_filter = FILTER_MAX - ((FILTER_MAX - m_filter) * m_charge);
+
+ if (m_filter > FILTER_MAX)
+ m_filter = FILTER_MAX;
+ }
+
+ /* simulate decay */
+ else
+ {
+ m_filter *= m_decay;
+
+ if (m_filter < FILTER_MIN)
+ m_filter = FILTER_MIN;
+ }
+
+ /* compute the sample as a 32-bit word */
+ temp = integrator * SAMPLE_GAIN;
+ m_integrator = integrator;
+
+ /* compress the sample range to fit better in a 16-bit word */
+ if (temp < 0)
+ m_next_sample = (int)(temp / (-temp * (1.0 / 32768.0) + 1.0));
+ else
+ m_next_sample = (int)(temp / (temp * (1.0 / 32768.0) + 1.0));
+}
+
+void hc55516_device::clock_w(int state)
+{
+ UINT8 clock_state = state ? TRUE : FALSE;
+
+ /* only makes sense for setups with a software driven clock */
+ assert(!is_external_oscillator());
+
+ /* speech clock changing? */
+ if (is_active_clock_transition(clock_state))
+ {
+ /* update the output buffer before changing the registers */
+ m_channel->update();
+
+ /* clear the update count */
+ m_update_count = 0;
+
+ process_digit();
+ }
+
+ /* update the clock */
+ m_last_clock_state = clock_state;
+}
+
+
+void hc55516_device::digit_w(int digit)
+{
+ if (is_external_oscillator())
+ {
+ m_channel->update();
+ m_new_digit = digit & 1;
+ }
+ else
+ m_digit = digit & 1;
+}
+
+
+int hc55516_device::clock_state_r()
+{
+ /* only makes sense for setups with an external oscillator */
+ assert(is_external_oscillator());
+
+ m_channel->update();
+
+ return current_clock_state();
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void hc55516_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *buffer = outputs[0];
+ int i;
+ INT32 sample, slope;
+
+ /* zero-length? bail */
+ if (samples == 0)
+ return;
+
+ if (!is_external_oscillator())
+ {
+ /* track how many samples we've updated without a clock */
+ m_update_count += samples;
+ if (m_update_count > SAMPLE_RATE / 32)
+ {
+ m_update_count = SAMPLE_RATE;
+ m_next_sample = 0;
+ }
+ }
+
+ /* compute the interpolation slope */
+ sample = m_curr_sample;
+ slope = ((INT32)m_next_sample - sample) / samples;
+ m_curr_sample = m_next_sample;
+
+ if (is_external_oscillator())
+ {
+ /* external oscillator */
+ for (i = 0; i < samples; i++, sample += slope)
+ {
+ UINT8 clock_state;
+
+ *buffer++ = sample;
+
+ m_update_count++;
+
+ clock_state = current_clock_state();
+
+ /* pull in next digit on the appropriate edge of the clock */
+ if (is_active_clock_transition(clock_state))
+ {
+ m_digit = m_new_digit;
+
+ process_digit();
+ }
+
+ m_last_clock_state = clock_state;
+ }
+ }
+
+ /* software driven clock */
+ else
+ for (i = 0; i < samples; i++, sample += slope)
+ *buffer++ = sample;
+}
+
+void mc3417_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ hc55516_device::sound_stream_update(stream, inputs, outputs, samples);
+}
+
+void mc3418_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ hc55516_device::sound_stream_update(stream, inputs, outputs, samples);
+}
diff --git a/src/devices/sound/hc55516.h b/src/devices/sound/hc55516.h
new file mode 100644
index 00000000000..a6323f8ee4f
--- /dev/null
+++ b/src/devices/sound/hc55516.h
@@ -0,0 +1,95 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+#pragma once
+
+#ifndef __HC55516_H__
+#define __HC55516_H__
+
+class hc55516_device : public device_t,
+ public device_sound_interface
+{
+public:
+ hc55516_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ hc55516_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ ~hc55516_device() {}
+
+ /* sets the digit (0 or 1) */
+ void digit_w(int digit);
+
+ /* sets the clock state (0 or 1, clocked on the rising edge) */
+ void clock_w(int state);
+
+ /* returns whether the clock is currently LO or HI */
+ int clock_state_r();
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ void start_common(UINT8 _shiftreg_mask, int _active_clock_hi);
+
+ // internal state
+ sound_stream *m_channel;
+ int m_active_clock_hi;
+ UINT8 m_shiftreg_mask;
+
+ UINT8 m_last_clock_state;
+ UINT8 m_digit;
+ UINT8 m_new_digit;
+ UINT8 m_shiftreg;
+
+ INT16 m_curr_sample;
+ INT16 m_next_sample;
+
+ UINT32 m_update_count;
+
+ double m_filter;
+ double m_integrator;
+
+ double m_charge;
+ double m_decay;
+ double m_leak;
+
+ inline int is_external_oscillator();
+ inline int is_active_clock_transition(int clock_state);
+ inline int current_clock_state();
+ void process_digit();
+};
+
+extern const device_type HC55516;
+
+class mc3417_device : public hc55516_device
+{
+public:
+ mc3417_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+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);
+};
+
+extern const device_type MC3417;
+
+class mc3418_device : public hc55516_device
+{
+public:
+ mc3418_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+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);
+};
+
+extern const device_type MC3418;
+
+
+#endif /* __HC55516_H__ */
diff --git a/src/devices/sound/i5000.c b/src/devices/sound/i5000.c
new file mode 100644
index 00000000000..df86311904e
--- /dev/null
+++ b/src/devices/sound/i5000.c
@@ -0,0 +1,303 @@
+// license:BSD-3-Clause
+// copyright-holders:hap
+/***************************************************************************
+
+ i5000.c - Imagetek I5000 sound emulator
+
+ Imagetek I5000 is a multi-purpose chip, this covers the sound part.
+ No official documentation is known to exist. It seems to be a simple
+ 16-channel ADPCM player.
+
+ TODO:
+ - verify that ADPCM is the same as standard OKI ADPCM
+ - verify volume balance
+ - sample command 0x0007
+ - any more sound formats than 3-bit and 4-bit ADPCM?
+
+***************************************************************************/
+
+#include "emu.h"
+#include "i5000.h"
+
+
+// device type definition
+const device_type I5000_SND = &device_creator<i5000snd_device>;
+
+i5000snd_device::i5000snd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, I5000_SND, "I5000", tag, owner, clock, "i5000snd", __FILE__),
+ device_sound_interface(mconfig, *this)
+{
+}
+
+
+void i5000snd_device::device_start()
+{
+ // fill volume table
+ double div = 1.032;
+ double vol = 2047.0;
+ for (int i = 0; i < 0x100; i++)
+ {
+ m_lut_volume[i] = vol + 0.5;
+ vol /= div;
+ }
+ m_lut_volume[0xff] = 0;
+
+ // create the stream
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, clock() / 0x400);
+
+ m_rom_base = (UINT16 *)device().machine().root_device().memregion(":i5000snd")->base();
+ m_rom_mask = device().machine().root_device().memregion(":i5000snd")->bytes() / 2 - 1;
+
+ // register for savestates
+ for (int ch = 0; ch < 16; ch++)
+ {
+ save_item(NAME(m_channels[ch].is_playing), ch);
+ save_item(NAME(m_channels[ch].m_adpcm.m_signal), ch);
+ save_item(NAME(m_channels[ch].m_adpcm.m_step), ch);
+
+ save_item(NAME(m_channels[ch].address), ch);
+ save_item(NAME(m_channels[ch].freq_timer), ch);
+ save_item(NAME(m_channels[ch].freq_base), ch);
+ save_item(NAME(m_channels[ch].freq_min), ch);
+ save_item(NAME(m_channels[ch].sample), ch);
+ save_item(NAME(m_channels[ch].shift_pos), ch);
+ save_item(NAME(m_channels[ch].shift_amount), ch);
+ save_item(NAME(m_channels[ch].shift_mask), ch);
+ save_item(NAME(m_channels[ch].vol_r), ch);
+ save_item(NAME(m_channels[ch].vol_l), ch);
+ save_item(NAME(m_channels[ch].output_r), ch);
+ save_item(NAME(m_channels[ch].output_l), ch);
+ }
+
+ save_item(NAME(m_regs));
+}
+
+
+void i5000snd_device::device_reset()
+{
+ // stop playing
+ write_reg16(0x43, 0xffff);
+
+ // reset channel regs
+ for (int i = 0; i < 0x40; i++)
+ write_reg16(i, 0);
+}
+
+
+bool i5000snd_device::read_sample(int ch)
+{
+ m_channels[ch].shift_pos &= 0xf;
+ m_channels[ch].sample = m_rom_base[m_channels[ch].address];
+ m_channels[ch].address = (m_channels[ch].address + 1) & m_rom_mask;
+
+ // handle command
+ if (m_channels[ch].sample == 0x7f7f)
+ {
+ UINT16 cmd = m_rom_base[m_channels[ch].address];
+ m_channels[ch].address = (m_channels[ch].address + 1) & m_rom_mask;
+
+ // volume envelope? or loop sample?
+ if ((cmd & 0x00ff) == 0x0007)
+ {
+ // TODO
+ return false;
+ }
+
+ // cmd 0x0000 = end sample
+ // other values: unused
+ else return false;
+
+ }
+
+ return true;
+}
+
+
+void i5000snd_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ for (int i = 0; i < samples; i++)
+ {
+ INT32 mix_l = 0;
+ INT32 mix_r = 0;
+
+ // loop over all channels
+ for (int ch = 0; ch < 16; ch++)
+ {
+ if (!m_channels[ch].is_playing)
+ continue;
+
+ m_channels[ch].freq_timer -= m_channels[ch].freq_min;
+ if (m_channels[ch].freq_timer > 0)
+ {
+ mix_r += m_channels[ch].output_r;
+ mix_l += m_channels[ch].output_l;
+ continue;
+ }
+ m_channels[ch].freq_timer += m_channels[ch].freq_base;
+
+ int adpcm_data = m_channels[ch].sample >> m_channels[ch].shift_pos;
+ m_channels[ch].shift_pos += m_channels[ch].shift_amount;
+ if (m_channels[ch].shift_pos & 0x10)
+ {
+ if (!read_sample(ch))
+ {
+ m_channels[ch].is_playing = false;
+ continue;
+ }
+
+ adpcm_data |= (m_channels[ch].sample << (m_channels[ch].shift_amount - m_channels[ch].shift_pos));
+ }
+
+ adpcm_data = m_channels[ch].m_adpcm.clock(adpcm_data & m_channels[ch].shift_mask);
+
+ m_channels[ch].output_r = adpcm_data * m_channels[ch].vol_r / 128;
+ m_channels[ch].output_l = adpcm_data * m_channels[ch].vol_l / 128;
+ mix_r += m_channels[ch].output_r;
+ mix_l += m_channels[ch].output_l;
+ }
+
+ outputs[0][i] = mix_r / 16;
+ outputs[1][i] = mix_l / 16;
+ }
+}
+
+
+void i5000snd_device::write_reg16(UINT8 reg, UINT16 data)
+{
+ // channel regs
+ if (reg < 0x40)
+ {
+ int ch = reg >> 2;
+ switch (reg & 3)
+ {
+ // 0, 1: address
+
+ // 2: frequency
+ case 2:
+ m_channels[ch].freq_base = (0x1ff - (data & 0xff)) << (~data >> 8 & 3);
+ break;
+
+ // 3: left/right volume
+ case 3:
+ m_channels[ch].vol_r = m_lut_volume[data & 0xff];
+ m_channels[ch].vol_l = m_lut_volume[data >> 8 & 0xff];
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // global regs
+ else
+ {
+ switch (reg)
+ {
+ // channel key on (0 has no effect)
+ case 0x42:
+ for (int ch = 0; ch < 16; ch++)
+ {
+ if (data & (1 << ch) && !m_channels[ch].is_playing)
+ {
+ UINT32 address = m_regs[ch << 2 | 1] << 16 | m_regs[ch << 2];
+ UINT16 start = m_rom_base[(address + 0) & m_rom_mask];
+ UINT16 param = m_rom_base[(address + 1) & m_rom_mask];
+
+ // check sample start ID
+ if (start != 0x7f7f)
+ {
+ logerror("i5000snd: channel %d wrong sample start ID %04X!\n", ch, start);
+ continue;
+ }
+
+ switch (param)
+ {
+ // 3-bit ADPCM
+ case 0x0104:
+ case 0x0304: // same?
+ m_channels[ch].freq_min = 0x140;
+ m_channels[ch].shift_amount = 3;
+ m_channels[ch].shift_mask = 0xe;
+ break;
+
+ default:
+ logerror("i5000snd: channel %d unknown sample param %04X!\n", ch, param);
+ // fall through (take settings from 0x0184)
+ // 4-bit ADPCM
+ case 0x0184:
+ m_channels[ch].freq_min = 0x100;
+ m_channels[ch].shift_amount = 4;
+ m_channels[ch].shift_mask = 0xf;
+ break;
+ }
+
+ m_channels[ch].address = (address + 4) & m_rom_mask;
+
+ m_channels[ch].freq_timer = 0;
+ m_channels[ch].shift_pos = 0;
+
+ m_channels[ch].m_adpcm.reset();
+ m_channels[ch].is_playing = read_sample(ch);
+ }
+ }
+ break;
+
+ // channel key off (0 has no effect)
+ case 0x43:
+ for (int ch = 0; ch < 16; ch++)
+ {
+ if (data & (1 << ch))
+ m_channels[ch].is_playing = false;
+ }
+ break;
+
+ default:
+ // not accessed often, assume that these are chip init registers
+ // 0x40: ?
+ // 0x41: ?
+ // 0x45: ?
+ // 0x46: ?
+ break;
+ }
+ }
+
+ m_regs[reg] = data;
+}
+
+
+READ16_MEMBER( i5000snd_device::read )
+{
+ UINT16 ret = 0;
+ m_stream->update();
+
+ switch (offset)
+ {
+ // channel active state
+ case 0x42:
+ for (int ch = 0; ch < 16; ch++)
+ {
+ if (m_channels[ch].is_playing)
+ ret |= (1 << ch);
+ }
+ break;
+
+ default:
+ // 0x41: ?
+ break;
+ }
+
+ return ret;
+}
+
+
+WRITE16_MEMBER( i5000snd_device::write )
+{
+ if (mem_mask != 0xffff)
+ {
+ logerror("i5000snd: wrong mask %04X!\n", mem_mask);
+ return;
+ }
+ m_stream->update();
+
+ write_reg16(offset, data);
+}
diff --git a/src/devices/sound/i5000.h b/src/devices/sound/i5000.h
new file mode 100644
index 00000000000..cdf8b33430e
--- /dev/null
+++ b/src/devices/sound/i5000.h
@@ -0,0 +1,89 @@
+// license:BSD-3-Clause
+// copyright-holders:hap
+/***************************************************************************
+
+ i5000.h - Imagetek I5000 sound emulator
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __I5000_H__
+#define __I5000_H__
+
+#include "sound/okiadpcm.h"
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_I5000_SND_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, I5000_SND, _clock)
+
+#define MCFG_I5000_SND_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, I5000_SND, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+class i5000snd_device : public device_t,
+ public device_sound_interface
+{
+public:
+ // construction/destruction
+ i5000snd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_READ16_MEMBER(read);
+ DECLARE_WRITE16_MEMBER(write);
+
+ sound_stream *m_stream;
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ struct channel_t
+ {
+ bool is_playing;
+ oki_adpcm_state m_adpcm;
+
+ UINT32 address;
+ int freq_timer;
+ int freq_base;
+ int freq_min;
+ UINT16 sample;
+ UINT8 shift_pos;
+ UINT8 shift_amount;
+ UINT8 shift_mask;
+ int vol_r;
+ int vol_l;
+ int output_r;
+ int output_l;
+
+ };
+
+ channel_t m_channels[16];
+
+ UINT16 m_regs[0x80];
+
+ UINT16 *m_rom_base;
+ UINT32 m_rom_mask;
+
+ int m_lut_volume[0x100];
+
+ bool read_sample(int ch);
+ void write_reg16(UINT8 reg, UINT16 data);
+};
+
+
+// device type definition
+extern const device_type I5000_SND;
+
+#endif /* __I5000_H__ */
diff --git a/src/devices/sound/ics2115.c b/src/devices/sound/ics2115.c
new file mode 100644
index 00000000000..af3557c28c3
--- /dev/null
+++ b/src/devices/sound/ics2115.c
@@ -0,0 +1,890 @@
+// license:???
+// copyright-holders:Alex Marshall,nimitz,austere
+//ICS2115 by Raiden II team (c) 2010
+//members: austere, nimitz, Alex Marshal
+//
+//Original driver by O. Galibert, ElSemi
+//
+//Use tab size = 4 for your viewing pleasure.
+
+#include "emu.h"
+#include "ics2115.h"
+#include <cmath>
+
+//#define ICS2115_DEBUG
+//#define ICS2115_ISOLATE 6
+
+// device type definition
+const device_type ICS2115 = &device_creator<ics2115_device>;
+
+ics2115_device::ics2115_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, ICS2115, "ICS2115", tag, owner, clock, "ics2115", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_rom(*this, DEVICE_SELF),
+ m_irq_cb(*this)
+{
+}
+
+void ics2115_device::device_start()
+{
+ m_timer[0].timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ics2115_device::timer_cb_0),this), this);
+ m_timer[1].timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ics2115_device::timer_cb_1),this), this);
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, 33075);
+
+ m_irq_cb.resolve_safe();
+
+ //Exact formula as per patent 5809466
+ //This seems to give the ok fit but it is not good enough.
+ /*double maxvol = ((1 << volume_bits) - 1) * pow(2., (double)1/0x100);
+ for (int i = 0; i < 0x1000; i++) {
+ m_volume[i] = floor(maxvol * pow(2.,(double)i/256 - 16) + 0.5);
+ }*/
+
+ //austere's table, derived from patent 5809466:
+ //See section V starting from page 195
+ //Subsection F (column 124, page 198) onwards
+ for (int i = 0; i<4096; i++) {
+ m_volume[i] = ((0x100 | (i & 0xff)) << (volume_bits-9)) >> (15 - (i>>8));
+ }
+
+ //u-Law table as per MIL-STD-188-113
+ UINT16 lut[8];
+ UINT16 lut_initial = 33 << 2; //shift up 2-bits for 16-bit range.
+ for(int i = 0; i < 8; i++)
+ lut[i] = (lut_initial << i) - lut_initial;
+ for(int i = 0; i < 256; i++) {
+ UINT8 exponent = (~i >> 4) & 0x07;
+ UINT8 mantissa = ~i & 0x0f;
+ INT16 value = lut[exponent] + (mantissa << (exponent + 3));
+ m_ulaw[i] = (i & 0x80) ? -value : value;
+ }
+
+ save_item(NAME(m_timer[0].period));
+ save_item(NAME(m_timer[0].scale));
+ save_item(NAME(m_timer[0].preset));
+ save_item(NAME(m_timer[1].period));
+ save_item(NAME(m_timer[1].scale));
+ save_item(NAME(m_timer[1].preset));
+ save_item(NAME(m_reg_select));
+ save_item(NAME(m_osc_select));
+ save_item(NAME(m_irq_enabled));
+ save_item(NAME(m_irq_pending));
+ save_item(NAME(m_irq_on));
+ save_item(NAME(m_active_osc));
+ save_item(NAME(m_vmode));
+
+ for(int i = 0; i < 32; i++) {
+ save_item(NAME(m_voice[i].osc_conf.value), i);
+ save_item(NAME(m_voice[i].state.value), i);
+ save_item(NAME(m_voice[i].vol_ctrl.value), i);
+ save_item(NAME(m_voice[i].osc.left), i);
+ save_item(NAME(m_voice[i].osc.acc), i);
+ save_item(NAME(m_voice[i].osc.start), i);
+ save_item(NAME(m_voice[i].osc.end), i);
+ save_item(NAME(m_voice[i].osc.fc), i);
+ save_item(NAME(m_voice[i].osc.ctl), i);
+ save_item(NAME(m_voice[i].osc.saddr), i);
+ save_item(NAME(m_voice[i].vol.left), i);
+ save_item(NAME(m_voice[i].vol.add), i);
+ save_item(NAME(m_voice[i].vol.start), i);
+ save_item(NAME(m_voice[i].vol.end), i);
+ save_item(NAME(m_voice[i].vol.acc), i);
+ save_item(NAME(m_voice[i].vol.regacc), i);
+ save_item(NAME(m_voice[i].vol.incr), i);
+ save_item(NAME(m_voice[i].vol.pan), i);
+ save_item(NAME(m_voice[i].vol.mode), i);
+ }
+}
+
+
+void ics2115_device::device_reset()
+{
+ m_irq_enabled = 0;
+ m_irq_pending = 0;
+ //possible re-suss
+ m_active_osc = 31;
+ m_osc_select = 0;
+ m_reg_select = 0;
+ m_vmode = 0;
+ m_irq_on = false;
+ memset(m_voice, 0, sizeof(m_voice));
+ for(int i = 0; i < 2; ++i)
+ {
+ m_timer[i].timer->adjust(attotime::never);
+ m_timer[i].period = 0;
+ m_timer[i].scale = 0;
+ m_timer[i].preset = 0;
+ }
+ for(int i = 0; i < 32; i++) {
+ m_voice[i].osc_conf.value = 2;
+ m_voice[i].osc.fc = 0;
+ m_voice[i].osc.acc = 0;
+ m_voice[i].osc.start = 0;
+ m_voice[i].osc.end = 0;
+ m_voice[i].osc.ctl = 0;
+ m_voice[i].osc.saddr = 0;
+ m_voice[i].vol.acc = 0;
+ m_voice[i].vol.incr = 0;
+ m_voice[i].vol.start = 0;
+ m_voice[i].vol.end = 0;
+ m_voice[i].vol.pan = 0x7F;
+ m_voice[i].vol_ctrl.value = 1;
+ m_voice[i].vol.mode = 0;
+ m_voice[i].state.value = 0;
+ }
+}
+
+//TODO: improve using next-state logic from column 126 of patent 5809466
+int ics2115_voice::update_volume_envelope()
+{
+ int ret = 0;
+ if(vol_ctrl.bitflags.done || vol_ctrl.bitflags.stop)
+ return ret;
+
+ if(vol_ctrl.bitflags.invert) {
+ vol.acc -= vol.add;
+ vol.left = vol.acc - vol.start;
+ } else {
+ vol.acc += vol.add;
+ vol.left = vol.end - vol.acc;
+ }
+
+ if(vol.left > 0)
+ return ret;
+
+ if(vol_ctrl.bitflags.irq) {
+ vol_ctrl.bitflags.irq_pending = true;
+ ret = 1;
+ }
+
+ if(osc_conf.bitflags.eightbit)
+ return ret;
+
+ if(vol_ctrl.bitflags.loop) {
+ if(vol_ctrl.bitflags.loop_bidir)
+ vol_ctrl.bitflags.invert = !vol_ctrl.bitflags.invert;
+
+ if(vol_ctrl.bitflags.invert)
+ vol.acc = vol.end + vol.left;
+ else
+ vol.acc = vol.start - vol.left;
+ } else {
+ state.bitflags.on = false;
+ vol_ctrl.bitflags.done = true;
+ if(vol_ctrl.bitflags.invert)
+ vol.acc = vol.end;
+ else
+ vol.acc = vol.start;
+ }
+
+ return ret;
+}
+
+/*UINT32 ics2115_voice::next_address()
+{
+ //Patent 6,246,774 B1, Column 111, Row 25
+ //LEN BLEN DIR BC NextAddress
+ //x x 0 0 add+fc
+ //x x 1 0 add-fc
+ //0 x x 1 add
+ //1 0 0 1 start-(end-(add+fc))
+ //1 0 1 1 end+((add+fc)-start)
+ //1 1 0 1 end+(end-(add+fc))
+ //1 1 1 1 start-((add-fc)-start)
+
+}*/
+
+
+int ics2115_voice::update_oscillator()
+{
+ int ret = 0;
+ if(osc_conf.bitflags.stop)
+ return ret;
+ if(osc_conf.bitflags.invert) {
+ osc.acc -= osc.fc << 2;
+ osc.left = osc.acc - osc.start;
+ } else {
+ osc.acc += osc.fc << 2;
+ osc.left = osc.end - osc.acc;
+ }
+ // > instead of >= to stop crackling?
+ if(osc.left > 0)
+ return ret;
+ if(osc_conf.bitflags.irq) {
+ osc_conf.bitflags.irq_pending = true;
+ ret = 1;
+ }
+ if(osc_conf.bitflags.loop) {
+ if(osc_conf.bitflags.loop_bidir)
+ osc_conf.bitflags.invert = !osc_conf.bitflags.invert;
+ //else
+ // printf("click!\n");
+
+ if(osc_conf.bitflags.invert) {
+ osc.acc = osc.end + osc.left;
+ osc.left = osc.acc - osc.start;
+ }
+ else {
+ osc.acc = osc.start - osc.left;
+ osc.left = osc.end - osc.acc;
+ }
+ } else {
+ state.bitflags.on = false;
+ osc_conf.bitflags.stop = true;
+ if(!osc_conf.bitflags.invert)
+ osc.acc = osc.end;
+ else
+ osc.acc = osc.start;
+ }
+ return ret;
+}
+
+//TODO: proper interpolation for uLaw (fill_output doesn't use this) and 8-bit samples (looping)
+stream_sample_t ics2115_device::get_sample(ics2115_voice& voice)
+{
+ UINT32 curaddr = ((voice.osc.saddr << 20) & 0xffffff) | (voice.osc.acc >> 12);
+ UINT32 nextaddr;
+
+ if (voice.state.bitflags.on && voice.osc_conf.bitflags.loop && !voice.osc_conf.bitflags.loop_bidir &&
+ (voice.osc.left < (voice.osc.fc <<2))) {
+ //printf("C?[%x:%x]", voice.osc.left, voice.osc.acc);
+ nextaddr = ((voice.osc.saddr << 20) & 0xffffff) | (voice.osc.start >> 12);
+ }
+ else
+ nextaddr = curaddr + 2;
+
+
+ INT16 sample1, sample2;
+ if (voice.osc_conf.bitflags.eightbit) {
+ sample1 = ((INT8)m_rom[curaddr]) << 8;
+ sample2 = ((INT8)m_rom[curaddr + 1]) << 8;
+ }
+ else {
+ sample1 = m_rom[curaddr + 0] | (((INT8)m_rom[curaddr + 1]) << 8);
+ sample2 = m_rom[nextaddr+ 0] | (((INT8)m_rom[nextaddr+ 1]) << 8);
+ //sample2 = m_rom[curaddr + 2] | (((INT8)m_rom[curaddr + 3]) << 8);
+ }
+
+ //no need for interpolation since it's around 1 note a cycle?
+ //if(voice.osc.fc >> 10)
+ // return sample1;
+
+ //linear interpolation as in US patent 6,246,774 B1, column 2 row 59
+ //LEN=1, BLEN=0, DIR=0, start+end interpolation
+ INT32 sample, diff;
+ UINT16 fract;
+ diff = sample2 - sample1;
+ fract = (voice.osc.acc >> 3) & 0x1ff;
+
+ sample = (((INT32)sample1 << 9) + diff * fract) >> 9;
+ //sample = sample1;
+ return sample;
+}
+
+bool ics2115_voice::playing()
+{
+ return state.bitflags.on && !((vol_ctrl.bitflags.done || vol_ctrl.bitflags.stop) && osc_conf.bitflags.stop);
+}
+
+void ics2115_voice::update_ramp() {
+ //slow attack
+ if (state.bitflags.on && !osc_conf.bitflags.stop) {
+ if (state.bitflags.ramp < 0x40)
+ state.bitflags.ramp += 0x1;
+ else
+ state.bitflags.ramp = 0x40;
+ }
+ //slow release
+ else {
+ if (state.bitflags.ramp)
+ state.bitflags.ramp -= 0x1;
+ }
+}
+
+int ics2115_device::fill_output(ics2115_voice& voice, stream_sample_t *outputs[2], int samples)
+{
+ bool irq_invalid = false;
+ UINT16 fine = 1 << (3*(voice.vol.incr >> 6));
+ voice.vol.add = (voice.vol.incr & 0x3F)<< (10 - fine);
+
+ for (int i = 0; i < samples; i++) {
+ UINT32 volacc = (voice.vol.acc >> 10) & 0xffff;
+ UINT32 volume = (m_volume[volacc >> 4] * voice.state.bitflags.ramp) >> 6;
+ UINT16 vleft = volume; //* (255 - voice.vol.pan) / 0x80];
+ UINT16 vright = volume; //* (voice.vol.pan + 1) / 0x80];
+
+ //From GUS doc:
+ //In general, it is necessary to remember that all voices are being summed in to the
+ //final output, even if they are not running. This means that whatever data value
+ //that the voice is pointing at is contributing to the summation.
+ //(austere note: this will of course fix some of the glitches due to multiple transition)
+ stream_sample_t sample;
+ if(voice.osc_conf.bitflags.ulaw) {
+ UINT32 curaddr = ((voice.osc.saddr << 20) & 0xffffff) | (voice.osc.acc >> 12);
+ sample = m_ulaw[m_rom[curaddr]];
+ }
+ else
+ sample = get_sample(voice);
+
+ //15-bit volume + (5-bit worth of 32 channel sum) + 16-bit samples = 4-bit extra
+ if (!m_vmode || voice.playing()) {
+ //if (voice.playing()) {
+ outputs[0][i] += (sample * vleft) >> (5 + volume_bits - 16);
+ outputs[1][i] += (sample * vright) >> (5 + volume_bits - 16);
+ }
+
+ voice.update_ramp();
+ if (voice.playing()) {
+ if (voice.update_oscillator())
+ irq_invalid = true;
+ if (voice.update_volume_envelope())
+ irq_invalid = true;
+ }
+ }
+ return irq_invalid;
+}
+
+void ics2115_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ memset(outputs[0], 0, samples * sizeof(stream_sample_t));
+ memset(outputs[1], 0, samples * sizeof(stream_sample_t));
+
+ bool irq_invalid = false;
+ for(int osc = 0; osc <= m_active_osc; osc++) {
+ ics2115_voice& voice = m_voice[osc];
+
+#ifdef ICS2115_ISOLATE
+ if(osc != ICS2115_ISOLATE)
+ continue;
+#endif
+/*
+#ifdef ICS2115_DEBUG
+ UINT32 curaddr = ((voice.osc.saddr << 20) & 0xffffff) | (voice.osc.acc >> 12);
+ stream_sample_t sample;
+ if(voice.osc_conf.bitflags.ulaw)
+ sample = m_ulaw[m_rom[curaddr]];
+ else
+ sample = get_sample(voice);
+ printf("[%06x=%04x]", curaddr, (INT16)sample);
+#endif
+*/
+ if(fill_output(voice, outputs, samples))
+ irq_invalid = true;
+
+#ifdef ICS2115_DEBUG
+ if(voice.playing()) {
+ printf("%d", osc);
+ if (voice.osc_conf.bitflags.invert)
+ printf("+");
+ else if ((voice.osc.fc >> 1) > 0x1ff)
+ printf("*");
+ printf(" ");
+
+ /*int min = 0x7fffffff, max = 0x80000000;
+ double average = 0;
+ for (int i = 0; i < samples; i++) {
+ if (outputs[0][i] > max) max = outputs[0][i];
+ if (outputs[0][i] < min) min = outputs[0][i];
+ average += fabs(outputs[0][i]);
+ }
+ average /= samples;
+ average /= 1 << 16;
+ printf("<Mi:%d Mx:%d Av:%g>", min >> 16, max >> 16, average);*/
+ }
+#endif
+ }
+
+#ifdef ICS2115_DEBUG
+ printf("|");
+#endif
+
+ //rescale
+ for (int i = 0; i < samples; i++) {
+ outputs[0][i] >>= 16;
+ outputs[1][i] >>= 16;
+ }
+
+ if(irq_invalid)
+ recalc_irq();
+
+}
+
+//Helper Function (Reads off current register)
+UINT16 ics2115_device::reg_read() {
+ UINT16 ret;
+ ics2115_voice& voice = m_voice[m_osc_select];
+
+ switch(m_reg_select) {
+ case 0x00: // [osc] Oscillator Configuration
+ ret = voice.osc_conf.value;
+ ret <<= 8;
+ break;
+
+ case 0x01: // [osc] Wavesample frequency
+ // freq = fc*33075/1024 in 32 voices mode, fc*44100/1024 in 24 voices mode
+ //ret = v->Osc.FC;
+ ret = voice.osc.fc;
+ break;
+
+ case 0x02: // [osc] Wavesample loop start high
+ //TODO: are these returns valid? might be 0x00ff for this one...
+ ret = (voice.osc.start >> 16) & 0xffff;
+ break;
+
+ case 0x03: // [osc] Wavesample loop start low
+ ret = (voice.osc.start >> 0) & 0xff00;
+ break;
+
+ case 0x04: // [osc] Wavesample loop end high
+ ret = (voice.osc.end >> 16) & 0xffff;
+ break;
+
+ case 0x05: // [osc] Wavesample loop end low
+ ret = (voice.osc.end >> 0) & 0xff00;
+ break;
+
+ case 0x06: // [osc] Volume Increment
+ ret = voice.vol.incr;
+ break;
+
+ case 0x07: // [osc] Volume Start
+ ret = voice.vol.start >> (10+8);
+ break;
+
+ case 0x08: // [osc] Volume End
+ ret = voice.vol.end >> (10+8);
+ break;
+
+ case 0x09: // [osc] Volume accumulator
+ //ret = v->Vol.Acc;
+ ret = voice.vol.acc >> (10);
+ break;
+
+ case 0x0A: // [osc] Wavesample address
+ ret = (voice.osc.acc >> 16) & 0xffff;
+ break;
+
+ case 0x0B: // [osc] Wavesample address
+ ret = (voice.osc.acc >> 0) & 0xfff8;
+ break;
+
+
+ case 0x0C: // [osc] Pan
+ ret = voice.vol.pan << 8;
+ break;
+
+ /* DDP3 code (trap15's reversal) */
+ /* 0xA13's work:
+ res = read() & 0xC3;
+ if(!(res & 2)) res |= 1;
+ e = d = res;
+ */
+ /* 0xA4F's work:
+ while(!(read() & 1))
+ */
+ case 0x0D: // [osc] Volume Envelope Control
+ //ret = v->Vol.Ctl | ((v->state & FLAG_STATE_VOLIRQ) ? 0x81 : 1);
+ // may expect |8 on voice irq with &40 == 0
+ // may expect |8 on reg 0 on voice irq with &80 == 0
+ // ret = 0xFF;
+ if (!m_vmode)
+ ret = voice.vol_ctrl.bitflags.irq ? 0x81 : 0x01;
+ else
+ ret = 0x01;
+ //ret = voice.vol_ctrl.bitflags.value | 0x1;
+ ret <<= 8;
+ break;
+
+ case 0x0E: // Active Voices
+ ret = m_active_osc;
+ break;
+
+ case 0x0F:{// [osc] Interrupt source/oscillator
+ ret = 0xff;
+ for (int i = 0; i <= m_active_osc; i++) {
+ ics2115_voice& v = m_voice[i];
+ if (v.osc_conf.bitflags.irq_pending || v.vol_ctrl.bitflags.irq_pending) {
+ ret = i | 0xe0;
+ ret &= v.vol_ctrl.bitflags.irq_pending ? (~0x40) : 0xff;
+ ret &= v.osc_conf.bitflags.irq_pending ? (~0x80) : 0xff;
+ recalc_irq();
+ if (v.osc_conf.bitflags.irq_pending) {
+ v.osc_conf.bitflags.irq_pending = 0;
+ ret &= ~0x80;
+ }
+ if (v.vol_ctrl.bitflags.irq_pending) {
+ v.vol_ctrl.bitflags.irq_pending = 0;
+ ret &= ~0x40;
+ }
+ break;
+ }
+ }
+ ret <<= 8;
+ break;}
+
+ case 0x10: // [osc] Oscillator Control
+ ret = voice.osc.ctl << 8;
+ break;
+
+ case 0x11: // [osc] Wavesample static address 27-20
+ ret = voice.osc.saddr << 8;
+ break;
+
+ case 0x40: // Timer 0 clear irq
+ case 0x41: // Timer 1 clear irq
+ //TODO: examine this suspect code
+ ret = m_timer[m_reg_select & 0x1].preset;
+ m_irq_pending &= ~(1 << (m_reg_select & 0x1));
+ recalc_irq();
+ break;
+
+ case 0x43: // Timer status
+ ret = m_irq_pending & 3;
+ break;
+
+ case 0x4A: // IRQ Pending
+ ret = m_irq_pending;
+ break;
+
+ case 0x4B: // Address of Interrupting Oscillator
+ ret = 0x80;
+ break;
+
+ case 0x4C: // Chip Revision
+ ret = revision;
+ break;
+
+ default:
+#ifdef ICS2115_DEBUG
+ printf("ICS2115: Unhandled read %x\n", m_reg_select);
+#endif
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+void ics2115_device::reg_write(UINT8 data, bool msb) {
+ ics2115_voice& voice = m_voice[m_osc_select];
+
+ switch(m_reg_select) {
+ case 0x00: // [osc] Oscillator Configuration
+ if(msb) {
+ voice.osc_conf.value &= 0x80;
+ voice.osc_conf.value |= data & 0x7f;
+ }
+ break;
+
+ case 0x01: // [osc] Wavesample frequency
+ // freq = fc*33075/1024 in 32 voices mode, fc*44100/1024 in 24 voices mode
+ if(msb)
+ voice.osc.fc = (voice.osc.fc & 0x00ff) | (data << 8);
+ else
+ //last bit not used!
+ voice.osc.fc = (voice.osc.fc & 0xff00) | (data & 0xfe);
+ break;
+
+ case 0x02: // [osc] Wavesample loop start high
+ if(msb)
+ voice.osc.start = (voice.osc.start & 0x00ffffff) | (data << 24);
+ else
+ voice.osc.start = (voice.osc.start & 0xff00ffff) | (data << 16);
+ break;
+
+ case 0x03: // [osc] Wavesample loop start low
+ if(msb)
+ voice.osc.start = (voice.osc.start & 0xffff00ff) | (data << 8);
+ // This is unused?
+ //else
+ //voice.osc.start = (voice.osc.start & 0xffffff00) | (data & 0);
+ break;
+
+ case 0x04: // [osc] Wavesample loop end high
+ if(msb)
+ voice.osc.end = (voice.osc.end & 0x00ffffff) | (data << 24);
+ else
+ voice.osc.end = (voice.osc.end & 0xff00ffff) | (data << 16);
+ break;
+
+ case 0x05: // [osc] Wavesample loop end low
+ if(msb)
+ voice.osc.end = (voice.osc.end & 0xffff00ff) | (data << 8);
+ // lsb is unused?
+ break;
+
+ case 0x06: // [osc] Volume Increment
+ if(msb)
+ voice.vol.incr = data;
+ break;
+
+ case 0x07: // [osc] Volume Start
+ if (!msb)
+ voice.vol.start = data << (10+8);
+ break;
+
+ case 0x08: // [osc] Volume End
+ if (!msb)
+ voice.vol.end = data << (10+8);
+ break;
+
+ case 0x09: // [osc] Volume accumulator
+ if(msb)
+ voice.vol.regacc = (voice.vol.regacc & 0x00ff) | (data << 8);
+ else
+ voice.vol.regacc = (voice.vol.regacc & 0xff00) | data;
+ voice.vol.acc = voice.vol.regacc << 10;
+ break;
+
+ case 0x0A: // [osc] Wavesample address high
+#ifdef ICS2115_DEBUG
+#ifdef ICS2115_ISOLATE
+ if(m_osc_select == ICS2115_ISOLATE)
+#endif
+ printf("<%d:oa:H[%d]=%x>", m_osc_select, msb, data);
+#endif
+ if(msb)
+ voice.osc.acc = (voice.osc.acc & 0x00ffffff) | (data << 24);
+ else
+ voice.osc.acc = (voice.osc.acc & 0xff00ffff) | (data << 16);
+ break;
+
+ case 0x0B: // [osc] Wavesample address low
+#ifdef ICS2115_DEBUG
+#ifdef ICS2115_ISOLATE
+ if(m_osc_select == ICS2115_ISOLATE)
+#endif
+ printf("<%d:oa:L[%d]=%x>", m_osc_select, msb, data);
+#endif
+ if(msb)
+ voice.osc.acc = (voice.osc.acc & 0xffff00ff) | (data << 8);
+ else
+ voice.osc.acc = (voice.osc.acc & 0xffffff00) | (data & 0xF8);
+ break;
+
+ case 0x0C: // [osc] Pan
+ if(msb)
+ voice.vol.pan = data;
+ break;
+
+ case 0x0D: // [osc] Volume Envelope Control
+ if(msb) {
+ voice.vol_ctrl.value &= 0x80;
+ voice.vol_ctrl.value |= data & 0x7F;
+ }
+ break;
+
+ case 0x0E: // Active Voices
+ //Does this value get added to 1? Not sure. Could trace for writes of 32.
+ if(msb) {
+ m_active_osc = data & 0x1F; // & 0x1F ? (Guessing)
+ }
+ break;
+ //2X8 ?
+ case 0x10: // [osc] Oscillator Control
+ //Could this be 2X9?
+ //[7 R | 6 M2 | 5 M1 | 4-2 Reserve | 1 - Timer 2 Strt | 0 - Timer 1 Strt]
+
+ if (msb) {
+ voice.osc.ctl = data;
+ if (!data)
+ keyon();
+ //guessing here
+ else if(data == 0xf) {
+#ifdef ICS2115_DEBUG
+#ifdef ICS2115_ISOLATE
+ if (m_osc_select == ICS2115_ISOLATE)
+#endif
+ if (!voice.osc_conf.bitflags.stop || !voice.vol_ctrl.bitflags.stop)
+ printf("[%02d STOP]\n", m_osc_select);
+#endif
+ if (!m_vmode) {
+ voice.osc_conf.bitflags.stop = true;
+ voice.vol_ctrl.bitflags.stop = true;
+ //try to key it off as well!
+ voice.state.bitflags.on = false;
+ }
+ }
+#ifdef ICS2115_DEBUG
+ else
+ printf("ICS2115: Unhandled* data write %d onto 0x10.\n", data);
+#endif
+ }
+ break;
+
+ case 0x11: // [osc] Wavesample static address 27-20
+ if(msb)
+ //v->Osc.SAddr = data;
+ voice.osc.saddr = data;
+ break;
+ case 0x12:
+ //Could be per voice! -- investigate.
+ if (msb)
+ m_vmode = data;
+ break;
+ case 0x40: // Timer 1 Preset
+ case 0x41: // Timer 2 Preset
+ if(!msb) {
+ m_timer[m_reg_select & 0x1].preset = data;
+ recalc_timer(m_reg_select & 0x1);
+ }
+ break;
+
+ case 0x42: // Timer 1 Prescale
+ case 0x43: // Timer 2 Prescale
+ if(!msb) {
+ m_timer[m_reg_select & 0x1].scale = data;
+ recalc_timer(m_reg_select & 0x1);
+ }
+ break;
+
+ case 0x4A: // IRQ Enable
+ if(!msb) {
+ m_irq_enabled = data;
+ recalc_irq();
+ }
+ break;
+
+ case 0x4F: // Oscillator Address being Programmed
+ if(!msb) {
+ m_osc_select = data % (1+m_active_osc);
+ }
+ break;
+ default:
+#ifdef ICS2115_DEBUG
+ printf("ICS2115: Unhandled write %x onto %x(%d) [voice = %d]\n", data, m_reg_select, msb, m_osc_select);
+#endif
+ break;
+ }
+}
+
+READ8_MEMBER(ics2115_device::read)
+{
+ UINT8 ret = 0;
+
+ switch(offset) {
+ case 0:
+ //TODO: check this suspect code
+ if (m_irq_on) {
+ ret |= 0x80;
+ if (m_irq_enabled && (m_irq_pending & 3))
+ ret |= 1;
+ for (int i = 0; i <= m_active_osc; i++) {
+ if (//m_voice[i].vol_ctrl.bitflags.irq_pending ||
+ m_voice[i].osc_conf.bitflags.irq_pending) {
+ ret |= 2;
+ break;
+ }
+ }
+ }
+
+ break;
+ case 1:
+ ret = m_reg_select;
+ break;
+ case 2:
+ ret = (UINT8)(reg_read());
+ break;
+ case 3:
+ ret = reg_read() >> 8;
+ break;
+ default:
+#ifdef ICS2115_DEBUG
+ printf("ICS2115: Unhandled memory read at %x\n", offset);
+#endif
+ break;
+ }
+ return ret;
+}
+
+WRITE8_MEMBER(ics2115_device::write)
+{
+ switch(offset) {
+ case 1:
+ m_reg_select = data;
+ break;
+ case 2:
+ reg_write(data,0);
+ break;
+ case 3:
+ reg_write(data,1);
+ break;
+ default:
+#ifdef ICS2115_DEBUG
+ printf("ICS2115: Unhandled memory write %02x to %x\n", data, offset);
+#endif
+ break;
+ }
+}
+
+void ics2115_device::keyon()
+{
+#ifdef ICS2115_ISOLATE
+ if (m_osc_select != ICS2115_ISOLATE)
+ return;
+#endif
+ //set initial condition (may need to invert?) -- does NOT work since these are set to zero even
+ m_voice[m_osc_select].state.bitflags.on = true;
+ //no ramp up...
+ m_voice[m_osc_select].state.bitflags.ramp = 0x40;
+
+#ifdef ICS2115_DEBUG
+ printf("[%02d vs:%04x ve:%04x va:%04x vi:%02x vc:%02x os:%06x oe:%06x oa:%06x of:%04x SA:%02x oc:%02x][%04x]\n", m_osc_select,
+ m_voice[m_osc_select].vol.start >> 10,
+ m_voice[m_osc_select].vol.end >> 10,
+ m_voice[m_osc_select].vol.acc >> 10,
+ m_voice[m_osc_select].vol.incr,
+ m_voice[m_osc_select].vol_ctrl.value,
+ m_voice[m_osc_select].osc.start >> 12,
+ m_voice[m_osc_select].osc.end >> 12,
+ m_voice[m_osc_select].osc.acc >> 12,
+ m_voice[m_osc_select].osc.fc,
+ m_voice[m_osc_select].osc.saddr,
+ m_voice[m_osc_select].osc_conf.value,
+ m_volume[(m_voice[m_osc_select].vol.acc >> 14)]
+ );
+#endif
+ //testing memory corruption issue with mame stream
+ //printf("m_volume[0x%x]=0x%x\n", mastervolume, m_volume[mastervolume]);
+}
+
+void ics2115_device::recalc_irq()
+{
+ //Suspect
+ bool irq = (m_irq_pending & m_irq_enabled);
+ for(int i = 0; (!irq) && (i < 32); i++)
+ irq |= m_voice[i].vol_ctrl.bitflags.irq_pending && m_voice[i].osc_conf.bitflags.irq_pending;
+ m_irq_on = irq;
+ if(!m_irq_cb.isnull())
+ m_irq_cb(irq ? ASSERT_LINE : CLEAR_LINE);
+}
+
+TIMER_CALLBACK_MEMBER( ics2115_device::timer_cb_0 )
+{
+ m_irq_pending |= 1 << 0;
+ recalc_irq();
+}
+
+TIMER_CALLBACK_MEMBER( ics2115_device::timer_cb_1 )
+{
+ m_irq_pending |= 1 << 1;
+ recalc_irq();
+}
+
+void ics2115_device::recalc_timer(int timer)
+{
+ //Old regression-based formula (minus constant)
+ //UINT64 period = m_timer[timer].preset * (m_timer[timer].scale << 16) / 60;
+
+ //New formula based on O.Galibert's reverse engineering of ICS2115 card firmware
+ UINT64 period = ((m_timer[timer].scale & 0x1f) + 1) * (m_timer[timer].preset + 1);
+ period = (period << (4 + (m_timer[timer].scale >> 5)))*78125/2646;
+
+ if(m_timer[timer].period != period) {
+ m_timer[timer].period = period;
+ // Adjust the timer lengths
+ if(period) // Reset the length
+ m_timer[timer].timer->adjust(attotime::from_nsec(period), 0, attotime::from_nsec(period));
+ else // Kill the timer if length == 0
+ m_timer[timer].timer->adjust(attotime::never);
+ }
+}
diff --git a/src/devices/sound/ics2115.h b/src/devices/sound/ics2115.h
new file mode 100644
index 00000000000..fbfeb64e24a
--- /dev/null
+++ b/src/devices/sound/ics2115.h
@@ -0,0 +1,158 @@
+// license:???
+// copyright-holders:Alex Marshall,nimitz,austere
+#pragma once
+
+#ifndef __ICS2115_H__
+#define __ICS2115_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_ICS2115_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, ICS2115, _clock)
+
+#define MCFG_ICS2115_IRQ_CB(_devcb) \
+ devcb = &ics2115_device::set_irq_callback(*device, DEVCB_##_devcb);
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+struct ics2115_voice {
+ struct {
+ INT32 left;
+ UINT32 acc, start, end;
+ UINT16 fc;
+ UINT8 ctl, saddr;
+ } osc;
+
+ struct {
+ INT32 left;
+ UINT32 add;
+ UINT32 start, end;
+ UINT32 acc;
+ UINT16 regacc;
+ UINT8 incr;
+ UINT8 pan, mode;
+ } vol;
+
+ union {
+ struct {
+ UINT8 ulaw : 1;
+ UINT8 stop : 1; //stops wave + vol envelope
+ UINT8 eightbit : 1;
+ UINT8 loop : 1;
+ UINT8 loop_bidir : 1;
+ UINT8 irq : 1;
+ UINT8 invert : 1;
+ UINT8 irq_pending: 1;
+ //IRQ on variable?
+ } bitflags;
+ UINT8 value;
+ } osc_conf;
+
+ union {
+ struct {
+ UINT8 done : 1; //indicates ramp has stopped
+ UINT8 stop : 1; //stops the ramp
+ UINT8 rollover : 1; //rollover (TODO)
+ UINT8 loop : 1;
+ UINT8 loop_bidir : 1;
+ UINT8 irq : 1; //enable IRQ generation
+ UINT8 invert : 1; //invert direction
+ UINT8 irq_pending: 1; //(read only) IRQ pending
+ //noenvelope == (done | disable)
+ } bitflags;
+ UINT8 value;
+ } vol_ctrl;
+
+ //Possibly redundant state. => improvements of wavetable logic
+ //may lead to its elimination.
+ union {
+ struct {
+ UINT8 on : 1;
+ UINT8 ramp : 7; // 100 0000 = 0x40 maximum
+ } bitflags;
+ UINT8 value;
+ } state;
+
+ bool playing();
+ int update_volume_envelope();
+ int update_oscillator();
+ void update_ramp();
+};
+
+// ======================> ics2115_device
+
+class ics2115_device : public device_t, public device_sound_interface
+{
+public:
+ // construction/destruction
+ ics2115_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _Object> static devcb_base &set_irq_callback(device_t &device, _Object object) { return downcast<ics2115_device &>(device).m_irq_cb.set_callback(object); }
+
+ DECLARE_READ8_MEMBER(read);
+ DECLARE_WRITE8_MEMBER(write);
+ //UINT8 read(offs_t offset);
+ //void write(offs_t offset, UINT8 data);
+ TIMER_CALLBACK_MEMBER(timer_cb_0);
+ TIMER_CALLBACK_MEMBER(timer_cb_1);
+
+ sound_stream *m_stream;
+
+ static const UINT16 revision = 0x1;
+
+protected:
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // internal callbacks
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ // internal state
+ required_region_ptr<UINT8> m_rom;
+ devcb_write_line m_irq_cb;
+
+ INT16 m_ulaw[256];
+ UINT16 m_volume[4096];
+ static const int volume_bits = 15;
+
+ ics2115_voice m_voice[32];
+ struct {
+ UINT8 scale, preset;
+ emu_timer *timer;
+ UINT64 period; /* in nsec */
+ } m_timer[2];
+
+ UINT8 m_active_osc;
+ UINT8 m_osc_select;
+ UINT8 m_reg_select;
+ UINT8 m_irq_enabled, m_irq_pending;
+ bool m_irq_on;
+
+ //Unknown variable, seems to be effected by 0x12. Further investigation
+ //Required.
+ UINT8 m_vmode;
+
+ //internal register helper functions
+ UINT16 reg_read();
+ void reg_write(UINT8 data, bool msb);
+ void recalc_timer(int timer);
+ void keyon();
+ void recalc_irq();
+
+ //stream helper functions
+ int fill_output(ics2115_voice& voice, stream_sample_t *outputs[2], int samples);
+ stream_sample_t get_sample(ics2115_voice& voice);
+};
+
+
+// device type definition
+extern const device_type ICS2115;
+
+#endif /* __ICS2115_H__ */
diff --git a/src/devices/sound/iremga20.c b/src/devices/sound/iremga20.c
new file mode 100644
index 00000000000..925814945fe
--- /dev/null
+++ b/src/devices/sound/iremga20.c
@@ -0,0 +1,268 @@
+// license:BSD-3-Clause
+// copyright-holders:Acho A. Tang,R. Belmont
+/*********************************************************
+
+Irem GA20 PCM Sound Chip
+
+It's not currently known whether this chip is stereo.
+
+
+Revisions:
+
+04-15-2002 Acho A. Tang
+- rewrote channel mixing
+- added prelimenary volume and sample rate emulation
+
+05-30-2002 Acho A. Tang
+- applied hyperbolic gain control to volume and used
+ a musical-note style progression in sample rate
+ calculation(still very inaccurate)
+
+02-18-2004 R. Belmont
+- sample rate calculation reverse-engineered.
+ Thanks to Fujix, Yasuhiro Ogawa, the Guru, and Tormod
+ for real PCB samples that made this possible.
+
+02-03-2007 R. Belmont
+- Cleaned up faux x86 assembly.
+
+*********************************************************/
+
+#include "emu.h"
+#include "iremga20.h"
+
+#define MAX_VOL 256
+
+
+// device type definition
+const device_type IREMGA20 = &device_creator<iremga20_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// iremga20_device - constructor
+//-------------------------------------------------
+
+iremga20_device::iremga20_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, IREMGA20, "Irem GA20", tag, owner, clock, "iremga20", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_rom(NULL),
+ m_rom_size(0),
+ m_stream(NULL)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void iremga20_device::device_start()
+{
+ int i;
+
+ /* Initialize our chip structure */
+ m_rom = m_region->base();
+ m_rom_size = m_region->bytes();
+
+ iremga20_reset();
+
+ for ( i = 0; i < 0x40; i++ )
+ m_regs[i] = 0;
+
+ m_stream = stream_alloc(0, 2, clock()/4);
+
+ save_item(NAME(m_regs));
+ for (i = 0; i < 4; i++)
+ {
+ save_item(NAME(m_channel[i].rate), i);
+ save_item(NAME(m_channel[i].size), i);
+ save_item(NAME(m_channel[i].start), i);
+ save_item(NAME(m_channel[i].pos), i);
+ save_item(NAME(m_channel[i].frac), i);
+ save_item(NAME(m_channel[i].end), i);
+ save_item(NAME(m_channel[i].volume), i);
+ save_item(NAME(m_channel[i].pan), i);
+ save_item(NAME(m_channel[i].effect), i);
+ save_item(NAME(m_channel[i].play), i);
+ }
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void iremga20_device::device_reset()
+{
+ iremga20_reset();
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void iremga20_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ UINT32 rate[4], pos[4], frac[4], end[4], vol[4], play[4];
+ UINT8 *pSamples;
+ stream_sample_t *outL, *outR;
+ int i, sampleout;
+
+ /* precache some values */
+ for (i=0; i < 4; i++)
+ {
+ rate[i] = m_channel[i].rate;
+ pos[i] = m_channel[i].pos;
+ frac[i] = m_channel[i].frac;
+ end[i] = m_channel[i].end - 0x20;
+ vol[i] = m_channel[i].volume;
+ play[i] = m_channel[i].play;
+ }
+
+ i = samples;
+ pSamples = m_rom;
+ outL = outputs[0];
+ outR = outputs[1];
+
+ for (i = 0; i < samples; i++)
+ {
+ sampleout = 0;
+
+ // update the 4 channels inline
+ if (play[0])
+ {
+ sampleout += (pSamples[pos[0]] - 0x80) * vol[0];
+ frac[0] += rate[0];
+ pos[0] += frac[0] >> 24;
+ frac[0] &= 0xffffff;
+ play[0] = (pos[0] < end[0]);
+ }
+ if (play[1])
+ {
+ sampleout += (pSamples[pos[1]] - 0x80) * vol[1];
+ frac[1] += rate[1];
+ pos[1] += frac[1] >> 24;
+ frac[1] &= 0xffffff;
+ play[1] = (pos[1] < end[1]);
+ }
+ if (play[2])
+ {
+ sampleout += (pSamples[pos[2]] - 0x80) * vol[2];
+ frac[2] += rate[2];
+ pos[2] += frac[2] >> 24;
+ frac[2] &= 0xffffff;
+ play[2] = (pos[2] < end[2]);
+ }
+ if (play[3])
+ {
+ sampleout += (pSamples[pos[3]] - 0x80) * vol[3];
+ frac[3] += rate[3];
+ pos[3] += frac[3] >> 24;
+ frac[3] &= 0xffffff;
+ play[3] = (pos[3] < end[3]);
+ }
+
+ sampleout >>= 2;
+ outL[i] = sampleout;
+ outR[i] = sampleout;
+ }
+
+ /* update the regs now */
+ for (i=0; i < 4; i++)
+ {
+ m_channel[i].pos = pos[i];
+ m_channel[i].frac = frac[i];
+ m_channel[i].play = play[i];
+ }
+}
+
+WRITE8_MEMBER( iremga20_device::irem_ga20_w )
+{
+ int channel;
+
+ //logerror("GA20: Offset %02x, data %04x\n",offset,data);
+
+ m_stream->update();
+
+ channel = offset >> 3;
+
+ m_regs[offset] = data;
+
+ switch (offset & 0x7)
+ {
+ case 0: /* start address low */
+ m_channel[channel].start = ((m_channel[channel].start)&0xff000) | (data<<4);
+ break;
+
+ case 1: /* start address high */
+ m_channel[channel].start = ((m_channel[channel].start)&0x00ff0) | (data<<12);
+ break;
+
+ case 2: /* end address low */
+ m_channel[channel].end = ((m_channel[channel].end)&0xff000) | (data<<4);
+ break;
+
+ case 3: /* end address high */
+ m_channel[channel].end = ((m_channel[channel].end)&0x00ff0) | (data<<12);
+ break;
+
+ case 4:
+ m_channel[channel].rate = 0x1000000 / (256 - data);
+ break;
+
+ case 5: //AT: gain control
+ m_channel[channel].volume = (data * MAX_VOL) / (data + 10);
+ break;
+
+ case 6: //AT: this is always written 2(enabling both channels?)
+ m_channel[channel].play = data;
+ m_channel[channel].pos = m_channel[channel].start;
+ m_channel[channel].frac = 0;
+ break;
+ }
+}
+
+READ8_MEMBER( iremga20_device::irem_ga20_r )
+{
+ int channel;
+
+ m_stream->update();
+
+ channel = offset >> 3;
+
+ switch (offset & 0x7)
+ {
+ case 7: // voice status. bit 0 is 1 if active. (routine around 0xccc in rtypeleo)
+ return m_channel[channel].play ? 1 : 0;
+
+ default:
+ logerror("GA20: read unk. register %d, channel %d\n", offset & 0xf, channel);
+ break;
+ }
+
+ return 0;
+}
+
+
+void iremga20_device::iremga20_reset()
+{
+ int i;
+
+ for( i = 0; i < 4; i++ ) {
+ m_channel[i].rate = 0;
+ m_channel[i].size = 0;
+ m_channel[i].start = 0;
+ m_channel[i].pos = 0;
+ m_channel[i].frac = 0;
+ m_channel[i].end = 0;
+ m_channel[i].volume = 0;
+ m_channel[i].pan = 0;
+ m_channel[i].effect = 0;
+ m_channel[i].play = 0;
+ }
+}
diff --git a/src/devices/sound/iremga20.h b/src/devices/sound/iremga20.h
new file mode 100644
index 00000000000..13bc8c0ef64
--- /dev/null
+++ b/src/devices/sound/iremga20.h
@@ -0,0 +1,78 @@
+// license:BSD-3-Clause
+// copyright-holders:Acho A. Tang,R. Belmont
+/*********************************************************
+
+ Irem GA20 PCM Sound Chip
+
+*********************************************************/
+#pragma once
+
+#ifndef __IREMGA20_H__
+#define __IREMGA20_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_IREMGA20_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, IREMGA20, _clock)
+#define MCFG_IREMGA20_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, IREMGA20, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+struct IremGA20_channel_def
+{
+ UINT32 rate;
+ UINT32 size;
+ UINT32 start;
+ UINT32 pos;
+ UINT32 frac;
+ UINT32 end;
+ UINT32 volume;
+ UINT32 pan;
+ UINT32 effect;
+ UINT32 play;
+};
+
+
+// ======================> iremga20_device
+
+class iremga20_device : public device_t,
+ public device_sound_interface
+{
+public:
+ iremga20_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~iremga20_device() { }
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+public:
+ DECLARE_WRITE8_MEMBER( irem_ga20_w );
+ DECLARE_READ8_MEMBER( irem_ga20_r );
+
+private:
+ void iremga20_reset();
+
+private:
+ UINT8 *m_rom;
+ INT32 m_rom_size;
+ sound_stream *m_stream;
+ UINT16 m_regs[0x40];
+ IremGA20_channel_def m_channel[4];
+};
+
+extern const device_type IREMGA20;
+
+
+#endif /* __IREMGA20_H__ */
diff --git a/src/devices/sound/k005289.c b/src/devices/sound/k005289.c
new file mode 100644
index 00000000000..746cf04413f
--- /dev/null
+++ b/src/devices/sound/k005289.c
@@ -0,0 +1,242 @@
+// license:BSD-3-Clause
+// copyright-holders:Bryan McPhail
+/***************************************************************************
+
+ Konami 005289 - SCC sound as used in Bubblesystem
+
+ This file is pieced together by Bryan McPhail from a combination of
+ Namco Sound, Amuse by Cab, Nemesis schematics and whoever first
+ figured out SCC!
+
+ The 005289 is a 2 channel sound generator. Each channel gets its
+ waveform from a prom (4 bits wide).
+
+ (From Nemesis schematics)
+
+ Address lines A0-A4 of the prom run to the 005289, giving 32 bytes
+ per waveform. Address lines A5-A7 of the prom run to PA5-PA7 of
+ the AY8910 control port A, giving 8 different waveforms. PA0-PA3
+ of the AY8910 control volume.
+
+ The second channel is the same as above except port B is used.
+
+ The 005289 has 12 address inputs and 4 control inputs: LD1, LD2, TG1, TG2.
+ It has no data bus, so data values written don't matter.
+ When LD1 or LD2 is asserted, the 12 bit value on the address bus is
+ latched. Each of the two channels has its own latch.
+ When TG1 or TG2 is asserted, the frequency of the respective channel is
+ set to the previously latched value.
+
+ The 005289 itself is nothing but an address generator. Digital to analog
+ conversion, volume control and mixing of the channels is all done
+ externally via resistor networks and 4066 switches and is only implemented
+ here for convenience.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "k005289.h"
+
+// is this an actual hardware limit? or just an arbitrary divider
+// to bring the output frequency down to a reasonable value for MAME?
+#define CLOCK_DIVIDER 32
+
+// device type definition
+const device_type K005289 = &device_creator<k005289_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// k005289_device - constructor
+//-------------------------------------------------
+
+k005289_device::k005289_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, K005289, "K005289 SCC", tag, owner, clock, "k005289", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_sound_prom(NULL),
+ m_stream(NULL),
+ m_rate(0),
+ m_mixer_table(NULL),
+ m_mixer_lookup(NULL),
+ m_mixer_buffer(NULL)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void k005289_device::device_start()
+{
+ /* get stream channels */
+ m_rate = clock() / CLOCK_DIVIDER;
+ m_stream = stream_alloc(0, 1, m_rate);
+
+ /* allocate a pair of buffers to mix into - 1 second's worth should be more than enough */
+ m_mixer_buffer = auto_alloc_array(machine(), short, 2 * m_rate);
+
+ /* build the mixer table */
+ make_mixer_table(2);
+
+ m_sound_prom = m_region->base();
+
+ /* reset all the voices */
+ for (int i = 0; i < 2; i++)
+ {
+ m_counter[i] = 0;
+ m_frequency[i] = 0;
+ m_freq_latch[i] = 0;
+ m_waveform[i] = i * 0x100;
+ m_volume[i] = 0;
+ }
+
+ save_item(NAME(m_counter));
+ save_item(NAME(m_frequency));
+ save_item(NAME(m_freq_latch));
+ save_item(NAME(m_waveform));
+ save_item(NAME(m_volume));
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void k005289_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *buffer = outputs[0];
+ short *mix;
+ int i,v,f;
+
+ /* zap the contents of the mixer buffer */
+ memset(m_mixer_buffer, 0, samples * sizeof(INT16));
+
+ v=m_volume[0];
+ f=m_frequency[0];
+ if (v && f)
+ {
+ const unsigned char *w = m_sound_prom + m_waveform[0];
+ int c = m_counter[0];
+
+ mix = m_mixer_buffer;
+
+ /* add our contribution */
+ for (i = 0; i < samples; i++)
+ {
+ int offs;
+
+ c += CLOCK_DIVIDER;
+ offs = (c / f) & 0x1f;
+ *mix++ += ((w[offs] & 0x0f) - 8) * v;
+ }
+
+ /* update the counter for this voice */
+ m_counter[0] = c % (f * 0x20);
+ }
+
+ v=m_volume[1];
+ f=m_frequency[1];
+ if (v && f)
+ {
+ const unsigned char *w = m_sound_prom + m_waveform[1];
+ int c = m_counter[1];
+
+ mix = m_mixer_buffer;
+
+ /* add our contribution */
+ for (i = 0; i < samples; i++)
+ {
+ int offs;
+
+ c += CLOCK_DIVIDER;
+ offs = (c / f) & 0x1f;
+ *mix++ += ((w[offs] & 0x0f) - 8) * v;
+ }
+
+ /* update the counter for this voice */
+ m_counter[1] = c % (f * 0x20);
+ }
+
+ /* mix it down */
+ mix = m_mixer_buffer;
+ for (i = 0; i < samples; i++)
+ *buffer++ = m_mixer_lookup[*mix++];
+}
+
+
+
+
+/********************************************************************************/
+
+/* build a table to divide by the number of voices */
+void k005289_device::make_mixer_table(int voices)
+{
+ int count = voices * 128;
+ int i;
+ int gain = 16;
+
+ /* allocate memory */
+ m_mixer_table = auto_alloc_array(machine(), INT16, 256 * voices);
+
+ /* find the middle of the table */
+ m_mixer_lookup = m_mixer_table + (128 * voices);
+
+ /* fill in the table - 16 bit case */
+ for (i = 0; i < count; i++)
+ {
+ int val = i * gain * 16 / voices;
+ if (val > 32767) val = 32767;
+ m_mixer_lookup[ i] = val;
+ m_mixer_lookup[-i] = -val;
+ }
+}
+
+
+WRITE8_MEMBER( k005289_device::k005289_control_A_w )
+{
+ m_stream->update();
+
+ m_volume[0] = data & 0xf;
+ m_waveform[0] = data & 0xe0;
+}
+
+
+WRITE8_MEMBER( k005289_device::k005289_control_B_w )
+{
+ m_stream->update();
+
+ m_volume[1] = data & 0xf;
+ m_waveform[1] = (data & 0xe0) + 0x100;
+}
+
+
+WRITE8_MEMBER( k005289_device::ld1_w )
+{
+ m_freq_latch[0] = 0xfff - offset;
+}
+
+
+WRITE8_MEMBER( k005289_device::ld2_w )
+{
+ m_freq_latch[1] = 0xfff - offset;
+}
+
+
+WRITE8_MEMBER( k005289_device::tg1_w )
+{
+ m_stream->update();
+
+ m_frequency[0] = m_freq_latch[0];
+}
+
+
+WRITE8_MEMBER( k005289_device::tg2_w )
+{
+ m_stream->update();
+
+ m_frequency[1] = m_freq_latch[1];
+}
diff --git a/src/devices/sound/k005289.h b/src/devices/sound/k005289.h
new file mode 100644
index 00000000000..8bee1534390
--- /dev/null
+++ b/src/devices/sound/k005289.h
@@ -0,0 +1,65 @@
+// license:BSD-3-Clause
+// copyright-holders:Bryan McPhail
+#pragma once
+
+#ifndef __K005289_H__
+#define __K005289_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_K005289_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, K005289, _clock)
+#define MCFG_K005289_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, K005289, _clock)
+
+
+// ======================> k005289_device
+
+class k005289_device : public device_t,
+ public device_sound_interface
+{
+public:
+ k005289_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~k005289_device() { }
+
+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);
+
+public:
+ DECLARE_WRITE8_MEMBER( k005289_control_A_w );
+ DECLARE_WRITE8_MEMBER( k005289_control_B_w );
+ DECLARE_WRITE8_MEMBER( ld1_w );
+ DECLARE_WRITE8_MEMBER( ld2_w );
+ DECLARE_WRITE8_MEMBER( tg1_w );
+ DECLARE_WRITE8_MEMBER( tg2_w );
+
+private:
+ void make_mixer_table(int voices);
+
+ const unsigned char *m_sound_prom;
+ sound_stream *m_stream;
+ int m_rate;
+
+ /* mixer tables and internal buffers */
+ INT16 *m_mixer_table;
+ INT16 *m_mixer_lookup;
+ short *m_mixer_buffer;
+
+ UINT32 m_counter[2];
+ UINT16 m_frequency[2];
+ UINT16 m_freq_latch[2];
+ UINT16 m_waveform[2];
+ UINT8 m_volume[2];
+};
+
+extern const device_type K005289;
+
+
+#endif /* __K005289_H__ */
diff --git a/src/devices/sound/k007232.c b/src/devices/sound/k007232.c
new file mode 100644
index 00000000000..6f5286b951f
--- /dev/null
+++ b/src/devices/sound/k007232.c
@@ -0,0 +1,424 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria,Hiromitsu Shioya
+/*********************************************************/
+/* Konami PCM controller */
+/*********************************************************/
+
+/*
+ Changelog, Hiromitsu Shioya 02/05/2002
+ fix start address decode timing. (sample loop bug.)
+
+ Changelog, Mish, August 1999:
+ Removed interface support for different memory regions per channel.
+ Removed interface support for differing channel volume.
+
+ Added bankswitching.
+ Added support for multiple chips.
+
+ (Nb: Should different memory regions per channel be needed
+ the bankswitching function can set this up).
+
+NS990821
+support for the k007232_VOL() macro.
+added external port callback, and functions to set the volume of the channels
+
+*/
+
+
+#include "emu.h"
+#include "k007232.h"
+
+#define BASE_SHIFT (12)
+
+
+#if 0
+static const int kdac_note[] = {
+ 261.63/8, 277.18/8,
+ 293.67/8, 311.13/8,
+ 329.63/8,
+ 349.23/8, 369.99/8,
+ 392.00/8, 415.31/8,
+ 440.00/8, 466.16/8,
+ 493.88/8,
+
+ 523.25/8,
+};
+
+static const float kdaca_fn[][2] = {
+ /* B */
+ { 0x03f, 493.88/8 }, /* ?? */
+ { 0x11f, 493.88/4 }, /* ?? */
+ { 0x18f, 493.88/2 }, /* ?? */
+ { 0x1c7, 493.88 },
+ { 0x1e3, 493.88*2 },
+ { 0x1f1, 493.88*4 }, /* ?? */
+ { 0x1f8, 493.88*8 }, /* ?? */
+ /* A+ */
+ { 0x020, 466.16/8 }, /* ?? */
+ { 0x110, 466.16/4 }, /* ?? */
+ { 0x188, 466.16/2 },
+ { 0x1c4, 466.16 },
+ { 0x1e2, 466.16*2 },
+ { 0x1f1, 466.16*4 }, /* ?? */
+ { 0x1f8, 466.16*8 }, /* ?? */
+ /* A */
+ { 0x000, 440.00/8 }, /* ?? */
+ { 0x100, 440.00/4 }, /* ?? */
+ { 0x180, 440.00/2 },
+ { 0x1c0, 440.00 },
+ { 0x1e0, 440.00*2 },
+ { 0x1f0, 440.00*4 }, /* ?? */
+ { 0x1f8, 440.00*8 }, /* ?? */
+ { 0x1fc, 440.00*16}, /* ?? */
+ { 0x1fe, 440.00*32}, /* ?? */
+ { 0x1ff, 440.00*64}, /* ?? */
+ /* G+ */
+ { 0x0f2, 415.31/4 },
+ { 0x179, 415.31/2 },
+ { 0x1bc, 415.31 },
+ { 0x1de, 415.31*2 },
+ { 0x1ef, 415.31*4 }, /* ?? */
+ { 0x1f7, 415.31*8 }, /* ?? */
+ /* G */
+ { 0x0e2, 392.00/4 },
+ { 0x171, 392.00/2 },
+ { 0x1b8, 392.00 },
+ { 0x1dc, 392.00*2 },
+ { 0x1ee, 392.00*4 }, /* ?? */
+ { 0x1f7, 392.00*8 }, /* ?? */
+ /* F+ */
+ { 0x0d0, 369.99/4 }, /* ?? */
+ { 0x168, 369.99/2 },
+ { 0x1b4, 369.99 },
+ { 0x1da, 369.99*2 },
+ { 0x1ed, 369.99*4 }, /* ?? */
+ { 0x1f6, 369.99*8 }, /* ?? */
+ /* F */
+ { 0x0bf, 349.23/4 }, /* ?? */
+ { 0x15f, 349.23/2 },
+ { 0x1af, 349.23 },
+ { 0x1d7, 349.23*2 },
+ { 0x1eb, 349.23*4 }, /* ?? */
+ { 0x1f5, 349.23*8 }, /* ?? */
+ /* E */
+ { 0x0ac, 329.63/4 },
+ { 0x155, 329.63/2 }, /* ?? */
+ { 0x1ab, 329.63 },
+ { 0x1d5, 329.63*2 },
+ { 0x1ea, 329.63*4 }, /* ?? */
+ { 0x1f4, 329.63*8 }, /* ?? */
+ /* D+ */
+ { 0x098, 311.13/4 }, /* ?? */
+ { 0x14c, 311.13/2 },
+ { 0x1a6, 311.13 },
+ { 0x1d3, 311.13*2 },
+ { 0x1e9, 311.13*4 }, /* ?? */
+ { 0x1f4, 311.13*8 }, /* ?? */
+ /* D */
+ { 0x080, 293.67/4 }, /* ?? */
+ { 0x140, 293.67/2 }, /* ?? */
+ { 0x1a0, 293.67 },
+ { 0x1d0, 293.67*2 },
+ { 0x1e8, 293.67*4 }, /* ?? */
+ { 0x1f4, 293.67*8 }, /* ?? */
+ { 0x1fa, 293.67*16}, /* ?? */
+ { 0x1fd, 293.67*32}, /* ?? */
+ /* C+ */
+ { 0x06d, 277.18/4 }, /* ?? */
+ { 0x135, 277.18/2 }, /* ?? */
+ { 0x19b, 277.18 },
+ { 0x1cd, 277.18*2 },
+ { 0x1e6, 277.18*4 }, /* ?? */
+ { 0x1f2, 277.18*8 }, /* ?? */
+ /* C */
+ { 0x054, 261.63/4 },
+ { 0x12a, 261.63/2 },
+ { 0x195, 261.63 },
+ { 0x1ca, 261.63*2 },
+ { 0x1e5, 261.63*4 },
+ { 0x1f2, 261.63*8 }, /* ?? */
+
+ { -1, -1 },
+};
+#endif
+
+/*************************************************************/
+
+
+const device_type K007232 = &device_creator<k007232_device>;
+
+k007232_device::k007232_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, K007232, "K007232 PCM Controller", tag, owner, clock, "k007232", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_rom(*this, DEVICE_SELF),
+ m_port_write_handler(*this)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void k007232_device::device_start()
+{
+ /* Set up the chips */
+ m_pcmlimit = region()->bytes();
+
+ m_port_write_handler.resolve();
+
+ for (int i = 0; i < KDAC_A_PCM_MAX; i++)
+ {
+ m_addr[i] = 0;
+ m_start[i] = 0;
+ m_step[i] = 0;
+ m_play[i] = 0;
+ m_bank[i] = 0;
+ }
+ m_vol[0][0] = 255; /* channel A output to output A */
+ m_vol[0][1] = 0;
+ m_vol[1][0] = 0;
+ m_vol[1][1] = 255; /* channel B output to output B */
+
+ for (int i = 0; i < 0x10; i++)
+ m_wreg[i] = 0;
+
+ m_stream = machine().sound().stream_alloc(*this, 0 , 2, clock()/128);
+
+ KDAC_A_make_fncode();
+
+ save_item(NAME(m_vol));
+ save_item(NAME(m_addr));
+ save_item(NAME(m_start));
+ save_item(NAME(m_step));
+ save_item(NAME(m_bank));
+ save_item(NAME(m_play));
+ save_item(NAME(m_wreg));
+}
+
+void k007232_device::KDAC_A_make_fncode()
+{
+#if 0
+ int i, j, k;
+ float fn;
+ for( i = 0; i < 0x200; i++ ) fncode[i] = 0;
+
+ i = 0;
+ while( (int)kdaca_fn[i][0] != -1 ){
+ fncode[(int)kdaca_fn[i][0]] = kdaca_fn[i][1];
+ i++;
+ }
+
+ i = j = 0;
+ while( i < 0x200 ){
+ if( fncode[i] != 0 ){
+ if( i != j ){
+ fn = (fncode[i] - fncode[j]) / (i - j);
+ for( k = 1; k < (i-j); k++ )
+ fncode[k+j] = fncode[j] + fn*k;
+ j = i;
+ }
+ }
+ i++;
+ }
+#if 0
+ for( i = 0; i < 0x200; i++ )
+ logerror("fncode[%04x] = %.2f\n", i, fncode[i] );
+#endif
+
+#else
+ int i;
+ for( i = 0; i < 0x200; i++ )
+ {
+ //fncode[i] = (0x200 * 55) / (0x200 - i);
+ m_fncode[i] = (32 << BASE_SHIFT) / (0x200 - i);
+// m_fncode[i] = ((0x200 * 55.2 / 880) / (0x200 - i));
+ // = 512 * 55.2 / 220 / (512 - i) = 128 / (512 - i)
+ // logerror("2 : fncode[%04x] = %.2f\n", i, fncode[i] );
+ }
+
+#endif
+}
+
+
+/************************************************/
+/* Konami PCM write register */
+/************************************************/
+WRITE8_MEMBER( k007232_device::write )
+{
+ int r = offset;
+ int v = data;
+
+ m_stream->update();
+
+ m_wreg[r] = v; /* stock write data */
+
+ if (r == 0x0c){
+ /* external port, usually volume control */
+ if (!m_port_write_handler.isnull()) m_port_write_handler(0, v, mem_mask);
+ return;
+ }
+ else if( r == 0x0d ){
+ /* loopflag. */
+ return;
+ }
+ else{
+ int reg_port;
+
+ reg_port = 0;
+ if (r >= 0x06){
+ reg_port = 1;
+ r -= 0x06;
+ }
+
+ switch (r){
+ case 0x00:
+ case 0x01:
+ {
+ /**** address step ****/
+ int idx = (((((unsigned int)m_wreg[reg_port*0x06 + 0x01])<<8)&0x0100) | (((unsigned int)m_wreg[reg_port*0x06 + 0x00])&0x00ff));
+#if 0
+ if( !reg_port && r == 1 )
+ logerror("%04x\n" ,idx );
+#endif
+
+ m_step[reg_port] = m_fncode[idx];
+ break;
+ }
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ break;
+ case 0x05:
+ /**** start address ****/
+ m_start[reg_port] =
+ ((((unsigned int)m_wreg[reg_port*0x06 + 0x04]<<16)&0x00010000) |
+ (((unsigned int)m_wreg[reg_port*0x06 + 0x03]<< 8)&0x0000ff00) |
+ (((unsigned int)m_wreg[reg_port*0x06 + 0x02] )&0x000000ff) |
+ m_bank[reg_port]);
+ if (m_start[reg_port] < m_pcmlimit ){
+ m_play[reg_port] = 1;
+ m_addr[reg_port] = 0;
+ }
+ break;
+ }
+ }
+}
+
+/************************************************/
+/* Konami PCM read register */
+/************************************************/
+READ8_MEMBER( k007232_device::read )
+{
+ int r = offset;
+ int ch = 0;
+
+ if( r == 0x0005 || r == 0x000b ){
+ ch = r/0x0006;
+ r = ch * 0x0006;
+
+ m_start[ch] =
+ ((((unsigned int)m_wreg[r + 0x04]<<16)&0x00010000) |
+ (((unsigned int)m_wreg[r + 0x03]<< 8)&0x0000ff00) |
+ (((unsigned int)m_wreg[r + 0x02] )&0x000000ff) |
+ m_bank[ch]);
+
+ if (m_start[ch] < m_pcmlimit ){
+ m_play[ch] = 1;
+ m_addr[ch] = 0;
+ }
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+
+void k007232_device::set_volume(int channel,int volumeA,int volumeB)
+{
+ m_vol[channel][0] = volumeA;
+ m_vol[channel][1] = volumeB;
+}
+
+void k007232_device::set_bank(int chABank, int chBBank )
+{
+ m_bank[0] = chABank<<17;
+ m_bank[1] = chBBank<<17;
+}
+
+/*****************************************************************************/
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void k007232_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int i;
+
+ memset(outputs[0],0,samples * sizeof(*outputs[0]));
+ memset(outputs[1],0,samples * sizeof(*outputs[1]));
+
+ for( i = 0; i < KDAC_A_PCM_MAX; i++ )
+ {
+ if (m_play[i])
+ {
+ int volA,volB,j,out;
+ unsigned int addr, old_addr;
+ //int cen;
+
+ /**** PCM setup ****/
+ addr = m_start[i] + ((m_addr[i]>>BASE_SHIFT)&0x000fffff);
+ volA = m_vol[i][0] * 2;
+ volB = m_vol[i][1] * 2;
+#if 0
+ cen = (volA + volB) / 2;
+ volA = (volA + cen) < 0x1fe ? (volA + cen) : 0x1fe;
+ volB = (volB + cen) < 0x1fe ? (volB + cen) : 0x1fe;
+#endif
+
+ for( j = 0; j < samples; j++ )
+ {
+ old_addr = addr;
+ addr = m_start[i] + ((m_addr[i]>>BASE_SHIFT)&0x000fffff);
+ while (old_addr <= addr)
+ {
+ if( (m_rom[old_addr] & 0x80) || old_addr >= m_pcmlimit )
+ {
+ /* end of sample */
+
+ if( m_wreg[0x0d]&(1<<i) )
+ {
+ /* loop to the beginning */
+ m_start[i] =
+ ((((unsigned int)m_wreg[i*0x06 + 0x04]<<16)&0x00010000) |
+ (((unsigned int)m_wreg[i*0x06 + 0x03]<< 8)&0x0000ff00) |
+ (((unsigned int)m_wreg[i*0x06 + 0x02] )&0x000000ff) |
+ m_bank[i]);
+ addr = m_start[i];
+ m_addr[i] = 0;
+ old_addr = addr; /* skip loop */
+ }
+ else
+ {
+ /* stop sample */
+ m_play[i] = 0;
+ }
+ break;
+ }
+
+ old_addr++;
+ }
+
+ if (m_play[i] == 0)
+ break;
+
+ m_addr[i] += m_step[i];
+
+ out = (m_rom[addr] & 0x7f) - 0x40;
+
+ outputs[0][j] += out * volA;
+ outputs[1][j] += out * volB;
+ }
+ }
+ }
+}
diff --git a/src/devices/sound/k007232.h b/src/devices/sound/k007232.h
new file mode 100644
index 00000000000..236fbd26671
--- /dev/null
+++ b/src/devices/sound/k007232.h
@@ -0,0 +1,74 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria
+/*********************************************************/
+/* Konami PCM controller */
+/*********************************************************/
+
+#pragma once
+
+#ifndef __K007232_H__
+#define __K007232_H__
+
+#define KDAC_A_PCM_MAX (2) /* Channels per chip */
+
+#define MCFG_K007232_PORT_WRITE_HANDLER(_devcb) \
+ devcb = &k007232_device::set_port_write_handler(*device, DEVCB_##_devcb);
+
+
+class k007232_device : public device_t,
+ public device_sound_interface
+{
+public:
+ k007232_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~k007232_device() {}
+
+ template<class _Object> static devcb_base &set_port_write_handler(device_t &device, _Object object) { return downcast<k007232_device &>(device).m_port_write_handler.set_callback(object); }
+
+ DECLARE_WRITE8_MEMBER( write );
+ DECLARE_READ8_MEMBER( read );
+
+ /*
+ The 007232 has two channels and produces two outputs. The volume control
+ is external, however to make it easier to use we handle that inside the
+ emulation. You can control volume and panning: for each of the two channels
+ you can set the volume of the two outputs. If panning is not required,
+ then volumeB will be 0 for channel 0, and volumeA will be 0 for channel 1.
+ Volume is in the range 0-255.
+ */
+ void set_volume(int channel,int volumeA,int volumeB);
+
+ void set_bank( int chABank, int chBBank );
+
+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);
+
+ void KDAC_A_make_fncode();
+
+private:
+ // internal state
+ required_region_ptr<UINT8> m_rom;
+
+ UINT8 m_vol[KDAC_A_PCM_MAX][2]; /* volume for the left and right channel */
+ UINT32 m_addr[KDAC_A_PCM_MAX];
+ UINT32 m_start[KDAC_A_PCM_MAX];
+ UINT32 m_step[KDAC_A_PCM_MAX];
+ UINT32 m_bank[KDAC_A_PCM_MAX];
+ int m_play[KDAC_A_PCM_MAX];
+
+ UINT8 m_wreg[0x10]; /* write data */
+
+ UINT32 m_pcmlimit;
+
+ sound_stream * m_stream;
+ UINT32 m_fncode[0x200];
+ devcb_write8 m_port_write_handler;
+};
+
+extern const device_type K007232;
+
+
+#endif /* __K007232_H__ */
diff --git a/src/devices/sound/k051649.c b/src/devices/sound/k051649.c
new file mode 100644
index 00000000000..d69b9f25445
--- /dev/null
+++ b/src/devices/sound/k051649.c
@@ -0,0 +1,288 @@
+// license:BSD-3-Clause
+// copyright-holders:Bryan McPhail
+/***************************************************************************
+
+ Konami 051649 - SCC1 sound as used in Haunted Castle, City Bomber
+
+ This file is pieced together by Bryan McPhail from a combination of
+ Namco Sound, Amuse by Cab, Haunted Castle schematics and whoever first
+ figured out SCC!
+
+ The 051649 is a 5 channel sound generator, each channel gets its
+ waveform from RAM (32 bytes per waveform, 8 bit signed data).
+
+ This sound chip is the same as the sound chip in some Konami
+ megaROM cartridges for the MSX. It is actually well researched
+ and documented:
+
+ http://bifi.msxnet.org/msxnet/tech/scc.html
+
+ Thanks to Sean Young (sean@mess.org) for some bugfixes.
+
+ K052539 is more or less equivalent to this chip except channel 5
+ does not share waveram with channel 4.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "k051649.h"
+
+#define FREQ_BITS 16
+#define DEF_GAIN 8
+
+
+// device type definition
+const device_type K051649 = &device_creator<k051649_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// k051649_device - constructor
+//-------------------------------------------------
+
+k051649_device::k051649_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, K051649, "K051649 SCC1", tag, owner, clock, "k051649", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_mclock(0),
+ m_rate(0),
+ m_mixer_table(NULL),
+ m_mixer_lookup(NULL),
+ m_mixer_buffer(NULL),
+ m_test(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void k051649_device::device_start()
+{
+ // get stream channels
+ m_rate = clock()/16;
+ m_stream = stream_alloc(0, 1, m_rate);
+ m_mclock = clock();
+
+ // allocate a buffer to mix into - 1 second's worth should be more than enough
+ m_mixer_buffer = auto_alloc_array(machine(), short, 2 * m_rate);
+
+ // build the mixer table
+ make_mixer_table(5);
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void k051649_device::device_reset()
+{
+ k051649_sound_channel *voice = m_channel_list;
+ int i;
+
+ // reset all the voices
+ for (i = 0; i < 5; i++)
+ {
+ voice[i].frequency = 0;
+ voice[i].volume = 0xf;
+ voice[i].counter = 0;
+ voice[i].key = 0;
+ }
+
+ // other parameters
+ m_test = 0;
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void k051649_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ k051649_sound_channel *voice=m_channel_list;
+ stream_sample_t *buffer = outputs[0];
+ short *mix;
+ int i,j;
+
+ // zap the contents of the mixer buffer
+ memset(m_mixer_buffer, 0, samples * sizeof(short));
+
+ for (j = 0; j < 5; j++)
+ {
+ // channel is halted for freq < 9
+ if (voice[j].frequency > 8)
+ {
+ const signed char *w = voice[j].waveram;
+ int v=voice[j].volume * voice[j].key;
+ int c=voice[j].counter;
+ int step = ((INT64)m_mclock * (1 << FREQ_BITS)) / (float)((voice[j].frequency + 1) * 16 * (m_rate / 32)) + 0.5f;
+
+ mix = m_mixer_buffer;
+
+ // add our contribution
+ for (i = 0; i < samples; i++)
+ {
+ int offs;
+
+ c += step;
+ offs = (c >> FREQ_BITS) & 0x1f;
+ *mix++ += (w[offs] * v)>>3;
+ }
+
+ // update the counter for this voice
+ voice[j].counter = c;
+ }
+ }
+
+ // mix it down
+ mix = m_mixer_buffer;
+ for (i = 0; i < samples; i++)
+ *buffer++ = m_mixer_lookup[*mix++];
+}
+
+
+/********************************************************************************/
+
+
+WRITE8_MEMBER( k051649_device::k051649_waveform_w )
+{
+ // waveram is read-only?
+ if (m_test & 0x40 || (m_test & 0x80 && offset >= 0x60))
+ return;
+
+ m_stream->update();
+
+ if (offset >= 0x60)
+ {
+ // channel 5 shares waveram with channel 4
+ m_channel_list[3].waveram[offset&0x1f]=data;
+ m_channel_list[4].waveram[offset&0x1f]=data;
+ }
+ else
+ m_channel_list[offset>>5].waveram[offset&0x1f]=data;
+}
+
+
+READ8_MEMBER ( k051649_device::k051649_waveform_r )
+{
+ // test-register bits 6/7 expose the internal counter
+ if (m_test & 0xc0)
+ {
+ m_stream->update();
+
+ if (offset >= 0x60)
+ offset += (m_channel_list[3 + (m_test >> 6 & 1)].counter >> FREQ_BITS);
+ else if (m_test & 0x40)
+ offset += (m_channel_list[offset>>5].counter >> FREQ_BITS);
+ }
+ return m_channel_list[offset>>5].waveram[offset&0x1f];
+}
+
+
+WRITE8_MEMBER( k051649_device::k052539_waveform_w )
+{
+ // waveram is read-only?
+ if (m_test & 0x40)
+ return;
+
+ m_stream->update();
+ m_channel_list[offset>>5].waveram[offset&0x1f]=data;
+}
+
+
+READ8_MEMBER ( k051649_device::k052539_waveform_r )
+{
+ // test-register bit 6 exposes the internal counter
+ if (m_test & 0x40)
+ {
+ m_stream->update();
+ offset += (m_channel_list[offset>>5].counter >> FREQ_BITS);
+ }
+ return m_channel_list[offset>>5].waveram[offset&0x1f];
+}
+
+
+WRITE8_MEMBER( k051649_device::k051649_volume_w )
+{
+ m_stream->update();
+ m_channel_list[offset&0x7].volume=data&0xf;
+}
+
+
+WRITE8_MEMBER( k051649_device::k051649_frequency_w )
+{
+ int freq_hi = offset & 1;
+ offset >>= 1;
+
+ m_stream->update();
+
+ // test-register bit 5 resets the internal counter
+ if (m_test & 0x20)
+ m_channel_list[offset].counter = ~0;
+ else if (m_channel_list[offset].frequency < 9)
+ m_channel_list[offset].counter |= ((1 << FREQ_BITS) - 1);
+
+ // update frequency
+ if (freq_hi)
+ m_channel_list[offset].frequency = (m_channel_list[offset].frequency & 0x0ff) | (data << 8 & 0xf00);
+ else
+ m_channel_list[offset].frequency = (m_channel_list[offset].frequency & 0xf00) | data;
+}
+
+
+WRITE8_MEMBER( k051649_device::k051649_keyonoff_w )
+{
+ int i;
+ m_stream->update();
+
+ for (i = 0; i < 5; i++)
+ {
+ m_channel_list[i].key=data&1;
+ data >>= 1;
+ }
+}
+
+
+WRITE8_MEMBER( k051649_device::k051649_test_w )
+{
+ m_test = data;
+}
+
+
+READ8_MEMBER ( k051649_device::k051649_test_r )
+{
+ // reading the test register sets it to $ff!
+ k051649_test_w(space, offset, 0xff);
+ return 0xff;
+}
+
+
+//-------------------------------------------------
+// build a table to divide by the number of voices
+//-------------------------------------------------
+
+void k051649_device::make_mixer_table(int voices)
+{
+ int i;
+
+ // allocate memory
+ m_mixer_table = auto_alloc_array(machine(), INT16, 512 * voices);
+
+ // find the middle of the table
+ m_mixer_lookup = m_mixer_table + (256 * voices);
+
+ // fill in the table - 16 bit case
+ for (i = 0; i < (voices * 256); i++)
+ {
+ int val = i * DEF_GAIN * 16 / voices;
+ if (val > 32767) val = 32767;
+ m_mixer_lookup[ i] = val;
+ m_mixer_lookup[-i] = -val;
+ }
+}
diff --git a/src/devices/sound/k051649.h b/src/devices/sound/k051649.h
new file mode 100644
index 00000000000..ee4ffcbfb8e
--- /dev/null
+++ b/src/devices/sound/k051649.h
@@ -0,0 +1,95 @@
+// license:BSD-3-Clause
+// copyright-holders:Bryan McPhail
+#pragma once
+
+#ifndef __K051649_H__
+#define __K051649_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_K051649_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, K051649, _clock)
+#define MCFG_K051649_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, K051649, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// Parameters for a channel
+struct k051649_sound_channel
+{
+ k051649_sound_channel() :
+ counter(0),
+ frequency(0),
+ volume(0),
+ key(0)
+ {
+ memset(waveram, 0, sizeof(signed char)*32);
+ }
+
+ unsigned long counter;
+ int frequency;
+ int volume;
+ int key;
+ signed char waveram[32];
+};
+
+
+// ======================> k051649_device
+
+class k051649_device : public device_t,
+ public device_sound_interface
+{
+public:
+ k051649_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~k051649_device() { }
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+public:
+ DECLARE_WRITE8_MEMBER( k051649_waveform_w );
+ DECLARE_READ8_MEMBER ( k051649_waveform_r );
+ DECLARE_WRITE8_MEMBER( k051649_volume_w );
+ DECLARE_WRITE8_MEMBER( k051649_frequency_w );
+ DECLARE_WRITE8_MEMBER( k051649_keyonoff_w );
+ DECLARE_WRITE8_MEMBER( k051649_test_w );
+ DECLARE_READ8_MEMBER ( k051649_test_r );
+
+ DECLARE_WRITE8_MEMBER( k052539_waveform_w );
+ DECLARE_READ8_MEMBER ( k052539_waveform_r );
+
+private:
+ void make_mixer_table(int voices);
+
+private:
+ k051649_sound_channel m_channel_list[5];
+
+ /* global sound parameters */
+ sound_stream *m_stream;
+ int m_mclock;
+ int m_rate;
+
+ /* mixer tables and internal buffers */
+ INT16 *m_mixer_table;
+ INT16 *m_mixer_lookup;
+ short *m_mixer_buffer;
+
+ /* chip registers */
+ UINT8 m_test;
+};
+
+extern const device_type K051649;
+
+
+#endif /* __K051649_H__ */
diff --git a/src/devices/sound/k053260.c b/src/devices/sound/k053260.c
new file mode 100644
index 00000000000..86054000829
--- /dev/null
+++ b/src/devices/sound/k053260.c
@@ -0,0 +1,474 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi, Alex W. Jackson
+/*********************************************************
+
+ Konami 053260 KDSC
+
+ The 053260 is a four voice PCM/ADPCM sound chip that
+ also incorporates four 8-bit ports for communication
+ between a main CPU and audio CPU. The chip's output
+ is compatible with a YM3012 DAC, and it has a digital
+ auxiliary input compatible with the output of a YM2151.
+ Some games (e.g. Simpsons) only connect one channel of
+ the YM2151, but others (e.g. Thunder Cross II) connect
+ both channels for stereo mixing.
+
+ The 053260 has a 21-bit address bus and 8-bit data bus
+ to ROM, allowing it to access up to 2 megabytes of
+ sample data. Sample data can be either signed 8-bit
+ PCM or a custom 4-bit ADPCM format. It is possible for
+ two 053260 chips to share access to the same ROMs
+ (used by Over Drive)
+
+ The 053260 has separate address and data buses to the
+ audio CPU controlling it and to the main CPU. Both data
+ buses are 8 bit. The audio CPU address bus has 6 lines
+ (64 addressable registers, but fewer than 48 are
+ actually used) while the main CPU "bus" has only 1 line
+ (2 addressable registers). All registers on the audio
+ CPU side seem to be either read-only or write-only,
+ although some games write 0 to all the registers in a
+ loop at startup (including otherwise read-only or
+ entirely unused registers).
+ On the main CPU side, reads and writes to the same
+ address access different communication ports.
+
+ The sound data ROMs of Simpsons and Vendetta have
+ "headers" listing all the samples in the ROM, their
+ formats ("PCM" or "KADPCM"), start and end addresses.
+ The header data doesn't seem to be used by the hardware
+ (none of the other games have headers) but provides
+ useful information about the chip.
+
+ 2004-02-28 (Oliver Achten)
+ Fixed ADPCM decoding. Games sound much better now.
+
+ 2014-10-06 (Alex W. Jackson)
+ Rewrote from scratch in C++; implemented communication
+ ports properly; used the actual up counters instead of
+ converting to fractional sample position; fixed ADPCM
+ decoding bugs; added documentation.
+
+
+*********************************************************/
+
+#include "emu.h"
+#include "k053260.h"
+
+#define LOG 0
+
+#define CLOCKS_PER_SAMPLE 32
+
+
+
+// device type definition
+const device_type K053260 = &device_creator<k053260_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// k053260_device - constructor
+//-------------------------------------------------
+
+k053260_device::k053260_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, K053260, "K053260 KDSC", tag, owner, clock, "k053260", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_rgnoverride(NULL),
+ m_stream(NULL),
+ m_rom(NULL),
+ m_rom_size(0),
+ m_keyon(0),
+ m_mode(0)
+{
+ memset(m_portdata, 0, sizeof(m_portdata));
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void k053260_device::device_start()
+{
+ memory_region *ROM = (m_rgnoverride) ? owner()->memregion(m_rgnoverride) : region();
+ m_rom = ROM->base();
+ m_rom_size = ROM->bytes();
+
+ m_stream = stream_alloc( 0, 2, clock() / CLOCKS_PER_SAMPLE );
+
+ /* register with the save state system */
+ save_item(NAME(m_portdata));
+ save_item(NAME(m_keyon));
+ save_item(NAME(m_mode));
+
+ for (int i = 0; i < 4; i++)
+ m_voice[i].voice_start(*this, i);
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void k053260_device::device_reset()
+{
+ for (int i = 0; i < 4; i++)
+ m_voice[i].voice_reset();
+}
+
+
+READ8_MEMBER( k053260_device::main_read )
+{
+ // sub-to-main ports
+ return m_portdata[2 + (offset & 1)];
+}
+
+
+WRITE8_MEMBER( k053260_device::main_write )
+{
+ // main-to-sub ports
+ m_portdata[offset & 1] = data;
+}
+
+
+READ8_MEMBER( k053260_device::read )
+{
+ offset &= 0x3f;
+ UINT8 ret = 0;
+
+ switch (offset)
+ {
+ case 0x00: // main-to-sub ports
+ case 0x01:
+ ret = m_portdata[offset];
+ break;
+
+ case 0x29: // voice status
+ m_stream->update();
+ for (int i = 0; i < 4; i++)
+ ret |= m_voice[i].playing() << i;
+ break;
+
+ case 0x2e: // read ROM
+ if (m_mode & 1)
+ ret = m_voice[0].read_rom();
+ else
+ logerror("%s: Attempting to read K053260 ROM without mode bit set\n", machine().describe_context());
+ break;
+
+ default:
+ logerror("%s: Read from unknown K053260 register %02x\n", machine().describe_context(), offset);
+ }
+ return ret;
+}
+
+
+WRITE8_MEMBER( k053260_device::write )
+{
+ offset &= 0x3f;
+
+ m_stream->update();
+
+ // per voice registers
+ if ((offset >= 0x08) && (offset <= 0x27))
+ {
+ m_voice[(offset - 8) / 8].set_register(offset, data);
+ return;
+ }
+
+ switch (offset)
+ {
+ // 0x00 and 0x01 are read registers
+
+ case 0x02: // sub-to-main ports
+ case 0x03:
+ m_portdata[offset] = data;
+ break;
+
+ // 0x04 through 0x07 seem to be unused
+
+ case 0x28: // key on/off
+ {
+ UINT8 rising_edge = data & ~m_keyon;
+
+ for (int i = 0; i < 4; i++)
+ {
+ if (rising_edge & (1 << i))
+ m_voice[i].key_on();
+ else if (!(data & (1 << i)))
+ m_voice[i].key_off();
+ }
+ m_keyon = data;
+ break;
+ }
+
+ // 0x29 is a read register
+
+ case 0x2a: // loop and pcm/adpcm select
+ for (int i = 0; i < 4; i++)
+ {
+ m_voice[i].set_loop_kadpcm(data);
+ data >>= 1;
+ }
+ break;
+
+ // 0x2b seems to be unused
+
+ case 0x2c: // pan, voices 0 and 1
+ m_voice[0].set_pan(data);
+ m_voice[1].set_pan(data >> 3);
+ break;
+
+ case 0x2d: // pan, voices 2 and 3
+ m_voice[2].set_pan(data);
+ m_voice[3].set_pan(data >> 3);
+ break;
+
+ // 0x2e is a read register
+
+ case 0x2f: // control
+ m_mode = data;
+ // bit 0 = enable ROM read from register 0x2e
+ // bit 1 = enable sound output
+ // bit 2 = enable aux input?
+ // (set by all games except Golfing Greats and Rollergames, both of which
+ // don't have a YM2151. Over Drive only sets it on one of the two chips)
+ // bit 3 = aux input or ROM sharing related?
+ // (only set by Over Drive, and only on the same chip that bit 2 is set on)
+ break;
+
+ default:
+ logerror("%s: Write to unknown K053260 register %02x (data = %02x)\n",
+ machine().describe_context(), offset, data);
+ }
+}
+
+
+INLINE int limit( int val, int max, int min )
+{
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+#define MAXOUT 0x7fff
+#define MINOUT -0x8000
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void k053260_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ if (m_mode & 2)
+ {
+ for ( int j = 0; j < samples; j++ )
+ {
+ stream_sample_t buffer[2] = {0, 0};
+
+ for (int i = 0; i < 4; i++)
+ {
+ KDSC_Voice &voice = m_voice[i];
+ if (voice.playing())
+ voice.play(buffer);
+ }
+
+ outputs[0][j] = limit( buffer[0] >> 1, MAXOUT, MINOUT );
+ outputs[1][j] = limit( buffer[1] >> 1, MAXOUT, MINOUT );
+ }
+ }
+ else
+ {
+ memset( outputs[0], 0, samples * sizeof(*outputs[0]));
+ memset( outputs[1], 0, samples * sizeof(*outputs[1]));
+ }
+}
+
+
+//**************************************************************************
+// KDSC_Voice - one of the four voices
+//**************************************************************************
+
+void k053260_device::KDSC_Voice::voice_start(k053260_device &device, int index)
+{
+ m_device = &device;
+
+ voice_reset();
+
+ device.save_item(NAME(m_position), index);
+ device.save_item(NAME(m_pan_volume), index);
+ device.save_item(NAME(m_counter), index);
+ device.save_item(NAME(m_output), index);
+ device.save_item(NAME(m_playing), index);
+ device.save_item(NAME(m_start), index);
+ device.save_item(NAME(m_length), index);
+ device.save_item(NAME(m_pitch), index);
+ device.save_item(NAME(m_volume), index);
+ device.save_item(NAME(m_pan), index);
+ device.save_item(NAME(m_loop), index);
+ device.save_item(NAME(m_kadpcm), index);
+}
+
+void k053260_device::KDSC_Voice::voice_reset()
+{
+ m_position = 0;
+ m_counter = 0;
+ m_output = 0;
+ m_playing = false;
+ m_start = 0;
+ m_length = 0;
+ m_pitch = 0;
+ m_volume = 0;
+ m_pan = 0;
+ m_loop = false;
+ m_kadpcm = false;
+ update_pan_volume();
+}
+
+void k053260_device::KDSC_Voice::set_register(offs_t offset, UINT8 data)
+{
+ switch (offset & 0x7)
+ {
+ case 0: // pitch, lower 8 bits
+ m_pitch = (m_pitch & 0x0f00) | data;
+ break;
+ case 1: // pitch, upper 4 bits
+ m_pitch = (m_pitch & 0x00ff) | ((data << 8) & 0x0f00);
+ break;
+ case 2: // length, lower 8 bits
+ m_length = (m_length & 0xff00) | data;
+ break;
+ case 3: // length, upper 8 bits
+ m_length = (m_length & 0x00ff) | (data << 8);
+ break;
+ case 4: // start, lower 8 bits
+ m_start = (m_start & 0x1fff00) | data;
+ break;
+ case 5: // start, middle 8 bits
+ m_start = (m_start & 0x1f00ff) | (data << 8);
+ break;
+ case 6: // start, upper 5 bits
+ m_start = (m_start & 0x00ffff) | ((data << 16) & 0x1f0000);
+ break;
+ case 7: // volume, 7 bits
+ m_volume = data & 0x7f;
+ update_pan_volume();
+ }
+}
+
+void k053260_device::KDSC_Voice::set_loop_kadpcm(UINT8 data)
+{
+ m_loop = BIT(data, 0);
+ m_kadpcm = BIT(data, 4);
+}
+
+void k053260_device::KDSC_Voice::set_pan(UINT8 data)
+{
+ m_pan = data & 0x7;
+ update_pan_volume();
+}
+
+void k053260_device::KDSC_Voice::update_pan_volume()
+{
+ m_pan_volume[0] = m_volume * (8 - m_pan);
+ m_pan_volume[1] = m_volume * m_pan;
+}
+
+void k053260_device::KDSC_Voice::key_on()
+{
+ if (m_start >= m_device->m_rom_size)
+ logerror("K053260: Attempting to start playing past the end of the ROM ( start = %06x, length = %06x )\n", m_start, m_length);
+
+ else if (m_start + m_length >= m_device->m_rom_size)
+ logerror("K053260: Attempting to play past the end of the ROM ( start = %06x, length = %06x )\n",
+ m_start, m_length);
+
+ else
+ {
+ m_position = m_kadpcm ? 1 : 0; // for kadpcm low bit is nybble offset, so must start at 1 due to preincrement
+ m_counter = 0x1000 - CLOCKS_PER_SAMPLE; // force update on next sound_stream_update
+ m_output = 0;
+ m_playing = true;
+ if (LOG) logerror("K053260: start = %06x, length = %06x, pitch = %04x, vol = %02x, loop = %s, %s\n",
+ m_start, m_length, m_pitch, m_volume, m_loop ? "yes" : "no", m_kadpcm ? "KADPCM" : "PCM" );
+ }
+}
+
+void k053260_device::KDSC_Voice::key_off()
+{
+ m_position = 0;
+ m_output = 0;
+ m_playing = false;
+}
+
+void k053260_device::KDSC_Voice::play(stream_sample_t *outputs)
+{
+ m_counter += CLOCKS_PER_SAMPLE;
+
+ while (m_counter >= 0x1000)
+ {
+ m_counter = m_counter - 0x1000 + m_pitch;
+
+ UINT32 bytepos = ++m_position >> ( m_kadpcm ? 1 : 0 );
+ /*
+ Yes, _pre_increment. Playback must start 1 byte position after the
+ start address written to the register, or else ADPCM sounds will
+ have DC offsets (e.g. TMNT2 theme song) or will overflow and be
+ distorted (e.g. various Vendetta sound effects)
+ The "headers" in the Simpsons and Vendetta sound ROMs provide
+ further evidence of this quirk (the start addresses listed in the
+ ROM header are all 1 greater than the addresses the CPU writes
+ into the register)
+ */
+ if (bytepos > m_length)
+ {
+ if (m_loop)
+ {
+ m_position = m_output = bytepos = 0;
+ }
+ else
+ {
+ m_playing = false;
+ return;
+ }
+ }
+
+ UINT8 romdata = m_device->m_rom[m_start + bytepos];
+
+ if (m_kadpcm)
+ {
+ if (m_position & 1) romdata >>= 4; // decode low nybble, then high nybble
+ static const INT8 kadpcm_table[] = {0,1,2,4,8,16,32,64,-128,-64,-32,-16,-8,-4,-2,-1};
+ m_output += kadpcm_table[romdata & 0xf];
+ }
+ else
+ {
+ m_output = romdata;
+ }
+ }
+
+ outputs[0] += m_output * m_pan_volume[0];
+ outputs[1] += m_output * m_pan_volume[1];
+}
+
+UINT8 k053260_device::KDSC_Voice::read_rom()
+{
+ UINT32 offs = m_start + m_position;
+
+ m_position = (m_position + 1) & 0xffff;
+
+ if (offs >= m_device->m_rom_size)
+ {
+ logerror("%s: K053260: Attempting to read past the end of the ROM (offs = %06x, size = %06x)\n",
+ m_device->machine().describe_context(), offs, m_device->m_rom_size);
+ return 0;
+ }
+
+ return m_device->m_rom[offs];
+}
diff --git a/src/devices/sound/k053260.h b/src/devices/sound/k053260.h
new file mode 100644
index 00000000000..4a6e6353d08
--- /dev/null
+++ b/src/devices/sound/k053260.h
@@ -0,0 +1,113 @@
+// license:BSD-3-Clause
+// copyright-holders:Ernesto Corvi, Alex W. Jackson
+/*********************************************************
+
+ Konami 053260 KDSC
+
+*********************************************************/
+
+#pragma once
+
+#ifndef __K053260_H__
+#define __K053260_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_K053260_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, K053260, _clock)
+#define MCFG_K053260_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, K053260, _clock)
+
+#define MCFG_K053260_REGION(_tag) \
+ k053260_device::set_region_tag(*device, _tag);
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> k053260_device
+
+class k053260_device : public device_t,
+ public device_sound_interface
+{
+public:
+ k053260_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~k053260_device() { }
+
+ static void set_region_tag(device_t &device, const char *tag) { downcast<k053260_device &>(device).m_rgnoverride = tag; }
+
+ DECLARE_READ8_MEMBER( main_read );
+ DECLARE_WRITE8_MEMBER( main_write );
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ // configuration
+ const char * m_rgnoverride;
+
+ sound_stream * m_stream;
+ UINT8 * m_rom;
+ UINT32 m_rom_size;
+
+ // live state
+ UINT8 m_portdata[4];
+ UINT8 m_keyon;
+ UINT8 m_mode;
+
+ // per voice state
+ class KDSC_Voice
+ {
+ public:
+ inline void voice_start(k053260_device &device, int index);
+ inline void voice_reset();
+ inline void set_register(offs_t offset, UINT8 data);
+ inline void set_loop_kadpcm(UINT8 data);
+ inline void set_pan(UINT8 data);
+ inline void update_pan_volume();
+ inline void key_on();
+ inline void key_off();
+ inline void play(stream_sample_t *outputs);
+ inline bool playing() { return m_playing; }
+ inline UINT8 read_rom();
+
+ private:
+ // pointer to owning device
+ k053260_device *m_device;
+
+ // live state
+ UINT32 m_position;
+ UINT16 m_pan_volume[2];
+ UINT16 m_counter;
+ INT8 m_output;
+ bool m_playing;
+
+ // per voice registers
+ UINT32 m_start;
+ UINT16 m_length;
+ UINT16 m_pitch;
+ UINT8 m_volume;
+
+ // bit packed registers
+ UINT8 m_pan;
+ bool m_loop;
+ bool m_kadpcm;
+ } m_voice[4];
+
+ friend class k053260_device::KDSC_Voice;
+};
+
+extern const device_type K053260;
+
+#endif /* __K053260_H__ */
diff --git a/src/devices/sound/k054539.c b/src/devices/sound/k054539.c
new file mode 100644
index 00000000000..965ed5b9b58
--- /dev/null
+++ b/src/devices/sound/k054539.c
@@ -0,0 +1,534 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/*********************************************************
+
+ Konami 054539 (TOP) PCM Sound Chip
+
+ A lot of information comes from Amuse.
+ Big thanks to them.
+
+*********************************************************/
+
+#include "emu.h"
+#include "k054539.h"
+
+const device_type K054539 = &device_creator<k054539_device>;
+
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+k054539_device::k054539_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, K054539, "K054539 ADPCM", tag, owner, clock, "k054539", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_timer_handler(*this),
+ m_rgnoverride(NULL)
+{
+}
+
+
+/* Registers:
+ 00..ff: 20 bytes/channel, 8 channels
+ 00..02: pitch (lsb, mid, msb)
+ 03: volume (0=max, 0x40=-36dB)
+ 04: reverb volume (idem)
+ 05: pan (1-f right, 10 middle, 11-1f left)
+ 06..07: reverb delay (0=max, current computation non-trusted)
+ 08..0a: loop (lsb, mid, msb)
+ 0c..0e: start (lsb, mid, msb) (and current position ?)
+
+ 100.1ff: effects?
+ 13f: pan of the analog input (1-1f)
+
+ 200..20f: 2 bytes/channel, 8 channels
+ 00: type (b2-3), reverse (b5)
+ 01: loop (b0)
+
+ 214: Key on (b0-7 = channel 0-7)
+ 215: Key off ""
+ 225: ?
+ 227: Timer frequency
+ 228: ?
+ 229: ?
+ 22a: ?
+ 22b: ?
+ 22c: Channel active? (b0-7 = channel 0-7)
+ 22d: Data read/write port
+ 22e: ROM/RAM select (00..7f == ROM banks, 80 = Reverb RAM)
+ 22f: Global control:
+ .......x - Enable PCM
+ ......x. - Timer related?
+ ...x.... - Enable ROM/RAM readback from 0x22d
+ ..x..... - Timer output enable?
+ x....... - Disable register RAM updates
+
+ The chip has an optional 0x8000 byte reverb buffer.
+ The reverb delay is actually an offset in this buffer.
+*/
+
+void k054539_device::init_flags(int _flags)
+{
+ flags = _flags;
+}
+
+void k054539_device::set_gain(int channel, double _gain)
+{
+ if(_gain >= 0)
+ gain[channel] = _gain;
+}
+//*
+
+bool k054539_device::regupdate()
+{
+ return !(regs[0x22f] & 0x80);
+}
+
+void k054539_device::keyon(int channel)
+{
+ if(regupdate())
+ regs[0x22c] |= 1 << channel;
+}
+
+void k054539_device::keyoff(int channel)
+{
+ if(regupdate())
+ regs[0x22c] &= ~(1 << channel);
+}
+
+void k054539_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+#define VOL_CAP 1.80
+
+ static const INT16 dpcm[16] = {
+ 0<<8, 1<<8, 4<<8, 9<<8, 16<<8, 25<<8, 36<<8, 49<<8,
+ -64<<8, -49<<8, -36<<8, -25<<8, -16<<8, -9<<8, -4<<8, -1<<8
+ };
+
+
+ INT16 *rbase = (INT16 *)ram;
+
+ if(!(regs[0x22f] & 1))
+ return;
+
+ for(int sample = 0; sample != samples; sample++) {
+ double lval, rval;
+ if(!(flags & DISABLE_REVERB))
+ lval = rval = rbase[reverb_pos];
+ else
+ lval = rval = 0;
+ rbase[reverb_pos] = 0;
+
+ for(int ch=0; ch<8; ch++)
+ if(regs[0x22c] & (1<<ch)) {
+ unsigned char *base1 = regs + 0x20*ch;
+ unsigned char *base2 = regs + 0x200 + 0x2*ch;
+ channel *chan = channels + ch;
+
+ int delta = base1[0x00] | (base1[0x01] << 8) | (base1[0x02] << 16);
+
+ int vol = base1[0x03];
+
+ int bval = vol + base1[0x04];
+ if (bval > 255)
+ bval = 255;
+
+ int pan = base1[0x05];
+ // DJ Main: 81-87 right, 88 middle, 89-8f left
+ if (pan >= 0x81 && pan <= 0x8f)
+ pan -= 0x81;
+ else if (pan >= 0x11 && pan <= 0x1f)
+ pan -= 0x11;
+ else
+ pan = 0x18 - 0x11;
+
+ double cur_gain = gain[ch];
+
+ double lvol = voltab[vol] * pantab[pan] * cur_gain;
+ if (lvol > VOL_CAP)
+ lvol = VOL_CAP;
+
+ double rvol = voltab[vol] * pantab[0xe - pan] * cur_gain;
+ if (rvol > VOL_CAP)
+ rvol = VOL_CAP;
+
+ double rbvol= voltab[bval] * cur_gain / 2;
+ if (rbvol > VOL_CAP)
+ rbvol = VOL_CAP;
+
+ int rdelta = (base1[6] | (base1[7] << 8)) >> 3;
+ rdelta = (rdelta + reverb_pos) & 0x3fff;
+
+ int cur_pos = (base1[0x0c] | (base1[0x0d] << 8) | (base1[0x0e] << 16)) & rom_mask;
+
+ int fdelta, pdelta;
+ if(base2[0] & 0x20) {
+ delta = -delta;
+ fdelta = +0x10000;
+ pdelta = -1;
+ } else {
+ fdelta = -0x10000;
+ pdelta = +1;
+ }
+
+ int cur_pfrac, cur_val, cur_pval;
+ if(cur_pos != chan->pos) {
+ chan->pos = cur_pos;
+ cur_pfrac = 0;
+ cur_val = 0;
+ cur_pval = 0;
+ } else {
+ cur_pfrac = chan->pfrac;
+ cur_val = chan->val;
+ cur_pval = chan->pval;
+ }
+
+ switch(base2[0] & 0xc) {
+ case 0x0: { // 8bit pcm
+ cur_pfrac += delta;
+ while(cur_pfrac & ~0xffff) {
+ cur_pfrac += fdelta;
+ cur_pos += pdelta;
+
+ cur_pval = cur_val;
+ cur_val = (INT16)(rom[cur_pos] << 8);
+ if(cur_val == (INT16)0x8000 && (base2[1] & 1)) {
+ cur_pos = (base1[0x08] | (base1[0x09] << 8) | (base1[0x0a] << 16)) & rom_mask;
+ cur_val = (INT16)(rom[cur_pos] << 8);
+ }
+ if(cur_val == (INT16)0x8000) {
+ keyoff(ch);
+ cur_val = 0;
+ break;
+ }
+ }
+ break;
+ }
+
+ case 0x4: { // 16bit pcm lsb first
+ pdelta <<= 1;
+
+ cur_pfrac += delta;
+ while(cur_pfrac & ~0xffff) {
+ cur_pfrac += fdelta;
+ cur_pos += pdelta;
+
+ cur_pval = cur_val;
+ cur_val = (INT16)(rom[cur_pos] | rom[cur_pos+1]<<8);
+ if(cur_val == (INT16)0x8000 && (base2[1] & 1)) {
+ cur_pos = (base1[0x08] | (base1[0x09] << 8) | (base1[0x0a] << 16)) & rom_mask;
+ cur_val = (INT16)(rom[cur_pos] | rom[cur_pos+1]<<8);
+ }
+ if(cur_val == (INT16)0x8000) {
+ keyoff(ch);
+ cur_val = 0;
+ break;
+ }
+ }
+ break;
+ }
+
+ case 0x8: { // 4bit dpcm
+ cur_pos <<= 1;
+ cur_pfrac <<= 1;
+ if(cur_pfrac & 0x10000) {
+ cur_pfrac &= 0xffff;
+ cur_pos |= 1;
+ }
+
+ cur_pfrac += delta;
+ while(cur_pfrac & ~0xffff) {
+ cur_pfrac += fdelta;
+ cur_pos += pdelta;
+
+ cur_pval = cur_val;
+ cur_val = rom[cur_pos>>1];
+ if(cur_val == 0x88 && (base2[1] & 1)) {
+ cur_pos = ((base1[0x08] | (base1[0x09] << 8) | (base1[0x0a] << 16)) & rom_mask) << 1;
+ cur_val = rom[cur_pos>>1];
+ }
+ if(cur_val == 0x88) {
+ keyoff(ch);
+ cur_val = 0;
+ break;
+ }
+ if(cur_pos & 1)
+ cur_val >>= 4;
+ else
+ cur_val &= 15;
+ cur_val = cur_pval + dpcm[cur_val];
+ if(cur_val < -32768)
+ cur_val = -32768;
+ else if(cur_val > 32767)
+ cur_val = 32767;
+ }
+
+ cur_pfrac >>= 1;
+ if(cur_pos & 1)
+ cur_pfrac |= 0x8000;
+ cur_pos >>= 1;
+ break;
+ }
+ default:
+ LOG(("Unknown sample type %x for channel %d\n", base2[0] & 0xc, ch));
+ break;
+ }
+ lval += cur_val * lvol;
+ rval += cur_val * rvol;
+ rbase[(rdelta + reverb_pos) & 0x1fff] += INT16(cur_val*rbvol);
+
+ chan->pos = cur_pos;
+ chan->pfrac = cur_pfrac;
+ chan->pval = cur_pval;
+ chan->val = cur_val;
+
+ if(regupdate()) {
+ base1[0x0c] = cur_pos & 0xff;
+ base1[0x0d] = cur_pos>> 8 & 0xff;
+ base1[0x0e] = cur_pos>>16 & 0xff;
+ }
+ }
+ reverb_pos = (reverb_pos + 1) & 0x1fff;
+ outputs[0][sample] = INT16(lval);
+ outputs[1][sample] = INT16(rval);
+ }
+}
+
+
+void k054539_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ if (regs[0x22f] & 0x20)
+ m_timer_handler(m_timer_state ^= 1);
+}
+
+void k054539_device::init_chip()
+{
+ memset(regs, 0, sizeof(regs));
+ memset(posreg_latch, 0, sizeof(posreg_latch)); //*
+ flags |= UPDATE_AT_KEYON; //* make it default until proven otherwise
+
+ ram = auto_alloc_array(machine(), unsigned char, 0x4000);
+ reverb_pos = 0;
+ cur_ptr = 0;
+ memset(ram, 0, 0x4000);
+
+ memory_region *reg = (m_rgnoverride != NULL) ? owner()->memregion(m_rgnoverride) : region();
+ rom = reg->base();
+ rom_size = reg->bytes();
+ rom_mask = 0xffffffffU;
+ for(int i=0; i<32; i++)
+ if((1U<<i) >= rom_size) {
+ rom_mask = (1U<<i) - 1;
+ break;
+ }
+
+ stream = stream_alloc(0, 2, clock() / 384);
+
+ save_item(NAME(regs));
+ save_pointer(NAME(ram), 0x4000);
+ save_item(NAME(cur_ptr));
+}
+
+WRITE8_MEMBER(k054539_device::write)
+{
+ if(0) {
+ int voice, reg;
+
+ /* The K054539 has behavior like many other wavetable chips including
+ the Ensoniq 550x and Gravis GF-1: if a voice is active, writing
+ to it's current position is silently ignored.
+
+ Dadandaan depends on this or the vocals go wrong.
+ */
+ if (offset < 8*0x20)
+ {
+ voice = offset / 0x20;
+ reg = offset & ~0x20;
+
+ if(regs[0x22c] & (1<<voice))
+ if (reg >= 0xc && reg <= 0xe)
+ return;
+ }
+ }
+
+ bool latch = (flags & UPDATE_AT_KEYON) && (regs[0x22f] & 1);
+
+ if (latch && offset < 0x100)
+ {
+ int offs = (offset & 0x1f) - 0xc;
+ int ch = offset >> 5;
+
+ if (offs >= 0 && offs <= 2)
+ {
+ // latch writes to the position index registers
+ posreg_latch[ch][offs] = data;
+ return;
+ }
+ }
+
+ else
+ switch(offset) {
+ case 0x13f: {
+ int pan = data >= 0x11 && data <= 0x1f ? data - 0x11 : 0x18 - 0x11;
+ if (!m_apan_cb.isnull())
+ m_apan_cb(pantab[pan], pantab[0xe - pan]);
+ break;
+ }
+
+ case 0x214:
+ if (latch)
+ {
+ for(int ch=0; ch<8; ch++)
+ {
+ if(data & (1<<ch))
+ {
+ UINT8 *posptr = &posreg_latch[ch][0];
+ UINT8 *regptr = regs + (ch<<5) + 0xc;
+
+ // update the chip at key-on
+ regptr[0] = posptr[0];
+ regptr[1] = posptr[1];
+ regptr[2] = posptr[2];
+
+ keyon(ch);
+ }
+ }
+ }
+ else
+ {
+ for(int ch=0; ch<8; ch++)
+ if(data & (1<<ch))
+ keyon(ch);
+ }
+ break;
+
+ case 0x215:
+ for(int ch=0; ch<8; ch++)
+ if(data & (1<<ch))
+ keyoff(ch);
+ break;
+
+ case 0x227:
+ {
+ attotime period = attotime::from_hz((float)(38 + data) * (clock()/384.0f/14400.0f)) / 2.0f;
+
+ m_timer->adjust(period, 0, period);
+
+ m_timer_state = 0;
+ m_timer_handler(m_timer_state);
+ }
+ break;
+
+ case 0x22d:
+ if(regs[0x22e] == 0x80)
+ cur_zone[cur_ptr] = data;
+ cur_ptr++;
+ if(cur_ptr == cur_limit)
+ cur_ptr = 0;
+ break;
+
+ case 0x22e:
+ cur_zone =
+ data == 0x80 ? ram :
+ rom + 0x20000*data;
+ cur_limit = data == 0x80 ? 0x4000 : 0x20000;
+ cur_ptr = 0;
+ break;
+
+ case 0x22f:
+ if (!(data & 0x20)) // Disable timer output?
+ {
+ m_timer_state = 0;
+ m_timer_handler(m_timer_state);
+ }
+ break;
+
+ default:
+#if 0
+ if(regs[offset] != data) {
+ if((offset & 0xff00) == 0) {
+ chanoff = offset & 0x1f;
+ if(chanoff < 4 || chanoff == 5 ||
+ (chanoff >=8 && chanoff <= 0xa) ||
+ (chanoff >= 0xc && chanoff <= 0xe))
+ break;
+ }
+ if(1 || ((offset >= 0x200) && (offset <= 0x210)))
+ break;
+ logerror("K054539 %03x = %02x\n", offset, data);
+ }
+#endif
+ break;
+ }
+
+ regs[offset] = data;
+}
+
+void k054539_device::device_post_load()
+{
+ int data = regs[0x22e];
+ cur_zone = data == 0x80 ? ram : rom + 0x20000*data;
+ cur_limit = data == 0x80 ? 0x4000 : 0x20000;
+}
+
+READ8_MEMBER(k054539_device::read)
+{
+ switch(offset) {
+ case 0x22d:
+ if(regs[0x22f] & 0x10) {
+ UINT8 res = cur_zone[cur_ptr];
+ cur_ptr++;
+ if(cur_ptr == cur_limit)
+ cur_ptr = 0;
+ return res;
+ } else
+ return 0;
+ case 0x22c:
+ break;
+ default:
+ LOG(("K054539 read %03x\n", offset));
+ break;
+ }
+ return regs[offset];
+}
+
+void k054539_device::device_start()
+{
+ m_timer = timer_alloc(0);
+
+ // resolve / bind callbacks
+ m_timer_handler.resolve_safe();
+ m_apan_cb.bind_relative_to(*owner());
+
+ for (int i = 0; i < 8; i++)
+ gain[i] = 1.0;
+
+ flags = RESET_FLAGS;
+
+ /*
+ I've tried various equations on volume control but none worked consistently.
+ The upper four channels in most MW/GX games simply need a significant boost
+ to sound right. For example, the bass and smash sound volumes in Violent Storm
+ have roughly the same values and the voices in Tokimeki Puzzledama are given
+ values smaller than those of the hihats. Needless to say the two K054539 chips
+ in Mystic Warriors are completely out of balance. Rather than forcing a
+ "one size fits all" function to the voltab the current invert exponential
+ appraoch seems most appropriate.
+ */
+ // Factor the 1/4 for the number of channels in the volume (1/8 is too harsh, 1/2 gives clipping)
+ // vol=0 -> no attenuation, vol=0x40 -> -36dB
+ for(int i=0; i<256; i++)
+ voltab[i] = pow(10.0, (-36.0 * (double)i / (double)0x40) / 20.0) / 4.0;
+
+ // Pan table for the left channel
+ // Right channel is identical with inverted index
+ // Formula is such that pan[i]**2+pan[0xe-i]**2 = 1 (constant output power)
+ // and pan[0xe] = 1 (full panning)
+ for(int i=0; i<0xf; i++)
+ pantab[i] = sqrt((double)i) / sqrt((double)0xe);
+
+ init_chip();
+}
+
+void k054539_device::device_reset()
+{
+ m_timer->enable(false);
+}
diff --git a/src/devices/sound/k054539.h b/src/devices/sound/k054539.h
new file mode 100644
index 00000000000..d6fa2dc73a1
--- /dev/null
+++ b/src/devices/sound/k054539.h
@@ -0,0 +1,124 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/*********************************************************
+
+ Konami 054539 PCM Sound Chip
+
+*********************************************************/
+
+#pragma once
+
+#ifndef __K054539_H__
+#define __K054539_H__
+
+typedef device_delegate<void (double left, double right)> k054539_cb_delegate;
+#define K054539_CB_MEMBER(_name) void _name(double left, double right)
+
+#define MCFG_K054539_APAN_CB(_class, _method) \
+ k054539_device::set_analog_callback(*device, k054539_cb_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_K054539_REGION_OVERRRIDE(_region) \
+ k054539_device::set_override(*device, _region);
+
+#define MCFG_K054539_TIMER_HANDLER(_devcb) \
+ devcb = &k054539_device::set_timer_handler(*device, DEVCB_##_devcb);
+
+
+//* control flags, may be set at DRIVER_INIT().
+#define K054539_RESET_FLAGS 0
+#define K054539_REVERSE_STEREO 1
+#define K054539_DISABLE_REVERB 2
+#define K054539_UPDATE_AT_KEYON 4
+
+class k054539_device : public device_t,
+ public device_sound_interface
+{
+public:
+ enum {
+ RESET_FLAGS = 0,
+ REVERSE_STEREO = 1,
+ DISABLE_REVERB = 2,
+ UPDATE_AT_KEYON = 4
+ };
+
+ // construction/destruction
+ k054539_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ static void set_analog_callback(device_t &device, k054539_cb_delegate callback) { downcast<k054539_device &>(device).m_apan_cb = callback; }
+ static void set_override(device_t &device, const char *rgnoverride) { downcast<k054539_device &>(device).m_rgnoverride = rgnoverride; }
+ template<class _Object> static devcb_base &set_timer_handler(device_t &device, _Object object) { return downcast<k054539_device &>(device).m_timer_handler.set_callback(object); }
+
+
+ DECLARE_WRITE8_MEMBER(write);
+ DECLARE_READ8_MEMBER(read);
+
+ void init_flags(int flags);
+
+ /*
+ Note that the eight PCM channels of a K054539 do not have separate
+ volume controls. Considering the global attenuation equation may not
+ be entirely accurate, k054539_set_gain() provides means to control
+ channel gain. It can be called anywhere but preferrably from
+ DRIVER_INIT().
+
+ Parameters:
+ channel : 0 - 7
+ gain : 0.0=silent, 1.0=no gain, 2.0=twice as loud, etc.
+ */
+ void set_gain(int channel, double gain);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_post_load();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ struct channel {
+ UINT32 pos;
+ UINT32 pfrac;
+ INT32 val;
+ INT32 pval;
+ };
+
+ double voltab[256];
+ double pantab[0xf];
+
+ double gain[8];
+ UINT8 posreg_latch[8][3];
+ int flags;
+
+ unsigned char regs[0x230];
+ unsigned char *ram;
+ int reverb_pos;
+
+ INT32 cur_ptr;
+ int cur_limit;
+ unsigned char *cur_zone;
+ unsigned char *rom;
+ UINT32 rom_size;
+ UINT32 rom_mask;
+
+ channel channels[8];
+ sound_stream *stream;
+
+ emu_timer *m_timer;
+ UINT32 m_timer_state;
+ devcb_write_line m_timer_handler;
+ const char *m_rgnoverride;
+ k054539_cb_delegate m_apan_cb;
+
+ bool regupdate();
+ void keyon(int channel);
+ void keyoff(int channel);
+ void init_chip();
+};
+
+extern const device_type K054539;
+
+#endif /* __K054539_H__ */
diff --git a/src/devices/sound/k056800.c b/src/devices/sound/k056800.c
new file mode 100644
index 00000000000..1534783f8de
--- /dev/null
+++ b/src/devices/sound/k056800.c
@@ -0,0 +1,178 @@
+// license:BSD-3-Clause
+// copyright-holders:Ville Linde
+/***********************************************************************
+
+ Konami K056800 (MIRAC)
+ Sound interface and audio control
+
+***********************************************************************/
+
+#include "emu.h"
+#include "sound/k056800.h"
+
+
+
+const device_type K056800 = &device_creator<k056800_device>;
+
+
+
+//-------------------------------------------------
+// k056800_device - constructor
+//-------------------------------------------------
+
+k056800_device::k056800_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, K056800, "K056800 MIRAC", tag, owner, clock, "k056800", __FILE__),
+ m_int_handler(*this)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void k056800_device::device_start()
+{
+ m_int_handler.resolve_safe();
+
+ save_item(NAME(m_int_pending));
+ save_item(NAME(m_host_to_snd_regs));
+ save_item(NAME(m_snd_to_host_regs));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void k056800_device::device_reset()
+{
+ m_int_pending = false;
+ m_int_enabled = false;
+ memset(m_host_to_snd_regs, 0, sizeof(m_host_to_snd_regs));
+ memset(m_snd_to_host_regs, 0, sizeof(m_snd_to_host_regs));
+}
+
+
+/*****************************************************************************
+ DEVICE HANDLERS
+*****************************************************************************/
+
+READ8_MEMBER( k056800_device::host_r )
+{
+ UINT32 r = offset & 7;
+ UINT8 data = 0;
+
+ switch (r)
+ {
+ case 0:
+ case 1:
+ data = m_snd_to_host_regs[r];
+ break;
+
+ case 2:
+ // .... ...x - Front volume busy
+ // .... ..x. - Rear volume busy
+ break;
+ }
+
+ return data;
+}
+
+
+WRITE8_MEMBER( k056800_device::host_w )
+{
+ UINT32 r = offset & 7;
+
+ switch (r)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ m_host_to_snd_regs[r] = data;
+ break;
+
+ case 4:
+ // xxxx xxxx - Front volume (CAh increments, 35h decrements)
+ break;
+
+ case 5:
+ // xxxx xxxx - Rear volume (as above)
+ break;
+
+ case 6:
+ // .... ...x - Mute front
+ // .... ..x. - Mute rear
+ break;
+
+ case 7:
+ // Sound interrupt
+ m_int_pending = true;
+
+ if (m_int_enabled)
+ m_int_handler(ASSERT_LINE);
+
+ break;
+ }
+}
+
+
+READ8_MEMBER( k056800_device::sound_r )
+{
+ UINT32 r = offset & 7;
+ UINT8 data = 0;
+
+ switch (r)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ data = m_host_to_snd_regs[r];
+ break;
+ }
+
+ return data;
+}
+
+
+WRITE8_MEMBER( k056800_device::sound_w )
+{
+ UINT32 r = offset & 7;
+
+ switch (r)
+ {
+ case 0:
+ case 1:
+ m_snd_to_host_regs[r] = data;
+ break;
+
+ case 2:
+ case 3:
+ // TODO: Unknown
+ break;
+
+ case 4:
+ // Sound CPU interrupt control
+ m_int_enabled = (data & 1) != 0;
+
+ if (m_int_enabled)
+ {
+ // Enable interrupt
+ if (m_int_pending)
+ m_int_handler(ASSERT_LINE);
+ }
+ else
+ {
+ // Disable/acknowledge interrupt
+ m_int_pending = false;
+ m_int_handler(CLEAR_LINE);
+ }
+ break;
+
+ case 5:
+ // TODO: Unknown
+ break;
+ }
+}
diff --git a/src/devices/sound/k056800.h b/src/devices/sound/k056800.h
new file mode 100644
index 00000000000..7a14647b3f1
--- /dev/null
+++ b/src/devices/sound/k056800.h
@@ -0,0 +1,61 @@
+// license:BSD-3-Clause
+// copyright-holders:Ville Linde
+/*********************************************************
+
+ Konami 056800 MIRAC sound interface
+
+*********************************************************/
+
+#ifndef __K056800_H__
+#define __K056800_H__
+
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_K056800_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, K056800, _clock)
+#define MCFG_K056800_INT_HANDLER(_devcb) \
+ devcb = &k056800_device::set_int_handler(*device, DEVCB_##_devcb);
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+class k056800_device : public device_t
+{
+public:
+ // construction/destruction
+ k056800_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_int_handler(device_t &device, _Object object) { return downcast<k056800_device &>(device).m_int_handler.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( host_r );
+ DECLARE_WRITE8_MEMBER( host_w );
+ DECLARE_READ8_MEMBER( sound_r );
+ DECLARE_WRITE8_MEMBER( sound_w );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ // internal state
+ bool m_int_pending;
+ bool m_int_enabled;
+ UINT8 m_host_to_snd_regs[4];
+ UINT8 m_snd_to_host_regs[2];
+
+ devcb_write_line m_int_handler;
+};
+
+extern const device_type K056800;
+
+
+
+#endif /* __K056800_H__ */
diff --git a/src/devices/sound/l7a1045_l6028_dsp_a.c b/src/devices/sound/l7a1045_l6028_dsp_a.c
new file mode 100644
index 00000000000..baf277b55e2
--- /dev/null
+++ b/src/devices/sound/l7a1045_l6028_dsp_a.c
@@ -0,0 +1,348 @@
+// license:LGPL-2.1+
+// copyright-holders:David Haywood, Angelo Salese, ElSemi
+/***************************************************************************
+
+ L7A1045 L6028 DSP-A
+ (QFP120 package)
+
+ this is the audio chip used on the following
+ SNK Hyper NeoGeo 64 (arcade platform)
+ AKAI MPC3000 (synth)
+
+ both are driven by a V53, the MPC3000 isn't dumped.
+
+ appears to write a register number and channel/voice using
+ l7a1045_sound_select_w (offset 0)
+ format:
+
+ ---- rrrr ---c cccc
+ r = register, c = channel
+
+ the channel select appears to address 32 different voices (5-bits)
+ the register select appears to use 4-bits with 0x0 to 0xa being valid
+
+ the registers data is written / read using offsets 1,2,3 after
+ setting the register + channel, this gives 3 16-bit values for
+ each register.
+
+ register format:
+
+ offset 3 offset 2 offset 1
+ fedcba9876543210 | fedcba9876543210 | fedcba9876543210
+
+ 0 ---------------- ---------------- ----------------
+
+ 1 ---------------- ---------------- ----------------
+
+ 2 ---------------- ---------------- ----------------
+
+ 3 ---------------- ---------------- ----------------
+
+ 4 ---------------- ---------------- ----------------
+
+ 5 ---------------- ---------------- ----------------
+
+ 6 ---------------- ---------------- ----------------
+
+ 7 ---------------- ---------------- llllllllrrrrrrrr left/right volume
+
+ 8 ---------------- ---------------- ---------------- (read only?)
+
+ 9 ---------------- ---------------- ---------------- (read only?)
+
+ a ---------------- ---------------- ----------------
+
+ Registers are not yet understood.
+
+ probably sample start, end, loop positions, panning etc.
+ like CPS3, Qsound etc.
+
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03: // 00003fffffff (startup only?)
+ case 0x04: // doesn't use 6
+ case 0x05: // 00003fffffff (mostly, often)
+ case 0x06: // 00007ff0ffff mostly
+ case 0x07: // 0000000f0708 etc. (low values)
+ case 0x08: // doesn't write to 2/4/6 with this set??
+ case 0x09: // doesn't write to 2/4/6 with this set??
+ case 0x0a: // random looking values
+
+ Some of the other ports on the HNG64 sound CPU may also be tied
+ to this chip, this isn't yet clear.
+ Port $8 bit 8 is keyon, low byte is sound status related (masked with 0x7f)
+
+ Sample data format TBA
+
+ TODO:
+ - Sample format needs to be double checked;
+ - Octave Control/BPM/Pitch, right now XRally Network BGM wants 66150 Hz which is definitely too fast for Terry Bogard speech;
+ - Key Off;
+ - ADSR (registers 2 & 4?);
+
+***************************************************************************/
+
+#include "emu.h"
+#include "l7a1045_l6028_dsp_a.h"
+#include "debugger.h"
+
+
+// device type definition
+const device_type L7A1045 = &device_creator<l7a1045_sound_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// l7a1045_sound_device - constructor
+//-------------------------------------------------
+
+l7a1045_sound_device::l7a1045_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, L7A1045, "L7A1045 L6028 DSP-A", tag, owner, clock, "l7a1045_custom", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_key(0),
+ m_rom(NULL),
+ m_rom_size(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void l7a1045_sound_device::device_start()
+{
+ /* Allocate the stream */
+ m_stream = stream_alloc(0, 2, 66150); //clock() / 384);
+
+ m_rom = m_region->base();
+ m_rom_size = m_region->bytes();
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void l7a1045_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ /* Clear the buffers */
+ memset(outputs[0], 0, samples*sizeof(*outputs[0]));
+ memset(outputs[1], 0, samples*sizeof(*outputs[1]));
+
+ for (int i = 0; i < 32; i++)
+ {
+ if (m_key & (1 << i))
+ {
+ l7a1045_voice *vptr = &m_voice[i];
+
+ UINT32 start = vptr->start;
+ UINT32 end = vptr->end;
+ UINT32 step = 0x400;
+
+ UINT32 pos = vptr->pos;
+ UINT32 frac = vptr->frac;
+
+ for (int j = 0; j < samples; j++)
+ {
+ INT32 sample;
+ UINT8 data;
+
+ pos += (frac >> 12);
+ frac &= 0xfff;
+
+ if ((start + pos) >= end)
+ {
+ if(vptr->mode == true) // loop
+ {
+ pos = vptr->pos = 0;
+ frac = vptr->frac = 0;
+ }
+ else // no loop, keyoff
+ {
+ m_key &= ~(1 << i);
+ break;
+ }
+ }
+
+
+ data = m_rom[(start + pos) & (m_rom_size-1)];
+ sample = ((INT8)(data & 0xfc)) << (3 - (data & 3));
+ frac += step;
+
+ outputs[0][j] += ((sample * vptr->l_volume) >> 9);
+ outputs[1][j] += ((sample * vptr->r_volume) >> 9);
+ }
+
+ vptr->pos = pos;
+ vptr->frac = frac;
+ }
+ }
+}
+
+// TODO: needs proper memory map
+WRITE16_MEMBER( l7a1045_sound_device::l7a1045_sound_w )
+{
+ m_stream->update(); // TODO
+
+ if(offset == 0)
+ sound_select_w(space, offset, data, mem_mask);
+ else if(offset == 8/2)
+ sound_status_w(space, offset, data, mem_mask);
+ else
+ sound_data_w(space,offset - 1,data,mem_mask);
+}
+
+
+READ16_MEMBER( l7a1045_sound_device::l7a1045_sound_r )
+{
+ m_stream->update();
+
+ if(offset == 0)
+ printf("sound_select_r?\n");
+ else
+ return sound_data_r(space,offset -1,mem_mask);
+
+ return 0xffff;
+}
+
+
+WRITE16_MEMBER(l7a1045_sound_device::sound_select_w)
+{
+ // I'm guessing these addresses are the sound chip / DSP?
+
+ // ---- ---- 000c cccc
+ // c = channel
+
+ if (ACCESSING_BITS_0_7)
+ {
+ m_audiochannel = data;
+ if (m_audiochannel & 0xe0) printf("%08x: l7a1045_sound_select_w unknown channel %01x\n", space.device().safe_pc(), m_audiochannel & 0xff);
+ m_audiochannel &= 0x1f;
+ }
+
+ if (ACCESSING_BITS_8_15)
+ {
+ m_audioregister = (data >> 8);
+ if (m_audioregister >0x0a) printf("%08x: l7a1045_sound_select_w unknown register %01x\n", space.device().safe_pc(), m_audioregister & 0xff);
+ m_audioregister &= 0x0f;
+ }
+
+}
+
+WRITE16_MEMBER(l7a1045_sound_device::sound_data_w)
+{
+ l7a1045_voice *vptr = &m_voice[m_audiochannel];
+
+ //if(m_audioregister != 0 && m_audioregister != 1 && m_audioregister != 7)
+ // printf("%04x %04x (%04x %04x)\n",offset,data,m_audioregister,m_audiochannel);
+
+ m_audiodat[m_audioregister][m_audiochannel].dat[offset] = data;
+
+ switch (m_audioregister)
+ {
+ case 0x00:
+
+ vptr->start = (m_audiodat[m_audioregister][m_audiochannel].dat[2] & 0x000f) << (16 + 4);
+ vptr->start |= (m_audiodat[m_audioregister][m_audiochannel].dat[1] & 0xffff) << (4);
+ vptr->start |= (m_audiodat[m_audioregister][m_audiochannel].dat[0] & 0xf000) >> (12);
+
+ vptr->start &= m_rom_size - 1;
+
+ break;
+ case 0x01:
+ // relative to start
+ //printf("%04x\n",m_audiodat[m_audioregister][m_audiochannel].dat[0]);
+ //printf("%04x\n",m_audiodat[m_audioregister][m_audiochannel].dat[1]);
+ //printf("%04x\n",m_audiodat[m_audioregister][m_audiochannel].dat[2]);
+
+ if(m_audiodat[m_audioregister][m_audiochannel].dat[2] & 0x100)
+ {
+ vptr->end = (m_audiodat[m_audioregister][m_audiochannel].dat[0] & 0xffff) << 2;
+ vptr->end += vptr->start;
+ vptr->mode = false;
+ // hopefully it'll never happen? Maybe assert here?
+ vptr->end &= m_rom_size - 1;
+
+ }
+ else // absolute
+ {
+ vptr->end = (m_audiodat[m_audioregister][m_audiochannel].dat[2] & 0x000f) << (16 + 4);
+ vptr->end |= (m_audiodat[m_audioregister][m_audiochannel].dat[1] & 0xffff) << (4);
+ vptr->end |= (m_audiodat[m_audioregister][m_audiochannel].dat[0] & 0xf000) >> (12);
+ vptr->mode = true;
+
+ vptr->end &= m_rom_size - 1;
+ }
+
+ break;
+
+ case 0x07:
+
+ vptr->r_volume = (m_audiodat[m_audioregister][m_audiochannel].dat[0] & 0xff);
+ /* TODO: volume tables, linear? */
+ vptr->r_volume = (vptr->r_volume) | (vptr->r_volume << 8);
+ vptr->l_volume = (m_audiodat[m_audioregister][m_audiochannel].dat[0] >> 8) & 0xff;
+ vptr->l_volume = (vptr->l_volume) | (vptr->l_volume << 8);
+ //printf("%04x %02x %02x\n",m_audiodat[m_audioregister][m_audiochannel].dat[0],vptr->l_volume,vptr->r_volume);
+
+ break;
+ }
+}
+
+
+READ16_MEMBER(l7a1045_sound_device::sound_data_r)
+{
+ //printf("%04x (%04x %04x)\n",offset,m_audioregister,m_audiochannel);
+ //debugger_break(machine());
+ l7a1045_voice *vptr = &m_voice[m_audiochannel];
+
+ switch(m_audioregister)
+ {
+ case 0x00:
+ {
+ UINT32 current_addr;
+ UINT16 res;
+
+ current_addr = vptr->start + vptr->pos;
+ if(offset == 0)
+ res = (current_addr & 0xf) << 12; // TODO: frac
+ else if(offset == 1)
+ res = (current_addr & 0xffff0) >> 4;
+ else
+ res = (current_addr & 0xf00000) >> 20;
+
+ return res;
+ }
+ }
+
+ return 0;
+}
+
+WRITE16_MEMBER(l7a1045_sound_device::sound_status_w)
+{
+ if(data & 0x100) // keyin
+ {
+ l7a1045_voice *vptr = &m_voice[m_audiochannel];
+
+ #if 0
+ if(vptr->start != 0)
+ {
+ printf("%08x START\n",vptr->start);
+ printf("%08x END\n",vptr->end);
+
+ for(int i=0;i<0x10;i++)
+ printf("%02x (%02x) = %04x%04x%04x\n",m_audiochannel,i,m_audiodat[i][m_audiochannel].dat[2],m_audiodat[i][m_audiochannel].dat[1],m_audiodat[i][m_audiochannel].dat[0]);
+ }
+ #endif
+
+ vptr->frac = 0;
+ vptr->pos = 0;
+ m_key |= 1 << m_audiochannel;
+ }
+}
diff --git a/src/devices/sound/l7a1045_l6028_dsp_a.h b/src/devices/sound/l7a1045_l6028_dsp_a.h
new file mode 100644
index 00000000000..c0283f36836
--- /dev/null
+++ b/src/devices/sound/l7a1045_l6028_dsp_a.h
@@ -0,0 +1,69 @@
+// license:LGPL-2.1+
+// copyright-holders:David Haywood, Angelo Salese, ElSemi
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+struct l7a1045_voice
+{
+ l7a1045_voice() :
+ pos(0),
+ frac(0)
+ {
+ //memset(regs, 0, sizeof(UINT32)*8);
+ start = 0;
+ }
+
+ UINT32 start;
+ UINT32 end;
+ bool mode;
+ UINT32 pos;
+ UINT32 frac;
+ UINT16 l_volume;
+ UINT16 r_volume;
+};
+
+// ======================> l7a1045_sound_device
+
+class l7a1045_sound_device : public device_t,
+ public device_sound_interface
+{
+public:
+ l7a1045_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~l7a1045_sound_device() { }
+
+// void set_base(INT8* base) { m_base = base; }
+
+ DECLARE_WRITE16_MEMBER( l7a1045_sound_w );
+ DECLARE_READ16_MEMBER( l7a1045_sound_r );
+
+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:
+ sound_stream *m_stream;
+ l7a1045_voice m_voice[32];
+ UINT32 m_key;
+ UINT8 *m_rom;
+ INT32 m_rom_size;
+
+ UINT8 m_audiochannel;
+ UINT8 m_audioregister;
+
+ struct l7a1045_48bit_data {
+ UINT16 dat[3];
+ };
+
+ l7a1045_48bit_data m_audiodat[0x10][0x20];
+
+ DECLARE_WRITE16_MEMBER(sound_select_w);
+ DECLARE_WRITE16_MEMBER(sound_data_w);
+ DECLARE_READ16_MEMBER(sound_data_r);
+ DECLARE_WRITE16_MEMBER(sound_status_w);
+};
+
+extern const device_type L7A1045;
diff --git a/src/devices/sound/lmc1992.c b/src/devices/sound/lmc1992.c
new file mode 100644
index 00000000000..f4007ea875d
--- /dev/null
+++ b/src/devices/sound/lmc1992.c
@@ -0,0 +1,232 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ LMC1992 Digitally-Controlled Stereo Tone and Volume Circuit with
+ Four-Channel Input-Selector emulation
+
+**********************************************************************/
+
+/*
+
+ TODO:
+
+ - inputs
+ - outputs
+ - bass
+ - treble
+ - volume
+ - balance
+
+*/
+
+#include "lmc1992.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+#define MICROWIRE_DEVICE_ADDRESS 2
+
+
+enum
+{
+ FUNCTION_INPUT_SELECT = 0,
+ FUNCTION_BASS,
+ FUNCTION_TREBLE,
+ FUNCTION_VOLUME,
+ FUNCTION_RIGHT_FRONT_FADER,
+ FUNCTION_LEFT_FRONT_FADER,
+ FUNCTION_RIGHT_REAR_FADER,
+ FUNCTION_LEFT_REAR_FADER
+};
+
+
+enum
+{
+ INPUT_SELECT_OPEN = 0,
+ INPUT_SELECT_INPUT1,
+ INPUT_SELECT_INPUT2,
+ INPUT_SELECT_INPUT3,
+ INPUT_SELECT_INPUT4
+};
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// devices
+const device_type LMC1992 = &device_creator<lmc1992_device>;
+
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// execute_command -
+//-------------------------------------------------
+
+inline void lmc1992_device::execute_command(int addr, int data)
+{
+ switch (addr)
+ {
+ case FUNCTION_INPUT_SELECT:
+ if (data == INPUT_SELECT_OPEN)
+ {
+ if (LOG) logerror("LMC1992 '%s' Input Select : OPEN\n", tag());
+ }
+ else
+ {
+ if (LOG) logerror("LMC1992 '%s' Input Select : INPUT%u\n", tag(), data);
+ }
+ m_input = data;
+ break;
+
+ case FUNCTION_BASS:
+ if (LOG) logerror("LMC1992 '%s' Bass : %i dB\n", tag(), -40 + (data * 2));
+ m_bass = data;
+ break;
+
+ case FUNCTION_TREBLE:
+ if (LOG) logerror("LMC1992 '%s' Treble : %i dB\n", tag(), -40 + (data * 2));
+ m_treble = data;
+ break;
+
+ case FUNCTION_VOLUME:
+ if (LOG) logerror("LMC1992 '%s' Volume : %i dB\n", tag(), -80 + (data * 2));
+ m_volume = data;
+ break;
+
+ case FUNCTION_RIGHT_FRONT_FADER:
+ if (LOG) logerror("LMC1992 '%s' Right Front Fader : %i dB\n", tag(), -40 + (data * 2));
+ m_fader_rf = data;
+ break;
+
+ case FUNCTION_LEFT_FRONT_FADER:
+ if (LOG) logerror("LMC1992 '%s' Left Front Fader : %i dB\n", tag(), -40 + (data * 2));
+ m_fader_lf = data;
+ break;
+
+ case FUNCTION_RIGHT_REAR_FADER:
+ if (LOG) logerror("LMC1992 '%s' Right Rear Fader : %i dB\n", tag(), -40 + (data * 2));
+ m_fader_rr = data;
+ break;
+
+ case FUNCTION_LEFT_REAR_FADER:
+ if (LOG) logerror("LMC1992 '%s' Left Rear Fader : %i dB\n", tag(), -40 + (data * 2));
+ m_fader_lr = data;
+ break;
+ }
+}
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// lmc1992_device - constructor
+//-------------------------------------------------
+
+lmc1992_device::lmc1992_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, LMC1992, "LMC1992", tag, owner, clock, "lmc1992", __FILE__),
+ device_sound_interface(mconfig, *this)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void lmc1992_device::device_start()
+{
+ // create sound streams
+
+ // register for state saving
+ save_item(NAME(m_enable));
+ save_item(NAME(m_data));
+ save_item(NAME(m_clk));
+ save_item(NAME(m_si));
+ save_item(NAME(m_input));
+ save_item(NAME(m_bass));
+ save_item(NAME(m_treble));
+ save_item(NAME(m_volume));
+ save_item(NAME(m_fader_rf));
+ save_item(NAME(m_fader_lf));
+ save_item(NAME(m_fader_rr));
+ save_item(NAME(m_fader_lr));
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void lmc1992_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+}
+
+
+//-------------------------------------------------
+// clock_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( lmc1992_device::clock_w )
+{
+ if ((m_enable == 0) && ((m_clk == 0) && (state == 1)))
+ {
+ m_si >>= 1;
+ m_si = m_si & 0x7fff;
+
+ if (m_data)
+ {
+ m_si &= 0x8000;
+ }
+ }
+
+ m_clk = state;
+}
+
+
+//-------------------------------------------------
+// data_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( lmc1992_device::data_w )
+{
+ m_data = state;
+}
+
+
+//-------------------------------------------------
+// enable_w -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( lmc1992_device::enable_w )
+{
+ if ((m_enable == 0) && (state == 1))
+ {
+ UINT8 device_addr = (m_si & 0xc000) >> 14;
+ UINT8 addr = (m_si & 0x3800) >> 11;
+ UINT8 data = (m_si & 0x07e0) >> 5;
+
+ if (device_addr == MICROWIRE_DEVICE_ADDRESS)
+ {
+ execute_command(addr, data);
+ }
+ }
+
+ m_enable = state;
+}
diff --git a/src/devices/sound/lmc1992.h b/src/devices/sound/lmc1992.h
new file mode 100644
index 00000000000..0f76e38544e
--- /dev/null
+++ b/src/devices/sound/lmc1992.h
@@ -0,0 +1,113 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ LMC1992 Digitally-Controlled Stereo Tone and Volume Circuit with
+ Four-Channel Input-Selector emulation
+
+**********************************************************************
+ _____ _____
+ Data 1 |* \_/ | 28 V+
+ Clock 2 | | 27 Bypass
+ Enable 3 | | 26 Right Input 1
+ Left Input 1 4 | | 25 Right Input 2
+ Left Input 2 5 | | 24 Right Input 3
+ Left Input 3 6 | | 23 Right Input 4
+ Left Input 4 7 | LMC1992 | 22 Right Select Out
+ Left Select Out 8 | | 21 Right Select In
+ Left Select In 9 | | 20 Right Tone In
+ Left Tone In 10 | | 19 Right Tone Out
+ Left Tone Out 11 | | 18 Right Op Amp Out
+ Left Op Amp Out 12 | | 17 Right Rear Out
+ Left Rear Out 13 | | 16 Right Front Out
+ Left Front Out 14 |_____________| 15 Ground
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __LMC1992__
+#define __LMC1992__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+enum
+{
+ LMC1992_LEFT_INPUT_1 = 0,
+ LMC1992_LEFT_INPUT_2,
+ LMC1992_LEFT_INPUT_3,
+ LMC1992_LEFT_INPUT_4,
+ LMC1992_RIGHT_INPUT_1,
+ LMC1992_RIGHT_INPUT_2,
+ LMC1992_RIGHT_INPUT_3,
+ LMC1992_RIGHT_INPUT_4
+};
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_LMC1992_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, LMC1992, 0)
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> lmc1992_device
+
+class lmc1992_device : public device_t,
+ public device_sound_interface
+{
+public:
+ // construction/destruction
+ lmc1992_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_WRITE_LINE_MEMBER( clock_w );
+ DECLARE_WRITE_LINE_MEMBER( data_w );
+ DECLARE_WRITE_LINE_MEMBER( enable_w );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+
+ // internal callbacks
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ inline void execute_command(int addr, int data);
+
+ //sound_stream *m_stream[4];
+
+ int m_enable; // enable latch
+ int m_data; // data latch
+ int m_clk; // clock latch
+ UINT16 m_si; // serial in shift register
+
+ int m_input; // input select
+ int m_bass; // bass
+ int m_treble; // treble
+ int m_volume; // volume
+ int m_fader_rf; // right front fader
+ int m_fader_lf; // left front fader
+ int m_fader_rr; // right rear fader
+ int m_fader_lr; // left rear fader
+};
+
+
+// device type definition
+extern const device_type LMC1992;
+
+
+
+#endif
diff --git a/src/devices/sound/mas3507d.c b/src/devices/sound/mas3507d.c
new file mode 100644
index 00000000000..a7be9bb4ba1
--- /dev/null
+++ b/src/devices/sound/mas3507d.c
@@ -0,0 +1,262 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+//
+// MAS 3507D MPEG audio decoder
+//
+
+#include "emu.h"
+#include "mas3507d.h"
+
+// device type definition
+const device_type MAS3507D = &device_creator<mas3507d_device>;
+
+mas3507d_device::mas3507d_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MAS3507D, "MAS3507D", tag, owner, clock, "mas3507d", __FILE__),
+ device_sound_interface(mconfig, *this)
+{
+}
+
+void mas3507d_device::device_start()
+{
+}
+
+void mas3507d_device::device_reset()
+{
+ i2c_scli = i2c_sdai = true;
+ i2c_sclo = i2c_sdao = true;
+ i2c_bus_state = IDLE;
+ i2c_bus_address = UNKNOWN;
+ i2c_bus_curbit = -1;
+ i2c_bus_curval = 0;
+}
+
+void mas3507d_device::i2c_scl_w(bool line)
+{
+ if(line == i2c_scli)
+ return;
+ i2c_scli = line;
+
+ if(i2c_scli) {
+ if(i2c_bus_state == STARTED) {
+ if(i2c_sdai)
+ i2c_bus_curval |= 1 << i2c_bus_curbit;
+ i2c_bus_curbit --;
+ if(i2c_bus_curbit == -1) {
+ if(i2c_bus_address == UNKNOWN) {
+ if(i2c_device_got_address(i2c_bus_curval)) {
+ i2c_bus_state = ACK;
+ i2c_bus_address = VALIDATED;
+ } else {
+ i2c_bus_state = NAK;
+ i2c_bus_address = WRONG;
+ }
+ } else if(i2c_bus_address == VALIDATED) {
+ i2c_bus_state = ACK;
+ i2c_device_got_byte(i2c_bus_curval);
+ }
+ }
+ } else if(i2c_bus_state == ACK) {
+ i2c_bus_state = ACK2;
+ i2c_sdao = false;
+ }
+ } else {
+ if(i2c_bus_state == ACK2) {
+ i2c_bus_state = STARTED;
+ i2c_bus_curbit = 7;
+ i2c_bus_curval = 0;
+ i2c_sdao = true;
+ }
+ }
+}
+
+void mas3507d_device::i2c_nak()
+{
+ assert(i2c_bus_state == ACK);
+ i2c_bus_state = NAK;
+}
+
+void mas3507d_device::i2c_sda_w(bool line)
+{
+ if(line == i2c_sdai)
+ return;
+ i2c_sdai = line;
+
+ if(i2c_scli) {
+ if(!i2c_sdai) {
+ i2c_bus_state = STARTED;
+ i2c_bus_address = UNKNOWN;
+ i2c_bus_curbit = 7;
+ i2c_bus_curval = 0;
+ } else {
+ i2c_device_got_stop();
+ i2c_bus_state = IDLE;
+ i2c_bus_address = UNKNOWN;
+ i2c_bus_curbit = 7;
+ i2c_bus_curval = 0;
+ }
+ }
+}
+
+int mas3507d_device::i2c_scl_r()
+{
+ return i2c_scli && i2c_sclo;
+}
+
+int mas3507d_device::i2c_sda_r()
+{
+ return i2c_sdai && i2c_sdao;
+}
+
+bool mas3507d_device::i2c_device_got_address(UINT8 address)
+{
+ i2c_subdest = UNDEFINED;
+ return (address & 0xfe) == 0x3a;
+}
+
+void mas3507d_device::i2c_device_got_byte(UINT8 byte)
+{
+ switch(i2c_subdest) {
+ case UNDEFINED:
+ if(byte == 0x68)
+ i2c_subdest = DATA;
+ else if(byte == 0x69)
+ i2c_subdest = DATA;
+ else if(byte == 0x6a)
+ i2c_subdest = CONTROL;
+ else
+ i2c_subdest = BAD;
+ i2c_bytecount = 0;
+ break;
+ case BAD:
+ logerror("MAS I2C: Dropping byte %02x\n", byte);
+ break;
+ case DATA:
+ if(!i2c_bytecount) {
+ switch(byte >> 4) {
+ case 0: case 1:
+ i2c_command = CMD_RUN;
+ i2c_io_adr = byte << 8;
+ break;
+ case 3:
+ i2c_command = CMD_READ_CTRL;
+ logerror("MAS I2C: READ_CTRL\n");
+ break;
+ case 9:
+ i2c_io_adr = (byte & 15) << 4;
+ i2c_command = CMD_WRITE_REG;
+ break;
+ case 0xa: case 0xb:
+ i2c_io_bank = (byte >> 4) & 1;
+ i2c_command = CMD_WRITE_MEM;
+ break;
+ case 0xd:
+ i2c_command = CMD_READ_REG;
+ logerror("MAS I2C: READ_REG\n");
+ break;
+ case 0xe: case 0xf:
+ i2c_io_bank = (byte >> 4) & 1;
+ i2c_command = CMD_READ_MEM;
+ logerror("MAS I2C: READ_MEM\n");
+ break;
+ default:
+ i2c_command = CMD_BAD;
+ logerror("MAS I2C: BAD\n");
+ break;
+ }
+ } else {
+ switch(i2c_command) {
+ default:
+ logerror("MAS I2C: Ignoring byte %02x\n", byte);
+ break;
+
+ case CMD_WRITE_REG:
+ switch(i2c_bytecount) {
+ case 1: i2c_io_adr |= byte >> 4; i2c_io_val = byte & 15; break;
+ case 2: i2c_io_val |= byte << 12; break;
+ case 3: i2c_io_val |= byte << 4; reg_write(i2c_io_adr, i2c_io_val); break;
+ case 4: i2c_nak(); return;
+ }
+ break;
+
+ case CMD_RUN:
+ if(i2c_bytecount > 1) {
+ i2c_nak();
+ return;
+ }
+ i2c_io_adr |= byte;
+ run_program(i2c_io_adr);
+ break;
+
+ case CMD_WRITE_MEM:
+ switch(i2c_bytecount) {
+ case 2: i2c_io_count = byte << 8; break;
+ case 3: i2c_io_count |= byte; break;
+ case 4: i2c_io_adr = byte << 8; break;
+ case 5: i2c_io_adr |= byte; break;
+ }
+ if(i2c_bytecount >= 6) {
+ UINT32 i2c_wordid = (i2c_bytecount - 6) >> 2;
+ UINT32 i2c_offset = (i2c_bytecount - 6) & 3;
+ if(i2c_wordid >= i2c_io_count) {
+ i2c_nak();
+ return;
+ }
+ switch(i2c_offset) {
+ case 0: i2c_io_val = byte << 8; break;
+ case 1: i2c_io_val |= byte; break;
+ case 3: i2c_io_val |= (byte & 15) << 16; mem_write(i2c_io_bank, i2c_io_adr + i2c_wordid, i2c_io_val); break;
+ }
+ }
+ break;
+ }
+ }
+
+ i2c_bytecount++;
+ break;
+ case CONTROL:
+ logerror("MAS I2C: Control byte %02x\n", byte);
+ break;
+ }
+}
+
+void mas3507d_device::i2c_device_got_stop()
+{
+ logerror("MAS I2C: got stop\n");
+}
+
+void mas3507d_device::mem_write(int bank, UINT32 adr, UINT32 val)
+{
+ switch(adr | (bank ? 0x10000 : 0)) {
+ case 0x0032f: logerror("MAS3507D: OutputConfig = %05x\n", val); break;
+ case 0x107f8: logerror("MAS3507D: left->left gain = %05x\n", val); break;
+ case 0x107f9: logerror("MAS3507D: left->right gain = %05x\n", val); break;
+ case 0x107fa: logerror("MAS3507D: right->left gain = %05x\n", val); break;
+ case 0x107fb: logerror("MAS3507D: right->right gain = %05x\n", val); break;
+ default: logerror("MAS3507D: %d:%04x = %05x\n", bank, adr, val); break;
+ }
+}
+
+void mas3507d_device::reg_write(UINT32 adr, UINT32 val)
+{
+ switch(adr) {
+ case 0x8e: logerror("MAS3507D: DCCF = %05x\n", val); break;
+ case 0xaa: logerror("MAS3507D: Mute/bypass = %05x\n", val); break;
+ case 0xe6: logerror("MAS3507D: StartupConfig = %05x\n", val); break;
+ case 0xe7: logerror("MAS3507D: Kprescale = %05x\n", val); break;
+ case 0x6b: logerror("MAS3507D: Kbass = %05x\n", val); break;
+ case 0x6f: logerror("MAS3507D: Ktreble = %05x\n", val); break;
+ default: logerror("MAS3507D: reg %02x = %05x\n", adr, val); break;
+ }
+}
+
+void mas3507d_device::run_program(UINT32 adr)
+{
+ switch(adr) {
+ case 0xfcb: logerror("MAS3507D: validate OutputConfig\n"); break;
+ default: logerror("MAS3507D: run %04x\n", adr); break;
+ }
+}
+
+void mas3507d_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+}
diff --git a/src/devices/sound/mas3507d.h b/src/devices/sound/mas3507d.h
new file mode 100644
index 00000000000..0dad34bfe61
--- /dev/null
+++ b/src/devices/sound/mas3507d.h
@@ -0,0 +1,68 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+#pragma once
+
+#ifndef __MAS3507D_H__
+#define __MAS3507D_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_MAS3507D_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, MAS3507D, 0)
+
+#define MCFG_MAS3507D_REPLACE(_tag) \
+ MCFG_DEVICE_REPLACE(_tag, MAS3507D, 0)
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+class mas3507d_device : public device_t, public device_sound_interface
+{
+public:
+ // construction/destruction
+ mas3507d_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ int i2c_scl_r();
+ int i2c_sda_r();
+ void i2c_scl_w(bool line);
+ void i2c_sda_w(bool line);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ enum { IDLE, STARTED, NAK, ACK, ACK2 } i2c_bus_state;
+ enum { UNKNOWN, VALIDATED, WRONG } i2c_bus_address;
+
+ bool i2c_scli, i2c_sclo, i2c_sdai, i2c_sdao;
+ int i2c_bus_curbit;
+ UINT8 i2c_bus_curval;
+
+ void i2c_nak();
+ bool i2c_device_got_address(UINT8 address);
+ void i2c_device_got_byte(UINT8 byte);
+ void i2c_device_got_stop();
+
+
+ enum { UNDEFINED, CONTROL, DATA, BAD } i2c_subdest;
+ enum { CMD_BAD, CMD_RUN, CMD_READ_CTRL, CMD_WRITE_REG, CMD_WRITE_MEM, CMD_READ_REG, CMD_READ_MEM } i2c_command;
+ int i2c_bytecount;
+ UINT32 i2c_io_bank, i2c_io_adr, i2c_io_count, i2c_io_val;
+
+
+ void mem_write(int bank, UINT32 adr, UINT32 val);
+ void run_program(UINT32 adr);
+ void reg_write(UINT32 adr, UINT32 val);
+};
+
+
+// device type definition
+extern const device_type MAS3507D;
+
+#endif /* __MAS3507D_H__ */
diff --git a/src/devices/sound/mos6560.c b/src/devices/sound/mos6560.c
new file mode 100644
index 00000000000..eb4ee0a5c30
--- /dev/null
+++ b/src/devices/sound/mos6560.c
@@ -0,0 +1,969 @@
+// license:BSD-3-Clause
+// copyright-holders:Peter Trauner
+/***************************************************************************
+
+ MOS 6560 / 6561 Video Interface Chip
+
+
+ Original code by PeT (mess@utanet.at), 1999
+
+
+ 2010 FP: converted to a device and merged the video & sound components
+
+ TODO:
+ - plenty of cleanups!
+ - investigate attckufo chip features (no invert mode, no multicolor, 16 col chars)
+ - investigate why some vic20 carts crash emulation
+
+****************************************************************************
+
+ Original notes:
+
+ 2 Versions
+ 6560 NTSC
+ 6561 PAL
+ 14 bit addr bus
+ 12 bit data bus
+ (16 8 bit registers)
+ alternates with MOS 6502 on the address bus
+ fetch 8 bit characternumber and 4 bit color
+ high bit of 4 bit color value determines:
+ 0: 2 color mode
+ 1: 4 color mode
+ than fetch characterbitmap for characternumber
+ 2 color mode:
+ set bit in characterbitmap gives pixel in color of the lower 3 color bits
+ cleared bit gives pixel in backgroundcolor
+ 4 color mode:
+ 2 bits in the characterbitmap are viewed together
+ 00: backgroundcolor
+ 11: colorram
+ 01: helpercolor
+ 10: framecolor
+ advance to next character in videorram until line is full
+ repeat this 8 or 16 lines, before moving to next line in videoram
+ screen ratio ntsc, pal 4/3
+
+ pal version:
+ can contain greater visible areas
+ expects other sync position (so ntsc modules may be displayed at
+ the upper left corner of the tv screen)
+ pixel ratio seems to be different on pal and ntsc
+
+ commodore vic20 notes
+ 6560 address line 13 is connected inverted to address line 15 of the board
+ 1 K 4 bit ram at 0x9400 is additional connected as 4 higher bits
+ of the 6560 (colorram) without decoding the 6560 address line a8..a13
+
+*****************************************************************************/
+
+
+#include "emu.h"
+#include "sound/mos6560.h"
+
+
+/*****************************************************************************
+ PARAMETERS
+*****************************************************************************/
+
+#define VERBOSE_LEVEL 0
+#define DBG_LOG(N,M,A) \
+ do { \
+ if(VERBOSE_LEVEL >= N) \
+ { \
+ if( M ) \
+ logerror("%11.6f: %-24s", machine().time().as_double(), (char*) M ); \
+ logerror A; \
+ } \
+ } while (0)
+
+
+/* 2008-05 FP: lightpen code needs to read input port from vc20.c */
+
+#define LIGHTPEN_BUTTON ((!m_lightpen_button_cb.isnull()) ? m_lightpen_button_cb(0) : 0)
+#define LIGHTPEN_X_VALUE ((!m_lightpen_x_cb.isnull()) ? m_lightpen_x_cb(0) : 0)
+#define LIGHTPEN_Y_VALUE ((!m_lightpen_y_cb.isnull()) ? m_lightpen_y_cb(0) : 0)
+
+/* lightpen delivers values from internal counters
+ * they do not start with the visual area or frame area */
+#define MOS6560_X_BEGIN 38
+#define MOS6560_Y_BEGIN -6 /* first 6 lines after retrace not for lightpen! */
+#define MOS6561_X_BEGIN 38
+#define MOS6561_Y_BEGIN -6
+#define MOS656X_X_BEGIN ((m_variant == TYPE_6561) ? MOS6561_X_BEGIN : MOS6560_X_BEGIN)
+#define MOS656X_Y_BEGIN ((m_variant == TYPE_6561) ? MOS6561_Y_BEGIN : MOS6560_Y_BEGIN)
+
+#define MOS656X_MAME_XPOS ((m_variant == TYPE_6561) ? MOS6561_MAME_XPOS : MOS6560_MAME_XPOS)
+#define MOS656X_MAME_YPOS ((m_variant == TYPE_6561) ? MOS6561_MAME_YPOS : MOS6560_MAME_YPOS)
+
+/* lightpen behaviour in pal or mono multicolor not tested */
+#define MOS656X_X_VALUE ((LIGHTPEN_X_VALUE + MOS656X_X_BEGIN + MOS656X_MAME_XPOS)/2)
+#define MOS656X_Y_VALUE ((LIGHTPEN_Y_VALUE + MOS656X_Y_BEGIN + MOS656X_MAME_YPOS)/2)
+
+#define MOS656X_VRETRACERATE ((m_variant == TYPE_6561) ? MOS6561_VRETRACERATE : MOS6560_VRETRACERATE)
+
+/* ntsc 1 - 8 */
+/* pal 5 - 19 */
+#define XPOS (((int)m_reg[0] & 0x7f) * 4)
+#define YPOS ((int)m_reg[1] * 2)
+
+/* ntsc values >= 31 behave like 31 */
+/* pal value >= 32 behave like 32 */
+#define CHARS_X ((int)m_reg[2] & 0x7f)
+#define CHARS_Y (((int)m_reg[3] & 0x7e) >> 1)
+
+/* colorram and backgroundcolor are changed */
+#define INVERTED (!(m_reg[0x0f] & 8))
+
+#define CHARGENADDR (((int)m_reg[5] & 0x0f) << 10)
+#define VIDEOADDR ((((int)m_reg[5] & 0xf0) << (10 - 4)) | (((int)m_reg[2] & 0x80) << (9-7)))
+#define VIDEORAMSIZE (YSIZE * XSIZE)
+#define CHARGENSIZE (256 * HEIGHTPIXEL)
+
+#define HELPERCOLOR (m_reg[0x0e] >> 4)
+#define BACKGROUNDCOLOR (m_reg[0x0f] >> 4)
+#define FRAMECOLOR (m_reg[0x0f] & 0x07)
+
+
+// VICE palette
+static const rgb_t PALETTE_MOS[] =
+{
+ rgb_t(0x00, 0x00, 0x00),
+ rgb_t(0xff, 0xff, 0xff),
+ rgb_t(0xf0, 0x00, 0x00),
+ rgb_t(0x00, 0xf0, 0xf0),
+
+ rgb_t(0x60, 0x00, 0x60),
+ rgb_t(0x00, 0xa0, 0x00),
+ rgb_t(0x00, 0x00, 0xf0),
+ rgb_t(0xd0, 0xd0, 0x00),
+
+ rgb_t(0xc0, 0xa0, 0x00),
+ rgb_t(0xff, 0xa0, 0x00),
+ rgb_t(0xf0, 0x80, 0x80),
+ rgb_t(0x00, 0xff, 0xff),
+
+ rgb_t(0xff, 0x00, 0xff),
+ rgb_t(0x00, 0xff, 0x00),
+ rgb_t(0x00, 0xa0, 0xff),
+ rgb_t(0xff, 0xff, 0x00)
+};
+
+
+
+/*****************************************************************************
+ IMPLEMENTATION
+*****************************************************************************/
+
+inline UINT8 mos6560_device::read_videoram(offs_t offset)
+{
+ m_last_data = space(AS_0).read_byte(offset & 0x3fff);
+
+ return m_last_data;
+}
+
+inline UINT8 mos6560_device::read_colorram(offs_t offset)
+{
+ return space(AS_1).read_byte(offset & 0x3ff);
+}
+
+/*-------------------------------------------------
+ draw_character
+-------------------------------------------------*/
+
+void mos6560_device::draw_character( int ybegin, int yend, int ch, int yoff, int xoff, UINT16 *color )
+{
+ int y, code;
+
+ for (y = ybegin; y <= yend; y++)
+ {
+ code = read_videoram((m_chargenaddr + ch * m_charheight + y) & 0x3fff);
+
+ m_bitmap.pix32(y + yoff, xoff + 0) = PALETTE_MOS[color[code >> 7]];
+ m_bitmap.pix32(y + yoff, xoff + 1) = PALETTE_MOS[color[(code >> 6) & 1]];
+ m_bitmap.pix32(y + yoff, xoff + 2) = PALETTE_MOS[color[(code >> 5) & 1]];
+ m_bitmap.pix32(y + yoff, xoff + 3) = PALETTE_MOS[color[(code >> 4) & 1]];
+ m_bitmap.pix32(y + yoff, xoff + 4) = PALETTE_MOS[color[(code >> 3) & 1]];
+ m_bitmap.pix32(y + yoff, xoff + 5) = PALETTE_MOS[color[(code >> 2) & 1]];
+ m_bitmap.pix32(y + yoff, xoff + 6) = PALETTE_MOS[color[(code >> 1) & 1]];
+ m_bitmap.pix32(y + yoff, xoff + 7) = PALETTE_MOS[color[code & 1]];
+ }
+}
+
+
+/*-------------------------------------------------
+ draw_character_multi
+-------------------------------------------------*/
+
+void mos6560_device::draw_character_multi( int ybegin, int yend, int ch, int yoff, int xoff, UINT16 *color )
+{
+ int y, code;
+
+ for (y = ybegin; y <= yend; y++)
+ {
+ code = read_videoram((m_chargenaddr + ch * m_charheight + y) & 0x3fff);
+
+ m_bitmap.pix32(y + yoff, xoff + 0) =
+ m_bitmap.pix32(y + yoff, xoff + 1) = PALETTE_MOS[color[code >> 6]];
+ m_bitmap.pix32(y + yoff, xoff + 2) =
+ m_bitmap.pix32(y + yoff, xoff + 3) = PALETTE_MOS[color[(code >> 4) & 3]];
+ m_bitmap.pix32(y + yoff, xoff + 4) =
+ m_bitmap.pix32(y + yoff, xoff + 5) = PALETTE_MOS[color[(code >> 2) & 3]];
+ m_bitmap.pix32(y + yoff, xoff + 6) =
+ m_bitmap.pix32(y + yoff, xoff + 7) = PALETTE_MOS[color[code & 3]];
+ }
+}
+
+
+/*-------------------------------------------------
+ drawlines - draw a certain numer of lines
+-------------------------------------------------*/
+
+void mos6560_device::drawlines( int first, int last )
+{
+ int line, vline;
+ int offs, yoff, xoff, ybegin, yend, i, j;
+ int attr, ch;
+
+ m_lastline = last;
+ if (first >= last)
+ return;
+
+ for (line = first; (line < m_ypos) && (line < last); line++)
+ {
+ for (j = 0; j < m_total_xsize; j++)
+ m_bitmap.pix32(line, j) = PALETTE_MOS[m_framecolor];
+ }
+
+ for (vline = line - m_ypos; (line < last) && (line < m_ypos + m_ysize);)
+ {
+ if (m_matrix8x16)
+ {
+ offs = (vline >> 4) * m_chars_x;
+ yoff = (vline & ~0xf) + m_ypos;
+ ybegin = vline & 0xf;
+ yend = (vline + 0xf < last - m_ypos) ? 0xf : ((last - line) & 0xf) + ybegin;
+ }
+ else
+ {
+ offs = (vline >> 3) * m_chars_x;
+ yoff = (vline & ~7) + m_ypos;
+ ybegin = vline & 7;
+ yend = (vline + 7 < last - m_ypos) ? 7 : ((last - line) & 7) + ybegin;
+ }
+
+ if (m_xpos > 0)
+ {
+ for (i = ybegin; i <= yend; i++)
+ for (j = 0; j < m_xpos; j++)
+ m_bitmap.pix32(yoff + i, j) = PALETTE_MOS[m_framecolor];
+ }
+
+ for (xoff = m_xpos; (xoff < m_xpos + m_xsize) && (xoff < m_total_xsize); xoff += 8, offs++)
+ {
+ ch = read_videoram((m_videoaddr + offs) & 0x3fff);
+
+ attr = (read_colorram((m_videoaddr + offs) & 0x3fff)) & 0xf;
+
+ if (m_variant == TYPE_ATTACK_UFO)
+ {
+ /* the mos6560 variant used in attckufo only has only one draw mode */
+ m_mono[1] = attr;
+ draw_character(ybegin, yend, ch, yoff, xoff, m_mono);
+ }
+ else if (m_inverted)
+ {
+ if (attr & 8)
+ {
+ m_multiinverted[0] = attr & 7;
+ draw_character_multi(ybegin, yend, ch, yoff, xoff, m_multiinverted);
+ }
+ else
+ {
+ m_monoinverted[0] = attr;
+ draw_character(ybegin, yend, ch, yoff, xoff, m_monoinverted);
+ }
+ }
+ else
+ {
+ if (attr & 8)
+ {
+ m_multi[2] = attr & 7;
+ draw_character_multi(ybegin, yend, ch, yoff, xoff, m_multi);
+ }
+ else
+ {
+ m_mono[1] = attr;
+ draw_character(ybegin, yend, ch, yoff, xoff, m_mono);
+ }
+ }
+ }
+
+ if (xoff < m_total_xsize)
+ {
+ for (i = ybegin; i <= yend; i++)
+ for (j = xoff; j < m_total_xsize; j++)
+ m_bitmap.pix32(yoff + i, j) = PALETTE_MOS[m_framecolor];
+ }
+
+ if (m_matrix8x16)
+ {
+ vline = (vline + 16) & ~0xf;
+ line = vline + m_ypos;
+ }
+ else
+ {
+ vline = (vline + 8) & ~7;
+ line = vline + m_ypos;
+ }
+ }
+
+ for (; line < last; line++)
+ for (j = 0; j < m_total_xsize; j++)
+ m_bitmap.pix32(line, j) = PALETTE_MOS[m_framecolor];
+}
+
+
+/*-------------------------------------------------
+ mos6560_port_w - write to regs
+-------------------------------------------------*/
+
+WRITE8_MEMBER( mos6560_device::write )
+{
+ DBG_LOG(1, "mos6560_port_w", ("%.4x:%.2x\n", offset, data));
+
+ switch (offset)
+ {
+ case 0xa:
+ case 0xb:
+ case 0xc:
+ case 0xd:
+ case 0xe:
+ soundport_w(offset, data);
+ break;
+ }
+
+ if (m_reg[offset] != data)
+ {
+ switch (offset)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 5:
+ case 0xe:
+ case 0xf:
+ drawlines(m_lastline, m_rasterline);
+ break;
+ }
+ m_reg[offset] = data;
+
+ switch (offset)
+ {
+ case 0:
+ if ((m_variant != TYPE_ATTACK_UFO))
+ m_xpos = XPOS;
+ break;
+ case 1:
+ if ((m_variant != TYPE_ATTACK_UFO))
+ m_ypos = YPOS;
+ break;
+ case 2:
+ /* ntsc values >= 31 behave like 31 */
+ /* pal value >= 32 behave like 32 */
+ m_chars_x = CHARS_X;
+ m_videoaddr = VIDEOADDR;
+ m_xsize = CHARS_X * 8;
+ break;
+ case 3:
+ if ((m_variant != TYPE_ATTACK_UFO))
+ {
+ m_matrix8x16 = data & 0x01;
+ m_charheight = m_matrix8x16 ? 16 : 8;
+ }
+ m_chars_y = CHARS_Y;
+ m_ysize = CHARS_Y * m_charheight;
+ break;
+ case 5:
+ m_chargenaddr = CHARGENADDR;
+ m_videoaddr = VIDEOADDR;
+ break;
+ case 0xe:
+ m_multi[3] = m_multiinverted[3] = m_helpercolor = HELPERCOLOR;
+ break;
+ case 0xf:
+ if ((m_variant != TYPE_ATTACK_UFO))
+ m_inverted = INVERTED;
+ m_multi[1] = m_multiinverted[1] = m_framecolor = FRAMECOLOR;
+ m_mono[0] = m_monoinverted[1] = m_multi[0] = m_multiinverted[2] = m_backgroundcolor = BACKGROUNDCOLOR;
+ break;
+ }
+ }
+}
+
+/*-------------------------------------------------
+ mos6560_port_r - read from regs
+-------------------------------------------------*/
+
+READ8_MEMBER( mos6560_device::read )
+{
+ int val;
+
+ switch (offset)
+ {
+ case 3:
+ val = ((m_rasterline & 1) << 7) | (m_reg[offset] & 0x7f);
+ break;
+ case 4: /*rasterline */
+ drawlines(m_lastline, m_rasterline);
+ val = (m_rasterline / 2) & 0xff;
+ break;
+ case 6: /*lightpen horizontal */
+ case 7: /*lightpen vertical */
+#ifdef UNUSED_FUNCTION
+ if (LIGHTPEN_BUTTON && ((machine().time().as_double() - m_lightpenreadtime) * MOS656X_VRETRACERATE >= 1))
+ {
+ /* only 1 update each frame */
+ /* and diode must recognize light */
+ if (1)
+ {
+ m_reg[6] = MOS656X_X_VALUE;
+ m_reg[7] = MOS656X_Y_VALUE;
+ }
+ m_lightpenreadtime = machine().time().as_double();
+ }
+#endif
+ val = m_reg[offset];
+ break;
+ case 8: /* poti 1 */
+ val = m_read_potx(0);
+ break;
+ case 9: /* poti 2 */
+ val = m_read_poty(0);
+ break;
+ default:
+ val = m_reg[offset];
+ break;
+ }
+ DBG_LOG(3, "mos6560_port_r", ("%.4x:%.2x\n", offset, val));
+ return val;
+}
+
+WRITE_LINE_MEMBER( mos6560_device::lp_w )
+{
+ // TODO
+}
+
+UINT8 mos6560_device::bus_r()
+{
+ return m_last_data;
+}
+
+/*-------------------------------------------------
+ mos6560_raster_interrupt_gen
+-------------------------------------------------*/
+
+void mos6560_device::raster_interrupt_gen()
+{
+ m_rasterline++;
+ if (m_rasterline >= m_total_lines)
+ {
+ m_rasterline = 0;
+ drawlines(m_lastline, m_total_lines);
+ m_lastline = 0;
+ }
+}
+
+
+/*-------------------------------------------------
+ mos6560_video_update - copy the VIC bitmap to
+ main screen bitmap
+-------------------------------------------------*/
+
+UINT32 mos6560_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+
+ return 0;
+}
+
+/*****************************************************************************
+ SOUND IMPLEMENTATION
+*****************************************************************************/
+
+/*
+ * assumed model:
+ * each write to a ton/noise generated starts it new
+ * each generator behaves like an timer
+ * when it reaches 0, the next samplevalue is given out
+ */
+
+/*
+ * noise channel
+ * based on a document by diku0748@diku.dk (Asger Alstrup Nielsen)
+ *
+ * 23 bit shift register
+ * initial value (0x7ffff8)
+ * after shift bit 0 is set to bit 22 xor bit 17
+ * dac sample bit22 bit20 bit16 bit13 bit11 bit7 bit4 bit2(lsb)
+ *
+ * emulation:
+ * allocate buffer for 5 sec sampledata (fastest played frequency)
+ * and fill this buffer in init with the required sample
+ * fast turning off channel, immediate change of frequency
+ */
+
+#define NOISE_BUFFER_SIZE_SEC 5
+
+#define TONE1_ON (m_reg[0x0a] & 0x80)
+#define TONE2_ON (m_reg[0x0b] & 0x80)
+#define TONE3_ON (m_reg[0x0c] & 0x80)
+#define NOISE_ON (m_reg[0x0d] & 0x80)
+#define VOLUME (m_reg[0x0e] & 0x0f)
+
+#define TONE_FREQUENCY_MIN (clock()/256/128)
+
+#define TONE1_VALUE (8 * (128 - ((m_reg[0x0a] + 1) & 0x7f)))
+#define TONE1_FREQUENCY (clock()/32/TONE1_VALUE)
+
+#define TONE2_VALUE (4 * (128 - ((m_reg[0x0b] + 1) & 0x7f)))
+#define TONE2_FREQUENCY (clock()/32/TONE2_VALUE)
+
+#define TONE3_VALUE (2 * (128 - ((m_reg[0x0c] + 1) & 0x7f)))
+#define TONE3_FREQUENCY (clock()/32/TONE3_VALUE)
+
+#define NOISE_VALUE (32 * (128 - ((m_reg[0x0d] + 1) & 0x7f)))
+#define NOISE_FREQUENCY (clock()/NOISE_VALUE)
+
+#define NOISE_FREQUENCY_MAX (clock()/32/1)
+
+
+/*-------------------------------------------------
+ mos6560_soundport_w - write to regs
+-------------------------------------------------*/
+
+void mos6560_device::soundport_w( int offset, int data )
+{
+ int old = m_reg[offset];
+ m_channel->update();
+
+ switch (offset)
+ {
+ case 0x0a:
+ m_reg[offset] = data;
+ if (!(old & 0x80) && TONE1_ON)
+ {
+ m_tone1pos = 0;
+ m_tone1samples = machine().sample_rate() / TONE1_FREQUENCY;
+ if (m_tone1samples == 0)
+ m_tone1samples = 1;
+ }
+ DBG_LOG(1, "mos6560", ("tone1 %.2x %d\n", data, TONE1_FREQUENCY));
+ break;
+ case 0x0b:
+ m_reg[offset] = data;
+ if (!(old & 0x80) && TONE2_ON)
+ {
+ m_tone2pos = 0;
+ m_tone2samples = machine().sample_rate() / TONE2_FREQUENCY;
+ if (m_tone2samples == 0)
+ m_tone2samples = 1;
+ }
+ DBG_LOG(1, "mos6560", ("tone2 %.2x %d\n", data, TONE2_FREQUENCY));
+ break;
+ case 0x0c:
+ m_reg[offset] = data;
+ if (!(old & 0x80) && TONE3_ON)
+ {
+ m_tone3pos = 0;
+ m_tone3samples = machine().sample_rate() / TONE3_FREQUENCY;
+ if (m_tone3samples == 0)
+ m_tone3samples = 1;
+ }
+ DBG_LOG(1, "mos6560", ("tone3 %.2x %d\n", data, TONE3_FREQUENCY));
+ break;
+ case 0x0d:
+ m_reg[offset] = data;
+ if (NOISE_ON)
+ {
+ m_noisesamples = (int) ((double) NOISE_FREQUENCY_MAX * machine().sample_rate()
+ * NOISE_BUFFER_SIZE_SEC / NOISE_FREQUENCY);
+ DBG_LOG (1, "mos6560", ("noise %.2x %d sample:%d\n",
+ data, NOISE_FREQUENCY, m_noisesamples));
+ if ((double) m_noisepos / m_noisesamples >= 1.0)
+ {
+ m_noisepos = 0;
+ }
+ }
+ else
+ {
+ m_noisepos = 0;
+ }
+ break;
+ case 0x0e:
+ m_reg[offset] = (old & ~0x0f) | (data & 0x0f);
+ DBG_LOG (3, "mos6560", ("volume %d\n", data & 0x0f));
+ break;
+ }
+}
+
+
+/*****************************************************************************
+ DEVICE INTERFACE
+*****************************************************************************/
+
+/*-------------------------------------------------
+ mos6560_sound_start - start audio emulation
+ (to be called at device start)
+-------------------------------------------------*/
+
+void mos6560_device::sound_start()
+{
+ int i;
+
+ m_channel = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate());
+
+ /* buffer for fastest played sample for 5 second so we have enough data for min 5 second */
+ m_noisesize = NOISE_FREQUENCY_MAX * NOISE_BUFFER_SIZE_SEC;
+ m_noise = auto_alloc_array(machine(), INT8, m_noisesize);
+ {
+ int noiseshift = 0x7ffff8;
+ char data;
+
+ for (i = 0; i < m_noisesize; i++)
+ {
+ data = 0;
+ if (noiseshift & 0x400000)
+ data |= 0x80;
+ if (noiseshift & 0x100000)
+ data |= 0x40;
+ if (noiseshift & 0x010000)
+ data |= 0x20;
+ if (noiseshift & 0x002000)
+ data |= 0x10;
+ if (noiseshift & 0x000800)
+ data |= 0x08;
+ if (noiseshift & 0x000080)
+ data |= 0x04;
+ if (noiseshift & 0x000010)
+ data |= 0x02;
+ if (noiseshift & 0x000004)
+ data |= 0x01;
+ m_noise[i] = data;
+ if (((noiseshift & 0x400000) == 0) != ((noiseshift & 0x002000) == 0))
+ noiseshift = (noiseshift << 1) | 1;
+ else
+ noiseshift <<= 1;
+ }
+ }
+ m_tonesize = machine().sample_rate() / TONE_FREQUENCY_MIN;
+
+ if (m_tonesize > 0)
+ {
+ m_tone = auto_alloc_array(machine(), INT16, m_tonesize);
+
+ for (i = 0; i < m_tonesize; i++)
+ {
+ m_tone[i] = (INT16)(sin (2 * M_PI * i / m_tonesize) * 127 + 0.5);
+ }
+ }
+ else
+ {
+ m_tone = NULL;
+ }
+}
+
+
+const device_type MOS6560 = &device_creator<mos6560_device>;
+const device_type MOS6561 = &device_creator<mos6561_device>;
+const device_type MOS656X_ATTACK_UFO = &device_creator<mos656x_attack_ufo_device>;
+
+// default address maps
+static ADDRESS_MAP_START( mos6560_videoram_map, AS_0, 8, mos6560_device )
+ AM_RANGE(0x0000, 0x3fff) AM_RAM
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( mos6560_colorram_map, AS_1, 8, mos6560_device )
+ AM_RANGE(0x000, 0x3ff) AM_RAM
+ADDRESS_MAP_END
+
+mos6560_device::mos6560_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_memory_interface(mconfig, *this),
+ device_sound_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_variant(variant),
+ m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 14, 0, NULL, *ADDRESS_MAP_NAME(mos6560_videoram_map)),
+ m_colorram_space_config("colorram", ENDIANNESS_LITTLE, 8, 10, 0, NULL, *ADDRESS_MAP_NAME(mos6560_colorram_map)),
+ m_read_potx(*this),
+ m_read_poty(*this)
+{
+}
+
+mos6560_device::mos6560_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MOS6560, "MOS6560", tag, owner, clock, "mos6560", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_sound_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_variant(TYPE_6560),
+ m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 14, 0, NULL, *ADDRESS_MAP_NAME(mos6560_videoram_map)),
+ m_colorram_space_config("colorram", ENDIANNESS_LITTLE, 8, 10, 0, NULL, *ADDRESS_MAP_NAME(mos6560_colorram_map)),
+ m_read_potx(*this),
+ m_read_poty(*this)
+{
+}
+
+mos6561_device::mos6561_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ :mos6560_device(mconfig, MOS6561, "MOS6561", tag, owner, clock, TYPE_6561, "mos6561", __FILE__) { }
+
+mos656x_attack_ufo_device::mos656x_attack_ufo_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ :mos6560_device(mconfig, MOS656X_ATTACK_UFO, "MOS656X", tag, owner, clock, TYPE_ATTACK_UFO, "mos656x_attack_ufo", __FILE__) { }
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *mos6560_device::memory_space_config(address_spacenum spacenum) const
+{
+ switch (spacenum)
+ {
+ case AS_0: return &m_videoram_space_config;
+ case AS_1: return &m_colorram_space_config;
+ default: return NULL;
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mos6560_device::device_start()
+{
+ m_screen->register_screen_bitmap(m_bitmap);
+
+ // resolve callbacks
+ m_read_potx.resolve_safe(0xff);
+ m_read_poty.resolve_safe(0xff);
+
+ switch (m_variant)
+ {
+ case TYPE_6560:
+ m_total_xsize = MOS6560_XSIZE;
+ m_total_ysize = MOS6560_YSIZE;
+ m_total_lines = MOS6560_LINES;
+ m_total_vretracerate = MOS6560_VRETRACERATE;
+ break;
+
+ case TYPE_ATTACK_UFO:
+ m_total_xsize = 23 * 8;
+ m_total_ysize = 22 * 8;
+ m_total_lines = MOS6560_LINES;
+ m_total_vretracerate = MOS6560_VRETRACERATE;
+ break;
+
+ case TYPE_6561:
+ m_total_xsize = MOS6561_XSIZE;
+ m_total_ysize = MOS6561_YSIZE;
+ m_total_lines = MOS6561_LINES;
+ m_total_vretracerate = MOS6561_VRETRACERATE;
+ break;
+ }
+
+ // allocate timers
+ m_line_timer = timer_alloc(TIMER_LINE);
+ m_line_timer->adjust(m_screen->scan_period(), 0, m_screen->scan_period());
+
+ // initialize sound
+ sound_start();
+
+ // state saving
+ save_item(NAME(m_lightpenreadtime));
+ save_item(NAME(m_rasterline));
+ save_item(NAME(m_lastline));
+
+ save_item(NAME(m_charheight));
+ save_item(NAME(m_matrix8x16));
+ save_item(NAME(m_inverted));
+ save_item(NAME(m_chars_x));
+ save_item(NAME(m_chars_y));
+ save_item(NAME(m_xsize));
+ save_item(NAME(m_ysize));
+ save_item(NAME(m_xpos));
+ save_item(NAME(m_ypos));
+ save_item(NAME(m_chargenaddr));
+ save_item(NAME(m_videoaddr));
+
+ save_item(NAME(m_backgroundcolor));
+ save_item(NAME(m_framecolor));
+ save_item(NAME(m_helpercolor));
+
+ save_item(NAME(m_reg));
+
+ save_item(NAME(m_mono));
+ save_item(NAME(m_monoinverted));
+ save_item(NAME(m_multi));
+ save_item(NAME(m_multiinverted));
+
+ save_item(NAME(m_last_data));
+
+ save_item(NAME(m_tone1pos));
+ save_item(NAME(m_tone2pos));
+ save_item(NAME(m_tone3pos));
+ save_item(NAME(m_tone1samples));
+ save_item(NAME(m_tone2samples));
+ save_item(NAME(m_tone3samples));
+ save_item(NAME(m_noisepos));
+ save_item(NAME(m_noisesamples));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void mos6560_device::device_reset()
+{
+ m_lightpenreadtime = 0.0;
+ m_rasterline = 0;
+ m_lastline = 0;
+
+ memset(m_reg, 0, 16);
+
+ m_charheight = 8;
+ m_matrix8x16 = 0;
+ m_inverted = 0;
+ m_chars_x = 0;
+ m_chars_y = 0;
+ m_xsize = 0;
+ m_ysize = 0;
+ m_xpos = 0;
+ m_ypos = 0;
+ m_chargenaddr = 0;
+ m_videoaddr = 0;
+
+ m_backgroundcolor = 0;
+ m_framecolor = 0;
+ m_helpercolor = 0;
+
+ m_mono[0] = 0;
+ m_mono[1] = 0;
+ m_monoinverted[0] = 0;
+ m_monoinverted[1] = 0;
+ m_multi[0] = 0;
+ m_multi[1] = 0;
+ m_multi[2] = 0;
+ m_multi[3] = 0;
+ m_multiinverted[0] = 0;
+ m_multiinverted[1] = 0;
+ m_multiinverted[2] = 0;
+ m_multiinverted[3] = 0;
+
+ m_last_data = 0;
+
+ m_tone1pos = 0;
+ m_tone2pos = 0;
+ m_tone3pos = 0;
+ m_tone1samples = 1;
+ m_tone2samples = 1;
+ m_tone3samples = 1;
+ m_noisepos = 0;
+ m_noisesamples = 1;
+}
+
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+
+void mos6560_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_LINE:
+ raster_interrupt_gen();
+ break;
+ }
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void mos6560_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int i, v;
+ stream_sample_t *buffer = outputs[0];
+
+ for (i = 0; i < samples; i++)
+ {
+ v = 0;
+ if (TONE1_ON /*||(m_tone1pos != 0) */ )
+ {
+ v += m_tone[m_tone1pos * m_tonesize / m_tone1samples];
+ m_tone1pos++;
+#if 0
+ m_tone1pos %= m_tone1samples;
+#else
+ if (m_tone1pos >= m_tone1samples)
+ {
+ m_tone1pos = 0;
+ m_tone1samples = machine().sample_rate() / TONE1_FREQUENCY;
+ if (m_tone1samples == 0)
+ m_tone1samples = 1;
+ }
+#endif
+ }
+ if (TONE2_ON /*||(m_tone2pos != 0) */ )
+ {
+ v += m_tone[m_tone2pos * m_tonesize / m_tone2samples];
+ m_tone2pos++;
+#if 0
+ m_tone2pos %= m_tone2samples;
+#else
+ if (m_tone2pos >= m_tone2samples)
+ {
+ m_tone2pos = 0;
+ m_tone2samples = machine().sample_rate() / TONE2_FREQUENCY;
+ if (m_tone2samples == 0)
+ m_tone2samples = 1;
+ }
+#endif
+ }
+ if (TONE3_ON /*||(m_tone3pos != 0) */ )
+ {
+ v += m_tone[m_tone3pos * m_tonesize / m_tone3samples];
+ m_tone3pos++;
+#if 0
+ m_tone3pos %= m_tone3samples;
+#else
+ if (m_tone3pos >= m_tone3samples)
+ {
+ m_tone3pos = 0;
+ m_tone3samples = machine().sample_rate() / TONE3_FREQUENCY;
+ if (m_tone3samples == 0)
+ m_tone3samples = 1;
+ }
+#endif
+ }
+ if (NOISE_ON)
+ {
+ v += m_noise[(int) ((double) m_noisepos * m_noisesize / m_noisesamples)];
+ m_noisepos++;
+ if ((double) m_noisepos / m_noisesamples >= 1.0)
+ {
+ m_noisepos = 0;
+ }
+ }
+ v = (v * VOLUME) << 2;
+ if (v > 32767)
+ buffer[i] = 32767;
+ else if (v < -32767)
+ buffer[i] = -32767;
+ else
+ buffer[i] = v;
+ }
+}
diff --git a/src/devices/sound/mos6560.h b/src/devices/sound/mos6560.h
new file mode 100644
index 00000000000..05768815bf0
--- /dev/null
+++ b/src/devices/sound/mos6560.h
@@ -0,0 +1,260 @@
+// license:BSD-3-Clause
+// copyright-holders:Peter Trauner
+/***************************************************************************
+
+ MOS 6560/6561 Video Interface Chip (VIC) emulation
+
+****************************************************************************
+ _____ _____
+ N/C 1 |* \_/ | 40 Vdd
+ CHROMA 2 | | 39 phi1
+ LUMA/SYNC 3 | | 38 phi2
+ R/W 4 | | 37 OPTION
+ D11 5 | | 36 Pphi2
+ D10 6 | | 35 Pphi1
+ D9 7 | | 34 A13
+ D8 8 | | 33 A12
+ D7 9 | | 32 A11
+ D6 10 | MOS6560 | 31 A10
+ D5 11 | MOS6561 | 30 A9
+ D4 12 | | 29 A8
+ D3 13 | | 28 A7
+ D2 14 | | 27 A6
+ D1 15 | | 26 A5
+ D0 16 | | 25 A4
+ POT X 17 | | 24 A3
+ POT Y 18 | | 23 A2
+ AUDIO 19 | | 22 A1
+ Vss 20 |_____________| 21 A0
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __MOS6560__
+#define __MOS6560__
+
+#include "emu.h"
+
+
+
+//***************************************************************************
+// DEVICE CONFIGURATION MACROS
+//***************************************************************************
+
+#define MCFG_MOS6560_ADD(_tag, _screen_tag, _clock, _videoram_map, _colorram_map) \
+ MCFG_SCREEN_ADD(_screen_tag, RASTER) \
+ MCFG_SCREEN_REFRESH_RATE(MOS6560_VRETRACERATE) \
+ MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) \
+ MCFG_SCREEN_SIZE((MOS6560_XSIZE + 7) & ~7, MOS6560_YSIZE) \
+ MCFG_SCREEN_VISIBLE_AREA(MOS6560_MAME_XPOS, MOS6560_MAME_XPOS + MOS6560_MAME_XSIZE - 1, MOS6560_MAME_YPOS, MOS6560_MAME_YPOS + MOS6560_MAME_YSIZE - 1) \
+ MCFG_SCREEN_UPDATE_DEVICE(_tag, mos6560_device, screen_update) \
+ MCFG_SOUND_ADD(_tag, MOS6560, _clock) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag) \
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, _videoram_map) \
+ MCFG_DEVICE_ADDRESS_MAP(AS_1, _colorram_map)
+
+#define MCFG_MOS6561_ADD(_tag, _screen_tag, _clock, _videoram_map, _colorram_map) \
+ MCFG_SCREEN_ADD(_screen_tag, RASTER) \
+ MCFG_SCREEN_REFRESH_RATE(MOS6561_VRETRACERATE) \
+ MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) \
+ MCFG_SCREEN_SIZE((MOS6561_XSIZE + 7) & ~7, MOS6561_YSIZE) \
+ MCFG_SCREEN_VISIBLE_AREA(MOS6561_MAME_XPOS, MOS6561_MAME_XPOS + MOS6561_MAME_XSIZE - 1, MOS6561_MAME_YPOS, MOS6561_MAME_YPOS + MOS6561_MAME_YSIZE - 1) \
+ MCFG_SCREEN_UPDATE_DEVICE(_tag, mos6560_device, screen_update) \
+ MCFG_SOUND_ADD(_tag, MOS6561, _clock) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag) \
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, _videoram_map) \
+ MCFG_DEVICE_ADDRESS_MAP(AS_1, _colorram_map)
+
+#define MCFG_MOS656X_ATTACK_UFO_ADD(_tag, _screen_tag, _clock, _videoram_map, _colorram_map) \
+ MCFG_SCREEN_ADD(_screen_tag, RASTER) \
+ MCFG_SCREEN_REFRESH_RATE(MOS6560_VRETRACERATE) \
+ MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) \
+ MCFG_SCREEN_SIZE((MOS6560_XSIZE + 7) & ~7, MOS6560_YSIZE) \
+ MCFG_SCREEN_VISIBLE_AREA(0, 23*8 - 1, 0, 22*8 - 1) \
+ MCFG_SCREEN_UPDATE_DEVICE(_tag, mos6560_device, screen_update) \
+ MCFG_SOUND_ADD(_tag, MOS656X_ATTACK_UFO, _clock) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag) \
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, _videoram_map) \
+ MCFG_DEVICE_ADDRESS_MAP(AS_1, _colorram_map)
+
+
+#define MCFG_MOS6560_POTX_CALLBACK(_read) \
+ devcb = &mos6560_device::set_potx_rd_callback(*device, DEVCB_##_read);
+
+#define MCFG_MOS6560_POTY_CALLBACK(_read) \
+ devcb = &mos6560_device::set_poty_rd_callback(*device, DEVCB_##_read);
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define MOS6560_VRETRACERATE 60
+#define MOS6561_VRETRACERATE 50
+
+#define MOS6560_MAME_XPOS 4 /* xleft not displayed */
+#define MOS6560_MAME_YPOS 10 /* y up not displayed */
+#define MOS6561_MAME_XPOS 20
+#define MOS6561_MAME_YPOS 10
+#define MOS6560_MAME_XSIZE 200
+#define MOS6560_MAME_YSIZE 248
+#define MOS6561_MAME_XSIZE 224
+#define MOS6561_MAME_YSIZE 296
+/* real values */
+
+#define MOS6560_LINES 261
+#define MOS6561_LINES 312
+
+#define MOS6560_XSIZE (4+201) /* 4 left not visible */
+#define MOS6560_YSIZE (10+251) /* 10 not visible */
+/* cycles 65 */
+
+#define MOS6561_XSIZE (20+229) /* 20 left not visible */
+#define MOS6561_YSIZE (10+302) /* 10 not visible */
+/* cycles 71 */
+
+
+/* the following values depend on the VIC clock,
+ * but to achieve TV-frequency the clock must have a fix frequency */
+#define MOS6560_CLOCK (14318181/14)
+#define MOS6561_CLOCK (4433618/4)
+
+
+
+//***************************************************************************
+// TYPE DEFINITIONS
+//***************************************************************************
+
+// ======================> mos6560_device
+
+class mos6560_device : public device_t,
+ public device_memory_interface,
+ public device_sound_interface,
+ public device_video_interface
+{
+public:
+ mos6560_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source);
+ mos6560_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _Object> static devcb_base &set_potx_rd_callback(device_t &device, _Object object) { return downcast<mos6560_device &>(device).m_read_potx.set_callback(object); }
+ template<class _Object> static devcb_base &set_poty_rd_callback(device_t &device, _Object object) { return downcast<mos6560_device &>(device).m_read_poty.set_callback(object); }
+
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ UINT8 bus_r();
+
+ DECLARE_WRITE_LINE_MEMBER( lp_w );
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+protected:
+ enum
+ {
+ TYPE_6560, // NTSC-M
+ TYPE_6561, // PAL-B
+ TYPE_ATTACK_UFO // NTSC-M, less features
+ };
+
+ enum
+ {
+ TIMER_LINE
+ };
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ inline UINT8 read_videoram(offs_t offset);
+ inline UINT8 read_colorram(offs_t offset);
+
+ void draw_character( int ybegin, int yend, int ch, int yoff, int xoff, UINT16 *color );
+ void draw_character_multi( int ybegin, int yend, int ch, int yoff, int xoff, UINT16 *color );
+ void drawlines( int first, int last );
+ void soundport_w( int offset, int data );
+ void sound_start();
+ void raster_interrupt_gen();
+
+ int m_variant;
+
+ const address_space_config m_videoram_space_config;
+ const address_space_config m_colorram_space_config;
+
+ devcb_read8 m_read_potx;
+ devcb_read8 m_read_poty;
+
+ UINT8 m_reg[16];
+
+ bitmap_rgb32 m_bitmap;
+
+ int m_rasterline, m_lastline;
+ double m_lightpenreadtime;
+
+ int m_charheight, m_matrix8x16, m_inverted;
+ int m_chars_x, m_chars_y;
+ int m_xsize, m_ysize, m_xpos, m_ypos;
+ int m_chargenaddr, m_videoaddr;
+
+ /* values in videoformat */
+ UINT16 m_backgroundcolor, m_framecolor, m_helpercolor;
+
+ /* arrays for bit to color conversion without condition checking */
+ UINT16 m_mono[2], m_monoinverted[2], m_multi[4], m_multiinverted[4];
+
+ /* video chip settings */
+ int m_total_xsize, m_total_ysize, m_total_lines, m_total_vretracerate;
+
+ /* DMA */
+ UINT8 m_last_data;
+
+ /* sound part */
+ int m_tone1pos, m_tone2pos, m_tone3pos,
+ m_tonesize, m_tone1samples, m_tone2samples, m_tone3samples,
+ m_noisesize, /* number of samples */
+ m_noisepos, /* pos of tone */
+ m_noisesamples; /* count of samples to give out per tone */
+
+ sound_stream *m_channel;
+ INT16 *m_tone;
+ INT8 *m_noise;
+
+ emu_timer *m_line_timer;
+};
+
+
+// ======================> mos6561_device
+
+class mos6561_device : public mos6560_device
+{
+public:
+ // construction/destruction
+ mos6561_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+
+// ======================> mos656x_attack_ufo_device
+
+class mos656x_attack_ufo_device : public mos6560_device
+{
+public:
+ // construction/destruction
+ mos656x_attack_ufo_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+
+// device type definitions
+extern const device_type MOS6560;
+extern const device_type MOS6561;
+extern const device_type MOS656X_ATTACK_UFO;
+
+
+
+#endif
diff --git a/src/devices/sound/mos6581.c b/src/devices/sound/mos6581.c
new file mode 100644
index 00000000000..73115c79f83
--- /dev/null
+++ b/src/devices/sound/mos6581.c
@@ -0,0 +1,157 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods, Curt Coder
+/**********************************************************************
+
+ MOS 6581/8580 Sound Interface Device emulation
+
+**********************************************************************/
+
+#include "mos6581.h"
+#include "sid.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+// device type definition
+const device_type MOS6581 = &device_creator<mos6581_device>;
+const device_type MOS8580 = &device_creator<mos8580_device>;
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// mos6581_device - constructor
+//-------------------------------------------------
+
+mos6581_device::mos6581_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_sound_interface(mconfig, *this),
+ m_read_potx(*this),
+ m_read_poty(*this),
+ m_stream(NULL),
+ m_variant(variant)
+{
+ m_token = global_alloc_clear(SID6581_t);
+}
+
+mos6581_device::mos6581_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MOS6581, "MOS6581", tag, owner, clock, "mos6581", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_read_potx(*this),
+ m_read_poty(*this),
+ m_stream(NULL),
+ m_variant(TYPE_6581)
+{
+ m_token = global_alloc_clear(SID6581_t);
+}
+
+mos6581_device::~mos6581_device()
+{
+ global_free(m_token);
+}
+
+//-------------------------------------------------
+// mos8580_device - constructor
+//-------------------------------------------------
+
+mos8580_device::mos8580_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : mos6581_device(mconfig, MOS8580, "MOS8580", tag, owner, clock, TYPE_8580, "mos8580", __FILE__)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mos6581_device::device_start()
+{
+ // resolve callbacks
+ m_read_potx.resolve_safe(0xff);
+ m_read_poty.resolve_safe(0xff);
+
+ // create sound stream
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate());
+
+ // initialize SID engine
+ m_token->device = this;
+ m_token->mixer_channel = m_stream;
+ m_token->PCMfreq = machine().sample_rate();
+ m_token->clock = clock();
+ m_token->type = m_variant;
+
+ sid6581_init(m_token);
+ sidInitWaveformTables(m_variant);
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void mos6581_device::device_reset()
+{
+ sidEmuReset(m_token);
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void mos6581_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ sidEmuFillBuffer(m_token, outputs[0], samples);
+}
+
+
+//-------------------------------------------------
+// read -
+//-------------------------------------------------
+
+READ8_MEMBER( mos6581_device::read )
+{
+ UINT8 data = 0;
+
+ switch (offset & 0x1f)
+ {
+ case 0x19:
+ data = m_read_potx(0);
+ break;
+
+ case 0x1a:
+ data = m_read_poty(0);
+ break;
+
+ default:
+ data = sid6581_port_r(machine(), m_token, offset);
+ break;
+ }
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// write -
+//-------------------------------------------------
+
+WRITE8_MEMBER( mos6581_device::write )
+{
+ sid6581_port_w(m_token, offset, data);
+}
diff --git a/src/devices/sound/mos6581.h b/src/devices/sound/mos6581.h
new file mode 100644
index 00000000000..4699bdc51d7
--- /dev/null
+++ b/src/devices/sound/mos6581.h
@@ -0,0 +1,109 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods, Curt Coder
+/**********************************************************************
+
+ MOS 6581/8580 Sound Interface Device emulation
+
+**********************************************************************
+ _____ _____
+ CAP1A 1 |* \_/ | 28 Vdd
+ CAP1B 2 | | 27 AUDIO OUT
+ CAP2A 3 | | 26 EXT IN
+ CAP2B 4 | | 25 Vcc
+ _RES 5 | | 24 POTX
+ phi2 6 | | 23 POTY
+ R/_W 7 | MOS6581 | 22 D7
+ _CS 8 | MOS8580 | 21 D6
+ A0 9 | | 20 D5
+ A1 10 | | 19 D4
+ A2 11 | | 18 D3
+ A3 12 | | 17 D2
+ A4 13 | | 16 D1
+ GND 14 |_____________| 15 D0
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __MOS6581__
+#define __MOS6581__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_MOS6581_POTX_CALLBACK(_read) \
+ devcb = &mos6581_device::set_potx_rd_callback(*device, DEVCB_##_read);
+
+#define MCFG_MOS6581_POTY_CALLBACK(_read) \
+ devcb = &mos6581_device::set_poty_rd_callback(*device, DEVCB_##_read);
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> mos6581_device
+
+struct SID6581_t;
+
+class mos6581_device : public device_t,
+ public device_sound_interface
+{
+public:
+ mos6581_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source);
+ mos6581_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~mos6581_device();
+
+ template<class _Object> static devcb_base &set_potx_rd_callback(device_t &device, _Object object) { return downcast<mos6581_device &>(device).m_read_potx.set_callback(object); }
+ template<class _Object> static devcb_base &set_poty_rd_callback(device_t &device, _Object object) { return downcast<mos6581_device &>(device).m_read_poty.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ enum
+ {
+ TYPE_6581,
+ TYPE_8580
+ };
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ devcb_read8 m_read_potx;
+ devcb_read8 m_read_poty;
+
+ sound_stream *m_stream;
+
+ int m_variant;
+
+ SID6581_t *m_token;
+};
+
+
+// ======================> mos8580_device
+
+class mos8580_device : public mos6581_device
+{
+public:
+ mos8580_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+};
+
+
+// device type definition
+extern const device_type MOS6581;
+extern const device_type MOS8580;
+
+
+#endif
diff --git a/src/devices/sound/mos7360.c b/src/devices/sound/mos7360.c
new file mode 100644
index 00000000000..fe8631cfee2
--- /dev/null
+++ b/src/devices/sound/mos7360.c
@@ -0,0 +1,1206 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ MOS 7360/8360 Text Edit Device (TED) emulation
+
+**********************************************************************/
+
+#include "emu.h"
+#include "mos7360.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+#define VERBOSE_LEVEL 0
+#define DBG_LOG(N,M,A) \
+ do { \
+ if(VERBOSE_LEVEL >= N) \
+ { \
+ if( M ) \
+ logerror("%11.6f: %-24s", machine().time().as_double(), (char*) M ); \
+ logerror A; \
+ } \
+ } while(0)
+
+
+#define VREFRESHINLINES 28
+
+#define TIMER1HELPER (m_reg[0] | (m_reg[1] << 8))
+#define TIMER2HELPER (m_reg[2] | (m_reg[3] << 8))
+#define TIMER3HELPER (m_reg[4] | (m_reg[5] << 8))
+#define TIMER1 (TIMER1HELPER ? TIMER1HELPER : 0x10000)
+#define TIMER2 (TIMER2HELPER ? TIMER2HELPER : 0x10000)
+#define TIMER3 (TIMER3HELPER ? TIMER3HELPER : 0x10000)
+
+#define TED7360_YPOS 40
+#define RASTERLINE_2_C16(a) ((a + m_lines - TED7360_YPOS - 5) % m_lines)
+#define C16_2_RASTERLINE(a) ((a + TED7360_YPOS + 5) % m_lines)
+#define XPOS 8
+#define YPOS 8
+
+#define SCREENON (m_reg[6] & 0x10)
+#define TEST (m_reg[6] & 0x80)
+#define VERTICALPOS (m_reg[6] & 0x07)
+#define HORICONTALPOS (m_reg[7] & 0x07)
+#define ECMON (m_reg[6] & 0x40)
+#define HIRESON (m_reg[6] & 0x20)
+#define MULTICOLORON (m_reg[7] & 0x10)
+#define REVERSEON (!(m_reg[7] & 0x80))
+
+/* hardware inverts character when bit 7 set (character taken &0x7f) */
+/* instead of fetching character with higher number! */
+#define LINES25 (m_reg[6] & 0x08) /* else 24 Lines */
+#define LINES (LINES25 ? 25 : 24)
+#define YSIZE (LINES * 8)
+#define COLUMNS40 (m_reg[7] & 0x08) /* else 38 Columns */
+#define COLUMNS (COLUMNS40 ? 40 : 38)
+#define XSIZE (COLUMNS * 8)
+
+#define INROM (m_reg[0x12] & 0x04)
+#define CHARGENADDR (REVERSEON && !HIRESON && !MULTICOLORON ? ((m_reg[0x13] & 0xfc) << 8) : ((m_reg[0x13] & 0xf8) << 8))
+#define BITMAPADDR ((m_reg[0x12] & 0x38) << 10)
+#define VIDEOADDR ((m_reg[0x14] & 0xf8) << 8)
+
+#define RASTERLINE (((m_reg[0xa] & 0x01) << 8) | m_reg[0xb])
+#define CURSOR1POS (m_reg[0xd] | ((m_reg[0xc] & 0x03) << 8))
+#define CURSOR2POS (m_reg[0x1b] | ((m_reg[0x1a] & 0x03) << 8))
+#define CURSORRATE ((m_reg[0x1f] & 0x7c) >> 2)
+
+#define BACKGROUNDCOLOR (m_reg[0x15] & 0x7f)
+#define FOREGROUNDCOLOR (m_reg[0x16] & 0x7f)
+#define MULTICOLOR1 (m_reg[0x17] & 0x7f)
+#define MULTICOLOR2 (m_reg[0x18] & 0x7f)
+#define FRAMECOLOR (m_reg[0x19] & 0x7f)
+
+#define TED7360_CLOCK (m_clock / 4)
+#define TED7360_VRETRACERATE ((m_clock == TED7360PAL_CLOCK) ? TED7360PAL_VRETRACERATE : TED7360NTSC_VRETRACERATE)
+#define TED7360_LINES ((m_clock == TED7360PAL_CLOCK) ? TED7360PAL_LINES : TED7360NTSC_LINES)
+
+static const rgb_t PALETTE_MOS[] =
+{
+/* black, white, red, cyan */
+/* purple, green, blue, yellow */
+/* orange, light orange, pink, light cyan, */
+/* light violett, light green, light blue, light yellow */
+/* these 16 colors are 8 times here in different luminance (dark..light) */
+/* taken from digitized tv screenshot */
+ rgb_t(0x06, 0x01, 0x03), rgb_t(0x2b, 0x2b, 0x2b), rgb_t(0x67, 0x0e, 0x0f), rgb_t(0x00, 0x3f, 0x42),
+ rgb_t(0x57, 0x00, 0x6d), rgb_t(0x00, 0x4e, 0x00), rgb_t(0x19, 0x1c, 0x94), rgb_t(0x38, 0x38, 0x00),
+ rgb_t(0x56, 0x20, 0x00), rgb_t(0x4b, 0x28, 0x00), rgb_t(0x16, 0x48, 0x00), rgb_t(0x69, 0x07, 0x2f),
+ rgb_t(0x00, 0x46, 0x26), rgb_t(0x06, 0x2a, 0x80), rgb_t(0x2a, 0x14, 0x9b), rgb_t(0x0b, 0x49, 0x00),
+
+ rgb_t(0x00, 0x03, 0x02), rgb_t(0x3d, 0x3d, 0x3d), rgb_t(0x75, 0x1e, 0x20), rgb_t(0x00, 0x50, 0x4f),
+ rgb_t(0x6a, 0x10, 0x78), rgb_t(0x04, 0x5c, 0x00), rgb_t(0x2a, 0x2a, 0xa3), rgb_t(0x4c, 0x47, 0x00),
+ rgb_t(0x69, 0x2f, 0x00), rgb_t(0x59, 0x38, 0x00), rgb_t(0x26, 0x56, 0x00), rgb_t(0x75, 0x15, 0x41),
+ rgb_t(0x00, 0x58, 0x3d), rgb_t(0x15, 0x3d, 0x8f), rgb_t(0x39, 0x22, 0xae), rgb_t(0x19, 0x59, 0x00),
+
+ rgb_t(0x00, 0x03, 0x04), rgb_t(0x42, 0x42, 0x42), rgb_t(0x7b, 0x28, 0x20), rgb_t(0x02, 0x56, 0x59),
+ rgb_t(0x6f, 0x1a, 0x82), rgb_t(0x0a, 0x65, 0x09), rgb_t(0x30, 0x34, 0xa7), rgb_t(0x50, 0x51, 0x00),
+ rgb_t(0x6e, 0x36, 0x00), rgb_t(0x65, 0x40, 0x00), rgb_t(0x2c, 0x5c, 0x00), rgb_t(0x7d, 0x1e, 0x45),
+ rgb_t(0x01, 0x61, 0x45), rgb_t(0x1c, 0x45, 0x99), rgb_t(0x42, 0x2d, 0xad), rgb_t(0x1d, 0x62, 0x00),
+
+ rgb_t(0x05, 0x00, 0x02), rgb_t(0x56, 0x55, 0x5a), rgb_t(0x90, 0x3c, 0x3b), rgb_t(0x17, 0x6d, 0x72),
+ rgb_t(0x87, 0x2d, 0x99), rgb_t(0x1f, 0x7b, 0x15), rgb_t(0x46, 0x49, 0xc1), rgb_t(0x66, 0x63, 0x00),
+ rgb_t(0x84, 0x4c, 0x0d), rgb_t(0x73, 0x55, 0x00), rgb_t(0x40, 0x72, 0x00), rgb_t(0x91, 0x33, 0x5e),
+ rgb_t(0x19, 0x74, 0x5c), rgb_t(0x32, 0x59, 0xae), rgb_t(0x59, 0x3f, 0xc3), rgb_t(0x32, 0x76, 0x00),
+
+ rgb_t(0x02, 0x01, 0x06), rgb_t(0x84, 0x7e, 0x85), rgb_t(0xbb, 0x67, 0x68), rgb_t(0x45, 0x96, 0x96),
+ rgb_t(0xaf, 0x58, 0xc3), rgb_t(0x4a, 0xa7, 0x3e), rgb_t(0x73, 0x73, 0xec), rgb_t(0x92, 0x8d, 0x11),
+ rgb_t(0xaf, 0x78, 0x32), rgb_t(0xa1, 0x80, 0x20), rgb_t(0x6c, 0x9e, 0x12), rgb_t(0xba, 0x5f, 0x89),
+ rgb_t(0x46, 0x9f, 0x83), rgb_t(0x61, 0x85, 0xdd), rgb_t(0x84, 0x6c, 0xef), rgb_t(0x5d, 0xa3, 0x29),
+
+ rgb_t(0x02, 0x00, 0x0a), rgb_t(0xb2, 0xac, 0xb3), rgb_t(0xe9, 0x92, 0x92), rgb_t(0x6c, 0xc3, 0xc1),
+ rgb_t(0xd9, 0x86, 0xf0), rgb_t(0x79, 0xd1, 0x76), rgb_t(0x9d, 0xa1, 0xff), rgb_t(0xbd, 0xbe, 0x40),
+ rgb_t(0xdc, 0xa2, 0x61), rgb_t(0xd1, 0xa9, 0x4c), rgb_t(0x93, 0xc8, 0x3d), rgb_t(0xe9, 0x8a, 0xb1),
+ rgb_t(0x6f, 0xcd, 0xab), rgb_t(0x8a, 0xb4, 0xff), rgb_t(0xb2, 0x9a, 0xff), rgb_t(0x88, 0xcb, 0x59),
+
+ rgb_t(0x02, 0x00, 0x0a), rgb_t(0xc7, 0xca, 0xc9), rgb_t(0xff, 0xac, 0xac), rgb_t(0x85, 0xd8, 0xe0),
+ rgb_t(0xf3, 0x9c, 0xff), rgb_t(0x92, 0xea, 0x8a), rgb_t(0xb7, 0xba, 0xff), rgb_t(0xd6, 0xd3, 0x5b),
+ rgb_t(0xf3, 0xbe, 0x79), rgb_t(0xe6, 0xc5, 0x65), rgb_t(0xb0, 0xe0, 0x57), rgb_t(0xff, 0xa4, 0xcf),
+ rgb_t(0x89, 0xe5, 0xc8), rgb_t(0xa4, 0xca, 0xff), rgb_t(0xca, 0xb3, 0xff), rgb_t(0xa2, 0xe5, 0x7a),
+
+ rgb_t(0x01, 0x01, 0x01), rgb_t(0xff, 0xff, 0xff), rgb_t(0xff, 0xf6, 0xf2), rgb_t(0xd1, 0xff, 0xff),
+ rgb_t(0xff, 0xe9, 0xff), rgb_t(0xdb, 0xff, 0xd3), rgb_t(0xfd, 0xff, 0xff), rgb_t(0xff, 0xff, 0xa3),
+ rgb_t(0xff, 0xff, 0xc1), rgb_t(0xff, 0xff, 0xb2), rgb_t(0xfc, 0xff, 0xa2), rgb_t(0xff, 0xee, 0xff),
+ rgb_t(0xd1, 0xff, 0xff), rgb_t(0xeb, 0xff, 0xff), rgb_t(0xff, 0xf8, 0xff), rgb_t(0xed, 0xff, 0xbc)
+};
+
+
+#define NOISE_BUFFER_SIZE_SEC 5
+
+#define TONE_ON (!(m_reg[0x11] & 0x80)) /* or tone update!? */
+#define TONE1_ON ((m_reg[0x11] & 0x10))
+#define TONE1_VALUE (m_reg[0x0e] | ((m_reg[0x12] & 3) << 8))
+#define TONE2_ON ((m_reg[0x11] & 0x20))
+#define TONE2_VALUE (m_reg[0x0f] | ((m_reg[0x10] & 3) << 8))
+#define VOLUME (m_reg[0x11] & 0x0f)
+#define NOISE_ON (m_reg[0x11] & 0x40)
+
+/*
+ * pal 111860.781
+ * ntsc 111840.45
+ */
+#define TONE_FREQUENCY(reg) ((TED7360_CLOCK >> 3) / (1024 - reg))
+#define TONE_FREQUENCY_MIN (TONE_FREQUENCY(0))
+#define NOISE_FREQUENCY (TED7360_CLOCK / 8 / (1024 - TONE2_VALUE))
+#define NOISE_FREQUENCY_MAX (TED7360_CLOCK / 8)
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+const device_type MOS7360 = &device_creator<mos7360_device>;
+
+
+// default address maps
+static ADDRESS_MAP_START( mos7360_videoram_map, AS_0, 8, mos7360_device )
+ AM_RANGE(0x0000, 0xffff) AM_RAM
+ADDRESS_MAP_END
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *mos7360_device::memory_space_config(address_spacenum spacenum) const
+{
+ switch (spacenum)
+ {
+ case AS_0: return &m_videoram_space_config;
+ default: return NULL;
+ }
+}
+
+
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+inline void mos7360_device::set_interrupt(int mask)
+{
+ /* kernel itself polls for timer 2 shot (interrupt disabled!) when cassette loading */
+ m_reg[9] |= mask;
+ if ((m_reg[0xa] & m_reg[9] & 0x5e))
+ {
+ if (!(m_reg[9] & 0x80))
+ {
+ //DBG_LOG(1, "ted7360", ("irq start %.2x\n", mask));
+ m_reg[9] |= 0x80;
+ m_write_irq(ASSERT_LINE);
+ }
+ }
+ m_reg[9] |= mask;
+}
+
+inline void mos7360_device::clear_interrupt(int mask)
+{
+ m_reg[9] &= ~mask;
+ if ((m_reg[9] & 0x80) && !(m_reg[9] & m_reg[0xa] & 0x5e))
+ {
+ DBG_LOG(1, "ted7360", ("irq end %.2x\n", mask));
+ m_reg[9] &= ~0x80;
+ m_write_irq(CLEAR_LINE);
+ }
+}
+
+inline int mos7360_device::rastercolumn()
+{
+ return (int) ((machine().time().as_double() - m_rastertime) * TED7360_VRETRACERATE * m_lines * 57 * 8 + 0.5);
+}
+
+inline UINT8 mos7360_device::read_ram(offs_t offset)
+{
+ int rom = m_rom;
+ m_rom = 0;
+
+ m_last_data = space(AS_0).read_byte(offset);
+
+ m_rom = rom;
+
+ return m_last_data;
+}
+
+inline UINT8 mos7360_device::read_rom(offs_t offset)
+{
+ int rom = m_rom;
+ m_rom = 1;
+
+ m_last_data = space(AS_0).read_byte(offset);
+
+ m_rom = rom;
+
+ return m_last_data;
+}
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// mos7360_device - constructor
+//-------------------------------------------------
+
+mos7360_device::mos7360_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MOS7360, "MOS7360", tag, owner, clock, "mos7360", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_sound_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 16, 0, NULL, *ADDRESS_MAP_NAME(mos7360_videoram_map)),
+ m_write_irq(*this),
+ m_read_k(*this),
+ m_stream(NULL)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mos7360_device::device_start()
+{
+ // get the CPU device
+ m_cpu = machine().device<cpu_device>(m_cpu_tag);
+ assert(m_cpu != NULL);
+
+ // resolve callbacks
+ m_write_irq.resolve_safe();
+ m_read_k.resolve_safe(0xff);
+
+ // allocate timers
+ m_timer1 = timer_alloc(TIMER_ID_1);
+ m_timer2 = timer_alloc(TIMER_ID_2);
+ m_timer3 = timer_alloc(TIMER_ID_3);
+ m_line_timer = timer_alloc(TIMER_LINE);
+ m_line_timer->adjust(m_screen->scan_period(), 0, m_screen->scan_period());
+ m_frame_timer = timer_alloc(TIMER_FRAME);
+ m_frame_timer->adjust(m_screen->frame_period(), 0, m_screen->frame_period());
+
+ // allocate screen bitmap
+ m_screen->register_screen_bitmap(m_bitmap);
+
+ // create sound stream
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate());
+
+ // buffer for fastest played sample for 5 second so we have enough data for min 5 second
+ m_noisesize = NOISE_FREQUENCY_MAX * NOISE_BUFFER_SIZE_SEC;
+ m_noise = auto_alloc_array(machine(), UINT8, m_noisesize);
+
+ {
+ int noiseshift = 0x7ffff8;
+ UINT8 data;
+
+ for (int i = 0; i < m_noisesize; i++)
+ {
+ data = 0;
+ if (noiseshift & 0x400000)
+ data |= 0x80;
+ if (noiseshift & 0x100000)
+ data |= 0x40;
+ if (noiseshift & 0x010000)
+ data |= 0x20;
+ if (noiseshift & 0x002000)
+ data |= 0x10;
+ if (noiseshift & 0x000800)
+ data |= 0x08;
+ if (noiseshift & 0x000080)
+ data |= 0x04;
+ if (noiseshift & 0x000010)
+ data |= 0x02;
+ if (noiseshift & 0x000004)
+ data |= 0x01;
+ m_noise[i] = data;
+ if (((noiseshift & 0x400000) == 0) != ((noiseshift & 0x002000) == 0))
+ noiseshift = (noiseshift << 1) | 1;
+ else
+ noiseshift <<= 1;
+ }
+ }
+
+ // register for state saving
+ save_item(NAME(m_reg));
+ save_item(NAME(m_last_data));
+ save_item(NAME(m_rom));
+ save_item(NAME(m_frame_count));
+ save_item(NAME(m_lines));
+ save_item(NAME(m_timer1_active));
+ save_item(NAME(m_timer2_active));
+ save_item(NAME(m_timer3_active));
+ save_item(NAME(m_cursor1));
+ save_item(NAME(m_chargenaddr));
+ save_item(NAME(m_bitmapaddr));
+ save_item(NAME(m_videoaddr));
+ save_item(NAME(m_x_begin));
+ save_item(NAME(m_x_end));
+ save_item(NAME(m_y_begin));
+ save_item(NAME(m_y_end));
+ save_item(NAME(m_c16_bitmap));
+ save_item(NAME(m_bitmapmulti));
+ save_item(NAME(m_mono));
+ save_item(NAME(m_monoinversed));
+ save_item(NAME(m_multi));
+ save_item(NAME(m_ecmcolor));
+ save_item(NAME(m_colors));
+ save_item(NAME(m_rasterline));
+ save_item(NAME(m_lastline));
+ save_item(NAME(m_rastertime));
+ save_item(NAME(m_tone1pos));
+ save_item(NAME(m_tone2pos));
+ save_item(NAME(m_tone1samples));
+ save_item(NAME(m_tone2samples));
+ save_item(NAME(m_noisepos));
+ save_item(NAME(m_noisesamples));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void mos7360_device::device_reset()
+{
+ memset(m_reg, 0, sizeof(m_reg));
+ m_last_data = 0;
+
+ m_rom = 1; // FIXME: at start should be RAM or ROM? old c16 code set it to ROM at init: is it correct?
+
+ m_lines = TED7360_LINES;
+ m_chargenaddr = m_bitmapaddr = m_videoaddr = 0;
+ m_timer1_active = m_timer2_active = m_timer3_active = 0;
+ m_cursor1 = 0;
+
+ m_rasterline = 0;
+ m_lastline = 0;
+
+ m_rastertime = 0.0;
+
+ m_frame_count = 0;
+
+ m_x_begin = 0;
+ m_x_end = 0;
+ m_y_begin = 0;
+ m_y_end = 0;
+
+ memset(m_c16_bitmap, 0, sizeof(m_c16_bitmap));
+ memset(m_bitmapmulti, 0, sizeof(m_bitmapmulti));
+ memset(m_mono, 0, sizeof(m_mono));
+ memset(m_monoinversed, 0, sizeof(m_monoinversed));
+ memset(m_multi, 0, sizeof(m_multi));
+ memset(m_ecmcolor, 0, sizeof(m_ecmcolor));
+ memset(m_colors, 0, sizeof(m_colors));
+
+ m_tone1pos = 0;
+ m_tone2pos = 0;
+ m_tone1samples = 1;
+ m_tone2samples = 1;
+ m_noisepos = 0;
+ m_noisesamples = 1;
+}
+
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+
+void mos7360_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_ID_1:
+ // proved by digisound of several intros like eoroidpro
+ m_timer1->adjust(clocks_to_attotime(TIMER1), 1);
+ m_timer1_active = 1;
+ set_interrupt(0x08);
+ break;
+
+ case TIMER_ID_2:
+ m_timer2->adjust(clocks_to_attotime(0x10000), 2);
+ m_timer2_active = 1;
+ set_interrupt(0x10);
+ break;
+
+ case TIMER_ID_3:
+ m_timer3->adjust(clocks_to_attotime(0x10000), 3);
+ m_timer3_active = 1;
+ set_interrupt(0x40);
+ break;
+
+ case TIMER_LINE:
+ raster_interrupt_gen();
+ break;
+
+ case TIMER_FRAME:
+ frame_interrupt_gen();
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void mos7360_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int i, v, a;
+ stream_sample_t *buffer = outputs[0];
+
+ for (i = 0; i < samples; i++)
+ {
+ v = 0;
+
+ if (TONE1_ON)
+ {
+ if (m_tone1pos <= m_tone1samples / 2 || !TONE_ON)
+ v += 0x2ff; // depends on the volume between sound and noise
+
+ m_tone1pos++;
+
+ if (m_tone1pos > m_tone1samples)
+ m_tone1pos = 0;
+ }
+
+ if (TONE2_ON || NOISE_ON )
+ {
+ if (TONE2_ON)
+ { /*higher priority ?! */
+ if (m_tone2pos <= m_tone2samples / 2 || !TONE_ON)
+ v += 0x2ff;
+
+ m_tone2pos++;
+
+ if (m_tone2pos > m_tone2samples)
+ m_tone2pos = 0;
+ }
+ else
+ {
+ v += m_noise[(int) ((double) m_noisepos * m_noisesize / m_noisesamples)];
+ m_noisepos++;
+
+ if ((double) m_noisepos / m_noisesamples >= 1.0)
+ m_noisepos = 0;
+ }
+ }
+
+ a = VOLUME;
+ if (a > 8)
+ a = 8;
+
+ v = v * a;
+
+ buffer[i] = v;
+ }
+}
+
+
+void mos7360_device::draw_character(int ybegin, int yend, int ch, int yoff, int xoff, UINT16 *color)
+{
+ int y, code;
+
+ for (y = ybegin; y <= yend; y++)
+ {
+ if (INROM)
+ code = read_rom(m_chargenaddr + ch * 8 + y);
+ else
+ code = read_ram(m_chargenaddr + ch * 8 + y);
+
+ m_bitmap.pix32(y + yoff, 0 + xoff) = PALETTE_MOS[color[code >> 7]];
+ m_bitmap.pix32(y + yoff, 1 + xoff) = PALETTE_MOS[color[(code >> 6) & 1]];
+ m_bitmap.pix32(y + yoff, 2 + xoff) = PALETTE_MOS[color[(code >> 5) & 1]];
+ m_bitmap.pix32(y + yoff, 3 + xoff) = PALETTE_MOS[color[(code >> 4) & 1]];
+ m_bitmap.pix32(y + yoff, 4 + xoff) = PALETTE_MOS[color[(code >> 3) & 1]];
+ m_bitmap.pix32(y + yoff, 5 + xoff) = PALETTE_MOS[color[(code >> 2) & 1]];
+ m_bitmap.pix32(y + yoff, 6 + xoff) = PALETTE_MOS[color[(code >> 1) & 1]];
+ m_bitmap.pix32(y + yoff, 7 + xoff) = PALETTE_MOS[color[code & 1]];
+ }
+}
+
+void mos7360_device::draw_character_multi(int ybegin, int yend, int ch, int yoff, int xoff)
+{
+ int y, code;
+
+ for (y = ybegin; y <= yend; y++)
+ {
+ if (INROM)
+ code = read_rom(m_chargenaddr + ch * 8 + y);
+ else
+ code = read_ram(m_chargenaddr + ch * 8 + y);
+
+ m_bitmap.pix32(y + yoff, 0 + xoff) =
+ m_bitmap.pix32(y + yoff, 1 + xoff) = PALETTE_MOS[m_multi[code >> 6]];
+ m_bitmap.pix32(y + yoff, 2 + xoff) =
+ m_bitmap.pix32(y + yoff, 3 + xoff) = PALETTE_MOS[m_multi[(code >> 4) & 3]];
+ m_bitmap.pix32(y + yoff, 4 + xoff) =
+ m_bitmap.pix32(y + yoff, 5 + xoff) = PALETTE_MOS[m_multi[(code >> 2) & 3]];
+ m_bitmap.pix32(y + yoff, 6 + xoff) =
+ m_bitmap.pix32(y + yoff, 7 + xoff) = PALETTE_MOS[m_multi[code & 3]];
+ }
+}
+
+void mos7360_device::draw_bitmap(int ybegin, int yend, int ch, int yoff, int xoff)
+{
+ int y, code;
+
+ for (y = ybegin; y <= yend; y++)
+ {
+ code = read_ram(m_bitmapaddr + ch * 8 + y);
+
+ m_bitmap.pix32(y + yoff, 0 + xoff) = PALETTE_MOS[m_c16_bitmap[code >> 7]];
+ m_bitmap.pix32(y + yoff, 1 + xoff) = PALETTE_MOS[m_c16_bitmap[(code >> 6) & 1]];
+ m_bitmap.pix32(y + yoff, 2 + xoff) = PALETTE_MOS[m_c16_bitmap[(code >> 5) & 1]];
+ m_bitmap.pix32(y + yoff, 3 + xoff) = PALETTE_MOS[m_c16_bitmap[(code >> 4) & 1]];
+ m_bitmap.pix32(y + yoff, 4 + xoff) = PALETTE_MOS[m_c16_bitmap[(code >> 3) & 1]];
+ m_bitmap.pix32(y + yoff, 5 + xoff) = PALETTE_MOS[m_c16_bitmap[(code >> 2) & 1]];
+ m_bitmap.pix32(y + yoff, 6 + xoff) = PALETTE_MOS[m_c16_bitmap[(code >> 1) & 1]];
+ m_bitmap.pix32(y + yoff, 7 + xoff) = PALETTE_MOS[m_c16_bitmap[code & 1]];
+ }
+}
+
+void mos7360_device::draw_bitmap_multi(int ybegin, int yend, int ch, int yoff, int xoff)
+{
+ int y, code;
+
+ for (y = ybegin; y <= yend; y++)
+ {
+ code = read_ram(m_bitmapaddr + ch * 8 + y);
+
+ m_bitmap.pix32(y + yoff, 0 + xoff) =
+ m_bitmap.pix32(y + yoff, 1 + xoff) = PALETTE_MOS[m_bitmapmulti[code >> 6]];
+ m_bitmap.pix32(y + yoff, 2 + xoff) =
+ m_bitmap.pix32(y + yoff, 3 + xoff) = PALETTE_MOS[m_bitmapmulti[(code >> 4) & 3]];
+ m_bitmap.pix32(y + yoff, 4 + xoff) =
+ m_bitmap.pix32(y + yoff, 5 + xoff) = PALETTE_MOS[m_bitmapmulti[(code >> 2) & 3]];
+ m_bitmap.pix32(y + yoff, 6 + xoff) =
+ m_bitmap.pix32(y + yoff, 7 + xoff) = PALETTE_MOS[m_bitmapmulti[code & 3]];
+ }
+}
+
+void mos7360_device::draw_cursor(int ybegin, int yend, int yoff, int xoff, int color)
+{
+ int y;
+
+ for (y = ybegin; y <= yend; y++)
+ {
+ for (int x = 0; x < 8; x++)
+ {
+ m_bitmap.pix32(y + yoff, x + xoff) = PALETTE_MOS[color];
+ }
+ }
+}
+
+void mos7360_device::drawlines(int first, int last)
+{
+ int line, vline, end;
+ int attr, ch, c1, c2, ecm;
+ int offs, yoff, xoff, ybegin, yend, xbegin, xend;
+ int i;
+
+ m_lastline = last;
+
+ /* top part of display not rastered */
+ first -= TED7360_YPOS;
+ last -= TED7360_YPOS;
+ if ((first >= last) || (last <= 0))
+ return;
+ if (first < 0)
+ first = 0;
+
+ if (!SCREENON)
+ {
+ for (line = first; (line < last) && (line < m_bitmap.height()); line++)
+ {
+ for (int x = 0; x < m_bitmap.width(); x++)
+ {
+ m_bitmap.pix32(line, x) = PALETTE_MOS[0];
+ }
+ }
+ return;
+ }
+
+ if (COLUMNS40)
+ xbegin = XPOS, xend = xbegin + 320;
+ else
+ xbegin = XPOS + 7, xend = xbegin + 304;
+
+ if (last < m_y_begin)
+ end = last;
+ else
+ end = m_y_begin + YPOS;
+ {
+ for (line = first; line < end; line++)
+ {
+ for (int x = 0; x < m_bitmap.width(); x++)
+ {
+ m_bitmap.pix32(line, x) = PALETTE_MOS[FRAMECOLOR];
+ }
+ }
+ }
+ if (LINES25)
+ vline = line - m_y_begin - YPOS;
+ else
+ vline = line - m_y_begin - YPOS + 8 - VERTICALPOS;
+
+ if (last < m_y_end + YPOS)
+ end = last;
+ else
+ end = m_y_end + YPOS;
+
+ for (; line < end; vline = (vline + 8) & ~7, line = line + 1 + yend - ybegin)
+ {
+ offs = (vline >> 3) * 40;
+ ybegin = vline & 7;
+ yoff = line - ybegin;
+ yend = (yoff + 7 < end) ? 7 : (end - yoff - 1);
+ /* rendering 39 characters */
+ /* left and right borders are overwritten later */
+
+ for (xoff = m_x_begin + XPOS; xoff < m_x_end + XPOS; xoff += 8, offs++)
+ {
+ if (HIRESON)
+ {
+ ch = read_ram((m_videoaddr | 0x400) + offs);
+ attr = read_ram(m_videoaddr + offs);
+ c1 = ((ch >> 4) & 0xf) | (attr << 4);
+ c2 = (ch & 0xf) | (attr & 0x70);
+ m_bitmapmulti[1] = m_c16_bitmap[1] = c1 & 0x7f;
+ m_bitmapmulti[2] = m_c16_bitmap[0] = c2 & 0x7f;
+ if (MULTICOLORON)
+ {
+ draw_bitmap_multi(ybegin, yend, offs, yoff, xoff);
+ }
+ else
+ {
+ draw_bitmap(ybegin, yend, offs, yoff, xoff);
+ }
+ }
+ else
+ {
+ ch = read_ram((m_videoaddr | 0x400) + offs);
+ attr = read_ram(m_videoaddr + offs);
+ // levente harsfalvi's docu says cursor off in ecm and multicolor
+ if (ECMON)
+ {
+ // hardware reverse off
+ ecm = ch >> 6;
+ m_ecmcolor[0] = m_colors[ecm];
+ m_ecmcolor[1] = attr & 0x7f;
+ draw_character(ybegin, yend, ch & ~0xc0, yoff, xoff, m_ecmcolor);
+ }
+ else if (MULTICOLORON)
+ {
+ // hardware reverse off
+ if (attr & 8)
+ {
+ m_multi[3] = attr & 0x77;
+ draw_character_multi(ybegin, yend, ch, yoff, xoff);
+ }
+ else
+ {
+ m_mono[1] = attr & 0x7f;
+ draw_character(ybegin, yend, ch, yoff, xoff, m_mono);
+ }
+ }
+ else if (m_cursor1 && (offs == CURSOR1POS))
+ {
+ draw_cursor(ybegin, yend, yoff, xoff, attr & 0x7f);
+ }
+ else if (REVERSEON && (ch & 0x80))
+ {
+ m_monoinversed[0] = attr & 0x7f;
+ if (m_cursor1 && (attr & 0x80))
+ draw_cursor(ybegin, yend, yoff, xoff, m_monoinversed[0]);
+ else
+ draw_character(ybegin, yend, ch & ~0x80, yoff, xoff, m_monoinversed);
+ }
+ else
+ {
+ m_mono[1] = attr & 0x7f;
+ if (m_cursor1 && (attr & 0x80))
+ draw_cursor(ybegin, yend, yoff, xoff, m_mono[0]);
+ else
+ draw_character(ybegin, yend, ch, yoff, xoff, m_mono);
+ }
+ }
+ }
+
+ for (i = ybegin; i <= yend; i++)
+ {
+ for (int x = 0; x < xbegin; x++)
+ {
+ m_bitmap.pix32(yoff + i, x) = PALETTE_MOS[FRAMECOLOR];
+ }
+
+ for (int x = xend; x < m_bitmap.width(); x++)
+ {
+ m_bitmap.pix32(yoff + i, x) = PALETTE_MOS[FRAMECOLOR];
+ }
+ }
+ }
+
+ if (last < m_bitmap.height())
+ end = last;
+ else
+ end = m_bitmap.height();
+
+ for (; line < end; line++)
+ {
+ for (int x = 0; x < m_bitmap.width(); x++)
+ {
+ m_bitmap.pix32(line, x) = PALETTE_MOS[FRAMECOLOR];
+ }
+ }
+}
+
+void mos7360_device::soundport_w(int offset, int data)
+{
+ // int old = m_reg[offset & 0x1f];
+ m_stream->update();
+
+ switch (offset)
+ {
+ case 0x0e:
+ case 0x12:
+ if (offset == 0x12)
+ m_reg[offset & 0x1f] = (m_reg[offset & 0x1f] & ~3) | (data & 3);
+ else
+ m_reg[offset & 0x1f] = data;
+
+ m_tone1samples = machine().sample_rate() / TONE_FREQUENCY (TONE1_VALUE);
+ DBG_LOG(1, "ted7360", ("tone1 %d %d sample:%d\n", TONE1_VALUE, TONE_FREQUENCY(TONE1_VALUE), m_tone1samples));
+ break;
+
+ case 0xf:
+ case 0x10:
+ m_reg[offset & 0x1f] = data;
+
+ m_tone2samples = machine().sample_rate() / TONE_FREQUENCY (TONE2_VALUE);
+ DBG_LOG (1, "ted7360", ("tone2 %d %d sample:%d\n", TONE2_VALUE, TONE_FREQUENCY(TONE2_VALUE), m_tone2samples));
+
+ m_noisesamples = (int) ((double) NOISE_FREQUENCY_MAX * machine().sample_rate() * NOISE_BUFFER_SIZE_SEC / NOISE_FREQUENCY);
+ DBG_LOG (1, "ted7360", ("noise %d sample:%d\n", NOISE_FREQUENCY, m_noisesamples));
+
+ if (!NOISE_ON || ((double) m_noisepos / m_noisesamples >= 1.0))
+ m_noisepos = 0;
+ break;
+
+ case 0x11:
+ m_reg[offset & 0x1f] = data;
+ DBG_LOG(1, "ted7360", ("%s volume %d, %s %s %s\n", TONE_ON?"on":"off",
+ VOLUME, TONE1_ON?"tone1":"", TONE2_ON?"tone2":"", NOISE_ON?"noise":""));
+
+ if (!TONE_ON||!TONE1_ON) m_tone1pos = 0;
+ if (!TONE_ON||!TONE2_ON) m_tone2pos = 0;
+ if (!TONE_ON||!NOISE_ON) m_noisepos = 0;
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// read - register read
+//-------------------------------------------------
+
+UINT8 mos7360_device::read(address_space &space, offs_t offset, int &cs0, int &cs1)
+{
+ UINT8 val = m_last_data;
+
+ cs0 = cs0_r(offset);
+ cs1 = cs1_r(offset);
+
+ switch (offset)
+ {
+ case 0xff00:
+ val = attotime_to_clocks(m_timer1->remaining()) & 0xff;
+ break;
+ case 0xff01:
+ val = attotime_to_clocks(m_timer1->remaining()) >> 8;
+ break;
+ case 0xff02:
+ val = attotime_to_clocks(m_timer2->remaining()) & 0xff;
+ break;
+ case 0xff03:
+ val = attotime_to_clocks(m_timer2->remaining()) >> 8;
+ break;
+ case 0xff04:
+ val = attotime_to_clocks(m_timer3->remaining()) & 0xff;
+ break;
+ case 0xff05:
+ val = attotime_to_clocks(m_timer3->remaining()) >> 8;
+ break;
+ case 0xff07:
+ val = (m_reg[offset & 0x1f] & ~0x40);
+ if (m_clock == TED7360NTSC_CLOCK)
+ val |= 0x40;
+ break;
+ case 0xff13:
+ val = m_reg[offset & 0x1f] & ~1;
+ if (m_rom)
+ val |= 1;
+ break;
+ case 0xff1c: /*rasterline */
+ drawlines(m_lastline, m_rasterline);
+ val = ((RASTERLINE_2_C16(m_rasterline) & 0x100) >> 8) | 0xfe; /* expected by matrix */
+ break;
+ case 0xff1d: /*rasterline */
+ drawlines(m_lastline, m_rasterline);
+ val = RASTERLINE_2_C16(m_rasterline) & 0xff;
+ break;
+ case 0xff1e: /*rastercolumn */
+ val = rastercolumn() / 2; /* pengo >=0x99 */
+ break;
+ case 0xff1f:
+ val = ((m_rasterline & 7) << 4) | (m_reg[offset & 0x1f] & 0x0f);
+ DBG_LOG(1, "port_w", ("read from cursorblink %.2x\n", val));
+ break;
+ case 0xff06:
+ case 0xff08:
+ case 0xff09:
+ case 0xff0a:
+ case 0xff0b:
+ case 0xff0c:
+ case 0xff0d:
+ case 0xff0e:
+ case 0xff0f:
+ case 0xff10:
+ case 0xff11:
+ case 0xff12:
+ case 0xff14:
+ case 0xff15:
+ case 0xff16:
+ case 0xff17:
+ case 0xff18:
+ case 0xff19:
+ case 0xff1a:
+ case 0xff1b:
+ val = m_reg[offset & 0x1f];
+ break;
+ }
+
+ return val;
+}
+
+
+//-------------------------------------------------
+// write - register write
+//-------------------------------------------------
+
+void mos7360_device::write(address_space &space, offs_t offset, UINT8 data, int &cs0, int &cs1)
+{
+ int old;
+
+ cs0 = cs0_r(offset);
+ cs1 = cs1_r(offset);
+
+ switch (offset)
+ {
+ case 0xff0e:
+ case 0xff0f:
+ case 0xff10:
+ case 0xff11:
+ case 0xff12:
+ soundport_w(offset & 0x1f, data);
+ break;
+ }
+
+ switch (offset)
+ {
+ case 0xff00: /* stop timer 1 */
+ m_reg[offset & 0x1f] = data;
+
+ if (m_timer1_active)
+ {
+ m_reg[1] = attotime_to_clocks(m_timer1->remaining()) >> 8;
+ m_timer1->reset();
+ m_timer1_active = 0;
+ }
+ break;
+ case 0xff01: /* start timer 1 */
+ m_reg[offset & 0x1f] = data;
+ m_timer1->adjust(clocks_to_attotime(TIMER1), 1);
+ m_timer1_active = 1;
+ break;
+ case 0xff02: /* stop timer 2 */
+ m_reg[offset & 0x1f] = data;
+ if (m_timer2_active)
+ {
+ m_reg[3] = attotime_to_clocks(m_timer2->remaining()) >> 8;
+ m_timer2->reset();
+ m_timer2_active = 0;
+ }
+ break;
+ case 0xff03: /* start timer 2 */
+ m_reg[offset & 0x1f] = data;
+ m_timer2->adjust(clocks_to_attotime(TIMER2), 2);
+ m_timer2_active = 1;
+ break;
+ case 0xff04: /* stop timer 3 */
+ m_reg[offset & 0x1f] = data;
+ if (m_timer3_active)
+ {
+ m_reg[5] = attotime_to_clocks(m_timer3->remaining()) >> 8;
+ m_timer3->reset();
+ m_timer3_active = 0;
+ }
+ break;
+ case 0xff05: /* start timer 3 */
+ m_reg[offset & 0x1f] = data;
+ m_timer3->adjust(clocks_to_attotime(TIMER3), 3);
+ m_timer3_active = 1;
+ break;
+ case 0xff06:
+ if (m_reg[offset & 0x1f] != data)
+ {
+ drawlines(m_lastline, m_rasterline);
+ m_reg[offset & 0x1f] = data;
+ if (LINES25)
+ {
+ m_y_begin = 0;
+ m_y_end = m_y_begin + 200;
+ }
+ else
+ {
+ m_y_begin = 4;
+ m_y_end = m_y_begin + 192;
+ }
+ m_chargenaddr = CHARGENADDR;
+ }
+ break;
+ case 0xff07:
+ if (m_reg[offset & 0x1f] != data)
+ {
+ drawlines(m_lastline, m_rasterline);
+ m_reg[offset & 0x1f] = data;
+ if (COLUMNS40)
+ {
+ m_x_begin = 0;
+ m_x_end = m_x_begin + 320;
+ }
+ else
+ {
+ m_x_begin = HORICONTALPOS;
+ m_x_end = m_x_begin + 320;
+ }
+ DBG_LOG(3, "port_w", ("%s %s\n", data & 0x40 ? "ntsc" : "pal", data & 0x20 ? "hori freeze" : ""));
+ m_chargenaddr = CHARGENADDR;
+ }
+ break;
+ case 0xff08:
+ m_reg[offset & 0x1f] = m_read_k(data);
+ break;
+ case 0xff09:
+ if (data & 0x08)
+ clear_interrupt(8);
+ if (data & 0x10)
+ clear_interrupt(0x10);
+ if (data & 0x40)
+ clear_interrupt(0x40);
+ if (data & 0x02)
+ clear_interrupt(2);
+ break;
+ case 0xff0a:
+ old = data;
+ m_reg[offset & 0x1f] = data | 0xa0;
+#if 0
+ m_reg[9] = (m_reg[9] & 0xa1) | (m_reg[9] & data & 0x5e);
+ if (m_reg[9] & 0x80)
+ clear_interrupt(0);
+#endif
+ if ((data ^ old) & 1)
+ {
+ /* DBG_LOG(1,"set rasterline hi",("soll:%d\n",RASTERLINE)); */
+ }
+ break;
+ case 0xff0b:
+ if (data != m_reg[offset & 0x1f])
+ {
+ drawlines(m_lastline, m_rasterline);
+ m_reg[offset & 0x1f] = data;
+ /* DBG_LOG(1,"set rasterline lo",("soll:%d\n",RASTERLINE)); */
+ }
+ break;
+ case 0xff0c:
+ case 0xff0d:
+ if (m_reg[offset & 0x1f] != data)
+ {
+ drawlines(m_lastline, m_rasterline);
+ m_reg[offset & 0x1f] = data;
+ }
+ break;
+ case 0xff12:
+ if (m_reg[offset & 0x1f] != data)
+ {
+ drawlines(m_lastline, m_rasterline);
+ m_reg[offset & 0x1f] = data;
+ m_bitmapaddr = BITMAPADDR;
+ m_chargenaddr = CHARGENADDR;
+ DBG_LOG(3, "port_w", ("bitmap %.4x %s\n", BITMAPADDR, INROM ? "rom" : "ram"));
+ }
+ break;
+ case 0xff13:
+ if (m_reg[offset & 0x1f] != data)
+ {
+ drawlines(m_lastline, m_rasterline);
+ m_reg[offset & 0x1f] = data;
+ m_chargenaddr = CHARGENADDR;
+ DBG_LOG(3, "port_w", ("chargen %.4x %s %d\n", CHARGENADDR, data & 2 ? "" : "doubleclock", data & 1));
+ }
+ break;
+ case 0xff14:
+ if (m_reg[offset & 0x1f] != data)
+ {
+ drawlines(m_lastline, m_rasterline);
+ m_reg[offset & 0x1f] = data;
+ m_videoaddr = VIDEOADDR;
+ DBG_LOG(3, "port_w", ("videoram %.4x\n", VIDEOADDR));
+ }
+ break;
+ case 0xff15: /* backgroundcolor */
+ if (m_reg[offset & 0x1f] != data)
+ {
+ drawlines(m_lastline, m_rasterline);
+ m_reg[offset & 0x1f] = data;
+ m_monoinversed[1] = m_mono[0] = m_bitmapmulti[0] = m_multi[0] = m_colors[0] = BACKGROUNDCOLOR;
+ }
+ break;
+ case 0xff16: /* foregroundcolor */
+ if (m_reg[offset & 0x1f] != data)
+ {
+ drawlines(m_lastline, m_rasterline);
+ m_reg[offset & 0x1f] = data;
+ m_bitmapmulti[3] = m_multi[1] = m_colors[1] = FOREGROUNDCOLOR;
+ }
+ break;
+ case 0xff17: /* multicolor 1 */
+ if (m_reg[offset & 0x1f] != data)
+ {
+ drawlines(m_lastline, m_rasterline);
+ m_reg[offset & 0x1f] = data;
+ m_multi[2] = m_colors[2] = MULTICOLOR1;
+ }
+ break;
+ case 0xff18: /* multicolor 2 */
+ if (m_reg[offset & 0x1f] != data)
+ {
+ drawlines(m_lastline, m_rasterline);
+ m_reg[offset & 0x1f] = data;
+ m_colors[3] = MULTICOLOR2;
+ }
+ break;
+ case 0xff19: /* framecolor */
+ if (m_reg[offset & 0x1f] != data)
+ {
+ drawlines(m_lastline, m_rasterline);
+ m_reg[offset & 0x1f] = data;
+ m_colors[4] = FRAMECOLOR;
+ }
+ break;
+ case 0xff1c:
+ m_reg[offset & 0x1f] = data; /*? */
+ DBG_LOG(1, "port_w", ("write to rasterline high %.2x\n",
+ data));
+ break;
+ case 0xff1f:
+ m_reg[offset & 0x1f] = data;
+ DBG_LOG(1, "port_w", ("write to cursorblink %.2x\n", data));
+ break;
+ case 0xff3e:
+ m_rom = 1;
+ break;
+ case 0xff3f:
+ m_rom = 0;
+ break;
+ case 0xff1a:
+ case 0xff1b:
+ case 0xff1d:
+ case 0xff1e:
+ m_reg[offset & 0x1f] = data;
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// update_screen - update screen
+//-------------------------------------------------
+
+UINT32 mos7360_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+
+ return 0;
+}
+
+void mos7360_device::frame_interrupt_gen()
+{
+ if ((m_reg[0x1f] & 0xf) >= 0x0f)
+ {
+ /* if (m_frame_count >= CURSORRATE) */
+ m_cursor1 ^= 1;
+ m_reg[0x1f] &= ~0xf;
+ m_frame_count = 0;
+ }
+ else
+ m_reg[0x1f]++;
+}
+
+void mos7360_device::raster_interrupt_gen()
+{
+ m_rasterline++;
+ m_rastertime = machine().time().as_double();
+ if (m_rasterline >= m_lines)
+ {
+ m_rasterline = 0;
+ drawlines(m_lastline, TED7360_LINES);
+ m_lastline = 0;
+ }
+
+ if (m_rasterline == C16_2_RASTERLINE(RASTERLINE))
+ {
+ drawlines(m_lastline, m_rasterline);
+ set_interrupt(2);
+ }
+}
+
+
+//-------------------------------------------------
+// cs0_r - chip select 0 read
+//-------------------------------------------------
+
+int mos7360_device::cs0_r(offs_t offset)
+{
+ if (m_rom && offset >= 0x8000 && offset < 0xc000)
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+//-------------------------------------------------
+// cs1_r - chip select 1 read
+//-------------------------------------------------
+
+int mos7360_device::cs1_r(offs_t offset)
+{
+ if (m_rom && ((offset >= 0xc000 && offset < 0xfd00) || (offset >= 0xff20)))
+ {
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/src/devices/sound/mos7360.h b/src/devices/sound/mos7360.h
new file mode 100644
index 00000000000..aea96b42bd9
--- /dev/null
+++ b/src/devices/sound/mos7360.h
@@ -0,0 +1,205 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/***************************************************************************
+
+ MOS 7360/8360 Text Edit Device (TED) emulation
+
+****************************************************************************
+ _____ _____
+ DB6 1 |* \_/ | 40 Vcc
+ DB5 2 | | 39 DB7
+ DB4 3 | | 38 DB8
+ DB3 4 | | 37 DB9
+ DB2 5 | | 36 DB10
+ DB1 6 | | 35 DB11
+ DB0 7 | | 34 A13
+ _IRQ 8 | | 33 A12
+ LP 9 | | 32 A11
+ _CS 10 | MOS7360 | 31 A10
+ R/W 11 | | 30 A9
+ BA 12 | | 29 A8
+ Vdd 13 | | 28 A7
+ COLOR 14 | | 27 A6
+ S/LUM 15 | | 26 A5
+ AEC 16 | | 25 A4
+ PH0 17 | | 24 A3
+ PHIN 18 | | 23 A2
+ PHCOL 19 | | 22 A1
+ Vss 20 |_____________| 21 A0
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __MOS7360__
+#define __MOS7360__
+
+#include "emu.h"
+
+
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_MOS7360_ADD(_tag, _screen_tag, _cpu_tag, _clock, _videoram_map, _irq, _k) \
+ MCFG_SCREEN_ADD(_screen_tag, RASTER) \
+ MCFG_SCREEN_REFRESH_RATE(TED7360PAL_VRETRACERATE) \
+ MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) \
+ MCFG_SCREEN_SIZE(336, 216) \
+ MCFG_SCREEN_VISIBLE_AREA(0, 336 - 1, 0, 216 - 1) \
+ MCFG_SCREEN_UPDATE_DEVICE(_tag, mos7360_device, screen_update) \
+ MCFG_DEVICE_ADD(_tag, MOS7360, _clock) \
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, _videoram_map) \
+ MCFG_VIDEO_SET_SCREEN(_screen_tag) \
+ downcast<mos7360_device *>(device)->set_callbacks(_cpu_tag, DEVCB_##_irq, DEVCB_##_k);
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define TED7360NTSC_VRETRACERATE 60
+#define TED7360PAL_VRETRACERATE 50
+#define TED7360_HRETRACERATE 15625
+
+/* the following values depend on the VIC clock,
+ * but to achieve TV-frequency the clock must have a fix frequency */
+#define TED7360_HSIZE 320
+#define TED7360_VSIZE 200
+
+/* of course you clock select an other clock, but for accurate */
+/* video timing (these are used in c16/c116/plus4) */
+#define TED7360NTSC_CLOCK (14318180/4)
+#define TED7360PAL_CLOCK (17734470/5)
+
+/* pal 50 Hz vertical screen refresh, screen consists of 312 lines
+ * ntsc 60 Hz vertical screen refresh, screen consists of 262 lines */
+#define TED7360NTSC_LINES 261
+#define TED7360PAL_LINES 312
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+// ======================> mos7360_device
+
+class mos7360_device : public device_t,
+ public device_memory_interface,
+ public device_sound_interface,
+ public device_video_interface
+{
+public:
+ // construction/destruction
+ //mos7360_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock);
+ mos7360_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _irq, class _k> void set_callbacks(const char *cpu_tag, _irq irq, _k k) {
+ m_cpu_tag = cpu_tag;
+ m_write_irq.set_callback(irq);
+ m_read_k.set_callback(k);
+ }
+
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ UINT8 read(address_space &space, offs_t offset, int &cs0, int &cs1);
+ void write(address_space &space, offs_t offset, UINT8 data, int &cs0, int &cs1);
+
+ UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+protected:
+ enum
+ {
+ TYPE_7360
+ };
+
+ enum
+ {
+ TIMER_ID_1,
+ TIMER_ID_2,
+ TIMER_ID_3,
+ TIMER_LINE,
+ TIMER_FRAME
+ };
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // device_sound_interface callbacks
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ inline void set_interrupt(int mask);
+ inline void clear_interrupt(int mask);
+ inline int rastercolumn();
+ inline UINT8 read_ram(offs_t offset);
+ inline UINT8 read_rom(offs_t offset);
+
+ void draw_character(int ybegin, int yend, int ch, int yoff, int xoff, UINT16 *color);
+ void draw_character_multi(int ybegin, int yend, int ch, int yoff, int xoff);
+ void draw_bitmap(int ybegin, int yend, int ch, int yoff, int xoff);
+ void draw_bitmap_multi(int ybegin, int yend, int ch, int yoff, int xoff);
+ void draw_cursor(int ybegin, int yend, int yoff, int xoff, int color);
+ void drawlines(int first, int last);
+ void soundport_w(int offset, int data);
+ void frame_interrupt_gen();
+ void raster_interrupt_gen();
+ int cs0_r(offs_t offset);
+ int cs1_r(offs_t offset);
+
+ const address_space_config m_videoram_space_config;
+
+ devcb_write_line m_write_irq;
+ devcb_read8 m_read_k;
+
+ const char *m_cpu_tag;
+ cpu_device *m_cpu;
+ sound_stream *m_stream;
+
+ UINT8 m_reg[0x20];
+ UINT8 m_last_data;
+
+ bitmap_rgb32 m_bitmap;
+
+ int m_rom;
+
+ int m_frame_count;
+
+ int m_lines;
+ int m_timer1_active, m_timer2_active, m_timer3_active;
+ emu_timer *m_timer1, *m_timer2, *m_timer3;
+ int m_cursor1;
+
+ int m_chargenaddr, m_bitmapaddr, m_videoaddr;
+
+ int m_x_begin, m_x_end;
+ int m_y_begin, m_y_end;
+
+ UINT16 m_c16_bitmap[2], m_bitmapmulti[4], m_mono[2], m_monoinversed[2], m_multi[4], m_ecmcolor[2], m_colors[5];
+
+ int m_rasterline, m_lastline;
+ double m_rastertime;
+
+ /* sound part */
+ UINT8 *m_noise;
+ int m_tone1pos, m_tone2pos,
+ m_tone1samples, m_tone2samples,
+ m_noisesize, /* number of samples */
+ m_noisepos, /* pos of tone */
+ m_noisesamples; /* count of samples to give out per tone */
+
+ emu_timer *m_line_timer;
+ emu_timer *m_frame_timer;
+};
+
+
+// device type definition
+extern const device_type MOS7360;
+
+
+
+#endif
diff --git a/src/devices/sound/mpeg_audio.c b/src/devices/sound/mpeg_audio.c
new file mode 100644
index 00000000000..bf024383a06
--- /dev/null
+++ b/src/devices/sound/mpeg_audio.c
@@ -0,0 +1,759 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/***************************************************************************
+
+ MPEG audio support. Only layer2 and variants for now.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "mpeg_audio.h"
+
+mpeg_audio::mpeg_audio(const void *_base, unsigned int _accepted, bool lsb_first, int _position_align)
+{
+ base = (const UINT8 *)_base;
+ accepted = _accepted;
+ do_gb = lsb_first ? do_gb_lsb : do_gb_msb;
+ position_align = _position_align ? _position_align - 1 : 0;
+
+ clear();
+}
+
+void mpeg_audio::clear()
+{
+ memset(audio_buffer, 0, sizeof(audio_buffer));
+ audio_buffer_pos[0] = 16*32;
+ audio_buffer_pos[1] = 16*32;
+}
+
+bool mpeg_audio::decode_buffer(int &pos, int limit, short *output,
+ int &output_samples, int &sample_rate, int &channels)
+{
+ if(limit - pos < 16)
+ return false;
+
+ // Scan for the sync mark
+ //
+ // Avoid the exception dance at the point where going out of bound
+ // is the most probable and easily avoidable
+
+ current_pos = pos;
+ current_limit = limit;
+ unsigned short sync = do_gb(base, current_pos, 12);
+
+ retry_sync:
+ while(sync != 0xfff && current_pos < limit)
+ sync = ((sync << 1) | do_gb(base, current_pos, 1)) & 0xfff;
+
+ if(limit - current_pos < 4)
+ return false;
+
+ int layer = 0;
+ int variant = do_gb(base, current_pos, 3);
+ switch(variant) {
+ case 2:
+ if(accepted & L2_5)
+ layer = 2;
+ else if(accepted & AMM)
+ layer = 4;
+ break;
+
+ case 5:
+ if(accepted & L3)
+ layer = 3;
+ break;
+
+ case 6:
+ if(accepted & (L2|L2_5))
+ layer = 2;
+ else if(accepted & AMM)
+ layer = 4;
+ break;
+
+ case 7:
+ if(accepted & L1)
+ layer = 1;
+ break;
+ }
+
+ if(!layer) {
+ current_pos -= 3;
+ sync = ((sync << 1) | do_gb(base, current_pos, 1)) & 0xfff;
+ goto retry_sync;
+ }
+
+ switch(layer) {
+ case 1:
+ abort();
+ case 2:
+ try {
+ read_header_mpeg2(variant == 2);
+ read_data_mpeg2();
+ decode_mpeg2(output, output_samples);
+ } catch(limit_hit) {
+ return false;
+ }
+ break;
+ case 3:
+ abort();
+ case 4:
+ try {
+ read_header_amm(variant == 2);
+ read_data_mpeg2();
+ if(last_frame_number)
+ decode_mpeg2(output, output_samples);
+ } catch(limit_hit) {
+ return false;
+ }
+ break;
+ }
+
+ if(position_align)
+ current_pos = (current_pos + position_align) & ~position_align;
+
+ pos = current_pos;
+ sample_rate = sample_rates[sampling_rate];
+ channels = channel_count;
+ return true;
+}
+
+void mpeg_audio::read_header_amm(bool layer25)
+{
+ gb(1); // unused
+ int full_packets_count = gb(4); // max 12
+ int srate_index = gb(2); // max 2
+ sampling_rate = srate_index + 4 * layer25;
+ int last_packet_frame_id = gb(2); // max 2
+ last_frame_number = 3*full_packets_count + last_packet_frame_id;
+ int stereo_mode = gb(2);
+ int stereo_mode_ext = gb(2);
+ param_index = gb(3);
+ gb(1); // must be zero
+
+ channel_count = stereo_mode != 3 ? 2 : 1;
+
+ total_bands = total_band_counts[param_index];
+ joint_bands = total_bands;
+ if(stereo_mode == 1) // joint stereo
+ joint_bands = joint_band_counts[stereo_mode_ext];
+ if(joint_bands > total_bands )
+ joint_bands = total_bands;
+}
+
+void mpeg_audio::read_header_mpeg2(bool layer25)
+{
+ int prot = gb(1);
+ int bitrate_index = gb(4);
+ sampling_rate = gb(2);
+ gb(1); // padding
+ gb(1);
+ last_frame_number = 36;
+ int stereo_mode = gb(2);
+ int stereo_mode_ext = gb(2);
+ gb(2); // copyright, original
+ gb(2); // emphasis
+ if(!prot)
+ gb(16); // crc
+
+ channel_count = stereo_mode != 3 ? 2 : 1;
+
+ param_index = layer2_param_index[channel_count-1][sampling_rate][bitrate_index];
+ assert(param_index != -1);
+
+ total_bands = total_band_counts[param_index];
+ joint_bands = total_bands;
+ if(stereo_mode == 1) // joint stereo
+ joint_bands = joint_band_counts[stereo_mode_ext];
+ if(joint_bands > total_bands )
+ joint_bands = total_bands;
+}
+
+void mpeg_audio::read_data_mpeg2()
+{
+ read_band_params();
+ read_scfci();
+ read_band_amplitude_params();
+}
+
+void mpeg_audio::decode_mpeg2(short *output, int &output_samples)
+{
+ output_samples = 0;
+ build_amplitudes();
+
+ // Supposed to stop at last_frame_number when it's not 12*3+2 = 38
+ int frame_number = 0;
+ for(int upper_step = 0; upper_step<3; upper_step++)
+ for(int middle_step = 0; middle_step < 4; middle_step++) {
+ build_next_segments(upper_step);
+ for(int lower_step = 0; lower_step < 3; lower_step++) {
+ retrieve_subbuffer(lower_step);
+
+ for(int chan=0; chan<channel_count; chan++) {
+ double resynthesis_buffer[32];
+ idct32(subbuffer[chan], audio_buffer[chan] + audio_buffer_pos[chan]);
+ resynthesis(audio_buffer[chan] + audio_buffer_pos[chan] + 16, resynthesis_buffer);
+ scale_and_clamp(resynthesis_buffer, output + chan, channel_count);
+ audio_buffer_pos[chan] -= 32;
+ if(audio_buffer_pos[chan]<0) {
+ memmove(audio_buffer[chan]+17*32, audio_buffer[chan], 15*32*sizeof(audio_buffer[chan][0]));
+ audio_buffer_pos[chan] = 16*32;
+ }
+ }
+ output += 32*channel_count;
+ output_samples += 32;
+ frame_number++;
+ if(frame_number == last_frame_number)
+ return;
+ }
+ }
+}
+
+const int mpeg_audio::sample_rates[8] = { 44100, 48000, 32000, 0, 22050, 24000, 16000, 0 };
+
+const int mpeg_audio::layer2_param_index[2][4][16] = {
+ {
+ { 1, 2, 2, 0, 0, 0, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1 },
+ { 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1 },
+ { 1, 3, 3, 0, 0, 0, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1 },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
+ },
+ {
+ { 1, -1, -1, -1, 2, -1, 2, 0, 0, 0, 1, 1, 1, 1, 1, -1 },
+ { 0, -1, -1, -1, 2, -1, 2, 0, 0, 0, 0, 0, 0, 0, 0, -1 },
+ { 1, -1, -1, -1, 3, -1, 3, 0, 0, 0, 1, 1, 1, 1, 1, -1 },
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
+ }
+};
+
+const int mpeg_audio::band_parameter_indexed_values[5][32][17] = {
+ {
+ { 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1, },
+ { 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1, },
+ { 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ },
+ {
+ { 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1, },
+ { 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1, },
+ { 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ },
+ {
+ { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ },
+ {
+ { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ },
+ {
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -1, },
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ }
+};
+
+const int mpeg_audio::band_parameter_index_bits_count[5][32] = {
+ { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 0, 0, 0, 0, 0, },
+ { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 0, 0, },
+ { 4, 4, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+ { 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+ { 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, },
+};
+
+const int mpeg_audio::total_band_counts[5] = { 27, 30, 8, 12, 30 };
+
+const int mpeg_audio::joint_band_counts[4] = { 4, 8, 12, 16 };
+
+const mpeg_audio::band_info mpeg_audio::band_infos[18] = {
+ { 0x0000, 0.00, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0x0003, 7.00, 2, 5, 3, 9, 1-1.0/ 4, -1.0/ 4, 1/(1-1.0/ 4), 1.0/ 2 },
+ { 0x0005, 11.00, 3, 7, 5, 25, 1-3.0/ 8, -3.0/ 8, 1/(1-3.0/ 8), 1.0/ 2 },
+ { 0x0007, 16.00, 3, 9, 0, 0, 1-1.0/ 8, -1.0/ 8, 1/(1-1.0/ 8), 1.0/ 4 },
+ { 0x0009, 20.84, 4, 10, 9, 81, 1-7.0/ 16, -7.0/ 16, 1/(1-7.0/ 16), 1.0/ 2 },
+ { 0x000f, 25.28, 4, 12, 0, 0, 1-1.0/ 16, -1.0/ 16, 1/(1-1.0/ 16), 1.0/ 8 },
+ { 0x001f, 31.59, 5, 15, 0, 0, 1-1.0/ 32, -1.0/ 32, 1/(1-1.0/ 32), 1.0/ 16 },
+ { 0x003f, 37.75, 6, 18, 0, 0, 1-1.0/ 64, -1.0/ 64, 1/(1-1.0/ 64), 1.0/ 32 },
+ { 0x007f, 43.84, 7, 21, 0, 0, 1-1.0/ 128, -1.0/ 128, 1/(1-1.0/ 128), 1.0/ 64 },
+ { 0x00ff, 49.89, 8, 24, 0, 0, 1-1.0/ 256, -1.0/ 256, 1/(1-1.0/ 256), 1.0/ 128 },
+ { 0x01ff, 55.93, 9, 27, 0, 0, 1-1.0/ 512, -1.0/ 512, 1/(1-1.0/ 512), 1.0/ 256 },
+ { 0x03ff, 61.96, 10, 30, 0, 0, 1-1.0/ 1024, -1.0/ 1024, 1/(1-1.0/ 1024), 1.0/ 512 },
+ { 0x07ff, 67.98, 11, 33, 0, 0, 1-1.0/ 2048, -1.0/ 2048, 1/(1-1.0/ 2048), 1.0/ 1024 },
+ { 0x0fff, 74.01, 12, 36, 0, 0, 1-1.0/ 4096, -1.0/ 4096, 1/(1-1.0/ 4096), 1.0/ 2048 },
+ { 0x1fff, 80.03, 13, 39, 0, 0, 1-1.0/ 8192, -1.0/ 8192, 1/(1-1.0/ 8192), 1.0/ 4096 },
+ { 0x3fff, 86.05, 14, 42, 0, 0, 1-1.0/16384, -1.0/16384, 1/(1-1.0/16384), 1.0/ 8192 },
+ { 0x7fff, 92.01, 15, 45, 0, 0, 1-1.0/32768, -1.0/32768, 1/(1-1.0/32768), 1.0/16384 },
+ { 0xffff, 98.01, 16, 48, 0, 0, 1-1.0/65536, -1.0/65536, 1/(1-1.0/65536), 1.0/32768 },
+};
+
+const double mpeg_audio::scalefactors[64] = {
+ 2.00000000000000, 1.58740105196820, 1.25992104989487, 1.00000000000000,
+ 0.79370052598410, 0.62996052494744, 0.50000000000000, 0.39685026299205,
+ 0.31498026247372, 0.25000000000000, 0.19842513149602, 0.15749013123686,
+ 0.12500000000000, 0.09921256574801, 0.07874506561843, 0.06250000000000,
+ 0.04960628287401, 0.03937253280921, 0.03125000000000, 0.02480314143700,
+ 0.01968626640461, 0.01562500000000, 0.01240157071850, 0.00984313320230,
+ 0.00781250000000, 0.00620078535925, 0.00492156660115, 0.00390625000000,
+ 0.00310039267963, 0.00246078330058, 0.00195312500000, 0.00155019633981,
+ 0.00123039165029, 0.00097656250000, 0.00077509816991, 0.00061519582514,
+ 0.00048828125000, 0.00038754908495, 0.00030759791257, 0.00024414062500,
+ 0.00019377454248, 0.00015379895629, 0.00012207031250, 0.00009688727124,
+ 0.00007689947814, 0.00006103515625, 0.00004844363562, 0.00003844973907,
+ 0.00003051757812, 0.00002422181781, 0.00001922486954, 0.00001525878906,
+ 0.00001211090890, 0.00000961243477, 0.00000762939453, 0.00000605545445,
+ 0.00000480621738, 0.00000381469727, 0.00000302772723, 0.00000240310869,
+ 0.00000190734863, 0.00000151386361, 0.00000120155435, 0.00000000000000
+};
+
+const double mpeg_audio::synthesis_filter[512] = {
+ +0.000000000, -0.000015259, -0.000015259, -0.000015259, -0.000015259, -0.000015259, -0.000015259, -0.000030518,
+ -0.000030518, -0.000030518, -0.000030518, -0.000045776, -0.000045776, -0.000061035, -0.000061035, -0.000076294,
+ -0.000076294, -0.000091553, -0.000106812, -0.000106812, -0.000122070, -0.000137329, -0.000152588, -0.000167847,
+ -0.000198364, -0.000213623, -0.000244141, -0.000259399, -0.000289917, -0.000320435, -0.000366211, -0.000396729,
+ -0.000442505, -0.000473022, -0.000534058, -0.000579834, -0.000625610, -0.000686646, -0.000747681, -0.000808716,
+ -0.000885010, -0.000961304, -0.001037598, -0.001113892, -0.001205444, -0.001296997, -0.001388550, -0.001480103,
+ -0.001586914, -0.001693726, -0.001785278, -0.001907349, -0.002014160, -0.002120972, -0.002243042, -0.002349854,
+ -0.002456665, -0.002578735, -0.002685547, -0.002792358, -0.002899170, -0.002990723, -0.003082275, -0.003173828,
+ +0.003250122, +0.003326416, +0.003387451, +0.003433228, +0.003463745, +0.003479004, +0.003479004, +0.003463745,
+ +0.003417969, +0.003372192, +0.003280640, +0.003173828, +0.003051758, +0.002883911, +0.002700806, +0.002487183,
+ +0.002227783, +0.001937866, +0.001617432, +0.001266479, +0.000869751, +0.000442505, -0.000030518, -0.000549316,
+ -0.001098633, -0.001693726, -0.002334595, -0.003005981, -0.003723145, -0.004486084, -0.005294800, -0.006118774,
+ -0.007003784, -0.007919312, -0.008865356, -0.009841919, -0.010848999, -0.011886597, -0.012939453, -0.014022827,
+ -0.015121460, -0.016235352, -0.017349243, -0.018463135, -0.019577026, -0.020690918, -0.021789550, -0.022857666,
+ -0.023910522, -0.024932861, -0.025909424, -0.026840210, -0.027725220, -0.028533936, -0.029281616, -0.029937744,
+ -0.030532837, -0.031005860, -0.031387330, -0.031661987, -0.031814575, -0.031845093, -0.031738280, -0.031478880,
+ +0.031082153, +0.030517578, +0.029785156, +0.028884888, +0.027801514, +0.026535034, +0.025085450, +0.023422241,
+ +0.021575928, +0.019531250, +0.017257690, +0.014801025, +0.012115479, +0.009231567, +0.006134033, +0.002822876,
+ -0.000686646, -0.004394531, -0.008316040, -0.012420654, -0.016708374, -0.021179200, -0.025817871, -0.030609130,
+ -0.035552980, -0.040634155, -0.045837402, -0.051132202, -0.056533813, -0.061996460, -0.067520140, -0.073059080,
+ -0.078628540, -0.084182740, -0.089706420, -0.095169070, -0.100540160, -0.105819700, -0.110946655, -0.115921020,
+ -0.120697020, -0.125259400, -0.129562380, -0.133590700, -0.137298580, -0.140670780, -0.143676760, -0.146255500,
+ -0.148422240, -0.150115970, -0.151306150, -0.151962280, -0.152069090, -0.151596070, -0.150497440, -0.148773200,
+ -0.146362300, -0.143264770, -0.139450070, -0.134887700, -0.129577640, -0.123474120, -0.116577150, -0.108856200,
+ +0.100311280, +0.090927124, +0.080688480, +0.069595340, +0.057617188, +0.044784546, +0.031082153, +0.016510010,
+ +0.001068115, -0.015228271, -0.032379150, -0.050354004, -0.069168090, -0.088775635, -0.109161380, -0.130310060,
+ -0.152206420, -0.174789430, -0.198059080, -0.221984860, -0.246505740, -0.271591200, -0.297210700, -0.323318480,
+ -0.349868770, -0.376800540, -0.404083250, -0.431655880, -0.459472660, -0.487472530, -0.515609740, -0.543823240,
+ -0.572036740, -0.600219700, -0.628295900, -0.656219500, -0.683914200, -0.711318970, -0.738372800, -0.765029900,
+ -0.791214000, -0.816864000, -0.841949460, -0.866363500, -0.890090940, -0.913055400, -0.935195900, -0.956481930,
+ -0.976852400, -0.996246340, -1.014617900, -1.031936600, -1.048156700, -1.063217200, -1.077117900, -1.089782700,
+ -1.101211500, -1.111373900, -1.120224000, -1.127746600, -1.133926400, -1.138763400, -1.142211900, -1.144287100,
+ +1.144989000, +1.144287100, +1.142211900, +1.138763400, +1.133926400, +1.127746600, +1.120224000, +1.111373900,
+ +1.101211500, +1.089782700, +1.077117900, +1.063217200, +1.048156700, +1.031936600, +1.014617900, +0.996246340,
+ +0.976852400, +0.956481930, +0.935195900, +0.913055400, +0.890090940, +0.866363500, +0.841949460, +0.816864000,
+ +0.791214000, +0.765029900, +0.738372800, +0.711318970, +0.683914200, +0.656219500, +0.628295900, +0.600219700,
+ +0.572036740, +0.543823240, +0.515609740, +0.487472530, +0.459472660, +0.431655880, +0.404083250, +0.376800540,
+ +0.349868770, +0.323318480, +0.297210700, +0.271591200, +0.246505740, +0.221984860, +0.198059080, +0.174789430,
+ +0.152206420, +0.130310060, +0.109161380, +0.088775635, +0.069168090, +0.050354004, +0.032379150, +0.015228271,
+ -0.001068115, -0.016510010, -0.031082153, -0.044784546, -0.057617188, -0.069595340, -0.080688480, -0.090927124,
+ +0.100311280, +0.108856200, +0.116577150, +0.123474120, +0.129577640, +0.134887700, +0.139450070, +0.143264770,
+ +0.146362300, +0.148773200, +0.150497440, +0.151596070, +0.152069090, +0.151962280, +0.151306150, +0.150115970,
+ +0.148422240, +0.146255500, +0.143676760, +0.140670780, +0.137298580, +0.133590700, +0.129562380, +0.125259400,
+ +0.120697020, +0.115921020, +0.110946655, +0.105819700, +0.100540160, +0.095169070, +0.089706420, +0.084182740,
+ +0.078628540, +0.073059080, +0.067520140, +0.061996460, +0.056533813, +0.051132202, +0.045837402, +0.040634155,
+ +0.035552980, +0.030609130, +0.025817871, +0.021179200, +0.016708374, +0.012420654, +0.008316040, +0.004394531,
+ +0.000686646, -0.002822876, -0.006134033, -0.009231567, -0.012115479, -0.014801025, -0.017257690, -0.019531250,
+ -0.021575928, -0.023422241, -0.025085450, -0.026535034, -0.027801514, -0.028884888, -0.029785156, -0.030517578,
+ +0.031082153, +0.031478880, +0.031738280, +0.031845093, +0.031814575, +0.031661987, +0.031387330, +0.031005860,
+ +0.030532837, +0.029937744, +0.029281616, +0.028533936, +0.027725220, +0.026840210, +0.025909424, +0.024932861,
+ +0.023910522, +0.022857666, +0.021789550, +0.020690918, +0.019577026, +0.018463135, +0.017349243, +0.016235352,
+ +0.015121460, +0.014022827, +0.012939453, +0.011886597, +0.010848999, +0.009841919, +0.008865356, +0.007919312,
+ +0.007003784, +0.006118774, +0.005294800, +0.004486084, +0.003723145, +0.003005981, +0.002334595, +0.001693726,
+ +0.001098633, +0.000549316, +0.000030518, -0.000442505, -0.000869751, -0.001266479, -0.001617432, -0.001937866,
+ -0.002227783, -0.002487183, -0.002700806, -0.002883911, -0.003051758, -0.003173828, -0.003280640, -0.003372192,
+ -0.003417969, -0.003463745, -0.003479004, -0.003479004, -0.003463745, -0.003433228, -0.003387451, -0.003326416,
+ +0.003250122, +0.003173828, +0.003082275, +0.002990723, +0.002899170, +0.002792358, +0.002685547, +0.002578735,
+ +0.002456665, +0.002349854, +0.002243042, +0.002120972, +0.002014160, +0.001907349, +0.001785278, +0.001693726,
+ +0.001586914, +0.001480103, +0.001388550, +0.001296997, +0.001205444, +0.001113892, +0.001037598, +0.000961304,
+ +0.000885010, +0.000808716, +0.000747681, +0.000686646, +0.000625610, +0.000579834, +0.000534058, +0.000473022,
+ +0.000442505, +0.000396729, +0.000366211, +0.000320435, +0.000289917, +0.000259399, +0.000244141, +0.000213623,
+ +0.000198364, +0.000167847, +0.000152588, +0.000137329, +0.000122070, +0.000106812, +0.000106812, +0.000091553,
+ +0.000076294, +0.000076294, +0.000061035, +0.000061035, +0.000045776, +0.000045776, +0.000030518, +0.000030518,
+ +0.000030518, +0.000030518, +0.000015259, +0.000015259, +0.000015259, +0.000015259, +0.000015259, +0.000015259,
+};
+
+int mpeg_audio::do_gb_msb(const unsigned char *data, int &pos, int count)
+{
+ int v = 0;
+ for(int i=0; i != count; i++) {
+ v <<= 1;
+ if(data[pos >> 3] & (0x80 >> (pos & 7)))
+ v |= 1;
+ pos++;
+ }
+ return v;
+}
+
+int mpeg_audio::do_gb_lsb(const unsigned char *data, int &pos, int count)
+{
+ int v = 0;
+ for(int i=0; i != count; i++) {
+ v <<= 1;
+ if(data[pos >> 3] & (0x01 << (pos & 7)))
+ v |= 1;
+ pos++;
+ }
+ return v;
+}
+
+int mpeg_audio::get_band_param(int band)
+{
+ int bit_count = band_parameter_index_bits_count[param_index][band];
+ int index = gb(bit_count);
+ return band_parameter_indexed_values[param_index][band][index];
+}
+
+void mpeg_audio::read_band_params()
+{
+ int band = 0;
+
+ while(band < joint_bands) {
+ for(int chan=0; chan < channel_count; chan++)
+ band_param[chan][band] = get_band_param(band);
+ band++;
+ }
+
+ while(band < total_bands) {
+ int val = get_band_param(band);
+ band_param[0][band] = val;
+ band_param[1][band] = val;
+ band++;
+ }
+
+ while(band < 32) {
+ band_param[0][band] = 0;
+ band_param[1][band] = 0;
+ band++;
+ }
+}
+
+void mpeg_audio::read_scfci()
+{
+ memset(scfsi, 0, sizeof(scfsi));
+ for(int band=0; band < total_bands; band++)
+ for(int chan=0; chan < channel_count; chan++)
+ if(band_param[chan][band])
+ scfsi[chan][band] = gb(2);
+}
+
+void mpeg_audio::read_band_amplitude_params()
+{
+ memset(scf, 0, sizeof(scf));
+ for(int band=0; band < total_bands; band++)
+ for(int chan=0; chan<channel_count; chan++)
+ if(band_param[chan][band]) {
+ switch(scfsi[chan][band]) {
+ case 0:
+ scf[chan][0][band] = gb(6);
+ scf[chan][1][band] = gb(6);
+ scf[chan][2][band] = gb(6);
+ break;
+
+ case 1: {
+ int val = gb(6);
+ scf[chan][0][band] = val;
+ scf[chan][1][band] = val;
+ scf[chan][2][band] = gb(6);
+ break;
+ }
+
+ case 2: {
+ int val = gb(6);
+ scf[chan][0][band] = val;
+ scf[chan][1][band] = val;
+ scf[chan][2][band] = val;
+ break;
+ }
+
+ case 3: {
+ scf[chan][0][band] = gb(6);
+ int val = gb(6);
+ scf[chan][1][band] = val;
+ scf[chan][2][band] = val;
+ break;
+ }
+ }
+ }
+}
+
+void mpeg_audio::build_amplitudes()
+{
+ memset(amp_values, 0, sizeof(amp_values));
+
+ for(int band=0; band < total_bands; band++)
+ for(int chan=0; chan<channel_count; chan++)
+ if(band_param[chan][band])
+ for(int step=0; step<3; step++)
+ amp_values[chan][step][band] = scalefactors[scf[chan][step][band]];
+}
+
+void mpeg_audio::read_band_value_triplet(int chan, int band)
+{
+ double buffer[3];
+
+ int band_idx = band_param[chan][band];
+ switch(band_idx) {
+ case 0:
+ bdata[chan][0][band] = 0;
+ bdata[chan][1][band] = 0;
+ bdata[chan][2][band] = 0;
+ return;
+
+ case 1:
+ case 2:
+ case 4: {
+ int modulo = band_infos[band_idx].modulo;
+ int val = gb(band_infos[band_idx].cube_bits);
+ buffer[0] = val % modulo;
+ val = val / modulo;
+ buffer[1] = val % modulo;
+ val = val / modulo;
+ buffer[2] = val % modulo;
+ break;
+ }
+
+ default: {
+ int bits = band_infos[band_idx].bits;
+ buffer[0] = gb(bits);
+ buffer[1] = gb(bits);
+ buffer[2] = gb(bits);
+ break;
+ }
+ }
+
+ double scale = 1 << (band_infos[band_idx].bits - 1);
+
+ bdata[chan][0][band] = ((buffer[0] - scale) / scale + band_infos[band_idx].offset) * band_infos[band_idx].scale;
+ bdata[chan][1][band] = ((buffer[1] - scale) / scale + band_infos[band_idx].offset) * band_infos[band_idx].scale;
+ bdata[chan][2][band] = ((buffer[2] - scale) / scale + band_infos[band_idx].offset) * band_infos[band_idx].scale;
+}
+
+void mpeg_audio::build_next_segments(int step)
+{
+ int band = 0;
+ while(band < joint_bands) {
+ for(int chan=0; chan<channel_count; chan++) {
+ read_band_value_triplet(chan, band);
+ double amp = amp_values[chan][step][band];
+ bdata[chan][0][band] *= amp;
+ bdata[chan][1][band] *= amp;
+ bdata[chan][2][band] *= amp;
+ }
+ band++;
+ }
+
+ while(band < joint_bands) {
+ read_band_value_triplet(0, band);
+ bdata[1][0][band] = bdata[0][0][band];
+ bdata[1][1][band] = bdata[0][1][band];
+ bdata[1][2][band] = bdata[0][2][band];
+
+ for(int chan=0; chan<channel_count; chan++) {
+ double amp = amp_values[chan][step][band];
+ bdata[chan][0][band] *= amp;
+ bdata[chan][1][band] *= amp;
+ bdata[chan][2][band] *= amp;
+ }
+ band++;
+ }
+
+ while(band < 32) {
+ bdata[0][0][band] = 0;
+ bdata[0][1][band] = 0;
+ bdata[0][2][band] = 0;
+ bdata[1][0][band] = 0;
+ bdata[1][1][band] = 0;
+ bdata[1][2][band] = 0;
+ band++;
+ }
+}
+
+void mpeg_audio::retrieve_subbuffer(int step)
+{
+ for(int chan=0; chan<channel_count; chan++)
+ memcpy(subbuffer[chan], bdata[chan][step], 32*sizeof(subbuffer[0][0]));
+}
+
+void mpeg_audio::idct32(const double *input, double *output)
+{
+ // Simplest idct32 ever, non-fast at all
+ for(int i=0; i<32; i++) {
+ double s = 0;
+ for(int j=0; j<32; j++)
+ s += input[j] * cos(i*(2*j+1)*M_PI/64);
+ output[i] = s;
+ }
+}
+
+void mpeg_audio::resynthesis(const double *input, double *output)
+{
+ memset(output, 0, 32*sizeof(output[0]));
+ for(int j=0; j<64*8; j+=64) {
+ for(int i=0; i<16; i++)
+ output[i] += input[ i+j]*synthesis_filter[i+j] - input[32-i+j]*synthesis_filter[32+i+j];
+ output[16] -= input[16+j]*synthesis_filter[32+16+j];
+ for(int i=17; i<32; i++)
+ output[i] -= input[32-i+j]*synthesis_filter[i+j] + input[ i+j]*synthesis_filter[32+i+j];
+ }
+}
+
+void mpeg_audio::scale_and_clamp(const double *input, short *output, int step)
+{
+ for(int i=0; i<32; i++) {
+ double val = input[i]*32768 + 0.5;
+ short cval;
+ if(val <= -32768)
+ cval = -32768;
+ else if(val >= 32767)
+ cval = 32767;
+ else
+ cval = int(val);
+ *output = cval;
+ output += step;
+ }
+}
diff --git a/src/devices/sound/mpeg_audio.h b/src/devices/sound/mpeg_audio.h
new file mode 100644
index 00000000000..615116b6021
--- /dev/null
+++ b/src/devices/sound/mpeg_audio.h
@@ -0,0 +1,134 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/***************************************************************************
+
+ MPEG audio support. Only layer2 and variants for now.
+
+***************************************************************************/
+
+#ifndef __MPEG_AUDIO_H__
+#define __MPEG_AUDIO_H__
+
+class mpeg_audio {
+public:
+ // Accepted layers. Beware that AMM is incompatible with L2 (and
+ // not automatically recognizable) and that 2.5 implies 2.
+
+ enum {
+ L1 = 1,
+ L2 = 2,
+ L2_5 = 4,
+ L3 = 8,
+ AMM = 16
+ };
+
+ // base = Start of the mpeg data block
+ // accepted = Binary or of accepted layers
+ // lsb_first = Read bits out of bytes lsb-first rather than msb first
+ // position_align = Position alignment after reading a block (0 = pure bitstream, must be a power of 2 otherwise)
+
+ mpeg_audio(const void *base, unsigned int accepted, bool lsb_first, int position_align);
+
+ // Decode one mpeg buffer.
+ // pos = position in *bits* relative to base
+ // limit = maximum accepted position in bits
+ // output = output samples, interleaved
+ // output_samples = number of samples written to output per channel
+ // sample_rate = output sample rate
+ // channels = number of channels written to output (total sample count is output_samples*channels)
+ //
+ // returns true if the buffer was complete and the new position in pos, false otherwise
+ //
+ // Sample rate and channels can change every buffer. That's mpeg
+ // for you. Channels rarely changes, sample rate sometimes do,
+ // especially in amm samples (drops to half at the end).
+ //
+ // One call to output buffer will generate 0 or 1 frame, which is
+ // 384 samples per channel in layer I and 1152 otherwise (up to
+ // 1152 in the amm case, <1152 indicating end of stream).
+
+ bool decode_buffer(int &pos, int limit, short *output,
+ int &output_samples, int &sample_rate, int &channels);
+
+
+ // Clear audio buffer
+ void clear();
+
+ // Change the base pointer
+ void set_base(const void *base);
+
+private:
+ struct limit_hit {};
+
+ struct band_info {
+ int modulo;
+ double s1;
+ int bits, cube_bits;
+ int s4, s5;
+ double range, s7, scale, offset;
+ };
+
+ static const double scalefactors[64];
+ static const int sample_rates[8];
+ static const int layer2_param_index[2][4][16];
+ static const int band_parameter_indexed_values[5][32][17];
+ static const int band_parameter_index_bits_count[5][32];
+ static const int joint_band_counts[4], total_band_counts[5];
+ static const band_info band_infos[18];
+ static const double synthesis_filter[512];
+
+ const UINT8 *base;
+ int accepted, position_align;
+
+ int sampling_rate, last_frame_number;
+ int param_index;
+
+ int channel_count, total_bands, joint_bands;
+
+ int band_param[2][32];
+ int scfsi[2][32];
+ int scf[2][3][32];
+ double amp_values[2][3][32];
+ double bdata[2][3][32];
+ double subbuffer[2][32];
+ double audio_buffer[2][32*32];
+ int audio_buffer_pos[2];
+
+ int master_pos;
+
+ int current_pos, current_limit;
+
+ void read_header_amm(bool layer25);
+ void read_header_mpeg2(bool layer25);
+ void read_data_mpeg2();
+ void decode_mpeg2(short *output, int &output_samples);
+
+ int get_band_param(int band);
+ void read_band_params();
+ void read_scfci();
+ void read_band_amplitude_params();
+ void read_band_value_triplet(int chan, int band);
+ void build_amplitudes();
+ void build_next_segments(int step);
+ void retrieve_subbuffer(int step);
+ void handle_block(int &pos);
+ void idct32(const double *input, double *output);
+ void resynthesis(const double *input, double *output);
+ void scale_and_clamp(const double *input, short *output, int step);
+
+
+ static int do_gb_msb(const unsigned char *data, int &pos, int count);
+ static int do_gb_lsb(const unsigned char *data, int &pos, int count);
+
+ int (*do_gb)(const unsigned char *data, int &pos, int count);
+
+ inline int gb(int count)
+ {
+ if(current_pos + count > current_limit)
+ throw limit_hit();
+
+ return do_gb(base, current_pos, count);
+ }
+};
+
+#endif
diff --git a/src/devices/sound/msm5205.c b/src/devices/sound/msm5205.c
new file mode 100644
index 00000000000..ae925a513b8
--- /dev/null
+++ b/src/devices/sound/msm5205.c
@@ -0,0 +1,340 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/*
+ * streaming ADPCM driver
+ * by Aaron Giles
+ *
+ * Library to transcode from an ADPCM source to raw PCM.
+ * Written by Buffoni Mirko in 08/06/97
+ * References: various sources and documents.
+ *
+ * HJB 08/31/98
+ * modified to use an automatically selected oversampling factor
+ * for the current sample rate
+ *
+ * 01/06/99
+ * separate MSM5205 emulator form adpcm.c and some fix
+ *
+ * 07/29/12
+ * added basic support for the MSM6585
+ */
+
+#include "emu.h"
+#include "msm5205.h"
+
+/*
+
+ MSM 5205 ADPCM chip:
+
+ Data is streamed from a CPU by means of a clock generated on the chip.
+
+ A reset signal is set high or low to determine whether playback (and interrupts) are occurring.
+
+ MSM6585: is an upgraded MSM5205 voice synth IC.
+ Improvements:
+ More precise internal DA converter
+ Built in low-pass filter
+ Expanded sampling frequency
+
+ Differences between MSM6585 & MSM5205:
+
+ MSM6586 MSM5205
+ Master clock frequency 640kHz 384kHz
+ Sampling frequency 4k/8k/16k/32kHz 4k/6k/8kHz
+ ADPCM bit length 4-bit 3-bit/4-bit
+ DA converter 12-bit 10-bit
+ Low-pass filter -40dB/oct N/A
+ Overflow prevent circuit Included N/A
+
+ Timer callback at VCLK low edge on MSM5205 (at rising edge on MSM6585)
+
+ TODO:
+ - lowpass filter for MSM6585
+
+ */
+
+const device_type MSM5205 = &device_creator<msm5205_device>;
+const device_type MSM6585 = &device_creator<msm6585_device>;
+
+
+msm5205_device::msm5205_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MSM5205, "MSM5205", tag, owner, clock, "msm5205", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_prescaler(0),
+ m_bitwidth(0),
+ m_select(0),
+ m_vclk_cb(*this)
+{
+}
+
+msm5205_device::msm5205_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_sound_interface(mconfig, *this),
+ m_prescaler(0),
+ m_bitwidth(0),
+ m_select(0),
+ m_vclk_cb(*this)
+{
+}
+
+
+msm6585_device::msm6585_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : msm5205_device(mconfig, MSM6585, "MSM6585", tag, owner, clock, "msm6585", __FILE__)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void msm5205_device::device_start()
+{
+ m_mod_clock = clock();
+ m_vclk_cb.resolve();
+
+ /* compute the difference tables */
+ compute_tables();
+
+ /* stream system initialize */
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, clock());
+ m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(msm5205_device::vclk_callback), this));
+
+ /* register for save states */
+ save_item(NAME(m_mod_clock));
+ save_item(NAME(m_data));
+ save_item(NAME(m_vclk));
+ save_item(NAME(m_reset));
+ save_item(NAME(m_prescaler));
+ save_item(NAME(m_bitwidth));
+ save_item(NAME(m_signal));
+ save_item(NAME(m_step));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void msm5205_device::device_reset()
+{
+ /* initialize work */
+ m_data = 0;
+ m_vclk = 0;
+ m_reset = 0;
+ m_signal = 0;
+ m_step = 0;
+
+ /* timer and bitwidth set */
+ playmode_w(m_select);
+}
+
+
+/*
+ * ADPCM lookup table
+ */
+
+/* step size index shift table */
+static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
+
+/*
+ * Compute the difference table
+ */
+
+void msm5205_device::compute_tables()
+{
+ /* nibble to bit map */
+ static const int nbl2bit[16][4] =
+ {
+ { 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
+ { 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
+ {-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
+ {-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
+ };
+
+ int step, nib;
+
+ /* loop over all possible steps */
+ for (step = 0; step <= 48; step++)
+ {
+ /* compute the step value */
+ int stepval = floor (16.0 * pow (11.0 / 10.0, (double)step));
+
+ /* loop over all nibbles and compute the difference */
+ for (nib = 0; nib < 16; nib++)
+ {
+ m_diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
+ (stepval * nbl2bit[nib][1] +
+ stepval/2 * nbl2bit[nib][2] +
+ stepval/4 * nbl2bit[nib][3] +
+ stepval/8);
+ }
+ }
+}
+
+/* timer callback at VCLK low edge on MSM5205 (at rising edge on MSM6585) */
+TIMER_CALLBACK_MEMBER( msm5205_device::vclk_callback )
+{
+ int val;
+ int new_signal;
+
+ /* callback user handler and latch next data */
+ if (!m_vclk_cb.isnull())
+ m_vclk_cb(1);
+
+ /* reset check at last hiedge of VCLK */
+ if (m_reset)
+ {
+ new_signal = 0;
+ m_step = 0;
+ }
+ else
+ {
+ /* update signal */
+ /* !! MSM5205 has internal 12bit decoding, signal width is 0 to 8191 !! */
+ val = m_data;
+ new_signal = m_signal + m_diff_lookup[m_step * 16 + (val & 15)];
+
+ if (new_signal > 2047) new_signal = 2047;
+ else if (new_signal < -2048) new_signal = -2048;
+
+ m_step += index_shift[val & 7];
+
+ if (m_step > 48) m_step = 48;
+ else if (m_step < 0) m_step = 0;
+ }
+
+ /* update when signal changed */
+ if( m_signal != new_signal)
+ {
+ m_stream->update();
+ m_signal = new_signal;
+ }
+}
+
+
+
+/*
+ * Handle an update of the vclk status of a chip (1 is reset ON, 0 is reset OFF)
+ * This function can use selector = MSM5205_SEX only
+ */
+void msm5205_device::vclk_w(int vclk)
+{
+ if (m_prescaler != 0)
+ logerror("error: msm5205_vclk_w() called with chip = '%s', but VCLK selected master mode\n", this->device().tag());
+ else
+ {
+ if (m_vclk != vclk)
+ {
+ m_vclk = vclk;
+ if (!vclk)
+ vclk_callback(this, 0);
+ }
+ }
+}
+
+/*
+ * Handle an update of the reset status of a chip (1 is reset ON, 0 is reset OFF)
+ */
+
+void msm5205_device::reset_w(int reset)
+{
+ m_reset = reset;
+}
+
+/*
+ * Handle an update of the data to the chip
+ */
+
+void msm5205_device::data_w(int data)
+{
+ if (m_bitwidth == 4)
+ m_data = data & 0x0f;
+ else
+ m_data = (data & 0x07) << 1; /* unknown */
+}
+
+/*
+ * Handle a change of the selector
+ */
+
+void msm5205_device::playmode_w(int select)
+{
+ static const int prescaler_table[2][4] =
+ {
+ { 96, 48, 64, 0},
+ {160, 40, 80, 20}
+ };
+ int prescaler = prescaler_table[select >> 3 & 1][select & 3];
+ int bitwidth = (select & 4) ? 4 : 3;
+
+ if (m_prescaler != prescaler)
+ {
+ m_stream->update();
+
+ m_prescaler = prescaler;
+
+ /* timer set */
+ if (prescaler)
+ {
+ attotime period = attotime::from_hz(m_mod_clock) * prescaler;
+ m_timer->adjust(period, 0, period);
+ }
+ else
+ m_timer->adjust(attotime::never);
+ }
+
+ if (m_bitwidth != bitwidth)
+ {
+ m_stream->update();
+ m_bitwidth = bitwidth;
+ }
+}
+
+
+void msm5205_device::set_volume(int volume)
+{
+ m_stream->set_output_gain(0,volume / 100.0);
+}
+
+void msm5205_device::change_clock_w(INT32 clock)
+{
+ attotime period;
+
+ m_mod_clock = clock;
+
+ period = attotime::from_hz(m_mod_clock) * m_prescaler;
+ m_timer->adjust(period, 0, period);
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void msm5205_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *buffer = outputs[0];
+
+ /* if this voice is active */
+ if(m_signal)
+ {
+ short val = m_signal * 16;
+ while (samples)
+ {
+ *buffer++ = val;
+ samples--;
+ }
+ }
+ else
+ memset(buffer, 0, samples * sizeof(*buffer));
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void msm6585_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ // should this be different?
+ msm5205_device::sound_stream_update(stream, inputs, outputs,samples);
+}
diff --git a/src/devices/sound/msm5205.h b/src/devices/sound/msm5205.h
new file mode 100644
index 00000000000..ab3b4219df2
--- /dev/null
+++ b/src/devices/sound/msm5205.h
@@ -0,0 +1,109 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+#pragma once
+
+#ifndef __MSM5205_H__
+#define __MSM5205_H__
+
+/* an interface for the MSM5205 and similar chips */
+
+/* prescaler selector defines */
+/* MSM5205 default master clock is 384KHz */
+#define MSM5205_S96_3B 0 /* prescaler 1/96(4KHz) , data 3bit */
+#define MSM5205_S48_3B 1 /* prescaler 1/48(8KHz) , data 3bit */
+#define MSM5205_S64_3B 2 /* prescaler 1/64(6KHz) , data 3bit */
+#define MSM5205_SEX_3B 3 /* VCLK slave mode , data 3bit */
+#define MSM5205_S96_4B 4 /* prescaler 1/96(4KHz) , data 4bit */
+#define MSM5205_S48_4B 5 /* prescaler 1/48(8KHz) , data 4bit */
+#define MSM5205_S64_4B 6 /* prescaler 1/64(6KHz) , data 4bit */
+#define MSM5205_SEX_4B 7 /* VCLK slave mode , data 4bit */
+
+/* MSM6585 default master clock is 640KHz */
+#define MSM6585_S160 (4+8) /* prescaler 1/160(4KHz), data 4bit */
+#define MSM6585_S40 (5+8) /* prescaler 1/40(16KHz), data 4bit */
+#define MSM6585_S80 (6+8) /* prescaler 1/80 (8KHz), data 4bit */
+#define MSM6585_S20 (7+8) /* prescaler 1/20(32KHz), data 4bit */
+
+
+#define MCFG_MSM5205_PRESCALER_SELECTOR(_select) \
+ msm5205_device::set_prescaler_selector(*device, _select);
+
+#define MCFG_MSM5205_VCLK_CB(_devcb) \
+ devcb = &msm5205_device::set_vclk_callback(*device, DEVCB_##_devcb);
+
+
+#define MCFG_MSM6585_PRESCALER_SELECTOR(_select) \
+ msm6585_device::set_prescaler_selector(*device, _select);
+
+#define MCFG_MSM6585_VCLK_CB(_devcb) \
+ devcb = &msm6585_device::set_vclk_callback(*device, DEVCB_##_devcb);
+
+
+class msm5205_device : public device_t,
+ public device_sound_interface
+{
+public:
+ msm5205_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ msm5205_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ ~msm5205_device() {}
+
+ static void set_prescaler_selector(device_t &device, int select) { downcast<msm5205_device &>(device).m_select = select; }
+ template<class _Object> static devcb_base &set_vclk_callback(device_t &device, _Object object) { return downcast<msm5205_device &>(device).m_vclk_cb.set_callback(object); }
+
+ // reset signal should keep for 2cycle of VCLK
+ void reset_w(int reset);
+ // adpcmata is latched after vclk_interrupt callback
+ void data_w(int data);
+ // VCLK slave mode option
+ // if VCLK and reset or data is changed at the same time,
+ // call vclk_w after data_w and reset_w.
+ void vclk_w(int vclk);
+ // option , selected pin seletor
+ void playmode_w(int select);
+
+ void set_volume(int volume);
+ void change_clock_w(INT32 clock);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ TIMER_CALLBACK_MEMBER(vclk_callback);
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ void compute_tables();
+
+ // internal state
+ sound_stream * m_stream; /* number of stream system */
+ INT32 m_mod_clock; /* clock rate */
+ emu_timer *m_timer; /* VCLK callback timer */
+ INT32 m_data; /* next adpcm data */
+ INT32 m_vclk; /* vclk signal (external mode) */
+ INT32 m_reset; /* reset pin signal */
+ INT32 m_prescaler; /* prescaler selector S1 and S2 */
+ INT32 m_bitwidth; /* bit width selector -3B/4B */
+ INT32 m_signal; /* current ADPCM signal */
+ INT32 m_step; /* current ADPCM step */
+ int m_diff_lookup[49*16];
+ int m_select;
+ devcb_write_line m_vclk_cb;
+};
+
+extern const device_type MSM5205;
+
+class msm6585_device : public msm5205_device
+{
+public:
+ msm6585_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+};
+
+extern const device_type MSM6585;
+
+
+#endif /* __MSM5205_H__ */
diff --git a/src/devices/sound/msm5232.c b/src/devices/sound/msm5232.c
new file mode 100644
index 00000000000..6bd36aac09e
--- /dev/null
+++ b/src/devices/sound/msm5232.c
@@ -0,0 +1,802 @@
+// license:???
+// copyright-holders:Jarek Burczynski, Hiromitsu Shioya
+#include "emu.h"
+
+#include "msm5232.h"
+
+#define CLOCK_RATE_DIVIDER 16
+
+/*
+ OKI MSM5232RS
+ 8 channel tone generator
+*/
+
+const device_type MSM5232 = &device_creator<msm5232_device>;
+
+msm5232_device::msm5232_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MSM5232, "MSM5232", tag, owner, clock, "msm5232", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_gate_handler_cb(*this)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void msm5232_device::device_start()
+{
+ int rate = clock()/CLOCK_RATE_DIVIDER;
+ int voicenum;
+
+ m_gate_handler_cb.resolve();
+
+ init(clock(), rate);
+
+ m_stream = machine().sound().stream_alloc(*this, 0, 11, rate);
+
+ /* register with the save state system */
+ machine().save().register_postload(save_prepost_delegate(FUNC(msm5232_device::postload), this));
+ save_item(NAME(m_EN_out16));
+ save_item(NAME(m_EN_out8));
+ save_item(NAME(m_EN_out4));
+ save_item(NAME(m_EN_out2));
+ save_item(NAME(m_noise_cnt));
+ save_item(NAME(m_noise_rng));
+ save_item(NAME(m_noise_clocks));
+ save_item(NAME(m_control1));
+ save_item(NAME(m_control2));
+ save_item(NAME(m_gate));
+ save_item(NAME(m_chip_clock));
+ save_item(NAME(m_rate));
+
+ /* register voice-specific data for save states */
+ for (voicenum = 0; voicenum < 8; voicenum++)
+ {
+ VOICE *voice = &m_voi[voicenum];
+
+ save_item(NAME(voice->mode), voicenum);
+ save_item(NAME(voice->TG_count_period), voicenum);
+ save_item(NAME(voice->TG_cnt), voicenum);
+ save_item(NAME(voice->TG_out16), voicenum);
+ save_item(NAME(voice->TG_out8), voicenum);
+ save_item(NAME(voice->TG_out4), voicenum);
+ save_item(NAME(voice->TG_out2), voicenum);
+ save_item(NAME(voice->egvol), voicenum);
+ save_item(NAME(voice->eg_sect), voicenum);
+ save_item(NAME(voice->counter), voicenum);
+ save_item(NAME(voice->eg), voicenum);
+ save_item(NAME(voice->eg_arm), voicenum);
+ save_item(NAME(voice->ar_rate), voicenum);
+ save_item(NAME(voice->dr_rate), voicenum);
+ save_item(NAME(voice->pitch), voicenum);
+ save_item(NAME(voice->GF), voicenum);
+ }
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void msm5232_device::device_reset()
+{
+ int i;
+
+ for (i=0; i<8; i++)
+ {
+ write(machine().driver_data()->generic_space(),i,0x80);
+ write(machine().driver_data()->generic_space(),i,0x00);
+ }
+ m_noise_cnt = 0;
+ m_noise_rng = 1;
+ m_noise_clocks = 0;
+
+ m_control1 = 0;
+ m_EN_out16[0] = 0;
+ m_EN_out8[0] = 0;
+ m_EN_out4[0] = 0;
+ m_EN_out2[0] = 0;
+
+ m_control2 = 0;
+ m_EN_out16[1] = 0;
+ m_EN_out8[1] = 0;
+ m_EN_out4[1] = 0;
+ m_EN_out2[1] = 0;
+
+ gate_update();
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void msm5232_device::device_stop()
+{
+ #ifdef SAVE_SAMPLE
+ fclose(sample[8]);
+#endif
+#ifdef SAVE_SEPARATE_CHANNELS
+ fclose(sample[0]);
+ fclose(sample[1]);
+ fclose(sample[2]);
+ fclose(sample[3]);
+ fclose(sample[4]);
+ fclose(sample[5]);
+ fclose(sample[6]);
+ fclose(sample[7]);
+#endif
+}
+
+void msm5232_device::static_set_capacitors(device_t &device, double cap1, double cap2, double cap3, double cap4, double cap5, double cap6, double cap7, double cap8)
+{
+ msm5232_device &msm = downcast<msm5232_device &>(device);
+ msm.m_external_capacity[0] = cap1;
+ msm.m_external_capacity[1] = cap2;
+ msm.m_external_capacity[2] = cap3;
+ msm.m_external_capacity[3] = cap4;
+ msm.m_external_capacity[4] = cap5;
+ msm.m_external_capacity[5] = cap6;
+ msm.m_external_capacity[6] = cap7;
+ msm.m_external_capacity[7] = cap8;
+}
+
+/* Default chip clock is 2119040 Hz */
+/* At this clock chip generates exactly 440.0 Hz signal on 8' output when pitch data=0x21 */
+
+
+/* ROM table to convert from pitch data into data for programmable counter and binary counter */
+/* Chip has 88x12bits ROM (addressing (in hex) from 0x00 to 0x57) */
+#define ROM(counter,bindiv) (counter|(bindiv<<9))
+
+static const UINT16 MSM5232_ROM[88]={
+/* higher values are Programmable Counter data (9 bits) */
+/* lesser values are Binary Counter shift data (3 bits) */
+
+/* 0 */ ROM (506, 7),
+
+/* 1 */ ROM (478, 7),/* 2 */ ROM (451, 7),/* 3 */ ROM (426, 7),/* 4 */ ROM (402, 7),
+/* 5 */ ROM (379, 7),/* 6 */ ROM (358, 7),/* 7 */ ROM (338, 7),/* 8 */ ROM (319, 7),
+/* 9 */ ROM (301, 7),/* A */ ROM (284, 7),/* B */ ROM (268, 7),/* C */ ROM (253, 7),
+
+/* D */ ROM (478, 6),/* E */ ROM (451, 6),/* F */ ROM (426, 6),/*10 */ ROM (402, 6),
+/*11 */ ROM (379, 6),/*12 */ ROM (358, 6),/*13 */ ROM (338, 6),/*14 */ ROM (319, 6),
+/*15 */ ROM (301, 6),/*16 */ ROM (284, 6),/*17 */ ROM (268, 6),/*18 */ ROM (253, 6),
+
+/*19 */ ROM (478, 5),/*1A */ ROM (451, 5),/*1B */ ROM (426, 5),/*1C */ ROM (402, 5),
+/*1D */ ROM (379, 5),/*1E */ ROM (358, 5),/*1F */ ROM (338, 5),/*20 */ ROM (319, 5),
+/*21 */ ROM (301, 5),/*22 */ ROM (284, 5),/*23 */ ROM (268, 5),/*24 */ ROM (253, 5),
+
+/*25 */ ROM (478, 4),/*26 */ ROM (451, 4),/*27 */ ROM (426, 4),/*28 */ ROM (402, 4),
+/*29 */ ROM (379, 4),/*2A */ ROM (358, 4),/*2B */ ROM (338, 4),/*2C */ ROM (319, 4),
+/*2D */ ROM (301, 4),/*2E */ ROM (284, 4),/*2F */ ROM (268, 4),/*30 */ ROM (253, 4),
+
+/*31 */ ROM (478, 3),/*32 */ ROM (451, 3),/*33 */ ROM (426, 3),/*34 */ ROM (402, 3),
+/*35 */ ROM (379, 3),/*36 */ ROM (358, 3),/*37 */ ROM (338, 3),/*38 */ ROM (319, 3),
+/*39 */ ROM (301, 3),/*3A */ ROM (284, 3),/*3B */ ROM (268, 3),/*3C */ ROM (253, 3),
+
+/*3D */ ROM (478, 2),/*3E */ ROM (451, 2),/*3F */ ROM (426, 2),/*40 */ ROM (402, 2),
+/*41 */ ROM (379, 2),/*42 */ ROM (358, 2),/*43 */ ROM (338, 2),/*44 */ ROM (319, 2),
+/*45 */ ROM (301, 2),/*46 */ ROM (284, 2),/*47 */ ROM (268, 2),/*48 */ ROM (253, 2),
+
+/*49 */ ROM (478, 1),/*4A */ ROM (451, 1),/*4B */ ROM (426, 1),/*4C */ ROM (402, 1),
+/*4D */ ROM (379, 1),/*4E */ ROM (358, 1),/*4F */ ROM (338, 1),/*50 */ ROM (319, 1),
+/*51 */ ROM (301, 1),/*52 */ ROM (284, 1),/*53 */ ROM (268, 1),/*54 */ ROM (253, 1),
+
+/*55 */ ROM (253, 1),/*56 */ ROM (253, 1),
+
+/*57 */ ROM (13, 7)
+};
+#undef ROM
+
+
+#define STEP_SH (16) /* step calculations accuracy */
+
+
+/* save output as raw 16-bit sample */
+/* #define SAVE_SAMPLE */
+/* #define SAVE_SEPARATE_CHANNELS */
+#if defined SAVE_SAMPLE || defined SAVE_SEPARATE_CHANNELS
+static FILE *sample[9];
+#endif
+
+
+
+/*
+ * resistance values are guesswork, default capacity is mentioned in the datasheets
+ *
+ * charges external capacitor (default is 0.39uF) via R51
+ * in approx. 5*1400 * 0.39e-6
+ *
+ * external capacitor is discharged through R52
+ * in approx. 5*28750 * 0.39e-6
+ */
+
+
+#define R51 1400 /* charge resistance */
+#define R52 28750 /* discharge resistance */
+
+#if 0
+/*
+ C24 = external capacity
+
+ osd_printf_debug("Time constant T=R*C =%f sec.\n",R51*C24);
+ osd_printf_debug("Cap fully charged after 5T=%f sec (sample=%f). Level=%f\n",(R51*C24)*5,(R51*C24)*5*sample_rate , VMAX*0.99326 );
+ osd_printf_debug("Cap charged after 5T=%f sec (sample=%f). Level=%20.16f\n",(R51*C24)*5,(R51*C24)*5*sample_rate ,
+ VMAX*(1.0-pow(2.718,-0.0748/(R51*C24))) );
+*/
+#endif
+
+
+
+
+void msm5232_device::init_tables()
+{
+ int i;
+ double scale;
+
+ /* sample rate = chip clock !!! But : */
+ /* highest possible frequency is chipclock/13/16 (pitch data=0x57) */
+ /* at 2MHz : 2000000/13/16 = 9615 Hz */
+
+ i = ((double)(1<<STEP_SH) * (double)m_rate) / (double)m_chip_clock;
+ m_UpdateStep = i;
+ /* logerror("clock=%i Hz rate=%i Hz, UpdateStep=%i\n",
+ m_chip_clock, m_rate, m_UpdateStep); */
+
+ scale = ((double)m_chip_clock) / (double)m_rate;
+ m_noise_step = ((1<<STEP_SH)/128.0) * scale; /* step of the rng reg in 16.16 format */
+ /* logerror("noise step=%8x\n", m_noise_step); */
+
+#if 0
+{
+ /* rate tables (in miliseconds) */
+ static const int ATBL[8] = { 2,4,8,16, 32,64, 32,64};
+ static const int DTBL[16]= { 40,80,160,320, 640,1280, 640,1280,
+ 333,500,1000,2000, 4000,8000, 4000,8000};
+ for (i=0; i<8; i++)
+ {
+ double clockscale = (double)m_chip_clock / 2119040.0;
+ double time = (ATBL[i] / 1000.0) / clockscale; /* attack time in seconds */
+ m_ar_tbl[i] = 0.50 * ( (1.0/time) / (double)m_rate );
+ /* logerror("ATBL[%i] = %20.16f time = %f s\n",i, m_ar_tbl[i], time); */
+ }
+
+ for (i=0; i<16; i++)
+ {
+ double clockscale = (double)m_chip_clock / 2119040.0;
+ double time = (DTBL[i] / 1000.0) / clockscale; /* decay time in seconds */
+ m_dr_tbl[i] = 0.50 * ( (1.0/time) / (double)m_rate );
+ /* logerror("DTBL[%i] = %20.16f time = %f s\n",i, m_dr_tbl[i], time); */
+ }
+}
+#endif
+
+
+ for (i=0; i<8; i++)
+ {
+ double clockscale = (double)m_chip_clock / 2119040.0;
+ m_ar_tbl[i] = ((1<<i) / clockscale) * (double)R51;
+ }
+
+ for (i=0; i<8; i++)
+ {
+ double clockscale = (double)m_chip_clock / 2119040.0;
+ m_dr_tbl[i] = ( (1<<i) / clockscale) * (double)R52;
+ m_dr_tbl[i+8] = (6.25*(1<<i) / clockscale) * (double)R52;
+ }
+
+
+#ifdef SAVE_SAMPLE
+ sample[8]=fopen("sampsum.pcm","wb");
+#endif
+#ifdef SAVE_SEPARATE_CHANNELS
+ sample[0]=fopen("samp0.pcm","wb");
+ sample[1]=fopen("samp1.pcm","wb");
+ sample[2]=fopen("samp2.pcm","wb");
+ sample[3]=fopen("samp3.pcm","wb");
+ sample[4]=fopen("samp4.pcm","wb");
+ sample[5]=fopen("samp5.pcm","wb");
+ sample[6]=fopen("samp6.pcm","wb");
+ sample[7]=fopen("samp7.pcm","wb");
+#endif
+}
+
+
+void msm5232_device::init_voice(int i)
+{
+ m_voi[i].ar_rate= m_ar_tbl[0] * m_external_capacity[i];
+ m_voi[i].dr_rate= m_dr_tbl[0] * m_external_capacity[i];
+ m_voi[i].rr_rate= m_dr_tbl[0] * m_external_capacity[i]; /* this is constant value */
+ m_voi[i].eg_sect= -1;
+ m_voi[i].eg = 0.0;
+ m_voi[i].eg_arm = 0;
+ m_voi[i].pitch = -1.0;
+}
+
+
+void msm5232_device::gate_update()
+{
+ int new_state = (m_control2 & 0x20) ? m_voi[7].GF : 0;
+
+ if (m_gate != new_state && !m_gate_handler_cb.isnull())
+ {
+ m_gate = new_state;
+ m_gate_handler_cb(new_state);
+ }
+}
+
+void msm5232_device::init(int clock, int rate)
+{
+ int j;
+
+ m_chip_clock = clock;
+ m_rate = rate ? rate : 44100; /* avoid division by 0 */
+
+ init_tables();
+
+ for (j=0; j<8; j++)
+ {
+ memset(&m_voi[j],0,sizeof(VOICE));
+ init_voice(j);
+ }
+}
+
+
+WRITE8_MEMBER( msm5232_device::write )
+{
+ if (offset > 0x0d)
+ return;
+
+ m_stream->update ();
+
+ if (offset < 0x08) /* pitch */
+ {
+ int ch = offset&7;
+
+ m_voi[ch].GF = ((data&0x80)>>7);
+ if (ch == 7)
+ gate_update();
+
+ if(data&0x80)
+ {
+ if(data >= 0xd8)
+ {
+ /*if ((data&0x7f) != 0x5f) logerror("MSM5232: WRONG PITCH CODE = %2x\n",data&0x7f);*/
+ m_voi[ch].mode = 1; /* noise mode */
+ m_voi[ch].eg_sect = 0; /* Key On */
+ }
+ else
+ {
+ if ( m_voi[ch].pitch != (data&0x7f) )
+ {
+ int n;
+ UINT16 pg;
+
+ m_voi[ch].pitch = data&0x7f;
+
+ pg = MSM5232_ROM[ data&0x7f ];
+
+ m_voi[ch].TG_count_period = (pg & 0x1ff) * m_UpdateStep / 2;
+
+ n = (pg>>9) & 7; /* n = bit number for 16' output */
+ m_voi[ch].TG_out16 = 1<<n;
+ /* for 8' it is bit n-1 (bit 0 if n-1<0) */
+ /* for 4' it is bit n-2 (bit 0 if n-2<0) */
+ /* for 2' it is bit n-3 (bit 0 if n-3<0) */
+ n = (n>0)? n-1: 0;
+ m_voi[ch].TG_out8 = 1<<n;
+
+ n = (n>0)? n-1: 0;
+ m_voi[ch].TG_out4 = 1<<n;
+
+ n = (n>0)? n-1: 0;
+ m_voi[ch].TG_out2 = 1<<n;
+ }
+ m_voi[ch].mode = 0; /* tone mode */
+ m_voi[ch].eg_sect = 0; /* Key On */
+ }
+ }
+ else
+ {
+ if ( !m_voi[ch].eg_arm ) /* arm = 0 */
+ m_voi[ch].eg_sect = 2; /* Key Off -> go to release */
+ else /* arm = 1 */
+ m_voi[ch].eg_sect = 1; /* Key Off -> go to decay */
+ }
+ }
+ else
+ {
+ int i;
+ switch(offset)
+ {
+ case 0x08: /* group1 attack */
+ for (i=0; i<4; i++)
+ m_voi[i].ar_rate = m_ar_tbl[data&0x7] * m_external_capacity[i];
+ break;
+
+ case 0x09: /* group2 attack */
+ for (i=0; i<4; i++)
+ m_voi[i+4].ar_rate = m_ar_tbl[data&0x7] * m_external_capacity[i+4];
+ break;
+
+ case 0x0a: /* group1 decay */
+ for (i=0; i<4; i++)
+ m_voi[i].dr_rate = m_dr_tbl[data&0xf] * m_external_capacity[i];
+ break;
+
+ case 0x0b: /* group2 decay */
+ for (i=0; i<4; i++)
+ m_voi[i+4].dr_rate = m_dr_tbl[data&0xf] * m_external_capacity[i+4];
+ break;
+
+ case 0x0c: /* group1 control */
+
+ /*if (m_control1 != data)
+ logerror("msm5232: control1 ctrl=%x OE=%x\n", data&0xf0, data&0x0f);*/
+
+ /*if (data & 0x10)
+ popmessage("msm5232: control1 ctrl=%2x\n", data);*/
+
+ m_control1 = data;
+
+ for (i=0; i<4; i++)
+ m_voi[i].eg_arm = data&0x10;
+
+ m_EN_out16[0] = (data&1) ? ~0:0;
+ m_EN_out8[0] = (data&2) ? ~0:0;
+ m_EN_out4[0] = (data&4) ? ~0:0;
+ m_EN_out2[0] = (data&8) ? ~0:0;
+
+ break;
+
+ case 0x0d: /* group2 control */
+
+ /*if (m_control2 != data)
+ logerror("msm5232: control2 ctrl=%x OE=%x\n", data&0xf0, data&0x0f);*/
+
+ /*if (data & 0x10)
+ popmessage("msm5232: control2 ctrl=%2x\n", data);*/
+
+ m_control2 = data;
+ gate_update();
+
+ for (i=0; i<4; i++)
+ m_voi[i+4].eg_arm = data&0x10;
+
+ m_EN_out16[1] = (data&1) ? ~0:0;
+ m_EN_out8[1] = (data&2) ? ~0:0;
+ m_EN_out4[1] = (data&4) ? ~0:0;
+ m_EN_out2[1] = (data&8) ? ~0:0;
+
+ break;
+ }
+ }
+}
+
+
+
+#define VMIN 0
+#define VMAX 32768
+
+
+void msm5232_device::EG_voices_advance()
+{
+ VOICE *voi = &m_voi[0];
+ int samplerate = m_rate;
+ int i;
+
+ i = 8;
+ do
+ {
+ switch(voi->eg_sect)
+ {
+ case 0: /* attack */
+
+ /* capacitor charge */
+ if (voi->eg < VMAX)
+ {
+ voi->counter -= (int)((VMAX - voi->eg) / voi->ar_rate);
+ if ( voi->counter <= 0 )
+ {
+ int n = -voi->counter / samplerate + 1;
+ voi->counter += n * samplerate;
+ if ( (voi->eg += n) > VMAX )
+ voi->eg = VMAX;
+ }
+ }
+
+ /* when ARM=0, EG switches to decay as soon as cap is charged to VT (EG inversion voltage; about 80% of MAX) */
+ if (!voi->eg_arm)
+ {
+ if(voi->eg >= VMAX * 80/100 )
+ {
+ voi->eg_sect = 1;
+ }
+ }
+ else
+ /* ARM=1 */
+ {
+ /* when ARM=1, EG stays at maximum until key off */
+ }
+
+ voi->egvol = voi->eg / 16; /*32768/16 = 2048 max*/
+
+ break;
+
+ case 1: /* decay */
+
+ /* capacitor discharge */
+ if (voi->eg > VMIN)
+ {
+ voi->counter -= (int)((voi->eg - VMIN) / voi->dr_rate);
+ if ( voi->counter <= 0 )
+ {
+ int n = -voi->counter / samplerate + 1;
+ voi->counter += n * samplerate;
+ if ( (voi->eg -= n) < VMIN )
+ voi->eg = VMIN;
+ }
+ }
+ else /* voi->eg <= VMIN */
+ {
+ voi->eg_sect =-1;
+ }
+
+ voi->egvol = voi->eg / 16; /*32768/16 = 2048 max*/
+
+ break;
+
+ case 2: /* release */
+
+ /* capacitor discharge */
+ if (voi->eg > VMIN)
+ {
+ voi->counter -= (int)((voi->eg - VMIN) / voi->rr_rate);
+ if ( voi->counter <= 0 )
+ {
+ int n = -voi->counter / samplerate + 1;
+ voi->counter += n * samplerate;
+ if ( (voi->eg -= n) < VMIN )
+ voi->eg = VMIN;
+ }
+ }
+ else /* voi->eg <= VMIN */
+ {
+ voi->eg_sect =-1;
+ }
+
+ voi->egvol = voi->eg / 16; /*32768/16 = 2048 max*/
+
+ break;
+
+ default:
+ break;
+ }
+
+ voi++;
+ i--;
+ } while (i>0);
+
+}
+
+static int o2,o4,o8,o16,solo8,solo16;
+
+void msm5232_device::TG_group_advance(int groupidx)
+{
+ VOICE *voi = &m_voi[groupidx*4];
+ int i;
+
+ o2 = o4 = o8 = o16 = solo8 = solo16 = 0;
+
+ i=4;
+ do
+ {
+ int out2, out4, out8, out16;
+
+ out2 = out4 = out8 = out16 = 0;
+
+ if (voi->mode==0) /* generate square tone */
+ {
+ int left = 1<<STEP_SH;
+ do
+ {
+ int nextevent = left;
+
+ if (voi->TG_cnt&voi->TG_out16) out16+=voi->TG_count;
+ if (voi->TG_cnt&voi->TG_out8) out8 +=voi->TG_count;
+ if (voi->TG_cnt&voi->TG_out4) out4 +=voi->TG_count;
+ if (voi->TG_cnt&voi->TG_out2) out2 +=voi->TG_count;
+
+ voi->TG_count -= nextevent;
+
+ while (voi->TG_count <= 0)
+ {
+ voi->TG_count += voi->TG_count_period;
+ voi->TG_cnt++;
+ if (voi->TG_cnt&voi->TG_out16) out16+=voi->TG_count_period;
+ if (voi->TG_cnt&voi->TG_out8 ) out8 +=voi->TG_count_period;
+ if (voi->TG_cnt&voi->TG_out4 ) out4 +=voi->TG_count_period;
+ if (voi->TG_cnt&voi->TG_out2 ) out2 +=voi->TG_count_period;
+
+ if (voi->TG_count > 0)
+ break;
+
+ voi->TG_count += voi->TG_count_period;
+ voi->TG_cnt++;
+ if (voi->TG_cnt&voi->TG_out16) out16+=voi->TG_count_period;
+ if (voi->TG_cnt&voi->TG_out8 ) out8 +=voi->TG_count_period;
+ if (voi->TG_cnt&voi->TG_out4 ) out4 +=voi->TG_count_period;
+ if (voi->TG_cnt&voi->TG_out2 ) out2 +=voi->TG_count_period;
+ }
+ if (voi->TG_cnt&voi->TG_out16) out16-=voi->TG_count;
+ if (voi->TG_cnt&voi->TG_out8 ) out8 -=voi->TG_count;
+ if (voi->TG_cnt&voi->TG_out4 ) out4 -=voi->TG_count;
+ if (voi->TG_cnt&voi->TG_out2 ) out2 -=voi->TG_count;
+
+ left -=nextevent;
+
+ }while (left>0);
+ }
+ else /* generate noise */
+ {
+ if (m_noise_clocks&8) out16+=(1<<STEP_SH);
+ if (m_noise_clocks&4) out8 +=(1<<STEP_SH);
+ if (m_noise_clocks&2) out4 +=(1<<STEP_SH);
+ if (m_noise_clocks&1) out2 +=(1<<STEP_SH);
+ }
+
+ /* calculate signed output */
+ o16 += ( (out16-(1<<(STEP_SH-1))) * voi->egvol) >> STEP_SH;
+ o8 += ( (out8 -(1<<(STEP_SH-1))) * voi->egvol) >> STEP_SH;
+ o4 += ( (out4 -(1<<(STEP_SH-1))) * voi->egvol) >> STEP_SH;
+ o2 += ( (out2 -(1<<(STEP_SH-1))) * voi->egvol) >> STEP_SH;
+
+ if (i == 1 && groupidx == 1)
+ {
+ solo16 += ( (out16-(1<<(STEP_SH-1))) << 11) >> STEP_SH;
+ solo8 += ( (out8 -(1<<(STEP_SH-1))) << 11) >> STEP_SH;
+ }
+
+ voi++;
+ i--;
+ }while (i>0);
+
+ /* cut off disabled output lines */
+ o16 &= m_EN_out16[groupidx];
+ o8 &= m_EN_out8 [groupidx];
+ o4 &= m_EN_out4 [groupidx];
+ o2 &= m_EN_out2 [groupidx];
+}
+
+
+/* macro saves feet data to mono file */
+#ifdef SAVE_SEPARATE_CHANNELS
+ #define SAVE_SINGLE_CHANNEL(j,val) \
+ { signed int pom= val; \
+ if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \
+ fputc((unsigned short)pom&0xff,sample[j]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[j]); }
+#else
+ #define SAVE_SINGLE_CHANNEL(j,val)
+#endif
+
+/* first macro saves all 8 feet outputs to mixed (mono) file */
+/* second macro saves one group into left and the other in right channel */
+#if 1 /*MONO*/
+ #ifdef SAVE_SAMPLE
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = buf1[i] + buf2[i]; \
+ fputc((unsigned short)pom&0xff,sample[8]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[8]); \
+ }
+ #else
+ #define SAVE_ALL_CHANNELS
+ #endif
+#else /*STEREO*/
+ #ifdef SAVE_SAMPLE
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = buf1[i]; \
+ fputc((unsigned short)pom&0xff,sample[8]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[8]); \
+ pom = buf2[i]; \
+ fputc((unsigned short)pom&0xff,sample[8]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[8]); \
+ }
+ #else
+ #define SAVE_ALL_CHANNELS
+ #endif
+#endif
+
+
+/* MAME Interface */
+void msm5232_device::postload()
+{
+ init_tables();
+}
+
+void msm5232_device::set_clock(int clock)
+{
+ if (m_chip_clock != clock)
+ {
+ m_stream->update ();
+ m_chip_clock = clock;
+ m_rate = clock/CLOCK_RATE_DIVIDER;
+ init_tables();
+ m_stream->set_sample_rate(m_rate);
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void msm5232_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *buf1 = outputs[0];
+ stream_sample_t *buf2 = outputs[1];
+ stream_sample_t *buf3 = outputs[2];
+ stream_sample_t *buf4 = outputs[3];
+ stream_sample_t *buf5 = outputs[4];
+ stream_sample_t *buf6 = outputs[5];
+ stream_sample_t *buf7 = outputs[6];
+ stream_sample_t *buf8 = outputs[7];
+ stream_sample_t *bufsolo1 = outputs[8];
+ stream_sample_t *bufsolo2 = outputs[9];
+ stream_sample_t *bufnoise = outputs[10];
+ int i;
+
+ for (i=0; i<samples; i++)
+ {
+ /* calculate all voices' envelopes */
+ EG_voices_advance();
+
+ TG_group_advance(0); /* calculate tones group 1 */
+ buf1[i] = o2;
+ buf2[i] = o4;
+ buf3[i] = o8;
+ buf4[i] = o16;
+
+ SAVE_SINGLE_CHANNEL(0,o2)
+ SAVE_SINGLE_CHANNEL(1,o4)
+ SAVE_SINGLE_CHANNEL(2,o8)
+ SAVE_SINGLE_CHANNEL(3,o16)
+
+ TG_group_advance(1); /* calculate tones group 2 */
+ buf5[i] = o2;
+ buf6[i] = o4;
+ buf7[i] = o8;
+ buf8[i] = o16;
+
+ bufsolo1[i] = solo8;
+ bufsolo2[i] = solo16;
+
+ SAVE_SINGLE_CHANNEL(4,o2)
+ SAVE_SINGLE_CHANNEL(5,o4)
+ SAVE_SINGLE_CHANNEL(6,o8)
+ SAVE_SINGLE_CHANNEL(7,o16)
+
+ SAVE_ALL_CHANNELS
+
+ /* update noise generator */
+ {
+ int cnt = (m_noise_cnt+=m_noise_step) >> STEP_SH;
+ m_noise_cnt &= ((1<<STEP_SH)-1);
+ while (cnt > 0)
+ {
+ int tmp = m_noise_rng & (1<<16); /* store current level */
+
+ if (m_noise_rng&1)
+ m_noise_rng ^= 0x24000;
+ m_noise_rng>>=1;
+
+ if ( (m_noise_rng & (1<<16)) != tmp ) /* level change detect */
+ m_noise_clocks++;
+
+ cnt--;
+ }
+ }
+
+ bufnoise[i] = (m_noise_rng & (1<<16)) ? 32767 : 0;
+ }
+}
diff --git a/src/devices/sound/msm5232.h b/src/devices/sound/msm5232.h
new file mode 100644
index 00000000000..a7e30e72a57
--- /dev/null
+++ b/src/devices/sound/msm5232.h
@@ -0,0 +1,111 @@
+// license:???
+// copyright-holders:Jarek Burczynski, Hiromitsu Shioya
+#pragma once
+
+#ifndef __MSM5232_H__
+#define __MSM5232_H__
+
+
+#define MCFG_MSM5232_SET_CAPACITORS(_a, _b, _c, _d, _e, _f, _g, _h) \
+ msm5232_device::static_set_capacitors(*device, _a, _b, _c, _d, _e, _f, _g, _h);
+
+#define MCFG_MSM5232_GATE_HANDLER_CB(_devcb) \
+ devcb = &msm5232_device::set_gate_handler_callback(*device, DEVCB_##_devcb);
+
+struct VOICE {
+ UINT8 mode;
+
+ int TG_count_period;
+ int TG_count;
+
+ UINT8 TG_cnt; /* 7 bits binary counter (frequency output) */
+ UINT8 TG_out16; /* bit number (of TG_cnt) for 16' output */
+ UINT8 TG_out8; /* bit number (of TG_cnt) for 8' output */
+ UINT8 TG_out4; /* bit number (of TG_cnt) for 4' output */
+ UINT8 TG_out2; /* bit number (of TG_cnt) for 2' output */
+
+ int egvol;
+ int eg_sect;
+ int counter;
+ int eg;
+
+ UINT8 eg_arm; /* attack/release mode */
+
+ double ar_rate;
+ double dr_rate;
+ double rr_rate;
+
+ int pitch; /* current pitch data */
+
+ int GF;
+};
+
+
+class msm5232_device : public device_t,
+ public device_sound_interface
+{
+public:
+ msm5232_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~msm5232_device() {}
+
+ static void static_set_capacitors(device_t &device, double cap1, double cap2, double cap3, double cap4, double cap5, double cap6, double cap7, double cap8);
+ template<class _Object> static devcb_base &set_gate_handler_callback(device_t &device, _Object object) { return downcast<msm5232_device &>(device).m_gate_handler_cb.set_callback(object); }
+
+ DECLARE_WRITE8_MEMBER( write );
+ void set_clock(int clock);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_stop();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ private:
+ // internal state
+ sound_stream *m_stream;
+
+ VOICE m_voi[8];
+
+ UINT32 m_EN_out16[2]; /* enable 16' output masks for both groups (0-disabled ; ~0 -enabled) */
+ UINT32 m_EN_out8[2]; /* enable 8' output masks */
+ UINT32 m_EN_out4[2]; /* enable 4' output masks */
+ UINT32 m_EN_out2[2]; /* enable 2' output masks */
+
+ int m_noise_cnt;
+ int m_noise_step;
+ int m_noise_rng;
+ int m_noise_clocks; /* number of the noise_rng (output) level changes */
+
+ unsigned int m_UpdateStep;
+
+ /* rate tables */
+ double m_ar_tbl[8];
+ double m_dr_tbl[16];
+
+ UINT8 m_control1;
+ UINT8 m_control2;
+
+ int m_gate; /* current state of the GATE output */
+
+ int m_chip_clock; /* chip clock in Hz */
+ int m_rate; /* sample rate in Hz */
+
+ double m_external_capacity[8]; /* in Farads, eg 0.39e-6 = 0.36 uF (microFarads) */
+ devcb_write_line m_gate_handler_cb;/* callback called when the GATE output pin changes state */
+
+ void init_tables();
+ void init_voice(int i);
+ void gate_update();
+ void init(int clock, int rate);
+ void EG_voices_advance();
+ void TG_group_advance(int groupidx);
+ void postload();
+};
+
+extern const device_type MSM5232;
+
+
+#endif /* __MSM5232_H__ */
diff --git a/src/devices/sound/multipcm.c b/src/devices/sound/multipcm.c
new file mode 100644
index 00000000000..69c164bad2c
--- /dev/null
+++ b/src/devices/sound/multipcm.c
@@ -0,0 +1,658 @@
+// license:BSD-3-Clause
+// copyright-holders:Miguel Angel Horna
+/*
+ * Sega System 32 Multi/Model 1/Model 2 custom PCM chip (315-5560) emulation.
+ *
+ * by Miguel Angel Horna (ElSemi) for Model 2 Emulator and MAME.
+ * Information by R. Belmont and the YMF278B (OPL4) manual.
+ *
+ * voice registers:
+ * 0: Pan
+ * 1: Index of sample
+ * 2: LSB of pitch (low 2 bits seem unused so)
+ * 3: MSB of pitch (ooooppppppppppxx) (o=octave (4 bit signed), p=pitch (10 bits), x=unused?
+ * 4: voice control: top bit = 1 for key on, 0 for key off
+ * 5: bit 0: 0: interpolate volume changes, 1: direct set volume,
+ bits 1-7 = volume attenuate (0=max, 7f=min)
+ * 6: LFO frequency + Phase LFO depth
+ * 7: Amplitude LFO size
+ *
+ * The first sample ROM contains a variable length table with 12
+ * bytes per instrument/sample. This is very similar to the YMF278B.
+ *
+ * The first 3 bytes are the offset into the file (big endian).
+ * The next 2 are the loop start offset into the file (big endian)
+ * The next 2 are the 2's complement of the total sample size (big endian)
+ * The next byte is LFO freq + depth (copied to reg 6 ?)
+ * The next 3 are envelope params (Attack, Decay1 and 2, sustain level, release, Key Rate Scaling)
+ * The next byte is Amplitude LFO size (copied to reg 7 ?)
+ *
+ * TODO
+ * - The YM278B manual states that the chip supports 512 instruments. The MultiPCM probably supports them
+ * too but the high bit position is unknown (probably reg 2 low bit). Any game use more than 256?
+ *
+ */
+
+#include "emu.h"
+#include "multipcm.h"
+
+//????
+#define MULTIPCM_CLOCKDIV (180.0f)
+
+ALLOW_SAVE_TYPE(STATE); // allow save_item on a non-fundamental type
+
+static signed int LPANTABLE[0x800],RPANTABLE[0x800];
+
+#define FIX(v) ((UINT32) ((float) (1<<SHIFT)*(v)))
+
+static const int val2chan[] =
+{
+ 0, 1, 2, 3, 4, 5, 6 , -1,
+ 7, 8, 9, 10,11,12,13, -1,
+ 14,15,16,17,18,19,20, -1,
+ 21,22,23,24,25,26,27, -1,
+};
+
+
+#define SHIFT 12
+
+
+#define MULTIPCM_RATE 44100.0
+
+
+
+
+/*******************************
+ ENVELOPE SECTION
+*******************************/
+
+//Times are based on a 44100Hz timebase. It's adjusted to the actual sampling rate on startup
+
+static const double BaseTimes[64]={0,0,0,0,6222.95,4978.37,4148.66,3556.01,3111.47,2489.21,2074.33,1778.00,1555.74,1244.63,1037.19,889.02,
+777.87,622.31,518.59,444.54,388.93,311.16,259.32,222.27,194.47,155.60,129.66,111.16,97.23,77.82,64.85,55.60,
+48.62,38.91,32.43,27.80,24.31,19.46,16.24,13.92,12.15,9.75,8.12,6.98,6.08,4.90,4.08,3.49,
+3.04,2.49,2.13,1.90,1.72,1.41,1.18,1.04,0.91,0.73,0.59,0.50,0.45,0.45,0.45,0.45};
+#define AR2DR 14.32833
+static signed int lin2expvol[0x400];
+static int TLSteps[2];
+
+#define EG_SHIFT 16
+
+static int EG_Update(SLOT *slot)
+{
+ switch(slot->EG.state)
+ {
+ case ATTACK:
+ slot->EG.volume+=slot->EG.AR;
+ if(slot->EG.volume>=(0x3ff<<EG_SHIFT))
+ {
+ slot->EG.state=DECAY1;
+ if(slot->EG.D1R>=(0x400<<EG_SHIFT)) //Skip DECAY1, go directly to DECAY2
+ slot->EG.state=DECAY2;
+ slot->EG.volume=0x3ff<<EG_SHIFT;
+ }
+ break;
+ case DECAY1:
+ slot->EG.volume-=slot->EG.D1R;
+ if(slot->EG.volume<=0)
+ slot->EG.volume=0;
+ if(slot->EG.volume>>EG_SHIFT<=(slot->EG.DL<<(10-4)))
+ slot->EG.state=DECAY2;
+ break;
+ case DECAY2:
+ slot->EG.volume-=slot->EG.D2R;
+ if(slot->EG.volume<=0)
+ slot->EG.volume=0;
+ break;
+ case RELEASE:
+ slot->EG.volume-=slot->EG.RR;
+ if(slot->EG.volume<=0)
+ {
+ slot->EG.volume=0;
+ slot->Playing=0;
+ }
+ break;
+ default:
+ return 1<<SHIFT;
+ }
+ return lin2expvol[slot->EG.volume>>EG_SHIFT];
+}
+
+static unsigned int Get_RATE(unsigned int *Steps,unsigned int rate,unsigned int val)
+{
+ int r=4*val+rate;
+ if(val==0)
+ return Steps[0];
+ if(val==0xf)
+ return Steps[0x3f];
+ if(r>0x3f)
+ r=0x3f;
+ return Steps[r];
+}
+
+void multipcm_device::EG_Calc(SLOT *slot)
+{
+ int octave=((slot->Regs[3]>>4)-1)&0xf;
+ int rate;
+ if(octave&8) octave=octave-16;
+ if(slot->Sample->KRS!=0xf)
+ rate=(octave+slot->Sample->KRS)*2+((slot->Regs[3]>>3)&1);
+ else
+ rate=0;
+
+ slot->EG.AR=Get_RATE(m_ARStep,rate,slot->Sample->AR);
+ slot->EG.D1R=Get_RATE(m_DRStep,rate,slot->Sample->DR1);
+ slot->EG.D2R=Get_RATE(m_DRStep,rate,slot->Sample->DR2);
+ slot->EG.RR=Get_RATE(m_DRStep,rate,slot->Sample->RR);
+ slot->EG.DL=0xf-slot->Sample->DL;
+
+}
+
+/*****************************
+ LFO SECTION
+*****************************/
+
+#define LFO_SHIFT 8
+
+
+#define LFIX(v) ((unsigned int) ((float) (1<<LFO_SHIFT)*(v)))
+
+//Convert DB to multiply amplitude
+#define DB(v) LFIX(powf(10.0f,v/20.0f))
+
+//Convert cents to step increment
+#define CENTS(v) LFIX(powf(2.0f,v/1200.0f))
+
+static int PLFO_TRI[256];
+static int ALFO_TRI[256];
+
+static const float LFOFreq[8]={0.168f,2.019f,3.196f,4.206f,5.215f,5.888f,6.224f,7.066f}; //Hz;
+static const float PSCALE[8]={0.0f,3.378f,5.065f,6.750f,10.114f,20.170f,40.180f,79.307f}; //cents
+static const float ASCALE[8]={0.0f,0.4f,0.8f,1.5f,3.0f,6.0f,12.0f,24.0f}; //DB
+static int PSCALES[8][256];
+static int ASCALES[8][256];
+
+static void LFO_Init(void)
+{
+ int i,s;
+ for(i=0;i<256;++i)
+ {
+ int a; //amplitude
+ int p; //phase
+
+ //Tri
+ if(i<128)
+ a=255-(i*2);
+ else
+ a=(i*2)-256;
+ if(i<64)
+ p=i*2;
+ else if(i<128)
+ p=255-i*2;
+ else if(i<192)
+ p=256-i*2;
+ else
+ p=i*2-511;
+ ALFO_TRI[i]=a;
+ PLFO_TRI[i]=p;
+ }
+
+ for(s=0;s<8;++s)
+ {
+ float limit=PSCALE[s];
+ for(i=-128;i<128;++i)
+ {
+ PSCALES[s][i+128]=CENTS(((limit*(float) i)/128.0f));
+ }
+ limit=-ASCALE[s];
+ for(i=0;i<256;++i)
+ {
+ ASCALES[s][i]=DB(((limit*(float) i)/256.0f));
+ }
+ }
+}
+
+INLINE signed int PLFO_Step(LFO_t *LFO)
+{
+ int p;
+ LFO->phase+=LFO->phase_step;
+ p=LFO->table[(LFO->phase>>LFO_SHIFT)&0xff];
+ p=LFO->scale[p+128];
+ return p<<(SHIFT-LFO_SHIFT);
+}
+
+INLINE signed int ALFO_Step(LFO_t *LFO)
+{
+ int p;
+ LFO->phase+=LFO->phase_step;
+ p=LFO->table[(LFO->phase>>LFO_SHIFT)&0xff];
+ p=LFO->scale[p];
+ return p<<(SHIFT-LFO_SHIFT);
+}
+
+void multipcm_device::LFO_ComputeStep(LFO_t *LFO,UINT32 LFOF,UINT32 LFOS,int ALFO)
+{
+ float step=(float) LFOFreq[LFOF]*256.0f/(float) m_Rate;
+ LFO->phase_step=(unsigned int) ((float) (1<<LFO_SHIFT)*step);
+ if(ALFO)
+ {
+ LFO->table=ALFO_TRI;
+ LFO->scale=ASCALES[LFOS];
+ }
+ else
+ {
+ LFO->table=PLFO_TRI;
+ LFO->scale=PSCALES[LFOS];
+ }
+}
+
+
+
+void multipcm_device::WriteSlot(SLOT *slot,int reg,unsigned char data)
+{
+ slot->Regs[reg]=data;
+
+ switch(reg)
+ {
+ case 0: //PANPOT
+ slot->Pan=(data>>4)&0xf;
+ break;
+ case 1: //Sample
+ //according to YMF278 sample write causes some base params written to the regs (envelope+lfos)
+ //the game should never change the sample while playing.
+ {
+ Sample_t *Sample=m_Samples+slot->Regs[1];
+ WriteSlot(slot,6,Sample->LFOVIB);
+ WriteSlot(slot,7,Sample->AM);
+ }
+ break;
+ case 2: //Pitch
+ case 3:
+ {
+ unsigned int oct=((slot->Regs[3]>>4)-1)&0xf;
+ unsigned int pitch=((slot->Regs[3]&0xf)<<6)|(slot->Regs[2]>>2);
+ pitch=m_FNS_Table[pitch];
+ if(oct&0x8)
+ pitch>>=(16-oct);
+ else
+ pitch<<=oct;
+ slot->step=pitch/m_Rate;
+ }
+ break;
+ case 4: //KeyOn/Off (and more?)
+ {
+ if(data&0x80) //KeyOn
+ {
+ slot->Sample=m_Samples+slot->Regs[1];
+ slot->Playing=1;
+ slot->Base=slot->Sample->Start;
+ slot->offset=0;
+ slot->Prev=0;
+ slot->TL=slot->DstTL<<SHIFT;
+
+ EG_Calc(slot);
+ slot->EG.state=ATTACK;
+ slot->EG.volume=0;
+
+ if(slot->Base>=0x100000)
+ {
+ if(slot->Pan&8)
+ slot->Base=(slot->Base&0xfffff)|(m_BankL);
+ else
+ slot->Base=(slot->Base&0xfffff)|(m_BankR);
+ }
+
+ }
+ else
+ {
+ if(slot->Playing)
+ {
+ if(slot->Sample->RR!=0xf)
+ slot->EG.state=RELEASE;
+ else
+ slot->Playing=0;
+ }
+ }
+ }
+ break;
+ case 5: //TL+Interpolation
+ {
+ slot->DstTL=(data>>1)&0x7f;
+ if(!(data&1)) //Interpolate TL
+ {
+ if((slot->TL>>SHIFT)>slot->DstTL)
+ slot->TLStep=TLSteps[0]; //decrease
+ else
+ slot->TLStep=TLSteps[1]; //increase
+ }
+ else
+ slot->TL=slot->DstTL<<SHIFT;
+ }
+ break;
+ case 6: //LFO freq+PLFO
+ {
+ if(data)
+ {
+ LFO_ComputeStep(&(slot->PLFO),(slot->Regs[6]>>3)&7,slot->Regs[6]&7,0);
+ LFO_ComputeStep(&(slot->ALFO),(slot->Regs[6]>>3)&7,slot->Regs[7]&7,1);
+ }
+ }
+ break;
+ case 7: //ALFO
+ {
+ if(data)
+ {
+ LFO_ComputeStep(&(slot->PLFO),(slot->Regs[6]>>3)&7,slot->Regs[6]&7,0);
+ LFO_ComputeStep(&(slot->ALFO),(slot->Regs[6]>>3)&7,slot->Regs[7]&7,1);
+ }
+ }
+ break;
+ }
+}
+
+READ8_MEMBER( multipcm_device::read )
+{
+ return 0;
+}
+
+
+WRITE8_MEMBER( multipcm_device::write )
+{
+ switch(offset)
+ {
+ case 0: //Data write
+ WriteSlot(m_Slots+m_CurSlot,m_Address,data);
+ break;
+ case 1:
+ m_CurSlot=val2chan[data&0x1f];
+ break;
+
+ case 2:
+ m_Address=(data>7)?7:data;
+ break;
+ }
+}
+
+/* MAME/M1 access functions */
+
+void multipcm_device::set_bank(UINT32 leftoffs, UINT32 rightoffs)
+{
+ m_BankL = leftoffs;
+ m_BankR = rightoffs;
+}
+
+const device_type MULTIPCM = &device_creator<multipcm_device>;
+
+// default address map
+static ADDRESS_MAP_START( multipcm, AS_0, 8, multipcm_device )
+ AM_RANGE(0x000000, 0x3fffff) AM_ROM
+ADDRESS_MAP_END
+
+multipcm_device::multipcm_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MULTIPCM, "Sega/Yamaha 315-5560", tag, owner, clock, "multipcm", __FILE__),
+ device_sound_interface(mconfig, *this),
+ device_memory_interface(mconfig, *this),
+ m_space_config("mpcm_samples", ENDIANNESS_LITTLE, 8, 24, 0, NULL),
+ m_stream(NULL),
+ //m_Samples(0x200),
+ //m_Slots[28],
+ m_CurSlot(0),
+ m_Address(0),
+ m_BankR(0),
+ m_BankL(0),
+ m_Rate(0)
+ //m_ARStep(0),
+ //m_DRStep(0),
+ //m_FNS_Table(0)
+{
+ m_address_map[0] = *ADDRESS_MAP_NAME(multipcm);
+}
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *multipcm_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == 0) ? &m_space_config : NULL;
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void multipcm_device::device_config_complete()
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void multipcm_device::device_start()
+{
+ int i;
+
+ // find our direct access
+ m_direct = &space().direct();
+
+ m_Rate=(float) clock() / MULTIPCM_CLOCKDIV;
+
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, m_Rate);
+
+ //Volume+pan table
+ for(i=0;i<0x800;++i)
+ {
+ float SegaDB=0;
+ float TL;
+ float LPAN,RPAN;
+
+ unsigned char iTL=i&0x7f;
+ unsigned char iPAN=(i>>7)&0xf;
+
+ SegaDB=(float) iTL*(-24.0f)/(float) 0x40;
+
+ TL=powf(10.0f,SegaDB/20.0f);
+
+
+ if(iPAN==0x8)
+ {
+ LPAN=RPAN=0.0;
+ }
+ else if(iPAN==0x0)
+ {
+ LPAN=RPAN=1.0;
+ }
+ else if(iPAN&0x8)
+ {
+ LPAN=1.0;
+
+ iPAN=0x10-iPAN;
+
+ SegaDB=(float) iPAN*(-12.0f)/(float) 0x4;
+
+ RPAN=pow(10.0f,SegaDB/20.0f);
+
+ if((iPAN&0x7)==7)
+ RPAN=0.0;
+ }
+ else
+ {
+ RPAN=1.0;
+
+ SegaDB=(float) iPAN*(-12.0f)/(float) 0x4;
+
+ LPAN=pow(10.0f,SegaDB/20.0f);
+ if((iPAN&0x7)==7)
+ LPAN=0.0;
+ }
+
+ TL/=4.0f;
+
+ LPANTABLE[i]=FIX((LPAN*TL));
+ RPANTABLE[i]=FIX((RPAN*TL));
+ }
+
+ //Pitch steps
+ for(i=0;i<0x400;++i)
+ {
+ float fcent=m_Rate*(1024.0f+(float) i)/1024.0f;
+ m_FNS_Table[i]=(unsigned int ) ((float) (1<<SHIFT) *fcent);
+ }
+
+ //Envelope steps
+ for(i=0;i<0x40;++i)
+ {
+ //Times are based on 44100 clock, adjust to real chip clock
+ m_ARStep[i]=(float) (0x400<<EG_SHIFT)/(float)(BaseTimes[i]*44100.0/(1000.0));
+ m_DRStep[i]=(float) (0x400<<EG_SHIFT)/(float)(BaseTimes[i]*AR2DR*44100.0/(1000.0));
+ }
+ m_ARStep[0]=m_ARStep[1]=m_ARStep[2]=m_ARStep[3]=0;
+ m_ARStep[0x3f]=0x400<<EG_SHIFT;
+ m_DRStep[0]=m_DRStep[1]=m_DRStep[2]=m_DRStep[3]=0;
+
+ //TL Interpolation steps
+ //lower
+ TLSteps[0]=-(float) (0x80<<SHIFT)/(78.2f*44100.0f/1000.0f);
+ //raise
+ TLSteps[1]=(float) (0x80<<SHIFT)/(78.2f*2*44100.0f/1000.0f);
+
+ //build the linear->exponential ramps
+ for(i=0;i<0x400;++i)
+ {
+ float db=-(96.0f-(96.0f*(float) i/(float) 0x400));
+ lin2expvol[i]=powf(10.0f,db/20.0f)*(float) (1<<SHIFT);
+ }
+
+
+ for(i=0;i<512;++i)
+ {
+ UINT8 ptSample[12];
+
+ for (int j = 0; j < 12; j++)
+ {
+ ptSample[j] = (UINT8)m_direct->read_byte((i*12) + j);
+ }
+
+ m_Samples[i].Start=(ptSample[0]<<16)|(ptSample[1]<<8)|(ptSample[2]<<0);
+ m_Samples[i].Loop=(ptSample[3]<<8)|(ptSample[4]<<0);
+ m_Samples[i].End=0xffff-((ptSample[5]<<8)|(ptSample[6]<<0));
+ m_Samples[i].LFOVIB=ptSample[7];
+ m_Samples[i].DR1=ptSample[8]&0xf;
+ m_Samples[i].AR=(ptSample[8]>>4)&0xf;
+ m_Samples[i].DR2=ptSample[9]&0xf;
+ m_Samples[i].DL=(ptSample[9]>>4)&0xf;
+ m_Samples[i].RR=ptSample[10]&0xf;
+ m_Samples[i].KRS=(ptSample[10]>>4)&0xf;
+ m_Samples[i].AM=ptSample[11];
+ }
+
+ save_item(NAME(m_CurSlot));
+ save_item(NAME(m_Address));
+ save_item(NAME(m_BankL));
+ save_item(NAME(m_BankR));
+
+ for(i=0;i<28;++i)
+ {
+ m_Slots[i].Num=i;
+ m_Slots[i].Playing=0;
+
+ save_item(NAME(m_Slots[i].Num), i);
+ save_item(NAME(m_Slots[i].Regs), i);
+ save_item(NAME(m_Slots[i].Playing), i);
+ save_item(NAME(m_Slots[i].Base), i);
+ save_item(NAME(m_Slots[i].offset), i);
+ save_item(NAME(m_Slots[i].step), i);
+ save_item(NAME(m_Slots[i].Pan), i);
+ save_item(NAME(m_Slots[i].TL), i);
+ save_item(NAME(m_Slots[i].DstTL), i);
+ save_item(NAME(m_Slots[i].TLStep), i);
+ save_item(NAME(m_Slots[i].Prev), i);
+ save_item(NAME(m_Slots[i].EG.volume), i);
+ save_item(NAME(m_Slots[i].EG.state), i);
+ save_item(NAME(m_Slots[i].EG.step), i);
+ save_item(NAME(m_Slots[i].EG.AR), i);
+ save_item(NAME(m_Slots[i].EG.D1R), i);
+ save_item(NAME(m_Slots[i].EG.D2R), i);
+ save_item(NAME(m_Slots[i].EG.RR), i);
+ save_item(NAME(m_Slots[i].EG.DL), i);
+ save_item(NAME(m_Slots[i].PLFO.phase), i);
+ save_item(NAME(m_Slots[i].PLFO.phase_step), i);
+ save_item(NAME(m_Slots[i].ALFO.phase), i);
+ save_item(NAME(m_Slots[i].ALFO.phase_step), i);
+ }
+
+ LFO_Init();
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void multipcm_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *datap[2];
+ int i,sl;
+
+ datap[0] = outputs[0];
+ datap[1] = outputs[1];
+
+ memset(datap[0], 0, sizeof(*datap[0])*samples);
+ memset(datap[1], 0, sizeof(*datap[1])*samples);
+
+ for(i=0;i<samples;++i)
+ {
+ signed int smpl=0;
+ signed int smpr=0;
+ for(sl=0;sl<28;++sl)
+ {
+ SLOT *slot=m_Slots+sl;
+ if(slot->Playing)
+ {
+ unsigned int vol=(slot->TL>>SHIFT)|(slot->Pan<<7);
+ unsigned int adr=slot->offset>>SHIFT;
+ signed int sample;
+ unsigned int step=slot->step;
+ signed int csample=(signed short) (m_direct->read_byte(slot->Base+adr)<<8);
+ signed int fpart=slot->offset&((1<<SHIFT)-1);
+ sample=(csample*fpart+slot->Prev*((1<<SHIFT)-fpart))>>SHIFT;
+
+ if(slot->Regs[6]&7) //Vibrato enabled
+ {
+ step=step*PLFO_Step(&(slot->PLFO));
+ step>>=SHIFT;
+ }
+
+ slot->offset+=step;
+ if(slot->offset>=(slot->Sample->End<<SHIFT))
+ {
+ slot->offset=slot->Sample->Loop<<SHIFT;
+ }
+ if(adr^(slot->offset>>SHIFT))
+ {
+ slot->Prev=csample;
+ }
+
+ if((slot->TL>>SHIFT)!=slot->DstTL)
+ slot->TL+=slot->TLStep;
+
+ if(slot->Regs[7]&7) //Tremolo enabled
+ {
+ sample=sample*ALFO_Step(&(slot->ALFO));
+ sample>>=SHIFT;
+ }
+
+ sample=(sample*EG_Update(slot))>>10;
+
+ smpl+=(LPANTABLE[vol]*sample)>>SHIFT;
+ smpr+=(RPANTABLE[vol]*sample)>>SHIFT;
+ }
+ }
+#define ICLIP16(x) (x<-32768)?-32768:((x>32767)?32767:x)
+ datap[0][i]=ICLIP16(smpl);
+ datap[1][i]=ICLIP16(smpr);
+ }
+}
diff --git a/src/devices/sound/multipcm.h b/src/devices/sound/multipcm.h
new file mode 100644
index 00000000000..18854902ef1
--- /dev/null
+++ b/src/devices/sound/multipcm.h
@@ -0,0 +1,110 @@
+// license:BSD-3-Clause
+// copyright-holders:Miguel Angel Horna
+#pragma once
+
+#ifndef __MULTIPCM_H__
+#define __MULTIPCM_H__
+
+struct Sample_t
+{
+ unsigned int Start;
+ unsigned int Loop;
+ unsigned int End;
+ unsigned char AR,DR1,DR2,DL,RR;
+ unsigned char KRS;
+ unsigned char LFOVIB;
+ unsigned char AM;
+};
+
+enum STATE {ATTACK,DECAY1,DECAY2,RELEASE};
+
+struct EG_t
+{
+ int volume; //
+ STATE state;
+ int step;
+ //step vals
+ int AR; //Attack
+ int D1R; //Decay1
+ int D2R; //Decay2
+ int RR; //Release
+ int DL; //Decay level
+};
+
+struct LFO_t
+{
+ unsigned short phase;
+ UINT32 phase_step;
+ int *table;
+ int *scale;
+};
+
+
+struct SLOT
+{
+ unsigned char Num;
+ unsigned char Regs[8];
+ int Playing;
+ Sample_t *Sample;
+ unsigned int Base;
+ unsigned int offset;
+ unsigned int step;
+ unsigned int Pan,TL;
+ unsigned int DstTL;
+ int TLStep;
+ signed int Prev;
+ EG_t EG;
+ LFO_t PLFO; //Phase lfo
+ LFO_t ALFO; //AM lfo
+};
+
+class multipcm_device : public device_t,
+ public device_sound_interface,
+ public device_memory_interface
+{
+public:
+ multipcm_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~multipcm_device() {}
+
+ DECLARE_WRITE8_MEMBER( write );
+ DECLARE_READ8_MEMBER( read );
+
+ void set_bank(UINT32 leftoffs, UINT32 rightoffs);
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ 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);
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ const address_space_config m_space_config;
+
+private:
+ // internal state
+ sound_stream * m_stream;
+ Sample_t m_Samples[0x200]; //Max 512 samples
+ SLOT m_Slots[28];
+ unsigned int m_CurSlot;
+ unsigned int m_Address;
+ unsigned int m_BankR, m_BankL;
+ float m_Rate;
+ //I include these in the chip because they depend on the chip clock
+ unsigned int m_ARStep[0x40], m_DRStep[0x40]; //Envelope step table
+ unsigned int m_FNS_Table[0x400]; //Frequency step table
+
+ void EG_Calc(SLOT *slot);
+ void LFO_ComputeStep(LFO_t *LFO,UINT32 LFOF,UINT32 LFOS,int ALFO);
+ void WriteSlot(SLOT *slot,int reg,unsigned char data);
+
+ direct_read_data *m_direct;
+};
+
+extern const device_type MULTIPCM;
+
+
+#endif /* __MULTIPCM_H__ */
diff --git a/src/devices/sound/n63701x.c b/src/devices/sound/n63701x.c
new file mode 100644
index 00000000000..a1308c36713
--- /dev/null
+++ b/src/devices/sound/n63701x.c
@@ -0,0 +1,153 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria
+/***************************************************************************
+
+An Hitachi HD637A01X0 MCU programmed to act as a sample player.
+Used by some Namco System 86 games.
+
+The MCU has internal ROM which hasn't been dumped, so here we simulate its
+simple functions.
+
+The chip can address ROM space up to 8 block of 0x10000 bytes. At the beginning
+of each block there's a table listing the start offset of each sample.
+Samples are 8 bit unsigned, 0xff marks the end of the sample. 0x00 is used for
+silence compression: '00 nn' must be replaced by nn+1 times '80'.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "n63701x.h"
+
+
+/* volume control has three resistors: 22000, 10000 and 3300 Ohm.
+ 22000 is always enabled, the other two can be turned off.
+ Since 0x00 and 0xff samples have special meaning, the available range is
+ 0x01 to 0xfe, therefore 258 * (0x01 - 0x80) = 0x8002 just keeps us
+ inside 16 bits without overflowing.
+ */
+static const int vol_table[4] = { 26, 84, 200, 258 };
+
+
+// device type definition
+const device_type NAMCO_63701X = &device_creator<namco_63701x_device>;
+
+namco_63701x_device::namco_63701x_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, NAMCO_63701X, "Namco 63701X", tag, owner, clock, "namco_63701x", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_rom(*this, DEVICE_SELF),
+ m_stream(NULL)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void namco_63701x_device::device_start()
+{
+ m_stream = stream_alloc(0, 2, clock()/1000);
+
+ for (int i = 0; i < 2; i++)
+ {
+ save_item(NAME(m_voices[i].select), i);
+ save_item(NAME(m_voices[i].playing), i);
+ save_item(NAME(m_voices[i].base_addr), i);
+ save_item(NAME(m_voices[i].position), i);
+ save_item(NAME(m_voices[i].volume), i);
+ save_item(NAME(m_voices[i].silence_counter), i);
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void namco_63701x_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int ch;
+
+ for (ch = 0;ch < 2;ch++)
+ {
+ stream_sample_t *buf = outputs[ch];
+ voice_63701x *v = &m_voices[ch];
+
+ if (v->playing)
+ {
+ UINT8 *base = m_rom + v->base_addr;
+ int pos = v->position;
+ int vol = vol_table[v->volume];
+ int p;
+
+ for (p = 0;p < samples;p++)
+ {
+ if (v->silence_counter)
+ {
+ v->silence_counter--;
+ *(buf++) = 0;
+ }
+ else
+ {
+ int data = base[(pos++) & 0xffff];
+
+ if (data == 0xff) /* end of sample */
+ {
+ v->playing = 0;
+ break;
+ }
+ else if (data == 0x00) /* silence compression */
+ {
+ data = base[(pos++) & 0xffff];
+ v->silence_counter = data;
+ *(buf++) = 0;
+ }
+ else
+ {
+ *(buf++) = vol * (data - 0x80);
+ }
+ }
+ }
+
+ v->position = pos;
+ }
+ else
+ memset(buf, 0, samples * sizeof(*buf));
+ }
+}
+
+
+
+WRITE8_MEMBER( namco_63701x_device::namco_63701x_w )
+{
+ int ch = offset / 2;
+
+ if (offset & 1)
+ m_voices[ch].select = data;
+ else
+ {
+ /*
+ should we stop the playing sample if voice_select[ch] == 0 ?
+ originally we were, but this makes us lose a sample in genpeitd,
+ after the continue counter reaches 0. Either we shouldn't stop
+ the sample, or genpeitd is returning to the title screen too soon.
+ */
+ if (m_voices[ch].select & 0x1f)
+ {
+ int rom_offs;
+
+ /* update the streams */
+ m_stream->update();
+
+ m_voices[ch].playing = 1;
+ m_voices[ch].base_addr = 0x10000 * ((m_voices[ch].select & 0xe0) >> 5);
+ rom_offs = m_voices[ch].base_addr + 2 * ((m_voices[ch].select & 0x1f) - 1);
+ m_voices[ch].position = (m_rom[rom_offs] << 8) + m_rom[rom_offs+1];
+ /* bits 6-7 = volume */
+ m_voices[ch].volume = data >> 6;
+ /* bits 0-5 = counter to indicate new sample start? we don't use them */
+
+ m_voices[ch].silence_counter = 0;
+ }
+ }
+}
diff --git a/src/devices/sound/n63701x.h b/src/devices/sound/n63701x.h
new file mode 100644
index 00000000000..9af9bfc62ab
--- /dev/null
+++ b/src/devices/sound/n63701x.h
@@ -0,0 +1,69 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria
+#pragma once
+
+#ifndef __N63701X_H__
+#define __N63701X_H__
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_NAMCO_63701X_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, NAMCO_63701X, _clock)
+#define MCFG_NAMCO_63701X_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, NAMCO_63701X, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+struct voice_63701x
+{
+ voice_63701x()
+ : select(0),
+ playing(0),
+ base_addr(0),
+ position(0),
+ volume(0),
+ silence_counter(0) {}
+
+ int select;
+ int playing;
+ int base_addr;
+ int position;
+ int volume;
+ int silence_counter;
+};
+
+
+// ======================> namco_63701x_device
+
+class namco_63701x_device : public device_t,
+ public device_sound_interface
+{
+public:
+ namco_63701x_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~namco_63701x_device() { }
+
+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);
+
+public:
+ DECLARE_WRITE8_MEMBER(namco_63701x_w);
+
+private:
+ required_region_ptr<UINT8> m_rom;
+ voice_63701x m_voices[2];
+ sound_stream *m_stream; /* channel assigned by the mixer */
+};
+
+extern const device_type NAMCO_63701X;
+
+
+#endif /* __N63701X_H__ */
diff --git a/src/devices/sound/namco.c b/src/devices/sound/namco.c
new file mode 100644
index 00000000000..ea59b5c6393
--- /dev/null
+++ b/src/devices/sound/namco.c
@@ -0,0 +1,850 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria,Aaron Giles
+/***************************************************************************
+
+ NAMCO sound driver.
+
+ This driver handles the four known types of NAMCO wavetable sounds:
+
+ - 3-voice mono (PROM-based design: Pac-Man, Pengo, Dig Dug, etc)
+ - 8-voice quadrophonic (Pole Position 1, Pole Position 2)
+ - 8-voice mono (custom 15XX: Mappy, Dig Dug 2, etc)
+ - 8-voice stereo (System 1)
+
+***************************************************************************/
+
+#include "emu.h"
+#include "namco.h"
+
+
+/* quality parameter: internal sample rate is 192 KHz, output is 48 KHz */
+#define INTERNAL_RATE 192000
+
+/* 16 bits: sample bits of the stream buffer */
+/* 4 bits: volume */
+/* 4 bits: prom sample bits */
+#define MIXLEVEL (1 << (16 - 4 - 4))
+
+/* stream output level */
+#define OUTPUT_LEVEL(n) ((n) * MIXLEVEL / m_voices)
+
+/* a position of waveform sample */
+#define WAVEFORM_POSITION(n) (((n) >> m_f_fracbits) & 0x1f)
+
+const device_type NAMCO = &device_creator<namco_device>;
+const device_type NAMCO_15XX = &device_creator<namco_15xx_device>;
+const device_type NAMCO_CUS30 = &device_creator<namco_cus30_device>;
+
+namco_audio_device::namco_audio_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_last_channel(NULL),
+ m_soundregs(NULL),
+ m_wavedata(NULL),
+ m_wave_size(0),
+ m_sound_enable(0),
+ m_stream(NULL),
+ m_namco_clock(0),
+ m_sample_rate(0),
+ m_f_fracbits(0),
+ m_voices(0),
+ m_stereo(0)
+{
+}
+
+namco_device::namco_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : namco_audio_device(mconfig, NAMCO, "Namco", tag, owner, clock, "namco", __FILE__)
+{
+}
+
+namco_15xx_device::namco_15xx_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ :namco_audio_device(mconfig, NAMCO_15XX, "Namco 15XX", tag, owner, clock, "namco_15xx", __FILE__)
+{
+}
+
+namco_cus30_device::namco_cus30_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : namco_audio_device(mconfig, NAMCO_CUS30, "Namco CUS30", tag, owner, clock, "namco_cus30", __FILE__)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void namco_audio_device::device_start()
+{
+ sound_channel *voice;
+ int clock_multiple;
+
+ /* extract globals from the interface */
+ m_last_channel = m_channel_list + m_voices;
+
+ m_soundregs = auto_alloc_array_clear(machine(), UINT8, 0x400);
+
+ /* adjust internal clock */
+ m_namco_clock = clock();
+ for (clock_multiple = 0; m_namco_clock < INTERNAL_RATE; clock_multiple++)
+ m_namco_clock *= 2;
+
+ m_f_fracbits = clock_multiple + 15;
+
+ /* adjust output clock */
+ m_sample_rate = m_namco_clock;
+
+ logerror("Namco: freq fractional bits = %d: internal freq = %d, output freq = %d\n", m_f_fracbits, m_namco_clock, m_sample_rate);
+
+ /* build the waveform table */
+ build_decoded_waveform(region()->base());
+
+ /* get stream channels */
+ if (m_stereo)
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, m_sample_rate);
+ else
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, m_sample_rate);
+
+ /* start with sound enabled, many games don't have a sound enable register */
+ m_sound_enable = 1;
+
+ /* register with the save state system */
+ save_pointer(NAME(m_soundregs), 0x400);
+
+ if (region() == NULL)
+ save_pointer(NAME(m_wavedata), 0x400);
+
+ save_item(NAME(m_voices));
+ save_item(NAME(m_sound_enable));
+ save_pointer(NAME(m_waveform[0]), MAX_VOLUME * 32 * 8 * (1+m_wave_size));
+
+ /* reset all the voices */
+ for (voice = m_channel_list; voice < m_last_channel; voice++)
+ {
+ int voicenum = voice - m_channel_list;
+
+ voice->frequency = 0;
+ voice->volume[0] = voice->volume[1] = 0;
+ voice->waveform_select = 0;
+ voice->counter = 0;
+ voice->noise_sw = 0;
+ voice->noise_state = 0;
+ voice->noise_seed = 1;
+ voice->noise_counter = 0;
+ voice->noise_hold = 0;
+
+ /* register with the save state system */
+ save_item(NAME(voice->frequency), voicenum);
+ save_item(NAME(voice->counter), voicenum);
+ save_item(NAME(voice->volume), voicenum);
+ save_item(NAME(voice->noise_sw), voicenum);
+ save_item(NAME(voice->noise_state), voicenum);
+ save_item(NAME(voice->noise_seed), voicenum);
+ save_item(NAME(voice->noise_hold), voicenum);
+ save_item(NAME(voice->noise_counter), voicenum);
+ save_item(NAME(voice->waveform_select), voicenum);
+ }
+}
+
+
+
+/* update the decoded waveform data */
+void namco_audio_device::update_namco_waveform(int offset, UINT8 data)
+{
+ if (m_wave_size == 1)
+ {
+ INT16 wdata;
+ int v;
+
+ /* use full byte, first 4 high bits, then low 4 bits */
+ for (v = 0; v < MAX_VOLUME; v++)
+ {
+ wdata = ((data >> 4) & 0x0f) - 8;
+ m_waveform[v][offset * 2] = OUTPUT_LEVEL(wdata * v);
+ wdata = (data & 0x0f) - 8;
+ m_waveform[v][offset * 2 + 1] = OUTPUT_LEVEL(wdata * v);
+ }
+ }
+ else
+ {
+ int v;
+
+ /* use only low 4 bits */
+ for (v = 0; v < MAX_VOLUME; v++)
+ m_waveform[v][offset] = OUTPUT_LEVEL(((data & 0x0f) - 8) * v);
+ }
+}
+
+
+/* build the decoded waveform table */
+void namco_audio_device::build_decoded_waveform(UINT8 *rgnbase)
+{
+ INT16 *p;
+ int size;
+ int offset;
+ int v;
+
+ m_wavedata = (rgnbase != NULL) ? rgnbase : auto_alloc_array_clear(machine(), UINT8, 0x400);
+
+ /* 20pacgal has waves in RAM but old sound system */
+ if (rgnbase == NULL && m_voices != 3)
+ {
+ m_wave_size = 1;
+ size = 32 * 16; /* 32 samples, 16 waveforms */
+ }
+ else
+ {
+ m_wave_size = 0;
+ size = 32 * 8; /* 32 samples, 8 waveforms */
+ }
+
+ p = auto_alloc_array(machine(), INT16, size * MAX_VOLUME);
+
+ for (v = 0; v < MAX_VOLUME; v++)
+ {
+ m_waveform[v] = p;
+ p += size;
+ }
+
+ /* We need waveform data. It fails if region is not specified. */
+ if (m_wavedata)
+ {
+ for (offset = 0; offset < 256; offset++)
+ update_namco_waveform(offset, m_wavedata[offset]);
+ }
+}
+
+
+/* generate sound by oversampling */
+UINT32 namco_audio_device::namco_update_one(stream_sample_t *buffer, int length, const INT16 *wave, UINT32 counter, UINT32 freq)
+{
+ while (length-- > 0)
+ {
+ *buffer++ += wave[WAVEFORM_POSITION(counter)];
+ counter += freq;
+ }
+
+ return counter;
+}
+
+
+/********************************************************************************/
+
+/* pacman register map
+ 0x05: ch 0 waveform select
+ 0x0a: ch 1 waveform select
+ 0x0f: ch 2 waveform select
+
+ 0x10: ch 0 the first voice has extra frequency bits
+ 0x11-0x14: ch 0 frequency
+ 0x15: ch 0 volume
+
+ 0x16-0x19: ch 1 frequency
+ 0x1a: ch 1 volume
+
+ 0x1b-0x1e: ch 2 frequency
+ 0x1f: ch 2 volume
+*/
+
+WRITE8_MEMBER( namco_device::pacman_sound_enable_w )
+{
+ m_sound_enable = data;
+}
+
+WRITE8_MEMBER( namco_device::pacman_sound_w )
+{
+ sound_channel *voice;
+ int ch;
+
+ data &= 0x0f;
+ if (m_soundregs[offset] == data)
+ return;
+
+ /* update the streams */
+ m_stream->update();
+
+ /* set the register */
+ m_soundregs[offset] = data;
+
+ if (offset < 0x10)
+ ch = (offset - 5) / 5;
+ else if (offset == 0x10)
+ ch = 0;
+ else
+ ch = (offset - 0x11) / 5;
+
+ if (ch >= m_voices)
+ return;
+
+ /* recompute the voice parameters */
+ voice = m_channel_list + ch;
+ switch (offset - ch * 5)
+ {
+ case 0x05:
+ voice->waveform_select = data & 7;
+ break;
+
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ /* the frequency has 20 bits */
+ /* the first voice has extra frequency bits */
+ voice->frequency = (ch == 0) ? m_soundregs[0x10] : 0;
+ voice->frequency += (m_soundregs[ch * 5 + 0x11] << 4);
+ voice->frequency += (m_soundregs[ch * 5 + 0x12] << 8);
+ voice->frequency += (m_soundregs[ch * 5 + 0x13] << 12);
+ voice->frequency += (m_soundregs[ch * 5 + 0x14] << 16); /* always 0 */
+ break;
+
+ case 0x15:
+ voice->volume[0] = data;
+ break;
+ }
+}
+
+WRITE8_MEMBER( namco_cus30_device::pacman_sound_w )
+{
+ sound_channel *voice;
+ int ch;
+
+ data &= 0x0f;
+ if (m_soundregs[offset] == data)
+ return;
+
+ /* update the streams */
+ m_stream->update();
+
+ /* set the register */
+ m_soundregs[offset] = data;
+
+ if (offset < 0x10)
+ ch = (offset - 5) / 5;
+ else if (offset == 0x10)
+ ch = 0;
+ else
+ ch = (offset - 0x11) / 5;
+
+ if (ch >= m_voices)
+ return;
+
+ /* recompute the voice parameters */
+ voice = m_channel_list + ch;
+ switch (offset - ch * 5)
+ {
+ case 0x05:
+ voice->waveform_select = data & 7;
+ break;
+
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ /* the frequency has 20 bits */
+ /* the first voice has extra frequency bits */
+ voice->frequency = (ch == 0) ? m_soundregs[0x10] : 0;
+ voice->frequency += (m_soundregs[ch * 5 + 0x11] << 4);
+ voice->frequency += (m_soundregs[ch * 5 + 0x12] << 8);
+ voice->frequency += (m_soundregs[ch * 5 + 0x13] << 12);
+ voice->frequency += (m_soundregs[ch * 5 + 0x14] << 16); /* always 0 */
+ break;
+
+ case 0x15:
+ voice->volume[0] = data;
+ break;
+ }
+}
+
+/********************************************************************************/
+
+/* polepos register map
+Note: even if there are 8 voices, the game doesn't use the first 2 because
+it select the 54XX/52XX outputs on those channels
+
+ 0x00-0x01 ch 0 frequency
+ 0x02 ch 0 xxxx---- GAIN 2 volume
+ 0x03 ch 0 xxxx---- GAIN 3 volume
+ ----xxxx GAIN 4 volume
+
+ 0x04-0x07 ch 1
+
+ .
+ .
+ .
+
+ 0x1c-0x1f ch 7
+
+ 0x23 ch 0 xxxx---- GAIN 1 volume
+ -----xxx waveform select
+ ----x-xx channel output select
+ 0-7 (all the same, shared with waveform select) = wave
+ 8 = CHANL1 (54XX pins 17-20)
+ 9 = CHANL2 (54XX pins 8-11)
+ A = CHANL3 (54XX pins 4-7)
+ B = CHANL4 (52XX)
+ 0x27 ch 1
+ 0x2b ch 2
+ 0x2f ch 3
+ 0x33 ch 4
+ 0x37 ch 5
+ 0x3b ch 6
+ 0x3f ch 7
+*/
+
+void namco_device::polepos_sound_enable(int enable)
+{
+ m_sound_enable = enable;
+}
+
+READ8_MEMBER( namco_device::polepos_sound_r )
+{
+ return m_soundregs[offset];
+}
+
+WRITE8_MEMBER( namco_device::polepos_sound_w )
+{
+ sound_channel *voice;
+ int ch;
+
+ if (m_soundregs[offset] == data)
+ return;
+
+ /* update the streams */
+ m_stream->update();
+
+ /* set the register */
+ m_soundregs[offset] = data;
+
+ ch = (offset & 0x1f) / 4;
+
+ /* recompute the voice parameters */
+ voice = m_channel_list + ch;
+ switch (offset & 0x23)
+ {
+ case 0x00:
+ case 0x01:
+ /* the frequency has 16 bits */
+ voice->frequency = m_soundregs[ch * 4 + 0x00];
+ voice->frequency += m_soundregs[ch * 4 + 0x01] << 8;
+ break;
+
+ case 0x23:
+ voice->waveform_select = data & 7;
+ /* fall through */
+ case 0x02:
+ case 0x03:
+ voice->volume[0] = voice->volume[1] = 0;
+ // front speakers ?
+ voice->volume[0] += m_soundregs[ch * 4 + 0x03] >> 4;
+ voice->volume[1] += m_soundregs[ch * 4 + 0x03] & 0x0f;
+ // rear speakers ?
+ voice->volume[0] += m_soundregs[ch * 4 + 0x23] >> 4;
+ voice->volume[1] += m_soundregs[ch * 4 + 0x02] >> 4;
+
+ voice->volume[0] /= 2;
+ voice->volume[1] /= 2;
+
+ /* if 54XX or 52XX selected, silence this voice */
+ if (m_soundregs[ch * 4 + 0x23] & 8)
+ voice->volume[0] = voice->volume[1] = 0;
+ break;
+ }
+}
+
+
+/********************************************************************************/
+
+/* 15XX register map
+ 0x03 ch 0 volume
+ 0x04-0x05 ch 0 frequency
+ 0x06 ch 0 waveform select & frequency
+
+ 0x0b ch 1 volume
+ 0x0c-0x0d ch 1 frequency
+ 0x0e ch 1 waveform select & frequency
+
+ .
+ .
+ .
+
+ 0x3b ch 7 volume
+ 0x3c-0x3d ch 7 frequency
+ 0x3e ch 7 waveform select & frequency
+*/
+
+void namco_15xx_device::mappy_sound_enable(int enable)
+{
+ m_sound_enable = enable;
+}
+
+WRITE8_MEMBER(namco_15xx_device::namco_15xx_w)
+{
+ sound_channel *voice;
+ int ch;
+
+ if (m_soundregs[offset] == data)
+ return;
+
+ /* update the streams */
+ m_stream->update();
+
+ /* set the register */
+ m_soundregs[offset] = data;
+
+ ch = offset / 8;
+ if (ch >= m_voices)
+ return;
+
+ /* recompute the voice parameters */
+ voice = m_channel_list + ch;
+ switch (offset - ch * 8)
+ {
+ case 0x03:
+ voice->volume[0] = data & 0x0f;
+ break;
+
+ case 0x06:
+ voice->waveform_select = (data >> 4) & 7;
+ case 0x04:
+ case 0x05:
+ /* the frequency has 20 bits */
+ voice->frequency = m_soundregs[ch * 8 + 0x04];
+ voice->frequency += m_soundregs[ch * 8 + 0x05] << 8;
+ voice->frequency += (m_soundregs[ch * 8 + 0x06] & 15) << 16; /* high bits are from here */
+ break;
+ }
+}
+
+
+/********************************************************************************/
+
+/* namcos1 register map
+ 0x00 ch 0 left volume
+ 0x01 ch 0 waveform select & frequency
+ 0x02-0x03 ch 0 frequency
+ 0x04 ch 0 right volume AND
+ 0x04 ch 1 noise sw
+
+ 0x08 ch 1 left volume
+ 0x09 ch 1 waveform select & frequency
+ 0x0a-0x0b ch 1 frequency
+ 0x0c ch 1 right volume AND
+ 0x0c ch 2 noise sw
+
+ .
+ .
+ .
+
+ 0x38 ch 7 left volume
+ 0x39 ch 7 waveform select & frequency
+ 0x3a-0x3b ch 7 frequency
+ 0x3c ch 7 right volume AND
+ 0x3c ch 0 noise sw
+*/
+
+ WRITE8_MEMBER(namco_cus30_device::namcos1_sound_w)
+{
+ sound_channel *voice;
+ int ch;
+ int nssw;
+
+
+ /* verify the offset */
+ if (offset > 63)
+ {
+ logerror("NAMCOS1 sound: Attempting to write past the 64 registers segment\n");
+ return;
+ }
+
+ m_soundregs = m_wavedata + 0x100;
+
+ if (m_soundregs[offset] == data)
+ return;
+
+ /* update the streams */
+ m_stream->update();
+
+ /* set the register */
+ m_soundregs[offset] = data;
+
+ ch = offset / 8;
+ if (ch >= m_voices)
+ return;
+
+ /* recompute the voice parameters */
+ voice = m_channel_list + ch;
+ switch (offset - ch * 8)
+ {
+ case 0x00:
+ voice->volume[0] = data & 0x0f;
+ break;
+
+ case 0x01:
+ voice->waveform_select = (data >> 4) & 15;
+ case 0x02:
+ case 0x03:
+ /* the frequency has 20 bits */
+ voice->frequency = (m_soundregs[ch * 8 + 0x01] & 15) << 16; /* high bits are from here */
+ voice->frequency += m_soundregs[ch * 8 + 0x02] << 8;
+ voice->frequency += m_soundregs[ch * 8 + 0x03];
+ break;
+
+ case 0x04:
+ voice->volume[1] = data & 0x0f;
+
+ nssw = ((data & 0x80) >> 7);
+ if (++voice == m_last_channel)
+ voice = m_channel_list;
+ voice->noise_sw = nssw;
+ break;
+ }
+}
+
+WRITE8_MEMBER( namco_cus30_device::namcos1_cus30_w )
+{
+ if (offset < 0x100)
+ {
+ if (m_wavedata[offset] != data)
+ {
+ /* update the streams */
+ m_stream->update();
+
+ m_wavedata[offset] = data;
+
+ /* update the decoded waveform table */
+ update_namco_waveform(offset, data);
+ }
+ }
+ else if (offset < 0x140)
+ namcos1_sound_w(space, offset - 0x100,data);
+ else
+ m_wavedata[offset] = data;
+}
+
+READ8_MEMBER( namco_cus30_device::namcos1_cus30_r )
+{
+ return m_wavedata[offset];
+}
+
+READ8_MEMBER( namco_15xx_device::sharedram_r )
+{
+ return m_soundregs[offset];
+}
+
+WRITE8_MEMBER( namco_15xx_device::sharedram_w )
+{
+ if (offset < 0x40)
+ namco_15xx_w(space, offset, data);
+ else
+ {
+ m_soundregs[offset] = data;
+ }
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void namco_audio_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ if (m_stereo)
+ {
+ sound_channel *voice;
+
+ /* zap the contents of the buffers */
+ memset(outputs[0], 0, samples * sizeof(*outputs[0]));
+ memset(outputs[1], 0, samples * sizeof(*outputs[1]));
+
+ /* if no sound, we're done */
+ if (m_sound_enable == 0)
+ return;
+
+ /* loop over each voice and add its contribution */
+ for (voice = m_channel_list; voice < m_last_channel; voice++)
+ {
+ stream_sample_t *lmix = outputs[0];
+ stream_sample_t *rmix = outputs[1];
+ int lv = voice->volume[0];
+ int rv = voice->volume[1];
+
+ if (voice->noise_sw)
+ {
+ int f = voice->frequency & 0xff;
+
+ /* only update if we have non-zero volume and frequency */
+ if ((lv || rv) && f)
+ {
+ int hold_time = 1 << (m_f_fracbits - 16);
+ int hold = voice->noise_hold;
+ UINT32 delta = f << 4;
+ UINT32 c = voice->noise_counter;
+ INT16 l_noise_data = OUTPUT_LEVEL(0x07 * (lv >> 1));
+ INT16 r_noise_data = OUTPUT_LEVEL(0x07 * (rv >> 1));
+ int i;
+
+ /* add our contribution */
+ for (i = 0; i < samples; i++)
+ {
+ int cnt;
+
+ if (voice->noise_state)
+ {
+ *lmix++ += l_noise_data;
+ *rmix++ += r_noise_data;
+ }
+ else
+ {
+ *lmix++ -= l_noise_data;
+ *rmix++ -= r_noise_data;
+ }
+
+ if (hold)
+ {
+ hold--;
+ continue;
+ }
+
+ hold = hold_time;
+
+ c += delta;
+ cnt = (c >> 12);
+ c &= (1 << 12) - 1;
+ for( ;cnt > 0; cnt--)
+ {
+ if ((voice->noise_seed + 1) & 2) voice->noise_state ^= 1;
+ if (voice->noise_seed & 1) voice->noise_seed ^= 0x28000;
+ voice->noise_seed >>= 1;
+ }
+ }
+
+ /* update the counter and hold time for this voice */
+ voice->noise_counter = c;
+ voice->noise_hold = hold;
+ }
+ }
+ else
+ {
+ /* only update if we have non-zero frequency */
+ if (voice->frequency)
+ {
+ /* save the counter for this voice */
+ UINT32 c = voice->counter;
+
+ /* only update if we have non-zero left volume */
+ if (lv)
+ {
+ const INT16 *lw = &m_waveform[lv][voice->waveform_select * 32];
+
+ /* generate sound into the buffer */
+ c = namco_update_one(lmix, samples, lw, voice->counter, voice->frequency);
+ }
+
+ /* only update if we have non-zero right volume */
+ if (rv)
+ {
+ const INT16 *rw = &m_waveform[rv][voice->waveform_select * 32];
+
+ /* generate sound into the buffer */
+ c = namco_update_one(rmix, samples, rw, voice->counter, voice->frequency);
+ }
+
+ /* update the counter for this voice */
+ voice->counter = c;
+ }
+ }
+ }
+ }
+ else
+ {
+ sound_channel *voice;
+
+ stream_sample_t *buffer = outputs[0];
+ /* zap the contents of the buffer */
+ memset(buffer, 0, samples * sizeof(*buffer));
+
+ /* if no sound, we're done */
+
+ if (m_sound_enable == 0)
+ return;
+
+ /* loop over each voice and add its contribution */
+ for (voice = m_channel_list; voice < m_last_channel; voice++)
+ {
+ stream_sample_t *mix = buffer;
+ int v = voice->volume[0];
+ if (voice->noise_sw)
+ {
+ int f = voice->frequency & 0xff;
+ /* only update if we have non-zero volume and frequency */
+ if (v && f)
+ {
+ int hold_time = 1 << (m_f_fracbits - 16);
+ int hold = voice->noise_hold;
+ UINT32 delta = f << 4;
+ UINT32 c = voice->noise_counter;
+ INT16 noise_data = OUTPUT_LEVEL(0x07 * (v >> 1));
+ int i;
+
+ /* add our contribution */
+ for (i = 0; i < samples; i++)
+ {
+ int cnt;
+
+ if (voice->noise_state)
+ *mix++ += noise_data;
+ else
+ *mix++ -= noise_data;
+
+ if (hold)
+ {
+ hold--;
+ continue;
+ }
+
+ hold = hold_time;
+
+ c += delta;
+ cnt = (c >> 12);
+ c &= (1 << 12) - 1;
+ for( ;cnt > 0; cnt--)
+ {
+ if ((voice->noise_seed + 1) & 2) voice->noise_state ^= 1;
+ if (voice->noise_seed & 1) voice->noise_seed ^= 0x28000;
+ voice->noise_seed >>= 1;
+ }
+ }
+
+ /* update the counter and hold time for this voice */
+ voice->noise_counter = c;
+ voice->noise_hold = hold;
+ }
+ }
+ else
+ {
+ /* only update if we have non-zero volume and frequency */
+ if (v && voice->frequency)
+ {
+ const INT16 *w = &m_waveform[v][voice->waveform_select * 32];
+
+ /* generate sound into buffer and update the counter for this voice */
+ voice->counter = namco_update_one(mix, samples, w, voice->counter, voice->frequency);
+ }
+ }
+ }
+ }
+}
+
+void namco_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ namco_audio_device::sound_stream_update(stream, inputs, outputs, samples);
+}
+
+void namco_15xx_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ namco_audio_device::sound_stream_update(stream, inputs, outputs, samples);
+}
+
+void namco_cus30_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ namco_audio_device::sound_stream_update(stream, inputs, outputs, samples);
+}
diff --git a/src/devices/sound/namco.h b/src/devices/sound/namco.h
new file mode 100644
index 00000000000..56be828b046
--- /dev/null
+++ b/src/devices/sound/namco.h
@@ -0,0 +1,132 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria,Aaron Giles
+#pragma once
+
+#ifndef __NAMCO_H__
+#define __NAMCO_H__
+
+/* 8 voices max */
+#define MAX_VOICES 8
+
+#define MAX_VOLUME 16
+
+
+#define MCFG_NAMCO_AUDIO_VOICES(_voices) \
+ namco_audio_device::set_voices(*device, _voices);
+
+#define MCFG_NAMCO_AUDIO_STEREO(_stereo) \
+ namco_audio_device::set_stereo(*device, _stereo);
+
+
+/* this structure defines the parameters for a channel */
+struct sound_channel
+{
+ UINT32 frequency;
+ UINT32 counter;
+ INT32 volume[2];
+ INT32 noise_sw;
+ INT32 noise_state;
+ INT32 noise_seed;
+ UINT32 noise_counter;
+ INT32 noise_hold;
+ INT32 waveform_select;
+};
+
+class namco_audio_device : public device_t,
+ public device_sound_interface
+{
+public:
+ namco_audio_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ ~namco_audio_device() {}
+
+ // static configuration
+ static void set_voices(device_t &device, int voices) { downcast<namco_audio_device &>(device).m_voices = voices; }
+ static void set_stereo(device_t &device, int stereo) { downcast<namco_audio_device &>(device).m_stereo = stereo; }
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+
+ // internal state
+
+ void build_decoded_waveform( UINT8 *rgnbase );
+ void update_namco_waveform(int offset, UINT8 data);
+ UINT32 namco_update_one(stream_sample_t *buffer, int length, const INT16 *wave, UINT32 counter, UINT32 freq);
+
+ /* data about the sound system */
+ sound_channel m_channel_list[MAX_VOICES];
+ sound_channel *m_last_channel;
+ UINT8 *m_soundregs;
+ UINT8 *m_wavedata;
+
+ /* global sound parameters */
+ int m_wave_size;
+ INT32 m_sound_enable;
+ sound_stream *m_stream;
+ int m_namco_clock;
+ int m_sample_rate;
+ int m_f_fracbits;
+
+ int m_voices; /* number of voices */
+ int m_stereo; /* set to 1 to indicate stereo (e.g., System 1) */
+
+ /* decoded waveform table */
+ INT16 *m_waveform[MAX_VOLUME];
+
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+};
+
+class namco_device : public namco_audio_device
+{
+public:
+ namco_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_WRITE8_MEMBER( pacman_sound_enable_w );
+ DECLARE_WRITE8_MEMBER( pacman_sound_w );
+
+ void polepos_sound_enable(int enable);
+
+ DECLARE_READ8_MEMBER( polepos_sound_r );
+ DECLARE_WRITE8_MEMBER( polepos_sound_w );
+
+protected:
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+};
+
+extern const device_type NAMCO;
+
+class namco_15xx_device : public namco_audio_device
+{
+public:
+ namco_15xx_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_WRITE8_MEMBER( namco_15xx_w );
+ DECLARE_READ8_MEMBER( sharedram_r );
+ DECLARE_WRITE8_MEMBER( sharedram_w );
+
+ void mappy_sound_enable(int enable);
+
+protected:
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+};
+
+extern const device_type NAMCO_15XX;
+
+class namco_cus30_device : public namco_audio_device
+{
+public:
+ namco_cus30_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_WRITE8_MEMBER( namcos1_cus30_w ); /* wavedata + sound registers + RAM */
+ DECLARE_READ8_MEMBER( namcos1_cus30_r );
+ DECLARE_WRITE8_MEMBER( namcos1_sound_w );
+
+ DECLARE_WRITE8_MEMBER( pacman_sound_w );
+
+protected:
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+};
+
+extern const device_type NAMCO_CUS30;
+
+#endif /* __NAMCO_H__ */
diff --git a/src/devices/sound/nes_apu.c b/src/devices/sound/nes_apu.c
new file mode 100644
index 00000000000..cd2dfa8e4cb
--- /dev/null
+++ b/src/devices/sound/nes_apu.c
@@ -0,0 +1,774 @@
+// license:GPL-2.0+
+// copyright-holders:Matthew Conte
+/*****************************************************************************
+
+ MAME/MESS NES APU CORE
+
+ Based on the Nofrendo/Nosefart NES N2A03 sound emulation core written by
+ Matthew Conte (matt@conte.com) and redesigned for use in MAME/MESS by
+ Who Wants to Know? (wwtk@mail.com)
+
+ This core is written with the advise and consent of Matthew Conte and is
+ released under the GNU Public License. This core is freely avaiable for
+ use in any freeware project, subject to the following terms:
+
+ Any modifications to this code must be duly noted in the source and
+ approved by Matthew Conte and myself prior to public submission.
+
+ timing notes:
+ master = 21477270
+ 2A03 clock = master/12
+ sequencer = master/89490 or CPU/7457
+
+ *****************************************************************************
+
+ NES_APU.C
+
+ Actual NES APU interface.
+
+ LAST MODIFIED 02/29/2004
+
+ - Based on Matthew Conte's Nofrendo/Nosefart core and redesigned to
+ use MAME system calls and to enable multiple APUs. Sound at this
+ point should be just about 100% accurate, though I cannot tell for
+ certain as yet.
+
+ A queue interface is also available for additional speed. However,
+ the implementation is not yet 100% (DPCM sounds are inaccurate),
+ so it is disabled by default.
+
+ *****************************************************************************
+
+ BUGFIXES:
+
+ - Various bugs concerning the DPCM channel fixed. (Oliver Achten)
+ - Fixed $4015 read behaviour. (Oliver Achten)
+
+ *****************************************************************************/
+
+#include "emu.h"
+#include "nes_apu.h"
+#include "cpu/m6502/n2a03.h"
+
+
+
+/* INTERNAL FUNCTIONS */
+
+/* INITIALIZE WAVE TIMES RELATIVE TO SAMPLE RATE */
+static void create_vbltimes(uint32 * table,const uint8 *vbl,unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < 0x20; i++)
+ table[i] = vbl[i] * rate;
+}
+
+/* INITIALIZE SAMPLE TIMES IN TERMS OF VSYNCS */
+void nesapu_device::create_syncs(unsigned long sps)
+{
+ int i;
+ unsigned long val = sps;
+
+ for (i = 0; i < SYNCS_MAX1; i++)
+ {
+ m_sync_times1[i] = val;
+ val += sps;
+ }
+
+ val = 0;
+ for (i = 0; i < SYNCS_MAX2; i++)
+ {
+ m_sync_times2[i] = val;
+ m_sync_times2[i] >>= 2;
+ val += sps;
+ }
+}
+
+/* INITIALIZE NOISE LOOKUP TABLE */
+static void create_noise(uint8 *buf, const int bits, int size)
+{
+ int m = 0x0011;
+ int xor_val, i;
+
+ for (i = 0; i < size; i++)
+ {
+ xor_val = m & 1;
+ m >>= 1;
+ xor_val ^= (m & 1);
+ m |= xor_val << (bits - 1);
+
+ buf[i] = m;
+ }
+}
+
+const device_type NES_APU = &device_creator<nesapu_device>;
+
+nesapu_device::nesapu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, NES_APU, "N2A03 APU", tag, owner, clock, "nesapu", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_apu_incsize(0.0),
+ m_samps_per_sync(0),
+ m_buffer_size(0),
+ m_real_rate(0),
+ m_stream(NULL),
+ m_cpu_tag("")
+{
+ for (int i = 0; i < NOISE_LONG; i++)
+ {
+ m_noise_lut[i] = 0;
+ }
+
+ for (int i = 0; i < 0X20; i++)
+ {
+ m_vbl_times[i] = 0;
+ }
+
+ for (int i = 0; i < SYNCS_MAX1; i++)
+ {
+ m_sync_times1[i] = 0;
+ }
+
+ for (int i = 0; i < SYNCS_MAX2; i++)
+ {
+ m_sync_times2[i] = 0;
+ }
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void nesapu_device::device_start()
+{
+ int rate = clock() / 4;
+
+ /* Initialize global variables */
+ m_samps_per_sync = rate / ATTOSECONDS_TO_HZ(machine().first_screen()->frame_period().attoseconds());
+ m_buffer_size = m_samps_per_sync;
+ m_real_rate = m_samps_per_sync * ATTOSECONDS_TO_HZ(machine().first_screen()->frame_period().attoseconds());
+ m_apu_incsize = (float) (clock() / (float) m_real_rate);
+
+ /* Use initializer calls */
+ create_noise(m_noise_lut, 13, NOISE_LONG);
+ create_vbltimes(m_vbl_times,vbl_length,m_samps_per_sync);
+ create_syncs(m_samps_per_sync);
+
+ /* Adjust buffer size if 16 bits */
+ m_buffer_size+=m_samps_per_sync;
+
+ /* Initialize individual chips */
+ (m_APU.dpcm).memory = &machine().device(m_cpu_tag)->memory().space(AS_PROGRAM);
+
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, rate);
+
+ /* register for save */
+ for (int i = 0; i < 2; i++)
+ {
+ save_item(NAME(m_APU.squ[i].regs), i);
+ save_item(NAME(m_APU.squ[i].vbl_length), i);
+ save_item(NAME(m_APU.squ[i].freq), i);
+ save_item(NAME(m_APU.squ[i].phaseacc), i);
+ save_item(NAME(m_APU.squ[i].output_vol), i);
+ save_item(NAME(m_APU.squ[i].env_phase), i);
+ save_item(NAME(m_APU.squ[i].sweep_phase), i);
+ save_item(NAME(m_APU.squ[i].adder), i);
+ save_item(NAME(m_APU.squ[i].env_vol), i);
+ save_item(NAME(m_APU.squ[i].enabled), i);
+ }
+
+ save_item(NAME(m_APU.tri.regs));
+ save_item(NAME(m_APU.tri.linear_length));
+ save_item(NAME(m_APU.tri.vbl_length));
+ save_item(NAME(m_APU.tri.write_latency));
+ save_item(NAME(m_APU.tri.phaseacc));
+ save_item(NAME(m_APU.tri.output_vol));
+ save_item(NAME(m_APU.tri.adder));
+ save_item(NAME(m_APU.tri.counter_started));
+ save_item(NAME(m_APU.tri.enabled));
+
+ save_item(NAME(m_APU.noi.regs));
+ save_item(NAME(m_APU.noi.cur_pos));
+ save_item(NAME(m_APU.noi.vbl_length));
+ save_item(NAME(m_APU.noi.phaseacc));
+ save_item(NAME(m_APU.noi.output_vol));
+ save_item(NAME(m_APU.noi.env_phase));
+ save_item(NAME(m_APU.noi.env_vol));
+ save_item(NAME(m_APU.noi.enabled));
+
+ save_item(NAME(m_APU.dpcm.regs));
+ save_item(NAME(m_APU.dpcm.address));
+ save_item(NAME(m_APU.dpcm.length));
+ save_item(NAME(m_APU.dpcm.bits_left));
+ save_item(NAME(m_APU.dpcm.phaseacc));
+ save_item(NAME(m_APU.dpcm.output_vol));
+ save_item(NAME(m_APU.dpcm.cur_byte));
+ save_item(NAME(m_APU.dpcm.enabled));
+ save_item(NAME(m_APU.dpcm.irq_occurred));
+ save_item(NAME(m_APU.dpcm.vol));
+
+ save_item(NAME(m_APU.regs));
+
+ #ifdef USE_QUEUE
+ save_item(NAME(m_APU.queue));
+ save_item(NAME(m_APU.head));
+ save_item(NAME(m_APU.tail));
+ #else
+ save_item(NAME(m_APU.buf_pos));
+ save_item(NAME(m_APU.step_mode));
+ #endif
+}
+
+/* TODO: sound channels should *ALL* have DC volume decay */
+
+/* OUTPUT SQUARE WAVE SAMPLE (VALUES FROM -16 to +15) */
+int8 nesapu_device::apu_square(square_t *chan)
+{
+ int env_delay;
+ int sweep_delay;
+ int8 output;
+
+ /* reg0: 0-3=volume, 4=envelope, 5=hold, 6-7=duty cycle
+ ** reg1: 0-2=sweep shifts, 3=sweep inc/dec, 4-6=sweep length, 7=sweep on
+ ** reg2: 8 bits of freq
+ ** reg3: 0-2=high freq, 7-4=vbl length counter
+ */
+
+ if (FALSE == chan->enabled)
+ return 0;
+
+ /* enveloping */
+ env_delay = m_sync_times1[chan->regs[0] & 0x0F];
+
+ /* decay is at a rate of (env_regs + 1) / 240 secs */
+ chan->env_phase -= 4;
+ while (chan->env_phase < 0)
+ {
+ chan->env_phase += env_delay;
+ if (chan->regs[0] & 0x20)
+ chan->env_vol = (chan->env_vol + 1) & 15;
+ else if (chan->env_vol < 15)
+ chan->env_vol++;
+ }
+
+ /* vbl length counter */
+ if (chan->vbl_length > 0 && 0 == (chan->regs [0] & 0x20))
+ chan->vbl_length--;
+
+ if (0 == chan->vbl_length)
+ return 0;
+
+ /* freqsweeps */
+ if ((chan->regs[1] & 0x80) && (chan->regs[1] & 7))
+ {
+ sweep_delay = m_sync_times1[(chan->regs[1] >> 4) & 7];
+ chan->sweep_phase -= 2;
+ while (chan->sweep_phase < 0)
+ {
+ chan->sweep_phase += sweep_delay;
+ if (chan->regs[1] & 8)
+ chan->freq -= chan->freq >> (chan->regs[1] & 7);
+ else
+ chan->freq += chan->freq >> (chan->regs[1] & 7);
+ }
+ }
+
+ if ((0 == (chan->regs[1] & 8) && (chan->freq >> 16) > freq_limit[chan->regs[1] & 7])
+ || (chan->freq >> 16) < 4)
+ return 0;
+
+ chan->phaseacc -= (float) m_apu_incsize; /* # of cycles per sample */
+
+ while (chan->phaseacc < 0)
+ {
+ chan->phaseacc += (chan->freq >> 16);
+ chan->adder = (chan->adder + 1) & 0x0F;
+ }
+
+ if (chan->regs[0] & 0x10) /* fixed volume */
+ output = chan->regs[0] & 0x0F;
+ else
+ output = 0x0F - chan->env_vol;
+
+ if (chan->adder < (duty_lut[chan->regs[0] >> 6]))
+ output = -output;
+
+ return (int8) output;
+}
+
+/* OUTPUT TRIANGLE WAVE SAMPLE (VALUES FROM -16 to +15) */
+int8 nesapu_device::apu_triangle(triangle_t *chan)
+{
+ int freq;
+ int8 output;
+ /* reg0: 7=holdnote, 6-0=linear length counter
+ ** reg2: low 8 bits of frequency
+ ** reg3: 7-3=length counter, 2-0=high 3 bits of frequency
+ */
+
+ if (FALSE == chan->enabled)
+ return 0;
+
+ if (FALSE == chan->counter_started && 0 == (chan->regs[0] & 0x80))
+ {
+ if (chan->write_latency)
+ chan->write_latency--;
+ if (0 == chan->write_latency)
+ chan->counter_started = TRUE;
+ }
+
+ if (chan->counter_started)
+ {
+ if (chan->linear_length > 0)
+ chan->linear_length--;
+ if (chan->vbl_length && 0 == (chan->regs[0] & 0x80))
+ chan->vbl_length--;
+
+ if (0 == chan->vbl_length)
+ return 0;
+ }
+
+ if (0 == chan->linear_length)
+ return 0;
+
+ freq = (((chan->regs[3] & 7) << 8) + chan->regs[2]) + 1;
+
+ if (freq < 4) /* inaudible */
+ return 0;
+
+ chan->phaseacc -= (float) m_apu_incsize; /* # of cycles per sample */
+ while (chan->phaseacc < 0)
+ {
+ chan->phaseacc += freq;
+ chan->adder = (chan->adder + 1) & 0x1F;
+
+ output = (chan->adder & 7) << 1;
+ if (chan->adder & 8)
+ output = 0x10 - output;
+ if (chan->adder & 0x10)
+ output = -output;
+
+ chan->output_vol = output;
+ }
+
+ return (int8) chan->output_vol;
+}
+
+/* OUTPUT NOISE WAVE SAMPLE (VALUES FROM -16 to +15) */
+int8 nesapu_device::apu_noise(noise_t *chan)
+{
+ int freq, env_delay;
+ uint8 outvol;
+ uint8 output;
+
+ /* reg0: 0-3=volume, 4=envelope, 5=hold
+ ** reg2: 7=small(93 byte) sample,3-0=freq lookup
+ ** reg3: 7-4=vbl length counter
+ */
+
+ if (FALSE == chan->enabled)
+ return 0;
+
+ /* enveloping */
+ env_delay = m_sync_times1[chan->regs[0] & 0x0F];
+
+ /* decay is at a rate of (env_regs + 1) / 240 secs */
+ chan->env_phase -= 4;
+ while (chan->env_phase < 0)
+ {
+ chan->env_phase += env_delay;
+ if (chan->regs[0] & 0x20)
+ chan->env_vol = (chan->env_vol + 1) & 15;
+ else if (chan->env_vol < 15)
+ chan->env_vol++;
+ }
+
+ /* length counter */
+ if (0 == (chan->regs[0] & 0x20))
+ {
+ if (chan->vbl_length > 0)
+ chan->vbl_length--;
+ }
+
+ if (0 == chan->vbl_length)
+ return 0;
+
+ freq = noise_freq[chan->regs[2] & 0x0F];
+ chan->phaseacc -= (float) m_apu_incsize; /* # of cycles per sample */
+ while (chan->phaseacc < 0)
+ {
+ chan->phaseacc += freq;
+
+ chan->cur_pos++;
+ if (NOISE_SHORT == chan->cur_pos && (chan->regs[2] & 0x80))
+ chan->cur_pos = 0;
+ else if (NOISE_LONG == chan->cur_pos)
+ chan->cur_pos = 0;
+ }
+
+ if (chan->regs[0] & 0x10) /* fixed volume */
+ outvol = chan->regs[0] & 0x0F;
+ else
+ outvol = 0x0F - chan->env_vol;
+
+ output = m_noise_lut[chan->cur_pos];
+ if (output > outvol)
+ output = outvol;
+
+ if (m_noise_lut[chan->cur_pos] & 0x80) /* make it negative */
+ output = -output;
+
+ return (int8) output;
+}
+
+/* RESET DPCM PARAMETERS */
+INLINE void apu_dpcmreset(dpcm_t *chan)
+{
+ chan->address = 0xC000 + (uint16) (chan->regs[2] << 6);
+ chan->length = (uint16) (chan->regs[3] << 4) + 1;
+ chan->bits_left = chan->length << 3;
+ chan->irq_occurred = FALSE;
+ chan->enabled = TRUE; /* Fixed * Proper DPCM channel ENABLE/DISABLE flag behaviour*/
+ chan->vol = 0; /* Fixed * DPCM DAC resets itself when restarted */
+}
+
+/* OUTPUT DPCM WAVE SAMPLE (VALUES FROM -64 to +63) */
+/* TODO: centerline naughtiness */
+int8 nesapu_device::apu_dpcm(dpcm_t *chan)
+{
+ int freq, bit_pos;
+
+ /* reg0: 7=irq gen, 6=looping, 3-0=pointer to clock table
+ ** reg1: output dc level, 7 bits unsigned
+ ** reg2: 8 bits of 64-byte aligned address offset : $C000 + (value * 64)
+ ** reg3: length, (value * 16) + 1
+ */
+
+ if (chan->enabled)
+ {
+ freq = dpcm_clocks[chan->regs[0] & 0x0F];
+ chan->phaseacc -= (float) m_apu_incsize; /* # of cycles per sample */
+
+ while (chan->phaseacc < 0)
+ {
+ chan->phaseacc += freq;
+
+ if (0 == chan->length)
+ {
+ chan->enabled = FALSE; /* Fixed * Proper DPCM channel ENABLE/DISABLE flag behaviour*/
+ chan->vol=0; /* Fixed * DPCM DAC resets itself when restarted */
+ if (chan->regs[0] & 0x40)
+ apu_dpcmreset(chan);
+ else
+ {
+ if (chan->regs[0] & 0x80) /* IRQ Generator */
+ {
+ chan->irq_occurred = TRUE;
+ downcast<n2a03_device &>(m_APU.dpcm.memory->device()).set_input_line(N2A03_APU_IRQ_LINE, ASSERT_LINE);
+ }
+ break;
+ }
+ }
+
+
+ chan->bits_left--;
+ bit_pos = 7 - (chan->bits_left & 7);
+ if (7 == bit_pos)
+ {
+ chan->cur_byte = m_APU.dpcm.memory->read_byte(chan->address);
+ chan->address++;
+ chan->length--;
+ }
+
+ if (chan->cur_byte & (1 << bit_pos))
+// chan->regs[1]++;
+ chan->vol+=2; /* FIXED * DPCM channel only uses the upper 6 bits of the DAC */
+ else
+// chan->regs[1]--;
+ chan->vol-=2;
+ }
+ }
+
+ if (chan->vol > 63)
+ chan->vol = 63;
+ else if (chan->vol < -64)
+ chan->vol = -64;
+
+ return (int8) (chan->vol);
+}
+
+/* WRITE REGISTER VALUE */
+inline void nesapu_device::apu_regwrite(int address, uint8 value)
+{
+ int chan = (address & 4) ? 1 : 0;
+
+ switch (address)
+ {
+ /* squares */
+ case APU_WRA0:
+ case APU_WRB0:
+ m_APU.squ[chan].regs[0] = value;
+ break;
+
+ case APU_WRA1:
+ case APU_WRB1:
+ m_APU.squ[chan].regs[1] = value;
+ break;
+
+ case APU_WRA2:
+ case APU_WRB2:
+ m_APU.squ[chan].regs[2] = value;
+ if (m_APU.squ[chan].enabled)
+ m_APU.squ[chan].freq = ((((m_APU.squ[chan].regs[3] & 7) << 8) + value) + 1) << 16;
+ break;
+
+ case APU_WRA3:
+ case APU_WRB3:
+ m_APU.squ[chan].regs[3] = value;
+
+ if (m_APU.squ[chan].enabled)
+ {
+ m_APU.squ[chan].vbl_length = m_vbl_times[value >> 3];
+ m_APU.squ[chan].env_vol = 0;
+ m_APU.squ[chan].freq = ((((value & 7) << 8) + m_APU.squ[chan].regs[2]) + 1) << 16;
+ }
+
+ break;
+
+ /* triangle */
+ case APU_WRC0:
+ m_APU.tri.regs[0] = value;
+
+ if (m_APU.tri.enabled)
+ { /* ??? */
+ if (FALSE == m_APU.tri.counter_started)
+ m_APU.tri.linear_length = m_sync_times2[value & 0x7F];
+ }
+
+ break;
+
+ case 0x4009:
+ /* unused */
+ m_APU.tri.regs[1] = value;
+ break;
+
+ case APU_WRC2:
+ m_APU.tri.regs[2] = value;
+ break;
+
+ case APU_WRC3:
+ m_APU.tri.regs[3] = value;
+
+ /* this is somewhat of a hack. there is some latency on the Real
+ ** Thing between when trireg0 is written to and when the linear
+ ** length counter actually begins its countdown. we want to prevent
+ ** the case where the program writes to the freq regs first, then
+ ** to reg 0, and the counter accidentally starts running because of
+ ** the sound queue's timestamp processing.
+ **
+ ** set to a few NES sample -- should be sufficient
+ **
+ ** 3 * (1789772.727 / 44100) = ~122 cycles, just around one scanline
+ **
+ ** should be plenty of time for the 6502 code to do a couple of table
+ ** dereferences and load up the other triregs
+ */
+
+ /* used to be 3, but now we run the clock faster, so base it on samples/sync */
+ m_APU.tri.write_latency = (m_samps_per_sync + 239) / 240;
+
+ if (m_APU.tri.enabled)
+ {
+ m_APU.tri.counter_started = FALSE;
+ m_APU.tri.vbl_length = m_vbl_times[value >> 3];
+ m_APU.tri.linear_length = m_sync_times2[m_APU.tri.regs[0] & 0x7F];
+ }
+
+ break;
+
+ /* noise */
+ case APU_WRD0:
+ m_APU.noi.regs[0] = value;
+ break;
+
+ case 0x400D:
+ /* unused */
+ m_APU.noi.regs[1] = value;
+ break;
+
+ case APU_WRD2:
+ m_APU.noi.regs[2] = value;
+ break;
+
+ case APU_WRD3:
+ m_APU.noi.regs[3] = value;
+
+ if (m_APU.noi.enabled)
+ {
+ m_APU.noi.vbl_length = m_vbl_times[value >> 3];
+ m_APU.noi.env_vol = 0; /* reset envelope */
+ }
+ break;
+
+ /* DMC */
+ case APU_WRE0:
+ m_APU.dpcm.regs[0] = value;
+ if (0 == (value & 0x80)) {
+ downcast<n2a03_device &>(m_APU.dpcm.memory->device()).set_input_line(N2A03_APU_IRQ_LINE, CLEAR_LINE);
+ m_APU.dpcm.irq_occurred = FALSE;
+ }
+ break;
+
+ case APU_WRE1: /* 7-bit DAC */
+ //m_APU.dpcm.regs[1] = value - 0x40;
+ m_APU.dpcm.regs[1] = value & 0x7F;
+ m_APU.dpcm.vol = (m_APU.dpcm.regs[1]-64);
+ break;
+
+ case APU_WRE2:
+ m_APU.dpcm.regs[2] = value;
+ //apu_dpcmreset(m_APU.dpcm);
+ break;
+
+ case APU_WRE3:
+ m_APU.dpcm.regs[3] = value;
+ break;
+
+ case APU_IRQCTRL:
+ if(value & 0x80)
+ m_APU.step_mode = 5;
+ else
+ m_APU.step_mode = 4;
+ break;
+
+ case APU_SMASK:
+ if (value & 0x01)
+ m_APU.squ[0].enabled = TRUE;
+ else
+ {
+ m_APU.squ[0].enabled = FALSE;
+ m_APU.squ[0].vbl_length = 0;
+ }
+
+ if (value & 0x02)
+ m_APU.squ[1].enabled = TRUE;
+ else
+ {
+ m_APU.squ[1].enabled = FALSE;
+ m_APU.squ[1].vbl_length = 0;
+ }
+
+ if (value & 0x04)
+ m_APU.tri.enabled = TRUE;
+ else
+ {
+ m_APU.tri.enabled = FALSE;
+ m_APU.tri.vbl_length = 0;
+ m_APU.tri.linear_length = 0;
+ m_APU.tri.counter_started = FALSE;
+ m_APU.tri.write_latency = 0;
+ }
+
+ if (value & 0x08)
+ m_APU.noi.enabled = TRUE;
+ else
+ {
+ m_APU.noi.enabled = FALSE;
+ m_APU.noi.vbl_length = 0;
+ }
+
+ if (value & 0x10)
+ {
+ /* only reset dpcm values if DMA is finished */
+ if (FALSE == m_APU.dpcm.enabled)
+ {
+ m_APU.dpcm.enabled = TRUE;
+ apu_dpcmreset(&m_APU.dpcm);
+ }
+ }
+ else
+ m_APU.dpcm.enabled = FALSE;
+
+ m_APU.dpcm.irq_occurred = FALSE;
+
+ break;
+ default:
+#ifdef MAME_DEBUG
+logerror("invalid apu write: $%02X at $%04X\n", value, address);
+#endif
+ break;
+ }
+}
+
+
+
+/* READ VALUES FROM REGISTERS */
+inline uint8 nesapu_device::apu_read(int address)
+{
+ if (address == 0x15) /*FIXED* Address $4015 has different behaviour*/
+ {
+ int readval = 0;
+ if (m_APU.squ[0].vbl_length > 0)
+ readval |= 0x01;
+
+ if (m_APU.squ[1].vbl_length > 0)
+ readval |= 0x02;
+
+ if (m_APU.tri.vbl_length > 0)
+ readval |= 0x04;
+
+ if (m_APU.noi.vbl_length > 0)
+ readval |= 0x08;
+
+ if (m_APU.dpcm.enabled == TRUE)
+ readval |= 0x10;
+
+ if (m_APU.dpcm.irq_occurred == TRUE)
+ readval |= 0x80;
+
+ return readval;
+ }
+ else
+ return m_APU.regs[address];
+}
+
+/* WRITE VALUE TO TEMP REGISTRY AND QUEUE EVENT */
+inline void nesapu_device::apu_write(int address, uint8 value)
+{
+ m_APU.regs[address]=value;
+ m_stream->update();
+ apu_regwrite(address,value);
+}
+
+/* EXTERNAL INTERFACE FUNCTIONS */
+
+/* REGISTER READ/WRITE FUNCTIONS */
+READ8_MEMBER( nesapu_device::read ) {return apu_read(offset);}
+WRITE8_MEMBER( nesapu_device::write ) {apu_write(offset,data);}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void nesapu_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int accum;
+ memset( outputs[0], 0, samples*sizeof(*outputs[0]) );
+
+ while (samples--)
+ {
+ accum = apu_square(&m_APU.squ[0]);
+ accum += apu_square(&m_APU.squ[1]);
+ accum += apu_triangle(&m_APU.tri);
+ accum += apu_noise(&m_APU.noi);
+ accum += apu_dpcm(&m_APU.dpcm);
+
+ /* 8-bit clamps */
+ if (accum > 127)
+ accum = 127;
+ else if (accum < -128)
+ accum = -128;
+
+ *(outputs[0]++)=accum<<8;
+ }
+}
diff --git a/src/devices/sound/nes_apu.h b/src/devices/sound/nes_apu.h
new file mode 100644
index 00000000000..48823246e39
--- /dev/null
+++ b/src/devices/sound/nes_apu.h
@@ -0,0 +1,96 @@
+// license:GPL-2.0+
+// copyright-holders:Matthew Conte
+/*****************************************************************************
+
+ MAME/MESS NES APU CORE
+
+ Based on the Nofrendo/Nosefart NES N2A03 sound emulation core written by
+ Matthew Conte (matt@conte.com) and redesigned for use in MAME/MESS by
+ Who Wants to Know? (wwtk@mail.com)
+
+ This core is written with the advise and consent of Matthew Conte and is
+ released under the GNU Public License. This core is freely avaiable for
+ use in any freeware project, subject to the following terms:
+
+ Any modifications to this code must be duly noted in the source and
+ approved by Matthew Conte and myself prior to public submission.
+
+ *****************************************************************************
+
+ NES_APU.H
+
+ NES APU external interface.
+
+ *****************************************************************************/
+
+#pragma once
+
+#ifndef __NES_APU_H__
+#define __NES_APU_H__
+
+
+/* AN EXPLANATION
+ *
+ * The NES APU is actually integrated into the Nintendo processor.
+ * You must supply the same number of APUs as you do processors.
+ * Also make sure to correspond the memory regions to those used in the
+ * processor, as each is shared.
+ */
+
+#include "nes_defs.h"
+
+/* GLOBAL CONSTANTS */
+#define SYNCS_MAX1 0x20
+#define SYNCS_MAX2 0x80
+
+class nesapu_device : public device_t,
+ public device_sound_interface
+{
+public:
+ nesapu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~nesapu_device() {}
+
+ static void set_cpu_tag(device_t &device, const char *tag) { downcast<nesapu_device &>(device).m_cpu_tag = tag; }
+
+ DECLARE_READ8_MEMBER( read );
+ 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:
+ // internal state
+ apu_t m_APU; /* Actual APUs */
+ float m_apu_incsize; /* Adjustment increment */
+ uint32 m_samps_per_sync; /* Number of samples per vsync */
+ uint32 m_buffer_size; /* Actual buffer size in bytes */
+ uint32 m_real_rate; /* Actual playback rate */
+ uint8 m_noise_lut[NOISE_LONG]; /* Noise sample lookup table */
+ uint32 m_vbl_times[0x20]; /* VBL durations in samples */
+ uint32 m_sync_times1[SYNCS_MAX1]; /* Samples per sync table */
+ uint32 m_sync_times2[SYNCS_MAX2]; /* Samples per sync table */
+ sound_stream *m_stream;
+
+ const char *m_cpu_tag;
+
+ void create_syncs(unsigned long sps);
+ int8 apu_square(square_t *chan);
+ int8 apu_triangle(triangle_t *chan);
+ int8 apu_noise(noise_t *chan);
+ int8 apu_dpcm(dpcm_t *chan);
+ inline void apu_regwrite(int address, uint8 value);
+ inline uint8 apu_read(int address);
+ inline void apu_write(int address, uint8 value);
+};
+
+extern const device_type NES_APU;
+
+#define MCFG_NES_APU_CPU(_tag) \
+ nesapu_device::set_cpu_tag(*device, _tag);
+
+
+#endif /* __NES_APU_H__ */
diff --git a/src/devices/sound/nes_defs.h b/src/devices/sound/nes_defs.h
new file mode 100644
index 00000000000..0215a6a1208
--- /dev/null
+++ b/src/devices/sound/nes_defs.h
@@ -0,0 +1,290 @@
+// license:GPL-2.0+
+// copyright-holders:Matthew Conte
+/*****************************************************************************
+
+ MAME/MESS NES APU CORE
+
+ Based on the Nofrendo/Nosefart NES N2A03 sound emulation core written by
+ Matthew Conte (matt@conte.com) and redesigned for use in MAME/MESS by
+ Who Wants to Know? (wwtk@mail.com)
+
+ This core is written with the advise and consent of Matthew Conte and is
+ released under the GNU Public License. This core is freely avaiable for
+ use in any freeware project, subject to the following terms:
+
+ Any modifications to this code must be duly noted in the source and
+ approved by Matthew Conte and myself prior to public submission.
+
+ *****************************************************************************
+
+ NES_DEFS.H
+
+ NES APU internal type definitions and constants.
+
+ *****************************************************************************/
+
+#pragma once
+
+#ifndef __NES_DEFS_H__
+#define __NES_DEFS_H__
+
+#include "nes_defs.h"
+
+/* BOOLEAN CONSTANTS */
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+/* REGULAR TYPE DEFINITIONS */
+typedef INT8 int8;
+typedef INT16 int16;
+typedef INT32 int32;
+typedef UINT8 uint8;
+typedef UINT16 uint16;
+typedef UINT32 uint32;
+typedef UINT8 boolean;
+
+
+/* QUEUE TYPES */
+#ifdef USE_QUEUE
+
+#define QUEUE_SIZE 0x2000
+#define QUEUE_MAX (QUEUE_SIZE-1)
+
+struct queue_t
+{
+ queue_t():
+ pos(0),
+ reg(""),val("") {}
+
+ int pos;
+ unsigned char reg, val;
+};
+
+#endif
+
+/* REGISTER DEFINITIONS */
+#define APU_WRA0 0x00
+#define APU_WRA1 0x01
+#define APU_WRA2 0x02
+#define APU_WRA3 0x03
+#define APU_WRB0 0x04
+#define APU_WRB1 0x05
+#define APU_WRB2 0x06
+#define APU_WRB3 0x07
+#define APU_WRC0 0x08
+#define APU_WRC2 0x0A
+#define APU_WRC3 0x0B
+#define APU_WRD0 0x0C
+#define APU_WRD2 0x0E
+#define APU_WRD3 0x0F
+#define APU_WRE0 0x10
+#define APU_WRE1 0x11
+#define APU_WRE2 0x12
+#define APU_WRE3 0x13
+#define APU_SMASK 0x15
+#define APU_IRQCTRL 0x17
+
+#define NOISE_LONG 0x4000
+#define NOISE_SHORT 93
+
+/* CHANNEL TYPE DEFINITIONS */
+
+/* Square Wave */
+struct square_t
+{
+ square_t()
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ regs[i] = 0;
+ }
+ vbl_length =0;
+ freq = 0;
+ phaseacc = 0.0;
+ output_vol = 0.0;
+ env_phase = 0.0;
+ sweep_phase = 0.0;
+ adder = 0;
+ env_vol = 0;
+ enabled = false;
+ }
+
+ uint8 regs[4];
+ int vbl_length;
+ int freq;
+ float phaseacc;
+ float output_vol;
+ float env_phase;
+ float sweep_phase;
+ uint8 adder;
+ uint8 env_vol;
+ boolean enabled;
+};
+
+/* Triangle Wave */
+struct triangle_t
+{
+ triangle_t()
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ regs[i] = 0;
+ }
+ linear_length =0;
+ vbl_length =0;
+ write_latency = 0;
+ phaseacc = 0.0;
+ output_vol = 0.0;
+ adder = 0;
+ counter_started = false;
+ enabled = false;
+ }
+
+ uint8 regs[4]; /* regs[1] unused */
+ int linear_length;
+ int vbl_length;
+ int write_latency;
+ float phaseacc;
+ float output_vol;
+ uint8 adder;
+ boolean counter_started;
+ boolean enabled;
+};
+
+/* Noise Wave */
+struct noise_t
+{
+ noise_t()
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ regs[i] = 0;
+ }
+ cur_pos =0;
+ vbl_length =0;
+ phaseacc = 0.0;
+ output_vol = 0.0;
+ env_phase = 0.0;
+ env_vol = 0;
+ enabled = false;
+ }
+
+ uint8 regs[4]; /* regs[1] unused */
+ int cur_pos;
+ int vbl_length;
+ float phaseacc;
+ float output_vol;
+ float env_phase;
+ uint8 env_vol;
+ boolean enabled;
+};
+
+/* DPCM Wave */
+struct dpcm_t
+{
+ dpcm_t()
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ regs[i] = 0;
+ }
+ address = 0;
+ length = 0;
+ bits_left = 0;
+ phaseacc = 0.0;
+ output_vol = 0.0;
+ cur_byte = 0;
+ enabled = false;
+ irq_occurred = false;
+ memory = NULL;
+ vol = 0;
+ }
+
+ uint8 regs[4];
+ uint32 address;
+ uint32 length;
+ int bits_left;
+ float phaseacc;
+ float output_vol;
+ uint8 cur_byte;
+ boolean enabled;
+ boolean irq_occurred;
+ address_space *memory;
+ signed char vol;
+};
+
+/* APU type */
+struct apu_t
+{
+ apu_t()
+ {
+ memset(regs, 0, sizeof(regs));
+ buffer = NULL;
+ buf_pos = 0;
+ step_mode = 0;
+ }
+
+ /* Sound channels */
+ square_t squ[2];
+ triangle_t tri;
+ noise_t noi;
+ dpcm_t dpcm;
+
+ /* APU registers */
+ unsigned char regs[0x18];
+
+ /* Sound pointers */
+ void *buffer;
+
+#ifdef USE_QUEUE
+
+ /* Event queue */
+ queue_t queue[QUEUE_SIZE];
+ int head, tail;
+
+#else
+
+ int buf_pos;
+
+#endif
+
+ int step_mode;
+};
+
+/* CONSTANTS */
+
+/* vblank length table used for squares, triangle, noise */
+static const uint8 vbl_length[32] =
+{
+ 5, 127, 10, 1, 19, 2, 40, 3, 80, 4, 30, 5, 7, 6, 13, 7,
+ 6, 8, 12, 9, 24, 10, 48, 11, 96, 12, 36, 13, 8, 14, 16, 15
+};
+
+/* frequency limit of square channels */
+static const int freq_limit[8] =
+{
+ 0x3FF, 0x555, 0x666, 0x71C, 0x787, 0x7C1, 0x7E0, 0x7F0,
+};
+
+/* table of noise frequencies */
+static const int noise_freq[16] =
+{
+ 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 2046
+};
+
+/* dpcm transfer freqs */
+static const int dpcm_clocks[16] =
+{
+ 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 85, 72, 54
+};
+
+/* ratios of pos/neg pulse for square waves */
+/* 2/16 = 12.5%, 4/16 = 25%, 8/16 = 50%, 12/16 = 75% */
+static const int duty_lut[4] =
+{
+ 2, 4, 8, 12
+};
+
+#endif /* __NES_DEFS_H__ */
diff --git a/src/devices/sound/nile.c b/src/devices/sound/nile.c
new file mode 100644
index 00000000000..a0fd9d8b5a5
--- /dev/null
+++ b/src/devices/sound/nile.c
@@ -0,0 +1,228 @@
+// license:BSD-3-Clause
+// copyright-holders:Tomasz Slanina
+/************************************
+ Seta custom Nile ST-0026 chip
+ sound emulation by Tomasz Slanina
+ based on ST-0016 emulation
+
+8 voices, 16 words of config data for each:
+
+ 00
+ 01 - sptr ?? (always 0)
+ 02 - sptr LO
+ 03 - sptr HI
+ 04
+ 05 - flags? 00000000 0000?L0? - bit 0 loops, other bits appear to be not used by the chip
+ 06 - freq
+ 07 - lsptr LO
+ 08
+ 09 - lsptr HI
+ 0a - leptr LO
+ 0b - leptr HI
+ 0c - eptr LO
+ 0d - eptr HI
+ 0e - vol R
+ 0f - vol L
+
+************************************/
+
+#include "emu.h"
+#include "nile.h"
+
+enum
+{
+ NILE_REG_UNK0=0,
+ NILE_REG_SPTR_TOP,
+ NILE_REG_SPTR_LO,
+ NILE_REG_SPTR_HI,
+ NILE_REG_UNK_4,
+ NILE_REG_FLAGS,
+ NILE_REG_FREQ,
+ NILE_REG_LSPTR_LO,
+ MILE_REG_UNK_8,
+ NILE_REG_LSPTR_HI,
+ NILE_REG_LEPTR_LO,
+ NILE_REG_LEPTR_HI,
+ NILE_REG_EPTR_LO,
+ NILE_REG_EPTR_HI,
+ NILE_REG_VOL_R,
+ NILE_REG_VOL_L
+};
+
+
+const device_type NILE = &device_creator<nile_device>;
+
+nile_device::nile_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, NILE, "NiLe", tag, owner, clock, "nile", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_sound_ram(NULL),
+ m_ctrl(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void nile_device::device_start()
+{
+ m_sound_ram = region()->base();
+ m_stream = stream_alloc(0, 2, 44100);
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests
+// for our sound stream
+//-------------------------------------------------
+
+void nile_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ UINT8 *sound_ram = m_sound_ram;
+ int v, i, snum;
+ UINT16 *slot;
+ INT32 mix[48000*2];
+ INT32 *mixp;
+ INT16 sample;
+ int sptr, eptr, freq, lsptr, leptr;
+
+ lsptr=leptr=0;
+
+ memset(mix, 0, sizeof(mix[0])*samples*2);
+
+ for (v = 0; v < NILE_VOICES; v++)
+ {
+ slot = &m_sound_regs[v * 16];
+
+ if (m_ctrl&(1<<v))
+ {
+ mixp = &mix[0];
+
+ sptr = slot[NILE_REG_SPTR_HI]<<16 | slot[NILE_REG_SPTR_LO];
+ eptr = slot[NILE_REG_EPTR_HI]<<16 | slot[NILE_REG_EPTR_LO];
+
+ freq=slot[NILE_REG_FREQ]*14;
+ lsptr = slot[NILE_REG_LSPTR_HI]<<16 | slot[NILE_REG_LSPTR_LO];
+ leptr = slot[NILE_REG_LEPTR_HI]<<16 | slot[NILE_REG_LEPTR_LO];
+
+ for (snum = 0; snum < samples; snum++)
+ {
+ sample = sound_ram[sptr + m_vpos[v]]<<8;
+
+ *mixp++ += (sample * (INT32)slot[NILE_REG_VOL_R]) >> 16;
+ *mixp++ += (sample * (INT32)slot[NILE_REG_VOL_L]) >> 16;
+
+ m_frac[v] += freq;
+ m_vpos[v] += m_frac[v]>>16;
+ m_frac[v] &= 0xffff;
+
+ // stop if we're at the end
+ if (m_lponce[v])
+ {
+ // we've looped once, check loop end rather than sample end
+ if ((m_vpos[v] + sptr) >= leptr)
+ {
+ m_vpos[v] = (lsptr - sptr);
+ }
+ }
+ else
+ {
+ // not looped yet, check sample end
+ if ((m_vpos[v] + sptr) >= eptr)
+ {
+ // code at 11d8c:
+ // if bit 2 (0x4) is set, check if loop start = loop end.
+ // if they are equal, clear bit 0 and don't set the loop start/end
+ // registers in the NiLe. if they aren't, set bit 0 and set
+ // the loop start/end registers in the NiLe.
+ if ((slot[NILE_REG_FLAGS] & 0x5) == 0x5)
+ {
+ m_vpos[v] = (lsptr - sptr);
+ m_lponce[v] = 1;
+ }
+ else
+ {
+ m_ctrl &= ~(1<<v);
+ m_vpos[v] = (eptr - sptr);
+ m_frac[v] = 0;
+ }
+
+ }
+ }
+ }
+ }
+ }
+ mixp = &mix[0];
+ for (i = 0; i < samples; i++)
+ {
+ outputs[0][i] = (*mixp++)>>4;
+ outputs[1][i] = (*mixp++)>>4;
+ }
+}
+
+
+WRITE16_MEMBER( nile_device::nile_sndctrl_w )
+{
+ UINT16 ctrl=m_ctrl;
+
+ m_stream->update();
+
+ COMBINE_DATA(&m_ctrl);
+
+// printf("CTRL: %04x -> %04x (PC=%x)\n", ctrl, m_ctrl, space.device().safe_pc());
+
+ ctrl^=m_ctrl;
+}
+
+
+READ16_MEMBER( nile_device::nile_sndctrl_r )
+{
+ m_stream->update();
+ return m_ctrl;
+}
+
+
+READ16_MEMBER( nile_device::nile_snd_r )
+{
+ int reg=offset&0xf;
+
+ m_stream->update();
+
+ if(reg==2 || reg==3)
+ {
+ int slot=offset/16;
+ int sptr = ((m_sound_regs[slot*16+3]<<16)|m_sound_regs[slot*16+2])+m_vpos[slot];
+
+ if(reg==2)
+ {
+ return sptr&0xffff;
+ }
+ else
+ {
+ return sptr>>16;
+ }
+ }
+ return m_sound_regs[offset];
+}
+
+
+WRITE16_MEMBER( nile_device::nile_snd_w )
+{
+ int v, r;
+
+ m_stream->update();
+
+ COMBINE_DATA(&m_sound_regs[offset]);
+
+ v = offset / 16;
+ r = offset % 16;
+
+ if ((r == 2) || (r == 3))
+ {
+ m_vpos[v] = m_frac[v] = m_lponce[v] = 0;
+ }
+
+ //printf("v%02d: %04x to reg %02d (PC=%x)\n", v, m_sound_regs[offset], r, space.device().safe_pc());
+}
diff --git a/src/devices/sound/nile.h b/src/devices/sound/nile.h
new file mode 100644
index 00000000000..9aa05d79511
--- /dev/null
+++ b/src/devices/sound/nile.h
@@ -0,0 +1,60 @@
+// license:BSD-3-Clause
+// copyright-holders:Tomasz Slanina
+#pragma once
+
+#ifndef __NILE_H__
+#define __NILE_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_NILE_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, NILE, _clock)
+#define MCFG_NILE_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, NILE, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+#define NILE_VOICES 8
+
+// ======================> nile_device
+
+class nile_device : public device_t,
+ public device_sound_interface
+{
+public:
+ nile_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~nile_device() { }
+
+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);
+
+public:
+ DECLARE_WRITE16_MEMBER( nile_snd_w );
+ DECLARE_READ16_MEMBER( nile_snd_r );
+ DECLARE_WRITE16_MEMBER( nile_sndctrl_w );
+ DECLARE_READ16_MEMBER( nile_sndctrl_r );
+
+private:
+ sound_stream *m_stream;
+ UINT8 *m_sound_ram;
+ UINT16 m_sound_regs[0x80];
+ int m_vpos[NILE_VOICES];
+ int m_frac[NILE_VOICES];
+ int m_lponce[NILE_VOICES];
+ UINT16 m_ctrl;
+};
+
+extern const device_type NILE;
+
+
+#endif /* __NILE_H__ */
diff --git a/src/devices/sound/okiadpcm.c b/src/devices/sound/okiadpcm.c
new file mode 100644
index 00000000000..611f7e2f13f
--- /dev/null
+++ b/src/devices/sound/okiadpcm.c
@@ -0,0 +1,192 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrew Gardner,Aaron Giles
+/***************************************************************************
+
+ okiadpcm.h
+
+ OKI ADCPM emulation.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "okiadpcm.h"
+
+
+//**************************************************************************
+// ADPCM STATE HELPER
+//**************************************************************************
+
+// ADPCM state and tables
+bool oki_adpcm_state::s_tables_computed = false;
+const INT8 oki_adpcm_state::s_index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
+int oki_adpcm_state::s_diff_lookup[49*16];
+
+//-------------------------------------------------
+// reset - reset the ADPCM state
+//-------------------------------------------------
+
+void oki_adpcm_state::reset()
+{
+ // reset the signal/step
+ m_signal = -2;
+ m_step = 0;
+}
+
+
+//-------------------------------------------------
+// device_clock_changed - called if the clock
+// changes
+//-------------------------------------------------
+
+INT16 oki_adpcm_state::clock(UINT8 nibble)
+{
+ // update the signal
+ m_signal += s_diff_lookup[m_step * 16 + (nibble & 15)];
+
+ // clamp to the maximum
+ if (m_signal > 2047)
+ m_signal = 2047;
+ else if (m_signal < -2048)
+ m_signal = -2048;
+
+ // adjust the step size and clamp
+ m_step += s_index_shift[nibble & 7];
+ if (m_step > 48)
+ m_step = 48;
+ else if (m_step < 0)
+ m_step = 0;
+
+ // return the signal
+ return m_signal;
+}
+
+
+//-------------------------------------------------
+// compute_tables - precompute tables for faster
+// sound generation
+//-------------------------------------------------
+
+void oki_adpcm_state::compute_tables()
+{
+ // skip if we already did it
+ if (s_tables_computed)
+ return;
+ s_tables_computed = true;
+
+ // nibble to bit map
+ static const INT8 nbl2bit[16][4] =
+ {
+ { 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
+ { 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
+ {-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
+ {-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
+ };
+
+ // loop over all possible steps
+ for (int step = 0; step <= 48; step++)
+ {
+ // compute the step value
+ int stepval = floor(16.0 * pow(11.0 / 10.0, (double)step));
+
+ // loop over all nibbles and compute the difference
+ for (int nib = 0; nib < 16; nib++)
+ {
+ s_diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
+ (stepval * nbl2bit[nib][1] +
+ stepval/2 * nbl2bit[nib][2] +
+ stepval/4 * nbl2bit[nib][3] +
+ stepval/8);
+ }
+ }
+}
+
+
+//**************************************************************************
+// ADPCM2 STATE HELPER
+//**************************************************************************
+
+// ADPCM state and tables
+bool oki_adpcm2_state::s_tables_computed = false;
+const INT8 oki_adpcm2_state::s_index_shift[8] = { -2, -2, -2, -2, 2, 6, 9, 11 };
+int oki_adpcm2_state::s_diff_lookup[49*16];
+
+//-------------------------------------------------
+// reset - reset the ADPCM state
+//-------------------------------------------------
+
+void oki_adpcm2_state::reset()
+{
+ // reset the signal/step
+ m_signal = -2;
+ m_step = 0;
+}
+
+
+//-------------------------------------------------
+// device_clock_changed - called if the clock
+// changes
+//-------------------------------------------------
+
+INT16 oki_adpcm2_state::clock(UINT8 nibble)
+{
+ // update the signal
+ m_signal += s_diff_lookup[m_step * 16 + (nibble & 15)];
+
+ // clamp to the maximum
+ if (m_signal > 2047)
+ m_signal = 2047;
+ else if (m_signal < -2048)
+ m_signal = -2048;
+
+ // adjust the step size and clamp
+ m_step += s_index_shift[nibble & 7];
+ if (m_step > 48)
+ m_step = 48;
+ else if (m_step < 0)
+ m_step = 0;
+
+ // return the signal
+ return m_signal;
+}
+
+
+//-------------------------------------------------
+// compute_tables - precompute tables for faster
+// sound generation
+//-------------------------------------------------
+
+void oki_adpcm2_state::compute_tables()
+{
+ // skip if we already did it
+ if (s_tables_computed)
+ return;
+ s_tables_computed = true;
+
+ // nibble to bit map
+ static const INT8 nbl2bit[16][4] =
+ {
+ { 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
+ { 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
+ {-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
+ {-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
+ };
+
+ // loop over all possible steps
+ float floatstep = 64;
+ for (int step = 0; step <= 48; step++)
+ {
+ // compute the step value
+ int stepval = floor(floatstep * 1.08f);
+ floatstep = floatstep * 1.08f;
+
+ // loop over all nibbles and compute the difference
+ for (int nib = 0; nib < 16; nib++)
+ {
+ s_diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
+ (stepval * nbl2bit[nib][1] +
+ stepval/2 * nbl2bit[nib][2] +
+ stepval/4 * nbl2bit[nib][3] +
+ stepval/8);
+ }
+ }
+}
diff --git a/src/devices/sound/okiadpcm.h b/src/devices/sound/okiadpcm.h
new file mode 100644
index 00000000000..311ee9d404f
--- /dev/null
+++ b/src/devices/sound/okiadpcm.h
@@ -0,0 +1,64 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrew Gardner,Aaron Giles
+/***************************************************************************
+
+ okiadpcm.h
+
+ OKI ADCPM emulation.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __OKIADPCM_H__
+#define __OKIADPCM_H__
+
+
+// ======================> oki_adpcm_state
+
+// Internal ADPCM state, used by external ADPCM generators with compatible specs to the OKIM 6295.
+class oki_adpcm_state
+{
+public:
+ oki_adpcm_state() { compute_tables(); reset(); }
+
+ void reset();
+ INT16 clock(UINT8 nibble);
+
+ INT32 m_signal;
+ INT32 m_step;
+
+private:
+ static const INT8 s_index_shift[8];
+ static int s_diff_lookup[49*16];
+
+ static void compute_tables();
+ static bool s_tables_computed;
+};
+
+
+// ======================> oki_adpcm2_state
+
+// Internal ADPCM2 state, used by external ADPCM generators with compatible specs to the OKI MSM9810.
+// TODO: not thoroughly tested: is the output 15 bit?
+class oki_adpcm2_state
+{
+public:
+ oki_adpcm2_state() { compute_tables(); reset(); }
+
+ void reset();
+ INT16 clock(UINT8 nibble);
+
+ INT32 m_signal;
+ INT32 m_step;
+
+private:
+ static const INT8 s_index_shift[8];
+ static int s_diff_lookup[49*16];
+
+ static void compute_tables();
+ static bool s_tables_computed;
+};
+
+
+#endif // __OKIADPCM_H__
diff --git a/src/devices/sound/okim6258.c b/src/devices/sound/okim6258.c
new file mode 100644
index 00000000000..d26810e33d5
--- /dev/null
+++ b/src/devices/sound/okim6258.c
@@ -0,0 +1,337 @@
+// license:BSD-3-Clause
+// copyright-holders:Barry Rodewald
+/**********************************************************************************************
+ *
+ * OKI MSM6258 ADPCM
+ *
+ * TODO:
+ * 3-bit ADPCM support
+ * Recording?
+ *
+ **********************************************************************************************/
+
+
+#include "emu.h"
+#include "okim6258.h"
+
+#define COMMAND_STOP (1 << 0)
+#define COMMAND_PLAY (1 << 1)
+#define COMMAND_RECORD (1 << 2)
+
+#define STATUS_PLAYING (1 << 1)
+#define STATUS_RECORDING (1 << 2)
+
+static const int dividers[4] = { 1024, 768, 512, 512 };
+
+/* step size index shift table */
+static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
+
+/* lookup table for the precomputed difference */
+static int diff_lookup[49*16];
+
+/* tables computed? */
+static int tables_computed = 0;
+
+
+
+// device type definition
+const device_type OKIM6258 = &device_creator<okim6258_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// okim6258_device - constructor
+//-------------------------------------------------
+
+okim6258_device::okim6258_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, OKIM6258, "OKI6258", tag, owner, clock, "okim6258", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_status(0),
+ m_master_clock(0),
+ m_divider(0),
+ m_adpcm_type(0),
+ m_data_in(0),
+ m_nibble_shift(0),
+ m_stream(NULL),
+ m_output_bits(0),
+ m_signal(0),
+ m_step(0)
+{
+}
+
+
+
+/**********************************************************************************************
+
+ compute_tables -- compute the difference tables
+
+***********************************************************************************************/
+
+static void compute_tables(void)
+{
+ /* nibble to bit map */
+ static const int nbl2bit[16][4] =
+ {
+ { 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
+ { 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
+ {-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
+ {-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
+ };
+
+ int step, nib;
+
+ /* loop over all possible steps */
+ for (step = 0; step <= 48; step++)
+ {
+ /* compute the step value */
+ int stepval = floor(16.0 * pow(11.0 / 10.0, (double)step));
+
+ /* loop over all nibbles and compute the difference */
+ for (nib = 0; nib < 16; nib++)
+ {
+ diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
+ (stepval * nbl2bit[nib][1] +
+ stepval/2 * nbl2bit[nib][2] +
+ stepval/4 * nbl2bit[nib][3] +
+ stepval/8);
+ }
+ }
+
+ tables_computed = 1;
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void okim6258_device::device_start()
+{
+ compute_tables();
+
+ m_master_clock = clock();
+
+ m_divider = dividers[m_start_divider];
+
+ m_stream = stream_alloc(0, 1, clock()/m_divider);
+
+ m_signal = -2;
+ m_step = 0;
+
+ okim6258_state_save_register();
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void okim6258_device::device_reset()
+{
+ m_stream->update();
+
+ m_signal = -2;
+ m_step = 0;
+ m_status = 0;
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void okim6258_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *buffer = outputs[0];
+
+ memset(outputs[0], 0, samples * sizeof(*outputs[0]));
+
+ if (m_status & STATUS_PLAYING)
+ {
+ int nibble_shift = m_nibble_shift;
+
+ while (samples)
+ {
+ /* Compute the new amplitude and update the current step */
+ int nibble = (m_data_in >> nibble_shift) & 0xf;
+
+ /* Output to the buffer */
+ INT16 sample = clock_adpcm(nibble);
+
+ nibble_shift ^= 4;
+
+ *buffer++ = sample;
+ samples--;
+ }
+
+ /* Update the parameters */
+ m_nibble_shift = nibble_shift;
+ }
+ else
+ {
+ /* Fill with 0 */
+ while (samples--)
+ *buffer++ = 0;
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ state save support for MAME
+
+***********************************************************************************************/
+
+void okim6258_device::okim6258_state_save_register()
+{
+ save_item(NAME(m_status));
+ save_item(NAME(m_master_clock));
+ save_item(NAME(m_divider));
+ save_item(NAME(m_data_in));
+ save_item(NAME(m_nibble_shift));
+ save_item(NAME(m_signal));
+ save_item(NAME(m_step));
+}
+
+
+INT16 okim6258_device::clock_adpcm(UINT8 nibble)
+{
+ INT32 max = (1 << (m_output_bits - 1)) - 1;
+ INT32 min = -(1 << (m_output_bits - 1));
+
+ m_signal += diff_lookup[m_step * 16 + (nibble & 15)];
+
+ /* clamp to the maximum */
+ if (m_signal > max)
+ m_signal = max;
+ else if (m_signal < min)
+ m_signal = min;
+
+ /* adjust the step size and clamp */
+ m_step += index_shift[nibble & 7];
+ if (m_step > 48)
+ m_step = 48;
+ else if (m_step < 0)
+ m_step = 0;
+
+ /* return the signal scaled up to 32767 */
+ return m_signal << 4;
+}
+
+
+/**********************************************************************************************
+
+ okim6258::set_divider -- set the master clock divider
+
+***********************************************************************************************/
+
+void okim6258_device::set_divider(int val)
+{
+ int divider = dividers[val];
+
+ m_divider = dividers[val];
+ m_stream->set_sample_rate(m_master_clock / divider);
+}
+
+
+/**********************************************************************************************
+
+ okim6258::set_clock -- set the master clock
+
+***********************************************************************************************/
+
+void okim6258_device::set_clock(int val)
+{
+ m_master_clock = val;
+ m_stream->set_sample_rate(m_master_clock / m_divider);
+}
+
+
+/**********************************************************************************************
+
+ okim6258::get_vclk -- get the VCLK/sampling frequency
+
+***********************************************************************************************/
+
+int okim6258_device::get_vclk()
+{
+ return (m_master_clock / m_divider);
+}
+
+
+/**********************************************************************************************
+
+ okim6258_status_r -- read the status port of an OKIM6258-compatible chip
+
+***********************************************************************************************/
+
+READ8_MEMBER( okim6258_device::okim6258_status_r )
+{
+ m_stream->update();
+
+ return (m_status & STATUS_PLAYING) ? 0x00 : 0x80;
+}
+
+
+/**********************************************************************************************
+
+ okim6258_data_w -- write to the control port of an OKIM6258-compatible chip
+
+***********************************************************************************************/
+WRITE8_MEMBER( okim6258_device::okim6258_data_w )
+{
+ /* update the stream */
+ m_stream->update();
+
+ m_data_in = data;
+ m_nibble_shift = 0;
+}
+
+
+/**********************************************************************************************
+
+ okim6258_ctrl_w -- write to the control port of an OKIM6258-compatible chip
+
+***********************************************************************************************/
+
+WRITE8_MEMBER( okim6258_device::okim6258_ctrl_w )
+{
+ m_stream->update();
+
+ if (data & COMMAND_STOP)
+ {
+ m_status &= ~(STATUS_PLAYING | STATUS_RECORDING);
+ return;
+ }
+
+ if (data & COMMAND_PLAY)
+ {
+ if (!(m_status & STATUS_PLAYING))
+ {
+ m_status |= STATUS_PLAYING;
+
+ /* Also reset the ADPCM parameters */
+ m_signal = -2;
+ m_step = 0;
+ m_nibble_shift = 0;
+ }
+ }
+ else
+ {
+ m_status &= ~STATUS_PLAYING;
+ }
+
+ if (data & COMMAND_RECORD)
+ {
+ logerror("M6258: Record enabled\n");
+ m_status |= STATUS_RECORDING;
+ }
+ else
+ {
+ m_status &= ~STATUS_RECORDING;
+ }
+}
diff --git a/src/devices/sound/okim6258.h b/src/devices/sound/okim6258.h
new file mode 100644
index 00000000000..c4cfed1c82c
--- /dev/null
+++ b/src/devices/sound/okim6258.h
@@ -0,0 +1,99 @@
+// license:BSD-3-Clause
+// copyright-holders:Barry Rodewald
+#pragma once
+
+#ifndef __OKIM6258_H__
+#define __OKIM6258_H__
+
+#define FOSC_DIV_BY_1024 0
+#define FOSC_DIV_BY_768 1
+#define FOSC_DIV_BY_512 2
+
+#define TYPE_3BITS 0
+#define TYPE_4BITS 1
+
+#define OUTPUT_10BITS 10
+#define OUTPUT_12BITS 12
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_OKIM6258_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, OKIM6258, _clock)
+#define MCFG_OKIM6258_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, OKIM6258, _clock)
+
+#define MCFG_OKIM6258_DIVIDER(_div) \
+ okim6258_device::set_start_div(*device, _div);
+
+#define MCFG_OKIM6258_ADPCM_TYPE(_type) \
+ okim6258_device::set_type(*device, _type);
+
+#define MCFG_OKIM6258_OUT_BITS(_bits) \
+ okim6258_device::set_outbits(*device, _bits);
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> okim6258_device
+
+class okim6258_device : public device_t,
+ public device_sound_interface
+{
+public:
+ okim6258_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~okim6258_device() { }
+
+ // static configuration
+ static void set_start_div(device_t &device, int div) { downcast<okim6258_device &>(device).m_start_divider = div; }
+ static void set_type(device_t &device, int type) { downcast<okim6258_device &>(device).m_adpcm_type = type; }
+ static void set_outbits(device_t &device, int outbit) { downcast<okim6258_device &>(device).m_output_bits = outbit; }
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+public:
+ DECLARE_READ8_MEMBER( okim6258_status_r );
+ DECLARE_WRITE8_MEMBER( okim6258_data_w );
+ DECLARE_WRITE8_MEMBER( okim6258_ctrl_w );
+
+public:
+ void set_divider(int val);
+ void set_clock(int val);
+ int get_vclk();
+
+private:
+ void okim6258_state_save_register();
+ INT16 clock_adpcm(UINT8 nibble);
+
+private:
+ UINT8 m_status;
+
+ UINT32 m_master_clock; /* master clock frequency */
+ UINT32 m_start_divider;
+ UINT32 m_divider; /* master clock divider */
+ UINT8 m_adpcm_type; /* 3/4 bit ADPCM select */
+ UINT8 m_data_in; /* ADPCM data-in register */
+ UINT8 m_nibble_shift; /* nibble select */
+ sound_stream *m_stream; /* which stream are we playing on? */
+
+ UINT8 m_output_bits; /* D/A precision is 10-bits but 12-bit data can be
+ output serially to an external DAC */
+
+ INT32 m_signal;
+ INT32 m_step;
+};
+
+extern const device_type OKIM6258;
+
+
+#endif /* __OKIM6258_H__ */
diff --git a/src/devices/sound/okim6295.c b/src/devices/sound/okim6295.c
new file mode 100644
index 00000000000..fc9f35e3a58
--- /dev/null
+++ b/src/devices/sound/okim6295.c
@@ -0,0 +1,421 @@
+// license:BSD-3-Clause
+// copyright-holders:Mirko Buffoni,Aaron Giles
+/***************************************************************************
+
+ okim6295.h
+
+ OKIM 6295 ADCPM sound chip.
+
+****************************************************************************
+
+ Library to transcode from an ADPCM source to raw PCM.
+ Written by Buffoni Mirko in 08/06/97
+ References: various sources and documents.
+
+ R. Belmont 31/10/2003
+ Updated to allow a driver to use both MSM6295s and "raw" ADPCM voices
+ (gcpinbal). Also added some error trapping for MAME_DEBUG builds
+
+****************************************************************************
+
+ OKIM 6295 ADPCM chip:
+
+ Command bytes are sent:
+
+ 1xxx xxxx = start of 2-byte command sequence, xxxxxxx is the sample
+ number to trigger
+ abcd vvvv = second half of command; one of the abcd bits is set to
+ indicate which voice the v bits seem to be volumed
+
+ 0abc d000 = stop playing; one or more of the abcd bits is set to
+ indicate which voice(s)
+
+ Status is read:
+
+ ???? abcd = one bit per voice, set to 0 if nothing is playing, or
+ 1 if it is active
+
+***************************************************************************/
+
+#include "emu.h"
+#include "okim6295.h"
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+const device_type OKIM6295 = &device_creator<okim6295_device>;
+
+// volume lookup table. The manual lists only 9 steps, ~3dB per step. Given the dB values,
+// that seems to map to a 5-bit volume control. Any volume parameter beyond the 9th index
+// results in silent playback.
+const UINT8 okim6295_device::s_volume_table[16] =
+{
+ 0x20, // 0 dB
+ 0x16, // -3.2 dB
+ 0x10, // -6.0 dB
+ 0x0b, // -9.2 dB
+ 0x08, // -12.0 dB
+ 0x06, // -14.5 dB
+ 0x04, // -18.0 dB
+ 0x03, // -20.5 dB
+ 0x02, // -24.0 dB
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+};
+
+// default address map
+static ADDRESS_MAP_START( okim6295, AS_0, 8, okim6295_device )
+ AM_RANGE(0x00000, 0x3ffff) AM_ROM
+ADDRESS_MAP_END
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// okim6295_device - constructor
+//-------------------------------------------------
+
+okim6295_device::okim6295_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, OKIM6295, "OKI6295", tag, owner, clock, "okim6295", __FILE__),
+ device_sound_interface(mconfig, *this),
+ device_memory_interface(mconfig, *this),
+ m_space_config("samples", ENDIANNESS_LITTLE, 8, 18, 0, NULL, *ADDRESS_MAP_NAME(okim6295)),
+ m_command(-1),
+ m_bank_installed(false),
+ m_bank_offs(0),
+ m_stream(NULL),
+ m_pin7_state(0),
+ m_direct(NULL)
+{
+}
+
+
+//-------------------------------------------------
+// static_set_pin7 - configuration helper to set
+// the pin 7 state
+//-------------------------------------------------
+
+void okim6295_device::static_set_pin7(device_t &device, int pin7)
+{
+ okim6295_device &okim6295 = downcast<okim6295_device &>(device);
+ okim6295.m_pin7_state = pin7;
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void okim6295_device::device_start()
+{
+ // find our direct access
+ m_direct = &space().direct();
+
+ // create the stream
+ int divisor = m_pin7_state ? 132 : 165;
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / divisor);
+
+ save_item(NAME(m_command));
+ save_item(NAME(m_bank_offs));
+ save_item(NAME(m_pin7_state));
+
+ for (int voicenum = 0; voicenum < OKIM6295_VOICES; voicenum++)
+ {
+ save_item(NAME(m_voice[voicenum].m_playing), voicenum);
+ save_item(NAME(m_voice[voicenum].m_sample), voicenum);
+ save_item(NAME(m_voice[voicenum].m_count), voicenum);
+ save_item(NAME(m_voice[voicenum].m_adpcm.m_signal), voicenum);
+ save_item(NAME(m_voice[voicenum].m_adpcm.m_step), voicenum);
+ save_item(NAME(m_voice[voicenum].m_volume), voicenum);
+ save_item(NAME(m_voice[voicenum].m_base_offset), voicenum);
+ }
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void okim6295_device::device_reset()
+{
+ m_stream->update();
+ for (int voicenum = 0; voicenum < OKIM6295_VOICES; voicenum++)
+ m_voice[voicenum].m_playing = false;
+}
+
+
+//-------------------------------------------------
+// device_post_load - device-specific post-load
+//-------------------------------------------------
+
+void okim6295_device::device_post_load()
+{
+ set_bank_base(m_bank_offs, true);
+ device_clock_changed();
+}
+
+
+//-------------------------------------------------
+// device_clock_changed - called if the clock
+// changes
+//-------------------------------------------------
+
+void okim6295_device::device_clock_changed()
+{
+ int divisor = m_pin7_state ? 132 : 165;
+ m_stream->set_sample_rate(clock() / divisor);
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *okim6295_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == 0) ? &m_space_config : NULL;
+}
+
+
+//-------------------------------------------------
+// stream_generate - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void okim6295_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ // reset the output stream
+ memset(outputs[0], 0, samples * sizeof(*outputs[0]));
+
+ // iterate over voices and accumulate sample data
+ for (int voicenum = 0; voicenum < OKIM6295_VOICES; voicenum++)
+ m_voice[voicenum].generate_adpcm(*m_direct, outputs[0], samples);
+}
+
+
+//-------------------------------------------------
+// set_bank_base - old-style bank management;
+// assumes multiple 256k banks
+//-------------------------------------------------
+
+void okim6295_device::set_bank_base(offs_t base, bool bDontUpdateStream)
+{
+ // flush out anything pending (but not on e.g. a state load)
+ if (!bDontUpdateStream)
+ {
+ m_stream->update();
+ }
+
+ // if we are setting a non-zero base, and we have no bank, allocate one
+ if (!m_bank_installed && base != 0)
+ {
+ // override our memory map with a bank
+ space().install_read_bank(0x00000, 0x3ffff, tag());
+ m_bank_installed = true;
+ }
+
+ // if we have a bank number, set the base pointer
+ if (m_bank_installed)
+ {
+ m_bank_offs = base;
+ membank(tag())->set_base(m_region->base() + base);
+ }
+}
+
+
+//-------------------------------------------------
+// set_pin7 - change the state of pin 7, which
+// alters the frequency we output
+//-------------------------------------------------
+
+void okim6295_device::set_pin7(int pin7)
+{
+ m_pin7_state = pin7;
+ device_clock_changed();
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+UINT8 okim6295_device::read_status()
+{
+ UINT8 result = 0xf0; // naname expects bits 4-7 to be 1
+
+ // set the bit to 1 if something is playing on a given channel
+ m_stream->update();
+ for (int voicenum = 0; voicenum < OKIM6295_VOICES; voicenum++)
+ if (m_voice[voicenum].m_playing)
+ result |= 1 << voicenum;
+
+ return result;
+}
+
+
+//-------------------------------------------------
+// read - memory interface for read
+//-------------------------------------------------
+
+READ8_MEMBER( okim6295_device::read )
+{
+ return read_status();
+}
+
+
+//-------------------------------------------------
+// write_command - write to the command register
+//-------------------------------------------------
+
+void okim6295_device::write_command(UINT8 command)
+{
+ // if a command is pending, process the second half
+ if (m_command != -1)
+ {
+ // the manual explicitly says that it's not possible to start multiple voices at the same time
+ int voicemask = command >> 4;
+ //if (voicemask != 0 && voicemask != 1 && voicemask != 2 && voicemask != 4 && voicemask != 8)
+ // popmessage("OKI6295 start %x contact MAMEDEV", voicemask);
+
+ // update the stream
+ m_stream->update();
+
+ // determine which voice(s) (voice is set by a 1 bit in the upper 4 bits of the second byte)
+ for (int voicenum = 0; voicenum < OKIM6295_VOICES; voicenum++, voicemask >>= 1)
+ if (voicemask & 1)
+ {
+ okim_voice &voice = m_voice[voicenum];
+
+ if (!voice.m_playing) // fixes Got-cha and Steel Force
+ {
+ // determine the start/stop positions
+ offs_t base = m_command * 8;
+
+ offs_t start = m_direct->read_byte(base + 0) << 16;
+ start |= m_direct->read_byte(base + 1) << 8;
+ start |= m_direct->read_byte(base + 2) << 0;
+ start &= 0x3ffff;
+
+ offs_t stop = m_direct->read_byte(base + 3) << 16;
+ stop |= m_direct->read_byte(base + 4) << 8;
+ stop |= m_direct->read_byte(base + 5) << 0;
+ stop &= 0x3ffff;
+
+ if (start < stop)
+ {
+ // set up the voice to play this sample
+ voice.m_playing = true;
+ voice.m_base_offset = start;
+ voice.m_sample = 0;
+ voice.m_count = 2 * (stop - start + 1);
+
+ // also reset the ADPCM parameters
+ voice.m_adpcm.reset();
+ voice.m_volume = s_volume_table[command & 0x0f];
+ }
+
+ // invalid samples go here
+ else
+ {
+ logerror("OKIM6295:'%s' requested to play invalid sample %02x\n",tag(),m_command);
+ }
+ }
+ else
+ {
+ logerror("OKIM6295:'%s' requested to play sample %02x on non-stopped voice\n",tag(),m_command);
+ }
+ }
+
+ // reset the command
+ m_command = -1;
+ }
+
+ // if this is the start of a command, remember the sample number for next time
+ else if (command & 0x80)
+ m_command = command & 0x7f;
+
+ // otherwise, see if this is a silence command
+ else
+ {
+ // update the stream, then turn it off
+ m_stream->update();
+
+ // determine which voice(s) (voice is set by a 1 bit in bits 3-6 of the command
+ int voicemask = command >> 3;
+ for (int voicenum = 0; voicenum < OKIM6295_VOICES; voicenum++, voicemask >>= 1)
+ if (voicemask & 1)
+ m_voice[voicenum].m_playing = false;
+ }
+}
+
+
+//-------------------------------------------------
+// write - memory interface for write
+//-------------------------------------------------
+
+WRITE8_MEMBER( okim6295_device::write )
+{
+ write_command(data);
+}
+
+
+
+//**************************************************************************
+// OKIM VOICE
+//**************************************************************************
+
+//-------------------------------------------------
+// okim_voice - constructor
+//-------------------------------------------------
+
+okim6295_device::okim_voice::okim_voice()
+ : m_playing(false),
+ m_base_offset(0),
+ m_sample(0),
+ m_count(0),
+ m_volume(0)
+{
+}
+
+
+//-------------------------------------------------
+// generate_adpcm - generate ADPCM samples and
+// add them to an output stream
+//-------------------------------------------------
+
+void okim6295_device::okim_voice::generate_adpcm(direct_read_data &direct, stream_sample_t *buffer, int samples)
+{
+ // skip if not active
+ if (!m_playing)
+ return;
+
+ // loop while we still have samples to generate
+ while (samples-- != 0)
+ {
+ // fetch the next sample byte
+ int nibble = direct.read_byte(m_base_offset + m_sample / 2) >> (((m_sample & 1) << 2) ^ 4);
+
+ // output to the buffer, scaling by the volume
+ // signal in range -2048..2047, volume in range 2..32 => signal * volume / 2 in range -32768..32767
+ *buffer++ += m_adpcm.clock(nibble) * m_volume / 2;
+
+ // next!
+ if (++m_sample >= m_count)
+ {
+ m_playing = false;
+ break;
+ }
+ }
+}
diff --git a/src/devices/sound/okim6295.h b/src/devices/sound/okim6295.h
new file mode 100644
index 00000000000..33c8962767a
--- /dev/null
+++ b/src/devices/sound/okim6295.h
@@ -0,0 +1,126 @@
+// license:BSD-3-Clause
+// copyright-holders:Mirko Buffoni,Aaron Giles
+/***************************************************************************
+
+ okim6295.h
+
+ OKIM 6295 ADCPM sound chip.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __OKIM6295_H__
+#define __OKIM6295_H__
+
+#include "sound/okiadpcm.h"
+
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+enum
+{
+ OKIM6295_PIN7_LOW = 0,
+ OKIM6295_PIN7_HIGH = 1
+};
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_OKIM6295_ADD(_tag, _clock, _pin7) \
+ MCFG_DEVICE_ADD(_tag, OKIM6295, _clock) \
+ MCFG_OKIM6295_PIN7(_pin7)
+
+#define MCFG_OKIM6295_REPLACE(_tag, _clock, _pin7) \
+ MCFG_DEVICE_REPLACE(_tag, OKIM6295, _clock) \
+ MCFG_OKIM6295_PIN7(_pin7)
+
+#define MCFG_OKIM6295_PIN7(_pin7) \
+ okim6295_device::static_set_pin7(*device, _pin7);
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+
+// ======================> okim6295_device
+
+class okim6295_device : public device_t,
+ public device_sound_interface,
+ public device_memory_interface
+{
+public:
+ // construction/destruction
+ okim6295_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // inline configuration helpers
+ static void static_set_pin7(device_t &device, int pin7);
+
+ // runtime configuration
+ void set_bank_base(offs_t base, bool bDontUpdateStream = false);
+ void set_pin7(int pin7);
+
+ UINT8 read_status();
+ void write_command(UINT8 command);
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_post_load();
+ virtual void device_clock_changed();
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ // a single voice
+ class okim_voice
+ {
+ public:
+ okim_voice();
+ void generate_adpcm(direct_read_data &direct, stream_sample_t *buffer, int samples);
+
+ oki_adpcm_state m_adpcm; // current ADPCM state
+ bool m_playing;
+ offs_t m_base_offset; // pointer to the base memory location
+ UINT32 m_sample; // current sample number
+ UINT32 m_count; // total samples to play
+ INT8 m_volume; // output volume
+ };
+
+ // configuration state
+ const address_space_config m_space_config;
+
+ // internal state
+ static const int OKIM6295_VOICES = 4;
+
+ okim_voice m_voice[OKIM6295_VOICES];
+ INT32 m_command;
+ bool m_bank_installed;
+ offs_t m_bank_offs;
+ sound_stream * m_stream;
+ UINT8 m_pin7_state;
+ direct_read_data * m_direct;
+
+ static const UINT8 s_volume_table[16];
+};
+
+
+// device type definition
+extern const device_type OKIM6295;
+
+
+#endif /* __OKIM6295_H__ */
diff --git a/src/devices/sound/okim6376.c b/src/devices/sound/okim6376.c
new file mode 100644
index 00000000000..2db0a475b04
--- /dev/null
+++ b/src/devices/sound/okim6376.c
@@ -0,0 +1,581 @@
+// license:BSD-3-Clause
+// copyright-holders:Mirko Buffoni, James Wallace
+/**********************************************************************************************
+ *
+ * OKI MSM6376 ADPCM
+ * by Mirko Buffoni, J. Wallace
+ *
+ * TODO:
+ * add BEEP tone generator
+ * confirm echo
+ * sample divisor in ROM table not implemented (no documentation)
+ * modernise
+ **********************************************************************************************/
+
+
+#include "emu.h"
+#include "okim6376.h"
+
+#define MAX_SAMPLE_CHUNK 10000
+//#define MAX_WORDS 111
+
+#define OKIVERBOSE 0
+#define MSM6376LOG(x) do { if (OKIVERBOSE) logerror x; } while (0)
+
+/* step size index shift table */
+static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
+
+/* lookup table for the precomputed difference */
+static int diff_lookup[49*16];
+
+/* volume lookup table. Upon configuration, the number of ST pulses determine how much
+ attenuation to apply to the sound signal. However, this only applies to the second
+ channel*/
+static const int volume_table[3] =
+{
+ 0x20, // 0 dB
+ 0x10, // -6.0 dB
+ 0x08, // -12.0 dB
+};
+
+/* divisor lookup table. When an individual word is selected, it can be assigned one of three different 'rates'.
+ These are implemented as clock divisors, and are looked up in the ROM header. More often than not, this value is 0,
+ relating to a division by 8, or nominally 8KHz sampling (based on the datasheet example of a 64KHz clock).*/
+static const int divisor_table[3] =
+{
+ 8,
+ 10,
+ 16,
+};
+
+/* tables computed? */
+static int tables_computed = 0;
+
+/**********************************************************************************************
+
+ compute_tables -- compute the difference tables
+
+***********************************************************************************************/
+
+static void compute_tables(void)
+{
+ /* nibble to bit map */
+ static const int nbl2bit[16][4] =
+ {
+ { 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
+ { 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
+ {-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
+ {-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
+ };
+
+ int step, nib;
+
+ /* loop over all possible steps */
+ for (step = 0; step <= 48; step++)
+ {
+ /* compute the step value */
+ int stepval = floor(16.0 * pow(11.0 / 10.0, (double)step));
+
+ /* loop over all nibbles and compute the difference */
+ for (nib = 0; nib < 16; nib++)
+ {
+ diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
+ (stepval * nbl2bit[nib][1] +
+ stepval/2 * nbl2bit[nib][2] +
+ stepval/4 * nbl2bit[nib][3] +
+ stepval/8);
+ }
+ }
+
+ tables_computed = 1;
+}
+
+
+
+/**********************************************************************************************
+
+ reset_adpcm -- reset the ADPCM stream
+
+***********************************************************************************************/
+
+static void reset_adpcm(struct ADPCMVoice *voice)
+{
+ /* make sure we have our tables */
+ if (!tables_computed)
+ compute_tables();
+
+ /* reset the signal/step */
+ voice->signal = -2;
+ voice->step = 0;
+}
+
+
+const device_type OKIM6376 = &device_creator<okim6376_device>;
+
+okim6376_device::okim6376_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, OKIM6376, "OKI6376", tag, owner, clock, "okim6376", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_region_base(*this, DEVICE_SELF),
+ //m_command[OKIM6376_VOICES],
+ m_latch(0),
+ //m_stage[OKIM6376_VOICES],
+ m_stream(NULL),
+ m_master_clock(0),
+ m_divisor(0),
+ m_channel(0),
+ m_nar(0),
+ m_nartimer(0),
+ m_busy(0),
+ m_ch2(0),
+ m_st(0),
+ m_st_pulses(0),
+ m_ch2_update(0),
+ m_st_update(0)
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void okim6376_device::device_config_complete()
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void okim6376_device::device_start()
+{
+ int voice;
+ compute_tables();
+
+ m_command[0] = -1;
+ m_command[1] = -1;
+ m_stage[0] = 0;
+ m_stage[1] = 0;
+ m_latch = 0;
+ m_master_clock = clock();
+ m_divisor = divisor_table[0];
+ m_nar = 1;
+ m_nartimer = 0;
+ m_busy = 1;
+ m_st = 1;
+ m_ch2 = 1;
+ m_st_update = 0;
+ m_ch2_update = 0;
+ m_st_pulses = 0;
+ /* generate the name and create the stream */
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / m_divisor);
+
+ /* initialize the voices */
+ for (voice = 0; voice < OKIM6376_VOICES; voice++)
+ {
+ /* initialize the rest of the structure */
+ m_voice[voice].volume = 0;
+ reset_adpcm(&m_voice[voice]);
+ }
+
+ okim6376_state_save_register();
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void okim6376_device::device_reset()
+{
+ int i;
+
+ m_stream->update();
+ for (i = 0; i < OKIM6376_VOICES; i++)
+ m_voice[i].playing = 0;
+}
+
+
+/**********************************************************************************************
+
+ clock_adpcm -- clock the next ADPCM byte
+
+***********************************************************************************************/
+
+static INT16 clock_adpcm(struct ADPCMVoice *voice, UINT8 nibble)
+{
+ voice->signal += diff_lookup[voice->step * 16 + (nibble & 15)];
+
+ /* clamp to the maximum 12bit */
+ if (voice->signal > 2047)
+ voice->signal = 2047;
+ else if (voice->signal < -2048)
+ voice->signal = -2048;
+
+ /* adjust the step size and clamp */
+ voice->step += index_shift[nibble & 7];
+ if (voice->step > 48)
+ voice->step = 48;
+ else if (voice->step < 0)
+ voice->step = 0;
+
+ /* return the signal */
+ return voice->signal;
+}
+
+
+void okim6376_device::oki_process(int channel, int command)
+{
+ /* if a command is pending, process the second half */
+ if ((command != -1) && (command != 0)) //process silence separately
+ {
+ int start;
+ unsigned char *base/*, *base_end*/;
+ /* update the stream */
+ m_stream->update();
+
+ /* determine which voice(s) (voice is set by the state of 2CH) */
+ {
+ struct ADPCMVoice *voice = &m_voice[channel];
+
+ /* determine the start position, max address space is 16Mbit */
+ base = &m_region_base[m_command[channel] * 4];
+ //base_end = &m_region_base[(MAX_WORDS+1) * 4];
+ start = ((base[0] << 16) + (base[1] << 8) + base[2]) & 0x1fffff;
+
+ if (start == 0)
+ {
+ voice->playing = 0;
+ }
+ else
+ {
+ /* set up the voice to play this sample */
+ if (!voice->playing)
+ {
+ voice->playing = 1;
+ voice->base_offset = start;
+ voice->sample = 0;
+ voice->count = 0;
+
+ /* also reset the ADPCM parameters */
+ reset_adpcm(voice);
+ if (channel == 0)
+ {
+ /* We set channel 2's audio separately */
+ voice->volume = volume_table[0];
+ }
+ }
+ else
+ {
+ if (((m_nar)&&(channel == 0))||(channel == 1))//Store the request, for later processing (channel 2 ignores NAR)
+ {
+ m_stage[channel] = 1;
+ }
+ }
+ }
+ }
+ }
+ /* otherwise, see if this is a silence command */
+ else
+ {
+ /* update the stream, then turn it off */
+ m_stream->update();
+
+ if (command ==0)
+ {
+ int i;
+ for (i = 0; i < OKIM6376_VOICES; i++)
+ {
+ struct ADPCMVoice *voice = &m_voice[i];
+ voice->playing = 0;
+ }
+ }
+ }
+}
+
+
+/**********************************************************************************************
+
+ generate_adpcm -- general ADPCM decoding routine
+
+***********************************************************************************************/
+
+void okim6376_device::generate_adpcm(struct ADPCMVoice *voice, INT16 *buffer, int samples,int channel)
+{
+ /* if this voice is active */
+ if (voice->playing)
+ {
+ UINT8 *base = m_region_base + voice->base_offset;
+ int sample = voice->sample;
+ int count = voice->count;
+
+ /* loop while we still have samples to generate */
+ while (samples)
+ {
+ int nibble;
+
+ if (count == 0)
+ {
+ /* get the number of samples to play */
+ count = (base[sample / 2] & 0x7f) << 1;
+
+ /* end of voice marker */
+ if (count == 0)
+ {
+ voice->playing = 0;
+ break;
+ }
+ else
+ {
+ /* step past the count byte */
+ sample += 2;
+ }
+ }
+
+ /* compute the new amplitude and update the current step */
+ nibble = base[sample / 2] >> (((sample & 1) << 2) ^ 4);
+
+ /* output to the buffer, scaling by the volume */
+ /* signal in range -4096..4095, volume in range 2..16 => signal * volume / 2 in range -32768..32767 */
+ *buffer++ = clock_adpcm(voice, nibble) * voice->volume / 2;
+
+ ++sample;
+ --count;
+ --samples;
+ }
+
+ /* update the parameters */
+ voice->sample = sample;
+ voice->count = count;
+ }
+
+ /* fill the rest with silence */
+ while (samples--)
+ *buffer++ = 0;
+
+ if ((!voice->playing)&&(m_stage[channel]))//end of samples, load anything staged in
+ {
+ m_stage[channel] = 0;
+ oki_process(channel,m_command[channel]);
+ }
+}
+
+
+/**********************************************************************************************
+
+ state save support for MAME
+
+***********************************************************************************************/
+
+void okim6376_device::postload()
+{
+ set_frequency(m_master_clock);
+}
+
+void okim6376_device::adpcm_state_save_register(struct ADPCMVoice *voice, int index)
+{
+ save_item(NAME(voice->playing), index);
+ save_item(NAME(voice->sample), index);
+ save_item(NAME(voice->count), index);
+ save_item(NAME(voice->signal), index);
+ save_item(NAME(voice->step), index);
+ save_item(NAME(voice->volume), index);
+ save_item(NAME(voice->base_offset), index);
+}
+
+void okim6376_device::okim6376_state_save_register()
+{
+ int j;
+ for (j = 0; j < OKIM6376_VOICES; j++)
+ {
+ adpcm_state_save_register(&m_voice[j], j);
+ }
+ machine().save().register_postload(save_prepost_delegate(FUNC(okim6376_device::postload), this));
+ save_item(NAME(m_command[0]));
+ save_item(NAME(m_command[1]));
+ save_item(NAME(m_stage[0]));
+ save_item(NAME(m_stage[1]));
+ save_item(NAME(m_latch));
+ save_item(NAME(m_divisor));
+ save_item(NAME(m_nar));
+ save_item(NAME(m_nartimer));
+ save_item(NAME(m_busy));
+ save_item(NAME(m_st));
+ save_item(NAME(m_st_pulses));
+ save_item(NAME(m_st_update));
+ save_item(NAME(m_ch2));
+ save_item(NAME(m_ch2_update));
+ save_item(NAME(m_master_clock));
+}
+
+void okim6376_device::set_frequency(int frequency)
+{
+ m_master_clock = frequency;
+ m_stream->set_sample_rate(m_master_clock / m_divisor);
+}
+
+
+/**********************************************************************************************
+
+ okim6376_status_r -- read the status port of an OKIM6376-compatible chip
+
+***********************************************************************************************/
+
+READ_LINE_MEMBER( okim6376_device::busy_r )
+{
+ struct ADPCMVoice *voice0 = &m_voice[0];
+ struct ADPCMVoice *voice1 = &m_voice[1];
+
+ /* set the bit low if it's playing */
+ if ((voice0->playing)||(voice1->playing))
+ {
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+READ_LINE_MEMBER( okim6376_device::nar_r )
+{
+ MSM6376LOG(("OKIM6376:'%s' NAR %x\n",tag(),m_nar));
+ return m_nar;
+}
+
+WRITE_LINE_MEMBER( okim6376_device::ch2_w )
+{
+ m_ch2_update = 0;//Clear flag
+ MSM6376LOG(("OKIM6376:'%s' CH2 %x\n",tag(),state));
+
+ if (m_ch2 != state)
+ {
+ m_ch2 = state;
+ m_ch2_update = 1;
+ }
+ if((!m_ch2)&&(m_ch2_update))
+ {
+ m_st_pulses = 0;
+ struct ADPCMVoice *voice0 = &m_voice[0];
+ struct ADPCMVoice *voice1 = &m_voice[1];
+ // We set to channel 2
+ MSM6376LOG(("OKIM6376:'%s' Channel 1\n",tag()));
+ m_channel = 1;
+
+ if ((voice0->playing)&&(m_st))
+ {
+ //Echo functions when Channel 1 is playing, and ST is still high
+ m_command[1] = m_command[0];//copy sample over
+ voice1->volume = volume_table[1]; //echo is 6dB attenuated
+ }
+ }
+
+ if((m_ch2)&&(m_ch2_update))
+ {
+ m_stage[1]=0;
+ oki_process(1, m_command[1]);
+ MSM6376LOG(("OKIM6376:'%s' Channel 0\n",tag()));
+ m_channel = 0;
+ }
+}
+
+
+WRITE_LINE_MEMBER( okim6376_device::st_w )
+{
+ //As in STart, presumably, this triggers everything
+
+ m_st_update = 0;//Clear flag
+ MSM6376LOG(("OKIM6376:'%s' ST %x\n",tag(),state));
+
+ if (m_st != state)
+ {
+ m_st = state;
+ m_st_update = 1;
+
+ if ((m_channel == 1) & !m_st)//ST acts as attenuation for Channel 2 when low, and stays at that level until the channel is reset
+ {
+ struct ADPCMVoice *voice = &m_voice[m_channel];
+ {
+ m_st_pulses ++;
+ MSM6376LOG(("OKIM6376:'%s' ST pulses %x\n",tag(),m_st_pulses));
+ if (m_st_pulses > 3)
+ {
+ m_st_pulses = 3; //undocumented behaviour beyond 3 pulses
+ }
+
+ voice->volume = volume_table[m_st_pulses - 1];
+ }
+ }
+ if (m_st && m_st_update)
+ {
+ m_command[m_channel] = m_latch;
+ if (m_channel ==0 && m_nar)
+ {
+ m_stage[m_channel]=0;
+ oki_process(0, m_command[0]);
+ m_nar = 0;
+ m_nartimer = 4;
+ /*According to datasheet, NAR timing is ~375 us at 8KHz, and is inversely proportional to sample rate, effectively 6 stream updates. */
+ }
+ }
+ }
+}
+
+/**********************************************************************************************
+
+ okim6376_data_w -- write to the data port of an OKIM6376-compatible chip
+
+***********************************************************************************************/
+
+WRITE8_MEMBER( okim6376_device::write )
+{
+ // The data port is purely used to set the latch, everything else is started by an ST pulse
+
+ m_latch = data & 0x7f;
+ // FIX: maximum adpcm words are 111, there are other 8 commands to generate BEEP tone (0x70 to 0x77),
+ // and others for internal testing, that manual explicitly says not to use (0x78 to 0x7f)
+ // We aren't doing anything with the BEEP at the moment, as we'd need to mix the ADPCM stream with beep.c
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void okim6376_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int i;
+
+ memset(outputs[0], 0, samples * sizeof(*outputs[0]));
+
+ for (i = 0; i < OKIM6376_VOICES; i++)
+ {
+ struct ADPCMVoice *voice = &m_voice[i];
+ stream_sample_t *buffer = outputs[0];
+ INT16 sample_data[MAX_SAMPLE_CHUNK];
+ int remaining = samples;
+ if (i == 0) //channel 1 is the only channel to affect NAR
+ {
+ if (m_nartimer > 0)
+ {
+ m_nartimer--;
+ if (!m_nartimer)
+ {
+ m_nar =1;
+ }
+ }
+ }
+
+ /* loop while we have samples remaining */
+ while (remaining)
+ {
+ int samples = (remaining > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : remaining;
+ int samp;
+
+ generate_adpcm(voice, sample_data, samples,i);
+ for (samp = 0; samp < samples; samp++)
+ *buffer++ += sample_data[samp];
+
+ remaining -= samples;
+ }
+ }
+}
diff --git a/src/devices/sound/okim6376.h b/src/devices/sound/okim6376.h
new file mode 100644
index 00000000000..df87344eb50
--- /dev/null
+++ b/src/devices/sound/okim6376.h
@@ -0,0 +1,82 @@
+// license:BSD-3-Clause
+// copyright-holders:Mirko Buffoni, James Wallace
+#pragma once
+
+#ifndef __OKIM6376_H__
+#define __OKIM6376_H__
+
+/* an interface for the OKIM6376 and similar chips (CPU interface only) */
+
+/* struct describing a single playing ADPCM voice */
+struct ADPCMVoice
+{
+ UINT8 playing; /* 1 if we are actively playing */
+
+ UINT32 base_offset; /* pointer to the base memory location */
+ UINT32 sample; /* current sample number */
+ UINT32 count; /* total samples to play */
+
+ UINT32 volume; /* output volume */
+ INT32 signal;
+ INT32 step;
+};
+
+class okim6376_device : public device_t,
+ public device_sound_interface
+{
+public:
+ okim6376_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~okim6376_device() {}
+
+ DECLARE_WRITE8_MEMBER( write );
+
+ DECLARE_WRITE_LINE_MEMBER( st_w );
+ DECLARE_WRITE_LINE_MEMBER( ch2_w );
+
+ DECLARE_READ_LINE_MEMBER( busy_r );
+ DECLARE_READ_LINE_MEMBER( nar_r );
+
+ void set_frequency(int frequency);
+
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ // internal state
+ required_region_ptr<UINT8> m_region_base; /* pointer to the base of the region */
+
+ #define OKIM6376_VOICES 2
+ struct ADPCMVoice m_voice[OKIM6376_VOICES];
+ INT32 m_command[OKIM6376_VOICES];
+ INT32 m_latch; /* Command data is held before transferring to either channel */
+ UINT8 m_stage[OKIM6376_VOICES];/* If a sample is playing, flag that we have a command staged */
+ sound_stream *m_stream; /* which stream are we playing on? */
+ UINT32 m_master_clock; /* master clock frequency */
+ UINT8 m_divisor; /* can be 8,10,16, and is read out of ROM data */
+ UINT8 m_channel;
+ UINT8 m_nar; /* Next Address Ready */
+ UINT8 m_nartimer;
+ UINT8 m_busy;
+ UINT8 m_ch2; /* 2CH pin - enables Channel 2 operation */
+ UINT8 m_st; /* STart */
+ UINT8 m_st_pulses; /* Keep track of attenuation */
+ UINT8 m_ch2_update; /* Pulse shape */
+ UINT8 m_st_update;
+
+ void oki_process(int channel, int command);
+ void generate_adpcm(struct ADPCMVoice *voice, INT16 *buffer, int samples,int channel);
+ void postload();
+ void okim6376_state_save_register();
+ void adpcm_state_save_register(struct ADPCMVoice *voice, int index);
+};
+
+extern const device_type OKIM6376;
+
+#endif /* __OKIM6376_H__ */
diff --git a/src/devices/sound/okim9810.c b/src/devices/sound/okim9810.c
new file mode 100644
index 00000000000..e134e646ddb
--- /dev/null
+++ b/src/devices/sound/okim9810.c
@@ -0,0 +1,585 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrew Gardner
+/***************************************************************************
+
+ okim9810.h
+
+ OKI MSM9810 ADPCM(2) sound chip.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "okim9810.h"
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+const device_type OKIM9810 = &device_creator<okim9810_device>;
+
+// volume lookup table. The manual lists a full 16 steps, 2dB per step.
+// Given the dB values, that seems to map to a 7-bit volume control.
+const UINT8 okim9810_device::okim_voice::s_volume_table[16] =
+{
+ 0x80, // 0 dB
+ 0x65, // -2 dB
+ 0x50, // -4 dB
+ 0x40, // -6 dB
+ 0x32, // -8.0 dB
+ 0x28, // -10.5 dB
+ 0x20, // -12.0 dB
+ 0x19, // -14.5 dB
+ 0x14, // -16.0 dB
+ 0x10, // -18.0 dB
+ 0x0c, // -20.0 dB
+ 0x0a, // -22.0 dB
+ 0x08, // -24.0 dB
+ 0x06, // -26.0 dB
+ 0x05, // -28.0 dB
+ 0x04, // -30.0 dB
+};
+
+// sampling frequency lookup table.
+const UINT32 okim9810_device::s_sampling_freq_table[16] =
+{
+ 4000,
+ 8000,
+ 16000,
+ 32000,
+ 0,
+ 6400,
+ 12800,
+ 25600,
+ 0,
+ 5300,
+ 10600,
+ 21200,
+ 0,
+ 0,
+ 0,
+ 0
+};
+
+// default address map
+static ADDRESS_MAP_START( okim9810, AS_0, 8, okim9810_device )
+ AM_RANGE(0x000000, 0xffffff) AM_ROM
+ADDRESS_MAP_END
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// okim9810_device - constructor
+//-------------------------------------------------
+
+okim9810_device::okim9810_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, OKIM9810, "OKI9810", tag, owner, clock, "okim9810", __FILE__),
+ device_sound_interface(mconfig, *this),
+ device_memory_interface(mconfig, *this),
+ m_space_config("samples", ENDIANNESS_BIG, 8, 24, 0, NULL, *ADDRESS_MAP_NAME(okim9810)),
+ m_stream(NULL),
+ m_TMP_register(0x00),
+ m_global_volume(0x00),
+ m_filter_type(OKIM9810_SECONDARY_FILTER),
+ m_output_level(OKIM9810_OUTPUT_TO_DIRECT_DAC)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void okim9810_device::device_start()
+{
+ // find our direct access
+ m_direct = &space().direct();
+
+ // create the stream
+ //int divisor = m_pin7 ? 132 : 165;
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, clock());
+
+ // save state stuff
+ save_item(NAME(m_TMP_register));
+ save_item(NAME(m_global_volume));
+ save_item(NAME(m_filter_type));
+ save_item(NAME(m_output_level));
+
+ for (int i = 0; i < OKIM9810_VOICES; i++)
+ {
+ okim_voice *voice = &m_voice[i];
+
+ save_item(NAME(voice->m_adpcm.m_signal), i);
+ save_item(NAME(voice->m_adpcm.m_step), i);
+ save_item(NAME(voice->m_adpcm2.m_signal), i);
+ save_item(NAME(voice->m_adpcm2.m_step), i);
+ save_item(NAME(voice->m_playbackAlgo), i);
+ save_item(NAME(voice->m_looping), i);
+ save_item(NAME(voice->m_startFlags), i);
+ save_item(NAME(voice->m_endFlags), i);
+ save_item(NAME(voice->m_base_offset), i);
+ save_item(NAME(voice->m_count), i);
+ save_item(NAME(voice->m_samplingFreq), i);
+ save_item(NAME(voice->m_playing), i);
+ save_item(NAME(voice->m_sample), i);
+ save_item(NAME(voice->m_channel_volume), i);
+ save_item(NAME(voice->m_pan_volume_left), i);
+ save_item(NAME(voice->m_pan_volume_right), i);
+ save_item(NAME(voice->m_startSample), i);
+ save_item(NAME(voice->m_endSample), i);
+ save_item(NAME(voice->m_interpSampleNum), i);
+ }
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void okim9810_device::device_reset()
+{
+ m_stream->update();
+ for (int voicenum = 0; voicenum < OKIM9810_VOICES; voicenum++)
+ m_voice[voicenum].m_playing = false;
+}
+
+
+//-------------------------------------------------
+// device_post_load - device-specific post-load
+//-------------------------------------------------
+
+void okim9810_device::device_post_load()
+{
+}
+
+
+//-------------------------------------------------
+// device_clock_changed - called if the clock
+// changes
+//-------------------------------------------------
+
+void okim9810_device::device_clock_changed()
+{
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *okim9810_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == 0) ? &m_space_config : NULL;
+}
+
+
+//-------------------------------------------------
+// stream_generate - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void okim9810_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ // reset the output streams
+ memset(outputs[0], 0, samples * sizeof(*outputs[0]));
+ memset(outputs[1], 0, samples * sizeof(*outputs[1]));
+
+ // iterate over voices and accumulate sample data
+ for (int voicenum = 0; voicenum < OKIM9810_VOICES; voicenum++)
+ m_voice[voicenum].generate_audio(*m_direct, outputs, samples, m_global_volume, clock(), m_filter_type);
+}
+
+
+//-------------------------------------------------
+// read_status - read the status register
+//-------------------------------------------------
+
+UINT8 okim9810_device::read_status()
+{
+ UINT8 result = 0x00;
+ UINT8 channelMask = 0x01;
+ for (int i = 0; i < OKIM9810_VOICES; i++, channelMask <<= 1)
+ {
+ if (!m_voice[i].m_playing)
+ result |= channelMask;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// read - memory interface for reading the active status
+//-------------------------------------------------
+
+READ8_MEMBER( okim9810_device::read )
+{
+ return read_status();
+}
+
+
+//-------------------------------------------------
+// write - memory interface for write
+//-------------------------------------------------
+
+// The command is written when the CMD pin is low
+void okim9810_device::write_command(UINT8 data)
+{
+ const UINT8 cmd = (data & 0xf8) >> 3;
+ const UINT8 channel = (data & 0x07);
+
+ switch(cmd)
+ {
+ case 0x00: // START
+ {
+ osd_printf_debug("START channel mask %02x\n", m_TMP_register);
+ UINT8 channelMask = 0x01;
+ for (int i = 0; i < OKIM9810_VOICES; i++, channelMask <<= 1)
+ {
+ if (channelMask & m_TMP_register)
+ {
+ m_voice[i].m_playing = true;
+ osd_printf_debug("\t\tPlaying channel %d: encoder type %d @ %dhz (volume = %d %d). From %08x for %d samples (looping=%d).\n",
+ i,
+ m_voice[i].m_playbackAlgo,
+ m_voice[i].m_samplingFreq,
+ m_voice[i].volume_scale(m_global_volume, m_voice[i].m_channel_volume, m_voice[i].m_pan_volume_left),
+ m_voice[i].volume_scale(m_global_volume, m_voice[i].m_channel_volume, m_voice[i].m_pan_volume_right),
+ m_voice[i].m_base_offset,
+ m_voice[i].m_count,
+ m_voice[i].m_looping);
+ }
+ }
+ break;
+ }
+ case 0x01: // STOP
+ {
+ osd_printf_debug("STOP channel mask %02x\n", m_TMP_register);
+ UINT8 channelMask = 0x01;
+ for (int i = 0; i < OKIM9810_VOICES; i++, channelMask <<= 1)
+ {
+ if (channelMask & m_TMP_register)
+ {
+ m_voice[i].m_playing = false;
+ osd_printf_debug("\tChannel %d stopping.\n", i);
+ }
+ }
+ break;
+ }
+ case 0x02: // LOOP
+ {
+ osd_printf_debug("LOOP channel mask %02x\n", m_TMP_register);
+ UINT8 channelMask = 0x01;
+ for (int i = 0; i < OKIM9810_VOICES; i++, channelMask <<= 1)
+ {
+ if (channelMask & m_TMP_register)
+ {
+ m_voice[i].m_looping = true;
+ osd_printf_debug("\tChannel %d looping.\n", i);
+ }
+ else
+ {
+ m_voice[i].m_looping = false;
+ osd_printf_debug("\tChannel %d done looping.\n", i);
+ }
+ }
+ break;
+ }
+ case 0x03: // OPT (options)
+ {
+ osd_printf_debug("OPT complex data %02x\n", m_TMP_register);
+ m_global_volume = (m_TMP_register & 0x18) >> 3;
+ m_filter_type = (m_TMP_register & 0x06) >> 1;
+ m_output_level = (m_TMP_register & 0x01);
+ osd_printf_debug("\tOPT setting main volume scale to Vdd/%d\n", m_global_volume+1);
+ osd_printf_debug("\tOPT setting output filter type to %d\n", m_filter_type);
+ osd_printf_debug("\tOPT setting output amp level to %d\n", m_output_level);
+ break;
+ }
+ case 0x04: // MUON (silence)
+ {
+ osd_printf_warning("MUON channel %d length %02x\n", channel, m_TMP_register);
+ osd_printf_warning("MSM9810: UNIMPLEMENTED COMMAND!\n");
+ break;
+ }
+
+ case 0x05: // FADR (phrase address)
+ {
+ const offs_t base = m_TMP_register * 8;
+
+ offs_t startAddr;
+ UINT8 startFlags = m_direct->read_byte(base + 0);
+ startAddr = m_direct->read_byte(base + 1) << 16;
+ startAddr |= m_direct->read_byte(base + 2) << 8;
+ startAddr |= m_direct->read_byte(base + 3) << 0;
+
+ offs_t endAddr;
+ UINT8 endFlags = m_direct->read_byte(base + 4);
+ endAddr = m_direct->read_byte(base + 5) << 16;
+ endAddr |= m_direct->read_byte(base + 6) << 8;
+ endAddr |= m_direct->read_byte(base + 7) << 0;
+
+ // Sub-table
+ if (startFlags & 0x80)
+ {
+ offs_t subTable = startAddr;
+ // TODO: New startFlags &= 0x80. Are there further subtables?
+ startFlags = m_direct->read_byte(subTable + 0);
+ startAddr = m_direct->read_byte(subTable + 1) << 16;
+ startAddr |= m_direct->read_byte(subTable + 2) << 8;
+ startAddr |= m_direct->read_byte(subTable + 3) << 0;
+
+ // TODO: What does byte (subTable + 4) refer to?
+ endAddr = m_direct->read_byte(subTable + 5) << 16;
+ endAddr |= m_direct->read_byte(subTable + 6) << 8;
+ endAddr |= m_direct->read_byte(subTable + 7) << 0;
+ }
+
+ m_voice[channel].m_sample = 0;
+ m_voice[channel].m_interpSampleNum = 0;
+ m_voice[channel].m_startFlags = startFlags;
+ m_voice[channel].m_base_offset = startAddr;
+ m_voice[channel].m_endFlags = endFlags;
+ m_voice[channel].m_count = (endAddr-startAddr) + 1; // Is there yet another extra byte at the end?
+
+ m_voice[channel].m_playbackAlgo = (startFlags & 0x30) >> 4;
+ m_voice[channel].m_samplingFreq = s_sampling_freq_table[startFlags & 0x0f];
+ if (m_voice[channel].m_playbackAlgo == OKIM9810_ADPCM_PLAYBACK ||
+ m_voice[channel].m_playbackAlgo == OKIM9810_ADPCM2_PLAYBACK)
+ m_voice[channel].m_count *= 2;
+ else
+ osd_printf_warning("MSM9810: UNIMPLEMENTED PLAYBACK METHOD %d\n", m_voice[channel].m_playbackAlgo);
+
+ osd_printf_debug("FADR channel %d phrase offset %02x => ", channel, m_TMP_register);
+ osd_printf_debug("startFlags(%02x) startAddr(%06x) endFlags(%02x) endAddr(%06x) bytes(%d)\n", startFlags, startAddr, endFlags, endAddr, endAddr-startAddr);
+ break;
+ }
+
+ case 0x06: // DADR (direct address playback)
+ {
+ osd_printf_warning("DADR channel %d complex data %02x\n", channel, m_TMP_register);
+ osd_printf_warning("MSM9810: UNIMPLEMENTED COMMAND!\n");
+ break;
+ }
+ case 0x07: // CVOL (channel volume)
+ {
+ osd_printf_debug("CVOL channel %d data %02x\n", channel, m_TMP_register);
+ osd_printf_debug("\tChannel %d -> volume index %d.\n", channel, m_TMP_register & 0x0f);
+
+ m_voice[channel].m_channel_volume = m_TMP_register & 0x0f;
+ break;
+ }
+ case 0x08: // PAN
+ {
+ const UINT8 leftVolIndex = (m_TMP_register & 0xf0) >> 4;
+ const UINT8 rightVolIndex = m_TMP_register & 0x0f;
+ osd_printf_debug("PAN channel %d left index: %02x right index: %02x (%02x)\n", channel, leftVolIndex, rightVolIndex, m_TMP_register);
+ osd_printf_debug("\tChannel %d left -> %d right -> %d\n", channel, leftVolIndex, rightVolIndex);
+ m_voice[channel].m_pan_volume_left = leftVolIndex;
+ m_voice[channel].m_pan_volume_right = rightVolIndex;
+ break;
+ }
+ default:
+ {
+ osd_printf_warning("MSM9810: UNKNOWN COMMAND!\n");
+ break;
+ }
+ }
+}
+
+WRITE8_MEMBER( okim9810_device::write )
+{
+ write_command(data);
+}
+
+
+//-----------------------------------------------------------
+// writeTMP - memory interface for writing the TMP register
+//-----------------------------------------------------------
+
+// TMP is written when the CMD pin is high
+void okim9810_device::write_TMP_register(UINT8 data)
+{
+ m_TMP_register = data;
+}
+
+WRITE8_MEMBER( okim9810_device::write_TMP_register )
+{
+ write_TMP_register(data);
+}
+
+
+//**************************************************************************
+// OKIM VOICE
+//**************************************************************************
+
+//-------------------------------------------------
+// okim_voice - constructor
+//-------------------------------------------------
+
+okim9810_device::okim_voice::okim_voice()
+ : m_playbackAlgo(OKIM9810_ADPCM2_PLAYBACK),
+ m_looping(false),
+ m_startFlags(0),
+ m_endFlags(0),
+ m_base_offset(0),
+ m_count(0),
+ m_samplingFreq(s_sampling_freq_table[2]),
+ m_playing(false),
+ m_sample(0),
+ m_channel_volume(0x00),
+ m_pan_volume_left(0x00),
+ m_pan_volume_right(0x00),
+ m_startSample(0),
+ m_endSample(0),
+ m_interpSampleNum(0)
+{
+}
+
+//-------------------------------------------------
+// generate_audio - generate audio samples and
+// add them to an output stream
+//-------------------------------------------------
+
+void okim9810_device::okim_voice::generate_audio(direct_read_data &direct,
+ stream_sample_t **buffers,
+ int samples,
+ const UINT8 global_volume,
+ const UINT32 clock,
+ const UINT8 filter_type)
+{
+ // skip if not active
+ if (!m_playing)
+ return;
+
+ // separate out left and right channels
+ stream_sample_t *outL = buffers[0];
+ stream_sample_t *outR = buffers[1];
+
+ // get left and right volumes
+ UINT8 volume_scale_left = volume_scale(global_volume, m_channel_volume, m_pan_volume_left);
+ UINT8 volume_scale_right = volume_scale(global_volume, m_channel_volume, m_pan_volume_right);
+
+ // total samples per byte
+ UINT32 totalInterpSamples = clock / m_samplingFreq;
+
+ // loop while we still have samples to generate
+ while (samples-- != 0)
+ {
+ // If interpSampleNum == 0, we are at the beginning of a new interp chunk, gather data
+ if (m_interpSampleNum == 0)
+ {
+ // If m_sample == 0, we have begun to play a new voice. Get both the first nibble & the second.
+ if (m_sample == 0)
+ {
+ // fetch the first sample nibble
+ int nibble0 = direct.read_byte(m_base_offset + m_sample / 2) >> (((m_sample & 1) << 2) ^ 4);
+ switch (m_playbackAlgo)
+ {
+ case OKIM9810_ADPCM_PLAYBACK:
+ {
+ m_adpcm.reset();
+ m_startSample = (INT32)m_adpcm.clock(nibble0);
+ break;
+ }
+ case OKIM9810_ADPCM2_PLAYBACK:
+ {
+ m_adpcm2.reset();
+ m_startSample = (INT32)m_adpcm2.clock(nibble0);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // Otherwise just move the second nibble back to the first spot.
+ m_startSample = m_endSample;
+ }
+
+ // And fetch the second sample nibble
+ int nibble1 = direct.read_byte(m_base_offset + (m_sample+1) / 2) >> ((((m_sample+1) & 1) << 2) ^ 4);
+ switch (m_playbackAlgo)
+ {
+ case OKIM9810_ADPCM_PLAYBACK:
+ {
+ m_endSample = (INT32)m_adpcm.clock(nibble1);
+ break;
+ }
+ case OKIM9810_ADPCM2_PLAYBACK:
+ {
+ m_endSample = (INT32)m_adpcm2.clock(nibble1);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ // TODO: Interpolate using proper numeric types.
+ float progress = (float)m_interpSampleNum / (float)totalInterpSamples;
+ INT32 interpValue = (INT32)((float)m_startSample + (((float)m_endSample-(float)m_startSample) * progress));
+
+ // if filtering is unwanted
+ if (filter_type != OKIM9810_SECONDARY_FILTER && filter_type != OKIM9810_PRIMARY_FILTER)
+ interpValue = m_startSample;
+
+ // output to the stereo buffers, scaling by the volume
+ // signal in range -2048..2047, volume in range 2..128 => signal * volume / 8 in range -32768..32767
+ INT32 interpValueL = (interpValue * (INT32)volume_scale_left) / 8;
+ *outL++ += interpValueL;
+
+ INT32 interpValueR = (interpValue * (INT32)volume_scale_right) / 8;
+ *outR++ += interpValueR;
+
+ // if the interpsample has reached its end, move on to the next sample
+ m_interpSampleNum++;
+ if (m_interpSampleNum >= totalInterpSamples)
+ {
+ m_interpSampleNum = 0;
+ m_sample++;
+ }
+
+ // the end of the stream has been reached
+ if (m_sample >= m_count)
+ {
+ if (!m_looping)
+ {
+ m_playing = false;
+ break;
+ }
+ else
+ {
+ m_sample = 0;
+ }
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// volume_scale - computes the volume equation as
+// seen on page 29 of the docs.
+// Returns a value from the volume lookup table.
+//-------------------------------------------------
+
+UINT8 okim9810_device::okim_voice::volume_scale(const UINT8 global_volume_index,
+ const UINT8 channel_volume_index,
+ const UINT8 pan_volume_index) const
+{
+ const UINT8& V = channel_volume_index;
+ const UINT8& L = pan_volume_index;
+ const UINT8& O = global_volume_index;
+ UINT32 index = (V+L) + (O*3);
+
+ if (index > 15)
+ index = 15;
+
+ return s_volume_table[index];
+}
diff --git a/src/devices/sound/okim9810.h b/src/devices/sound/okim9810.h
new file mode 100644
index 00000000000..6d3595c59b2
--- /dev/null
+++ b/src/devices/sound/okim9810.h
@@ -0,0 +1,163 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrew Gardner
+/***************************************************************************
+
+ okim9810.h
+
+ OKI MSM9810 ADPCM(2) sound chip.
+
+ Notes:
+ The master clock frequency for this chip can range from 3.5MHz to 4.5Mhz.
+ The typical oscillator is a 4.096Mhz crystal.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __OKIM9810_H__
+#define __OKIM9810_H__
+
+#include "okiadpcm.h"
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+enum
+{
+ OKIM9810_ADPCM_PLAYBACK = 0,
+ OKIM9810_ADPCM2_PLAYBACK = 1,
+ OKIM9810_STRAIGHT8_PLAYBACK = 2,
+ OKIM9810_NONLINEAR8_PLAYBACK = 3
+};
+
+enum
+{
+ OKIM9810_SECONDARY_FILTER = 0,
+ OKIM9810_PRIMARY_FILTER = 1,
+ OKIM9810_NO_FILTER = 2,
+ OKIM9810_NO_FILTER2 = 3
+};
+
+enum
+{
+ OKIM9810_OUTPUT_TO_DIRECT_DAC = 0,
+ OKIM9810_OUTPUT_TO_VOLTAGE_FOLLOWER = 1
+};
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_OKIM9810_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, OKIM9810, _clock)
+
+#define MCFG_OKIM9810_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, OKIM9810, _clock)
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+
+// ======================> okim9810_device
+
+class okim9810_device : public device_t,
+ public device_sound_interface,
+ public device_memory_interface
+{
+public:
+ // construction/destruction
+ okim9810_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ UINT8 read_status();
+ void write_TMP_register(UINT8 command);
+ void write_command(UINT8 command);
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+ DECLARE_WRITE8_MEMBER( write_TMP_register );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_post_load();
+ virtual void device_clock_changed();
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ // a single voice
+ class okim_voice
+ {
+ public:
+ okim_voice();
+ void generate_audio(direct_read_data &direct,
+ stream_sample_t **buffers,
+ int samples,
+ const UINT8 global_volume,
+ const UINT32 clock,
+ const UINT8 filter_type);
+
+ // computes volume scale from 3 volume numbers
+ UINT8 volume_scale(const UINT8 global_volume,
+ const UINT8 channel_volume,
+ const UINT8 pan_volume) const;
+
+ oki_adpcm_state m_adpcm; // current ADPCM state
+ oki_adpcm2_state m_adpcm2; // current ADPCM2 state
+ UINT8 m_playbackAlgo; // current playback method
+ bool m_looping;
+ UINT8 m_startFlags;
+ UINT8 m_endFlags;
+ offs_t m_base_offset; // pointer to the base memory location
+ UINT32 m_count; // total samples to play
+ UINT32 m_samplingFreq; // voice sampling frequency
+
+ bool m_playing; // playback state
+ UINT32 m_sample; // current sample number
+
+ UINT8 m_channel_volume; // volume index set with the CVOL command
+ UINT8 m_pan_volume_left; // volume index set with the PAN command
+ UINT8 m_pan_volume_right; // volume index set with the PAN command
+
+ INT32 m_startSample; // interpolation state - sample to interpolate from
+ INT32 m_endSample; // interpolation state - sample to interpolate to
+ UINT32 m_interpSampleNum; // interpolation state - fraction between start & end
+
+ static const UINT8 s_volume_table[16];
+ };
+
+ // internal state
+ const address_space_config m_space_config;
+
+ sound_stream* m_stream;
+ direct_read_data* m_direct;
+
+ UINT8 m_TMP_register;
+
+ UINT8 m_global_volume; // volume index set with the OPT command
+ UINT8 m_filter_type; // interpolation filter type set with the OPT command
+ UINT8 m_output_level; // flag stating if a voltage follower is connected
+
+ static const int OKIM9810_VOICES = 8;
+ okim_voice m_voice[OKIM9810_VOICES];
+
+ static const UINT32 s_sampling_freq_table[16];
+};
+
+
+// device type definition
+extern const device_type OKIM9810;
+
+
+
+#endif // __OKIM9810_H__
diff --git a/src/devices/sound/pci-ac97.c b/src/devices/sound/pci-ac97.c
new file mode 100644
index 00000000000..84018534e46
--- /dev/null
+++ b/src/devices/sound/pci-ac97.c
@@ -0,0 +1,36 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+#include "pci-ac97.h"
+
+const device_type AC97 = &device_creator<ac97_device>;
+
+DEVICE_ADDRESS_MAP_START(native_audio_mixer_map, 32, ac97_device)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(native_audio_bus_mastering_map, 32, ac97_device)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(mixer_map, 32, ac97_device)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(bus_mastering_map, 32, ac97_device)
+ADDRESS_MAP_END
+
+ac97_device::ac97_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : pci_device(mconfig, AC97, "AC97 audio", tag, owner, clock, "ac97", __FILE__)
+{
+}
+
+void ac97_device::device_start()
+{
+ pci_device::device_start();
+ add_map(256, M_IO, FUNC(ac97_device::native_audio_mixer_map));
+ add_map(64, M_IO, FUNC(ac97_device::native_audio_bus_mastering_map));
+ add_map(512, M_MEM, FUNC(ac97_device::mixer_map));
+ add_map(256, M_MEM, FUNC(ac97_device::bus_mastering_map));
+}
+
+void ac97_device::device_reset()
+{
+ pci_device::device_reset();
+}
diff --git a/src/devices/sound/pci-ac97.h b/src/devices/sound/pci-ac97.h
new file mode 100644
index 00000000000..56a3e4c3ace
--- /dev/null
+++ b/src/devices/sound/pci-ac97.h
@@ -0,0 +1,28 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+#ifndef PCI_AC97_H
+#define PCI_AC97_H
+
+#include "machine/pci.h"
+
+#define MCFG_AC97_ADD(_tag, _main_id, _revision, _subdevice_id) \
+ MCFG_PCI_DEVICE_ADD(_tag, AC97, _main_id, _revision, 0x040300, _subdevice_id)
+
+class ac97_device : public pci_device {
+public:
+ ac97_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ DECLARE_ADDRESS_MAP(native_audio_mixer_map, 32);
+ DECLARE_ADDRESS_MAP(native_audio_bus_mastering_map, 32);
+ DECLARE_ADDRESS_MAP(mixer_map, 32);
+ DECLARE_ADDRESS_MAP(bus_mastering_map, 32);
+};
+
+extern const device_type AC97;
+
+#endif
diff --git a/src/devices/sound/pokey.c b/src/devices/sound/pokey.c
new file mode 100644
index 00000000000..89279c16184
--- /dev/null
+++ b/src/devices/sound/pokey.c
@@ -0,0 +1,1453 @@
+// license:BSD-3-Clause
+// copyright-holders:Brad Oliver, Eric Smith, Juergen Buchmueller
+/*****************************************************************************
+ *
+ * POKEY chip emulator 4.6
+ *
+ * Based on original info found in Ron Fries' Pokey emulator,
+ * with additions by Brad Oliver, Eric Smith and Juergen Buchmueller,
+ * paddle (a/d conversion) details from the Atari 400/800 Hardware Manual.
+ * Polynome algorithms according to info supplied by Perry McFarlane.
+ *
+ * 4.6:
+ * [1] http://ploguechipsounds.blogspot.de/2009/10/how-i-recorded-and-decoded-pokeys.html
+ * - changed audio emulation to emulate borrow 3 clock delay and
+ * proper channel reset. New frequency only becomes effective
+ * after the counter hits 0. Emulation also treats counters
+ * as 8 bit counters which are linked now instead of monolytic
+ * 16 bit counters.
+ *
+ * 4.51:
+ * - changed to use the attotime datatype
+ * 4.5:
+ * - changed the 9/17 bit polynomial formulas such that the values
+ * required for the Tempest Pokey protection will be found.
+ * Tempest expects the upper 4 bits of the RNG to appear in the
+ * lower 4 bits after four cycles, so there has to be a shift
+ * of 1 per cycle (which was not the case before). Bits #6-#13 of the
+ * new RNG give this expected result now, bits #0-7 of the 9 bit poly.
+ * - reading the RNG returns the shift register contents ^ 0xff.
+ * That way resetting the Pokey with SKCTL (which resets the
+ * polynome shifters to 0) returns the expected 0xff value.
+ * 4.4:
+ * - reversed sample values to make OFF channels produce a zero signal.
+ * actually de-reversed them; don't remember that I reversed them ;-/
+ * 4.3:
+ * - for POT inputs returning zero, immediately assert the ALLPOT
+ * bit after POTGO is written, otherwise start trigger timer
+ * depending on SK_PADDLE mode, either 1-228 scanlines or 1-2
+ * scanlines, depending on the SK_PADDLE bit of SKCTL.
+ * 4.2:
+ * - half volume for channels which are inaudible (this should be
+ * close to the real thing).
+ * 4.1:
+ * - default gain increased to closely match the old code.
+ * - random numbers repeat rate depends on POLY9 flag too!
+ * - verified sound output with many, many Atari 800 games,
+ * including the SUPPRESS_INAUDIBLE optimizations.
+ * 4.0:
+ * - rewritten from scratch.
+ * - 16bit stream interface.
+ * - serout ready/complete delayed interrupts.
+ * - reworked pot analog/digital conversion timing.
+ * - optional non-indexing pokey update functions.
+ *
+ * TODO: liberatr clipping
+ *
+ *
+ *****************************************************************************/
+
+#include "emu.h"
+#include "pokey.h"
+
+#include "debugger.h"
+
+/* Four channels with a range of 0..32767 and volume 0..15 */
+//#define POKEY_DEFAULT_GAIN (32767/15/4)
+
+/*
+ * But we raise the gain and risk clipping, the old Pokey did
+ * this too. It defined POKEY_DEFAULT_GAIN 6 and this was
+ * 6 * 15 * 4 = 360, 360/256 = 1.40625
+ * I use 15/11 = 1.3636, so this is a little lower.
+ */
+
+#define POKEY_DEFAULT_GAIN (32767/11/4)
+
+#define VERBOSE 0
+#define VERBOSE_SOUND 0
+#define VERBOSE_TIMER 0
+#define VERBOSE_POLY 0
+#define VERBOSE_RAND 0
+
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+#define LOG_SOUND(x) do { if (VERBOSE_SOUND) logerror x; } while (0)
+
+#define LOG_TIMER(x) do { if (VERBOSE_TIMER) logerror x; } while (0)
+
+#define LOG_POLY(x) do { if (VERBOSE_POLY) logerror x; } while (0)
+
+#define LOG_RAND(x) do { if (VERBOSE_RAND) logerror x; } while (0)
+
+#define CHAN1 0
+#define CHAN2 1
+#define CHAN3 2
+#define CHAN4 3
+
+#define TIMER1 0
+#define TIMER2 1
+#define TIMER4 2
+
+/* values to add to the divisors for the different modes */
+#define DIVADD_LOCLK 1
+#define DIVADD_HICLK 4
+#define DIVADD_HICLK_JOINED 7
+
+/* AUDCx */
+#define NOTPOLY5 0x80 /* selects POLY5 or direct CLOCK */
+#define POLY4 0x40 /* selects POLY4 or POLY17 */
+#define PURE 0x20 /* selects POLY4/17 or PURE tone */
+#define VOLUME_ONLY 0x10 /* selects VOLUME OUTPUT ONLY */
+#define VOLUME_MASK 0x0f /* volume mask */
+
+/* AUDCTL */
+#define POLY9 0x80 /* selects POLY9 or POLY17 */
+#define CH1_HICLK 0x40 /* selects 1.78979 MHz for Ch 1 */
+#define CH3_HICLK 0x20 /* selects 1.78979 MHz for Ch 3 */
+#define CH12_JOINED 0x10 /* clocks channel 1 w/channel 2 */
+#define CH34_JOINED 0x08 /* clocks channel 3 w/channel 4 */
+#define CH1_FILTER 0x04 /* selects channel 1 high pass filter */
+#define CH2_FILTER 0x02 /* selects channel 2 high pass filter */
+#define CLK_15KHZ 0x01 /* selects 15.6999 kHz or 63.9211 kHz */
+
+/* IRQEN (D20E) */
+#define IRQ_BREAK 0x80 /* BREAK key pressed interrupt */
+#define IRQ_KEYBD 0x40 /* keyboard data ready interrupt */
+#define IRQ_SERIN 0x20 /* serial input data ready interrupt */
+#define IRQ_SEROR 0x10 /* serial output register ready interrupt */
+#define IRQ_SEROC 0x08 /* serial output complete interrupt */
+#define IRQ_TIMR4 0x04 /* timer channel #4 interrupt */
+#define IRQ_TIMR2 0x02 /* timer channel #2 interrupt */
+#define IRQ_TIMR1 0x01 /* timer channel #1 interrupt */
+
+/* SKSTAT (R/D20F) */
+#define SK_FRAME 0x80 /* serial framing error */
+#define SK_KBERR 0x40 /* keyboard overrun error - pokey documentation states *some bit as IRQST */
+#define SK_OVERRUN 0x20 /* serial overrun error - pokey documentation states *some bit as IRQST */
+#define SK_SERIN 0x10 /* serial input high */
+#define SK_SHIFT 0x08 /* shift key pressed */
+#define SK_KEYBD 0x04 /* keyboard key pressed */
+#define SK_SEROUT 0x02 /* serial output active */
+
+/* SKCTL (W/D20F) */
+#define SK_BREAK 0x80 /* serial out break signal */
+#define SK_BPS 0x70 /* bits per second */
+#define SK_FM 0x08 /* FM mode */
+#define SK_PADDLE 0x04 /* fast paddle a/d conversion */
+#define SK_RESET 0x03 /* reset serial/keyboard interface */
+#define SK_KEYSCAN 0x02 /* key scanning enabled ? */
+#define SK_DEBOUNCE 0x01 /* Debouncing ?*/
+
+#define DIV_64 28 /* divisor for 1.78979 MHz clock to 63.9211 kHz */
+#define DIV_15 114 /* divisor for 1.78979 MHz clock to 15.6999 kHz */
+
+#define P4(chip) chip->poly4[chip->p4]
+#define P5(chip) chip->poly5[chip->p5]
+#define P9(chip) chip->poly9[chip->p9]
+#define P17(chip) chip->poly17[chip->p17]
+
+#define CLK_1 0
+#define CLK_28 1
+#define CLK_114 2
+
+static const int clock_divisors[3] = {1, DIV_64, DIV_15};
+
+
+
+
+// device type definition
+const device_type POKEY = &device_creator<pokey_device>;
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// okim9810_device - constructor
+//-------------------------------------------------
+
+pokey_device::pokey_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, POKEY, "POKEY", tag, owner, clock, "pokey", __FILE__),
+ device_sound_interface(mconfig, *this),
+ device_execute_interface(mconfig, *this),
+ device_state_interface(mconfig, *this),
+ m_output_type(LEGACY_LINEAR),
+ m_icount(0),
+ m_stream(NULL),
+ m_pot0_r_cb(*this),
+ m_pot1_r_cb(*this),
+ m_pot2_r_cb(*this),
+ m_pot3_r_cb(*this),
+ m_pot4_r_cb(*this),
+ m_pot5_r_cb(*this),
+ m_pot6_r_cb(*this),
+ m_pot7_r_cb(*this),
+ m_allpot_r_cb(*this),
+ m_serin_r_cb(*this),
+ m_serout_w_cb(*this)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void pokey_device::device_start()
+{
+ //int sample_rate = clock();
+ int i;
+
+ m_clock_period = attotime::from_hz(clock());
+
+ /* Setup channels */
+ for (i=0; i<POKEY_CHANNELS; i++)
+ {
+ m_channel[i].m_parent = this;
+ m_channel[i].m_INTMask = 0;
+ }
+ m_channel[CHAN1].m_INTMask = IRQ_TIMR1;
+ m_channel[CHAN2].m_INTMask = IRQ_TIMR2;
+ m_channel[CHAN4].m_INTMask = IRQ_TIMR4;
+
+ // bind callbacks
+ m_keyboard_r.bind_relative_to(*owner());
+ m_irq_f.bind_relative_to(*owner());
+
+ /* calculate the A/D times
+ * In normal, slow mode (SKCTL bit SK_PADDLE is clear) the conversion
+ * takes N scanlines, where N is the paddle value. A single scanline
+ * takes approximately 64us to finish (1.78979MHz clock).
+ * In quick mode (SK_PADDLE set) the conversion is done very fast
+ * (takes two scanlines) but the result is not as accurate.
+ */
+
+ /* initialize the poly counters */
+ poly_init_4_5(m_poly4, 4, 1, 0);
+ poly_init_4_5(m_poly5, 5, 2, 1);
+
+ /* initialize 9 / 17 arrays */
+ poly_init_9_17(m_poly9, 9);
+ poly_init_9_17(m_poly17, 17);
+ vol_init();
+
+ /* The pokey does not have a reset line. These should be initialized
+ * with random values.
+ */
+
+ m_KBCODE = 0x09; /* Atari 800 'no key' */
+ m_SKCTL = SK_RESET; /* let the RNG run after reset */
+ m_SKSTAT = 0;
+ /* This bit should probably get set later. Acid5200 pokey_setoc test tests this. */
+ m_IRQST = IRQ_SEROC;
+ m_IRQEN = 0;
+ m_AUDCTL = 0;
+ m_p4 = 0;
+ m_p5 = 0;
+ m_p9 = 0;
+ m_p17 = 0;
+ m_ALLPOT = 0x00;
+
+ m_pot_counter = 0;
+ m_kbd_cnt = 0;
+ m_out_filter = 0;
+ m_output = 0;
+ m_kbd_state = 0;
+
+ /* reset more internal state */
+ for (i=0; i<3; i++)
+ {
+ m_clock_cnt[i] = 0;
+ }
+
+ for (i=0; i<8; i++)
+ {
+ m_POTx[i] = 0;
+ }
+
+ m_pot0_r_cb.resolve();
+ m_pot1_r_cb.resolve();
+ m_pot2_r_cb.resolve();
+ m_pot3_r_cb.resolve();
+ m_pot4_r_cb.resolve();
+ m_pot5_r_cb.resolve();
+ m_pot6_r_cb.resolve();
+ m_pot7_r_cb.resolve();
+ m_allpot_r_cb.resolve();
+ m_serin_r_cb.resolve();
+ m_serout_w_cb.resolve_safe();
+
+ m_stream = stream_alloc(0, 1, clock());
+
+ timer_alloc(SYNC_WRITE); /* timer for sync operation */
+ timer_alloc(SYNC_NOOP);
+ timer_alloc(SYNC_POT);
+ timer_alloc(SYNC_SET_IRQST);
+
+ for (i=0; i<POKEY_CHANNELS; i++)
+ {
+ save_item(NAME(m_channel[i].m_borrow_cnt), i);
+ save_item(NAME(m_channel[i].m_counter), i);
+ save_item(NAME(m_channel[i].m_filter_sample), i);
+ save_item(NAME(m_channel[i].m_output), i);
+ save_item(NAME(m_channel[i].m_AUDF), i);
+ save_item(NAME(m_channel[i].m_AUDC), i);
+ }
+
+ save_item(NAME(m_divisor));
+ save_item(NAME(m_clock_cnt));
+ save_item(NAME(m_p4));
+ save_item(NAME(m_p5));
+ save_item(NAME(m_p9));
+ save_item(NAME(m_p17));
+ save_item(NAME(m_pot_counter));
+ save_item(NAME(m_kbd_cnt));
+ save_item(NAME(m_kbd_latch));
+ save_item(NAME(m_kbd_state));
+
+ save_item(NAME(m_POTx));
+ save_item(NAME(m_AUDCTL));
+ save_item(NAME(m_ALLPOT));
+ save_item(NAME(m_KBCODE));
+ save_item(NAME(m_SERIN));
+ save_item(NAME(m_SEROUT));
+ save_item(NAME(m_IRQST));
+ save_item(NAME(m_IRQEN));
+ save_item(NAME(m_SKSTAT));
+ save_item(NAME(m_SKCTL));
+
+ // State support
+
+ state_add(AUDF1_C, "AUDF1", m_channel[0].m_AUDF);
+ state_add(AUDC1_C, "AUDC1", m_channel[0].m_AUDC);
+ state_add(AUDF2_C, "AUDF2", m_channel[1].m_AUDF);
+ state_add(AUDC2_C, "AUDC2", m_channel[1].m_AUDC);
+ state_add(AUDF3_C, "AUDF3", m_channel[2].m_AUDF);
+ state_add(AUDC3_C, "AUDC3", m_channel[2].m_AUDC);
+ state_add(AUDF4_C, "AUDF4", m_channel[3].m_AUDF);
+ state_add(AUDC4_C, "AUDC4", m_channel[3].m_AUDC);
+ state_add(AUDCTL_C, "AUDCTL", m_AUDCTL);
+#if 0
+ state_add(STIMER_C, "STIMER", m_STIMER);
+ state_add(SKREST_C, "SKREST_C", m_SKREST);
+ state_add(POTGO_C, "POTGO", m_POTGO_C);
+#endif
+ state_add(SEROUT_C, "SEROUT", m_SEROUT);
+ state_add(IRQEN_C, "IRQEN", m_IRQEN);
+ state_add(SKCTL_C, "SKCTL", m_SKCTL);
+
+ // set our instruction counter
+ m_icountptr = &m_icount;
+
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void pokey_device::device_reset()
+{
+ m_stream->update();
+}
+
+
+//-------------------------------------------------
+// device_post_load - device-specific post-load
+//-------------------------------------------------
+
+void pokey_device::device_post_load()
+{
+}
+
+
+//-------------------------------------------------
+// device_clock_changed - called if the clock
+// changes
+//-------------------------------------------------
+
+void pokey_device::device_clock_changed()
+{
+}
+
+//-------------------------------------------------
+// stream_generate - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void pokey_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case 3:
+ /* serout_ready_cb */
+ if (m_IRQEN & IRQ_SEROR)
+ {
+ m_IRQST |= IRQ_SEROR;
+ if (!m_irq_f.isnull())
+ m_irq_f(IRQ_SEROR);
+ }
+ break;
+ case 4:
+ /* serout_complete */
+ if (m_IRQEN & IRQ_SEROC)
+ {
+ m_IRQST |= IRQ_SEROC;
+ if (!m_irq_f.isnull())
+ m_irq_f(IRQ_SEROC);
+ }
+ break;
+ case 5:
+ /* serin_ready */
+ if (m_IRQEN & IRQ_SERIN)
+ {
+ m_IRQST |= IRQ_SERIN;
+ if (!m_irq_f.isnull())
+ m_irq_f(IRQ_SERIN);
+ }
+ break;
+ case SYNC_WRITE:
+ {
+ offs_t offset = (param >> 8) & 0xff;
+ UINT8 data = param & 0xff;
+ write_internal(offset, data);
+ }
+ break;
+ case SYNC_NOOP:
+ /* do nothing, caused by a forced resync */
+ break;
+ case SYNC_POT:
+ //logerror("x %02x \n", (param & 0x20));
+ m_ALLPOT |= (param & 0xff);
+ break;
+ case SYNC_SET_IRQST:
+ m_IRQST |= (param & 0xff);
+ break;
+ default:
+ assert_always(FALSE, "Unknown id in pokey_device::device_timer");
+ }
+}
+
+void pokey_device::execute_run()
+{
+ bool check_debugger = ((device_t::machine().debug_flags & DEBUG_FLAG_ENABLED) != 0);
+
+ do
+ {
+ // debugging
+ //m_ppc = m_pc; // copy PC to previous PC
+ if (check_debugger)
+ debugger_instruction_hook(this, 0); //m_pc);
+
+ // instruction fetch
+ //UINT16 op = opcode_read();
+
+ UINT32 new_out = step_one_clock();
+ if (m_output != new_out)
+ {
+ //printf("forced update %08d %08x\n", m_icount, m_output);
+ m_stream->update();
+ m_output = new_out;
+ }
+
+ m_icount--;
+ } while (m_icount > 0);
+
+}
+
+
+//-------------------------------------------------
+// step_one_clock - step the whole chip one
+// clock cycle.
+//-------------------------------------------------
+
+void pokey_device::step_keyboard()
+{
+ if (++m_kbd_cnt > 63)
+ m_kbd_cnt = 0;
+ if (!m_keyboard_r.isnull())
+ {
+ UINT8 ret = m_keyboard_r(m_kbd_cnt);
+
+ switch (m_kbd_cnt)
+ {
+ case POK_KEY_BREAK:
+ if (ret & 2)
+ {
+ /* check if the break IRQ is enabled */
+ if (m_IRQEN & IRQ_BREAK)
+ {
+ m_IRQST |= IRQ_BREAK;
+ if (!m_irq_f.isnull())
+ m_irq_f(IRQ_BREAK);
+ }
+ }
+ break;
+ case POK_KEY_SHIFT:
+ m_kbd_latch = (m_kbd_latch & 0xbf) | ((ret & 2) << 5);
+ if (m_kbd_latch & 0x40)
+ m_SKSTAT |= SK_SHIFT;
+ else
+ m_SKSTAT &= ~SK_SHIFT;
+ /* FIXME: sync ? */
+ break;
+ case POK_KEY_CTRL:
+ m_kbd_latch = (m_kbd_latch & 0x7f) | ((ret & 2) << 6);
+ break;
+ }
+ switch (m_kbd_state)
+ {
+ case 0: /* waiting for key */
+ if (ret & 1)
+ {
+ m_kbd_latch = (m_kbd_latch & 0xc0) | m_kbd_cnt;
+ m_kbd_state++;
+ }
+ break;
+ case 1: /* waiting for key confirmation */
+ if ((m_kbd_latch & 0x3f) == m_kbd_cnt)
+ {
+ if (ret & 1)
+ {
+ m_KBCODE = m_kbd_latch;
+ m_SKSTAT |= SK_KEYBD;
+ if (m_IRQEN & IRQ_KEYBD)
+ {
+ /* last interrupt not acknowledged ? */
+ if(m_IRQST & IRQ_KEYBD)
+ m_SKSTAT |= SK_KBERR;
+ m_IRQST |= IRQ_KEYBD;
+ if (!m_irq_f.isnull())
+ m_irq_f(IRQ_KEYBD);
+ }
+ m_kbd_state++;
+ }
+ else
+ m_kbd_state = 0;
+ }
+ break;
+ case 2: /* waiting for release */
+ if ((m_kbd_latch & 0x3f) == m_kbd_cnt)
+ {
+ if ((ret & 1)==0)
+ m_kbd_state++;
+ else
+ m_SKSTAT |= SK_KEYBD;
+ }
+ break;
+ case 3:
+ if ((m_kbd_latch & 0x3f) == m_kbd_cnt)
+ {
+ if (ret & 1)
+ m_kbd_state = 2;
+ else
+ {
+ m_SKSTAT &= ~SK_KEYBD;
+ m_kbd_state = 0;
+ }
+ }
+ break;
+ }
+ }
+}
+
+void pokey_device::step_pot()
+{
+ int pot;
+ UINT8 upd = 0;
+ m_pot_counter++;
+ for (pot = 0; pot < 8; pot++)
+ {
+ if ((m_POTx[pot]<m_pot_counter) || (m_pot_counter == 228))
+ {
+ upd |= (1<<pot);
+ /* latching is emulated in read */
+ }
+ }
+ synchronize(SYNC_POT, upd);
+}
+
+/*
+ * http://www.atariage.com/forums/topic/3328-sio-protocol/page__st__100#entry1680190:
+ * I noticed that the Pokey counters have clocked carry (actually, "borrow") positions that delay the
+ * counter by 3 cycles, plus the 1 reset clock. So 16 bit mode has 6 carry delays and a reset clock.
+ * I'm sure this was done because the propagation delays limited the number of cells the subtraction could ripple though.
+ *
+ */
+
+UINT32 pokey_device::step_one_clock(void)
+{
+ int ch, clk;
+ UINT32 sum = 0;
+ int clock_triggered[3] = {0,0,0};
+ int base_clock = (m_AUDCTL & CLK_15KHZ) ? CLK_114 : CLK_28;
+
+ if( (m_SKCTL & SK_RESET) )
+ {
+ /* Clocks only count if we are not in a reset */
+ for (clk = 0; clk < 3; clk++)
+ {
+ m_clock_cnt[clk]++;
+ if (m_clock_cnt[clk] >= clock_divisors[clk])
+ {
+ m_clock_cnt[clk] = 0;
+ clock_triggered[clk] = 1;
+ }
+ }
+
+ m_p4 = (m_p4 + 1) % 0x0000f;
+ m_p5 = (m_p5 + 1) % 0x0001f;
+ m_p9 = (m_p9 + 1) % 0x001ff;
+ m_p17 = (m_p17 + 1 ) % 0x1ffff;
+
+ clk = (m_AUDCTL & CH1_HICLK) ? CLK_1 : base_clock;
+ if (clock_triggered[clk])
+ m_channel[CHAN1].inc_chan();
+
+ clk = (m_AUDCTL & CH3_HICLK) ? CLK_1 : base_clock;
+ if (clock_triggered[clk])
+ m_channel[CHAN3].inc_chan();
+
+ if (clock_triggered[base_clock])
+ {
+ if (!(m_AUDCTL & CH12_JOINED))
+ m_channel[CHAN2].inc_chan();
+ if (!(m_AUDCTL & CH34_JOINED))
+ m_channel[CHAN4].inc_chan();
+ }
+
+ /* Potentiometer handling */
+ if ((clock_triggered[CLK_114] || (m_SKCTL & SK_PADDLE)) && (m_pot_counter < 228))
+ step_pot();
+
+ /* Keyboard */
+ if (clock_triggered[CLK_114] && (m_SKCTL & SK_KEYSCAN))
+ step_keyboard();
+ }
+
+ /* do CHAN2 before CHAN1 because CHAN1 may set borrow! */
+ if (m_channel[CHAN2].check_borrow())
+ {
+ int isJoined = (m_AUDCTL & CH12_JOINED);
+ if (isJoined)
+ m_channel[CHAN1].reset_channel();
+ m_channel[CHAN2].reset_channel();
+ process_channel(CHAN2);
+
+ /* check if some of the requested timer interrupts are enabled */
+ if ((m_IRQST & IRQ_TIMR2) && !m_irq_f.isnull())
+ m_irq_f(IRQ_TIMR2);
+ }
+
+ if (m_channel[CHAN1].check_borrow())
+ {
+ int isJoined = (m_AUDCTL & CH12_JOINED);
+ if (isJoined)
+ m_channel[CHAN2].inc_chan();
+ else
+ m_channel[CHAN1].reset_channel();
+ process_channel(CHAN1);
+ /* check if some of the requested timer interrupts are enabled */
+ if ((m_IRQST & IRQ_TIMR1) && !m_irq_f.isnull())
+ m_irq_f(IRQ_TIMR1);
+ }
+
+ /* do CHAN4 before CHAN3 because CHAN3 may set borrow! */
+ if (m_channel[CHAN4].check_borrow())
+ {
+ int isJoined = (m_AUDCTL & CH34_JOINED);
+ if (isJoined)
+ m_channel[CHAN3].reset_channel();
+ m_channel[CHAN4].reset_channel();
+ process_channel(CHAN4);
+ /* is this a filtering channel (3/4) and is the filter active? */
+ if (m_AUDCTL & CH2_FILTER)
+ m_channel[CHAN2].sample();
+ else
+ m_channel[CHAN2].m_filter_sample = 1;
+ if ((m_IRQST & IRQ_TIMR4) && !m_irq_f.isnull())
+ m_irq_f(IRQ_TIMR4);
+ }
+
+ if (m_channel[CHAN3].check_borrow())
+ {
+ int isJoined = (m_AUDCTL & CH34_JOINED);
+ if (isJoined)
+ m_channel[CHAN4].inc_chan();
+ else
+ m_channel[CHAN3].reset_channel();
+ process_channel(CHAN3);
+ /* is this a filtering channel (3/4) and is the filter active? */
+ if (m_AUDCTL & CH1_FILTER)
+ m_channel[CHAN1].sample();
+ else
+ m_channel[CHAN1].m_filter_sample = 1;
+ }
+
+ for (ch = 0; ch < 4; ch++)
+ {
+ sum |= (((((m_channel[ch].m_output ^ m_channel[ch].m_filter_sample) || (m_channel[ch].m_AUDC & VOLUME_ONLY)) ? (m_channel[ch].m_AUDC & VOLUME_MASK) : 0 )) << (ch * 4));
+ }
+ return sum;
+}
+
+//-------------------------------------------------
+// stream_generate - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+
+void pokey_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *buffer = outputs[0];
+
+ if (m_output_type == LEGACY_LINEAR)
+ {
+ INT32 out = 0;
+ for (int i = 0; i < 4; i++)
+ out += ((m_output >> (4*i)) & 0x0f);
+ out *= POKEY_DEFAULT_GAIN;
+ out = (out > 0x7fff) ? 0x7fff : out;
+ while( samples > 0 )
+ {
+ *buffer++ = out;
+ samples--;
+ }
+ }
+ else if (m_output_type == RC_LOWPASS)
+ {
+ double rTot = m_voltab[m_output];
+
+ double V0 = rTot / (rTot+m_r_pullup) * m_v_ref / 5.0 * 32767.0;
+ double mult = (m_cap == 0.0) ? 1.0 : 1.0 - exp(-(rTot + m_r_pullup) / (m_cap * m_r_pullup * rTot) * m_clock_period.as_double());
+
+ while( samples > 0 )
+ {
+ /* store sum of output signals into the buffer */
+ m_out_filter += (V0 - m_out_filter) * mult;
+ *buffer++ = m_out_filter;
+ samples--;
+
+ }
+ }
+ else if (m_output_type == OPAMP_C_TO_GROUND)
+ {
+ double rTot = m_voltab[m_output];
+ /* In this configuration there is a capacitor in parallel to the pokey output to ground.
+ * With a LM324 in LTSpice this causes the opamp circuit to oscillate at around 100 kHz.
+ * We are ignoring the capacitor here, since this oscillation would not be audible.
+ */
+
+ /* This post-pokey stage usually has a high-pass filter behind it
+ * It is approximated by eliminating m_v_ref ( -1.0 term)
+ */
+
+ double V0 = ((rTot+m_r_pullup) / rTot - 1.0) * m_v_ref / 5.0 * 32767.0;
+
+ while( samples > 0 )
+ {
+ /* store sum of output signals into the buffer */
+ *buffer++ = V0;
+ samples--;
+
+ }
+ }
+ else if (m_output_type == OPAMP_LOW_PASS)
+ {
+ double rTot = m_voltab[m_output];
+ /* This post-pokey stage usually has a low-pass filter behind it
+ * It is approximated by not adding in VRef below.
+ */
+
+ double V0 = (m_r_pullup / rTot) * m_v_ref / 5.0 * 32767.0;
+ double mult = (m_cap == 0.0) ? 1.0 : 1.0 - exp(-1.0 / (m_cap * m_r_pullup) * m_clock_period.as_double());
+
+ while( samples > 0 )
+ {
+ /* store sum of output signals into the buffer */
+ m_out_filter += (V0 - m_out_filter) * mult;
+ *buffer++ = m_out_filter /* + m_v_ref */; // see above
+ samples--;
+ }
+ }
+ else if (m_output_type == DISCRETE_VAR_R)
+ {
+ INT32 out = m_voltab[m_output];
+ while( samples > 0 )
+ {
+ *buffer++ = out;
+ samples--;
+ }
+ }
+}
+
+//-------------------------------------------------
+// read - memory interface for reading the active status
+//-------------------------------------------------
+
+READ8_MEMBER( pokey_device::read )
+{
+ return read(offset);
+}
+
+UINT8 pokey_device::read(offs_t offset)
+{
+ int data = 0, pot;
+
+ synchronize(SYNC_NOOP); /* force resync */
+
+ switch (offset & 15)
+ {
+ case POT0_C: case POT1_C: case POT2_C: case POT3_C:
+ case POT4_C: case POT5_C: case POT6_C: case POT7_C:
+ pot = offset & 7;
+ if( m_ALLPOT & (1 << pot) )
+ {
+ /* we have a value measured */
+ data = m_POTx[pot];
+ LOG(("POKEY '%s' read POT%d (final value) $%02x\n", tag(), pot, data));
+ }
+ else
+ {
+ data = m_pot_counter;
+ LOG(("POKEY '%s' read POT%d (interpolated) $%02x\n", tag(), pot, data));
+ }
+ break;
+
+ case ALLPOT_C:
+ /****************************************************************
+ * If the 2 least significant bits of SKCTL are 0, the ALLPOTs
+ * are disabled (SKRESET). Thanks to MikeJ for pointing this out.
+ ****************************************************************/
+ if( (m_SKCTL & SK_RESET) == 0)
+ {
+ data = 0;
+ LOG(("POKEY '%s' ALLPOT internal $%02x (reset)\n", tag(), data));
+ }
+ else if( !m_allpot_r_cb.isnull() )
+ {
+ data = m_allpot_r_cb(offset);
+ LOG(("%s: POKEY '%s' ALLPOT callback $%02x\n", machine().describe_context(), tag(), data));
+ }
+ else
+ {
+ data = m_ALLPOT ^ 0xff;
+ LOG(("POKEY '%s' ALLPOT internal $%02x\n", tag(), data));
+ }
+ break;
+
+ case KBCODE_C:
+ data = m_KBCODE;
+ break;
+
+ case RANDOM_C:
+ if( m_AUDCTL & POLY9 )
+ {
+ data = m_poly9[m_p9] & 0xff;
+ LOG_RAND(("POKEY '%s' rand9[$%05x]: $%02x\n", tag(), m_p9, data));
+ }
+ else
+ {
+ data = (m_poly17[m_p17] >> 8) & 0xff;
+ LOG_RAND(("POKEY '%s' rand17[$%05x]: $%02x\n", tag(), m_p17, data));
+ }
+ break;
+
+ case SERIN_C:
+ if( !m_serin_r_cb.isnull() )
+ m_SERIN = m_serin_r_cb(offset);
+ data = m_SERIN;
+ LOG(("POKEY '%s' SERIN $%02x\n", tag(), data));
+ break;
+
+ case IRQST_C:
+ /* IRQST is an active low input port; we keep it active high */
+ /* internally to ease the (un-)masking of bits */
+ data = m_IRQST ^ 0xff;
+ LOG(("POKEY '%s' IRQST $%02x\n", tag(), data));
+ break;
+
+ case SKSTAT_C:
+ /* SKSTAT is also an active low input port */
+ data = m_SKSTAT ^ 0xff;
+ LOG(("POKEY '%s' SKSTAT $%02x\n", tag(), data));
+ break;
+
+ default:
+ LOG(("POKEY '%s' register $%02x\n", tag(), offset));
+ data = 0xff;
+ break;
+ }
+ return data;
+
+}
+
+
+//-------------------------------------------------
+// write - memory interface for write
+//-------------------------------------------------
+
+void pokey_device::write(offs_t offset, UINT8 data)
+{
+ synchronize(SYNC_WRITE, (offset<<8) | data);
+}
+
+WRITE8_MEMBER( pokey_device::write )
+{
+ write(offset, data);
+}
+
+void pokey_device::write_internal(offs_t offset, UINT8 data)
+{
+ /* determine which address was changed */
+ switch (offset & 15)
+ {
+ case AUDF1_C:
+ LOG_SOUND(("POKEY '%s' AUDF1 $%02x\n", tag(), data));
+ m_channel[CHAN1].m_AUDF = data;
+ break;
+
+ case AUDC1_C:
+ LOG_SOUND(("POKEY '%s' AUDC1 $%02x (%s)\n", tag(), data, audc2str(data)));
+ m_channel[CHAN1].m_AUDC = data;
+ break;
+
+ case AUDF2_C:
+ LOG_SOUND(("POKEY '%s' AUDF2 $%02x\n", tag(), data));
+ m_channel[CHAN2].m_AUDF = data;
+ break;
+
+ case AUDC2_C:
+ LOG_SOUND(("POKEY '%s' AUDC2 $%02x (%s)\n", tag(), data, audc2str(data)));
+ m_channel[CHAN2].m_AUDC = data;
+ break;
+
+ case AUDF3_C:
+ LOG_SOUND(("POKEY '%s' AUDF3 $%02x\n", tag(), data));
+ m_channel[CHAN3].m_AUDF = data;
+ break;
+
+ case AUDC3_C:
+ LOG_SOUND(("POKEY '%s' AUDC3 $%02x (%s)\n", tag(), data, audc2str(data)));
+ m_channel[CHAN3].m_AUDC = data;
+ break;
+
+ case AUDF4_C:
+ LOG_SOUND(("POKEY '%s' AUDF4 $%02x\n", tag(), data));
+ m_channel[CHAN4].m_AUDF = data;
+ break;
+
+ case AUDC4_C:
+ LOG_SOUND(("POKEY '%s' AUDC4 $%02x (%s)\n", tag(), data, audc2str(data)));
+ m_channel[CHAN4].m_AUDC = data;
+ break;
+
+ case AUDCTL_C:
+ if( data == m_AUDCTL )
+ return;
+ LOG_SOUND(("POKEY '%s' AUDCTL $%02x (%s)\n", tag(), data, audctl2str(data)));
+ m_AUDCTL = data;
+
+ break;
+
+ case STIMER_C:
+ LOG_TIMER(("POKEY '%s' STIMER $%02x\n", tag(), data));
+
+ /* From the pokey documentation:
+ * reset all counters to zero (side effect)
+ * Actually this takes 4 cycles to actually happen.
+ * FIXME: Use timer for delayed reset !
+ */
+ for (int i = 0; i < POKEY_CHANNELS; i++)
+ {
+ m_channel[i].reset_channel();
+ m_channel[i].m_output = 0;
+ m_channel[i].m_filter_sample = (i<2 ? 1 : 0);
+ }
+
+ break;
+
+ case SKREST_C:
+ /* reset SKSTAT */
+ LOG(("POKEY '%s' SKREST $%02x\n", tag(), data));
+ m_SKSTAT &= ~(SK_FRAME|SK_OVERRUN|SK_KBERR);
+ break;
+
+ case POTGO_C:
+ LOG(("POKEY '%s' POTGO $%02x\n", tag(), data));
+ pokey_potgo();
+ break;
+
+ case SEROUT_C:
+ LOG(("POKEY '%s' SEROUT $%02x\n", tag(), data));
+ m_serout_w_cb(offset, data);
+ m_SKSTAT |= SK_SEROUT;
+ /*
+ * These are arbitrary values, tested with some custom boot
+ * loaders from Ballblazer and Escape from Fractalus
+ * The real times are unknown
+ */
+ timer_set(attotime::from_usec(200), 3);
+ /* 10 bits (assumption 1 start, 8 data and 1 stop bit) take how long? */
+ timer_set(attotime::from_usec(2000), 4);// FUNC(pokey_serout_complete), 0, p);
+ break;
+
+ case IRQEN_C:
+ LOG(("POKEY '%s' IRQEN $%02x\n", tag(), data));
+
+ /* acknowledge one or more IRQST bits ? */
+ if( m_IRQST & ~data )
+ {
+ /* reset IRQST bits that are masked now, except the SEROC bit (acid5200 pokey_seroc test) */
+ m_IRQST &= (IRQ_SEROC | data);
+ }
+ /* store irq enable */
+ m_IRQEN = data;
+ /* if SEROC irq is enabled trigger an irq (acid5200 pokey_seroc test) */
+ if (m_IRQEN & m_IRQST & IRQ_SEROC)
+ {
+ if (!m_irq_f.isnull())
+ m_irq_f(IRQ_SEROC);
+ }
+ break;
+
+ case SKCTL_C:
+ if( data == m_SKCTL )
+ return;
+ LOG(("POKEY '%s' SKCTL $%02x\n", tag(), data));
+ m_SKCTL = data;
+ if( !(data & SK_RESET) )
+ {
+ write_internal(IRQEN_C, 0);
+ write_internal(SKREST_C, 0);
+ /****************************************************************
+ * If the 2 least significant bits of SKCTL are 0, the random
+ * number generator is disabled (SKRESET). Thanks to Eric Smith
+ * for pointing out this critical bit of info!
+ * Couriersud: Actually, the 17bit poly is reset and kept in a
+ * reset state.
+ ****************************************************************/
+ m_p9 = 0;
+ m_p17 = 0;
+ m_p4 = 0;
+ m_p5 = 0;
+ m_clock_cnt[0] = 0;
+ m_clock_cnt[1] = 0;
+ m_clock_cnt[2] = 0;
+ /* FIXME: Serial port reset ! */
+ }
+ break;
+ }
+
+ /************************************************************
+ * As defined in the manual, the exact counter values are
+ * different depending on the frequency and resolution:
+ * 64 kHz or 15 kHz - AUDF + 1
+ * 1.79 MHz, 8-bit - AUDF + 4
+ * 1.79 MHz, 16-bit - AUDF[CHAN1]+256*AUDF[CHAN2] + 7
+ ************************************************************/
+
+}
+
+WRITE_LINE_MEMBER( pokey_device::sid_w )
+{
+ if (state)
+ {
+ m_SKSTAT |= SK_SERIN;
+ }
+ else
+ {
+ m_SKSTAT &= ~SK_SERIN;
+ }
+}
+
+void pokey_device::serin_ready(int after)
+{
+ timer_set(m_clock_period * after, 5, 0);
+}
+
+//-------------------------------------------------
+// private stuff
+//-------------------------------------------------
+
+inline void pokey_device::process_channel(int ch)
+{
+ if ((m_channel[ch].m_AUDC & NOTPOLY5) || (m_poly5[m_p5] & 1))
+ {
+ if (m_channel[ch].m_AUDC & PURE)
+ m_channel[ch].m_output ^= 1;
+ else if (m_channel[ch].m_AUDC & POLY4)
+ m_channel[ch].m_output = (m_poly4[m_p4] & 1);
+ else if (m_AUDCTL & POLY9)
+ m_channel[ch].m_output = (m_poly9[m_p9] & 1);
+ else
+ m_channel[ch].m_output = (m_poly17[m_p17] & 1);
+ }
+}
+
+
+void pokey_device::pokey_potgo(void)
+{
+ int pot;
+
+ LOG(("POKEY #%p pokey_potgo\n", (void *) this));
+
+ m_ALLPOT = 0x00;
+ m_pot_counter = 0;
+
+ for( pot = 0; pot < 8; pot++ )
+ {
+ m_POTx[pot] = 228;
+ switch (pot)
+ {
+ case 0:
+ if( !m_pot0_r_cb.isnull() )
+ {
+ int r = m_pot0_r_cb(pot);
+
+ LOG(("POKEY %s pot_r(%d) returned $%02x\n", tag(), pot, r));
+ if (r >= 228)
+ {
+ r = 228;
+ }
+ if (r == 0)
+ {
+ /* immediately set the ready - bit of m_ALLPOT
+ * In this case, most likely no capacitor is connected
+ */
+ m_ALLPOT |= (1<<pot);
+ }
+
+ /* final value */
+ m_POTx[pot] = r;
+ }
+ break;
+ case 1:
+ if( !m_pot1_r_cb.isnull() )
+ {
+ int r = m_pot1_r_cb(pot);
+
+ LOG(("POKEY %s pot_r(%d) returned $%02x\n", tag(), pot, r));
+ if (r >= 228)
+ {
+ r = 228;
+ }
+ if (r == 0)
+ {
+ /* immediately set the ready - bit of m_ALLPOT
+ * In this case, most likely no capacitor is connected
+ */
+ m_ALLPOT |= (1<<pot);
+ }
+
+ /* final value */
+ m_POTx[pot] = r;
+ }
+ break;
+ case 2:
+ if( !m_pot2_r_cb.isnull() )
+ {
+ int r = m_pot2_r_cb(pot);
+
+ LOG(("POKEY %s pot_r(%d) returned $%02x\n", tag(), pot, r));
+ if (r >= 228)
+ {
+ r = 228;
+ }
+ if (r == 0)
+ {
+ /* immediately set the ready - bit of m_ALLPOT
+ * In this case, most likely no capacitor is connected
+ */
+ m_ALLPOT |= (1<<pot);
+ }
+
+ /* final value */
+ m_POTx[pot] = r;
+ }
+ break;
+ case 3:
+ if( !m_pot3_r_cb.isnull() )
+ {
+ int r = m_pot3_r_cb(pot);
+
+ LOG(("POKEY %s pot_r(%d) returned $%02x\n", tag(), pot, r));
+ if (r >= 228)
+ {
+ r = 228;
+ }
+ if (r == 0)
+ {
+ /* immediately set the ready - bit of m_ALLPOT
+ * In this case, most likely no capacitor is connected
+ */
+ m_ALLPOT |= (1<<pot);
+ }
+
+ /* final value */
+ m_POTx[pot] = r;
+ }
+ break;
+ case 4:
+ if( !m_pot4_r_cb.isnull() )
+ {
+ int r = m_pot4_r_cb(pot);
+
+ LOG(("POKEY %s pot_r(%d) returned $%02x\n", tag(), pot, r));
+ if (r >= 228)
+ {
+ r = 228;
+ }
+ if (r == 0)
+ {
+ /* immediately set the ready - bit of m_ALLPOT
+ * In this case, most likely no capacitor is connected
+ */
+ m_ALLPOT |= (1<<pot);
+ }
+
+ /* final value */
+ m_POTx[pot] = r;
+ }
+ break;
+ case 5:
+ if( !m_pot5_r_cb.isnull() )
+ {
+ int r = m_pot5_r_cb(pot);
+
+ LOG(("POKEY %s pot_r(%d) returned $%02x\n", tag(), pot, r));
+ if (r >= 228)
+ {
+ r = 228;
+ }
+ if (r == 0)
+ {
+ /* immediately set the ready - bit of m_ALLPOT
+ * In this case, most likely no capacitor is connected
+ */
+ m_ALLPOT |= (1<<pot);
+ }
+
+ /* final value */
+ m_POTx[pot] = r;
+ }
+ break;
+ case 6:
+ if( !m_pot6_r_cb.isnull() )
+ {
+ int r = m_pot6_r_cb(pot);
+
+ LOG(("POKEY %s pot_r(%d) returned $%02x\n", tag(), pot, r));
+ if (r >= 228)
+ {
+ r = 228;
+ }
+ if (r == 0)
+ {
+ /* immediately set the ready - bit of m_ALLPOT
+ * In this case, most likely no capacitor is connected
+ */
+ m_ALLPOT |= (1<<pot);
+ }
+
+ /* final value */
+ m_POTx[pot] = r;
+ }
+ break;
+ case 7:
+ if( !m_pot7_r_cb.isnull() )
+ {
+ int r = m_pot7_r_cb(pot);
+
+ LOG(("POKEY %s pot_r(%d) returned $%02x\n", tag(), pot, r));
+ if (r >= 228)
+ {
+ r = 228;
+ }
+ if (r == 0)
+ {
+ /* immediately set the ready - bit of m_ALLPOT
+ * In this case, most likely no capacitor is connected
+ */
+ m_ALLPOT |= (1<<pot);
+ }
+
+ /* final value */
+ m_POTx[pot] = r;
+ }
+ break;
+ }
+ }
+}
+
+void pokey_device::vol_init()
+{
+ double resistors[4] = {90000, 26500, 8050, 3400};
+ double pull_up = 10000;
+ /* just a guess, there has to be a resistance since the doc specifies that
+ * Vout is at least 4.2V if all channels turned off.
+ */
+ double r_off = 8e6;
+ double r_chan[16];
+ double rTot;
+
+ for (int j=0; j<16; j++)
+ {
+ rTot = 1.0 / 1e12; /* avoid div by 0 */;
+ for (int i=0; i<4; i++)
+ {
+ if (j & (1 << i))
+ rTot += 1.0 / resistors[i];
+ else
+ rTot += 1.0 / r_off;
+ }
+ r_chan[j] = 1.0 / rTot;
+ }
+ if (VERBOSE)
+ for (int j=0; j<16; j++)
+ {
+ rTot = 1.0 / r_chan[j] + 3.0 / r_chan[0];
+ rTot = 1.0 / rTot;
+ LOG(("%s: %3d - %4.3f\n", tag(), j, rTot / (rTot+pull_up)*4.75));
+ }
+ for (int j=0; j<0x10000; j++)
+ {
+ rTot = 0;
+ for (int i=0; i<4; i++)
+ {
+ rTot += 1.0 / r_chan[(j>>(i*4)) & 0x0f];
+ }
+ rTot = 1.0 / rTot;
+ m_voltab[j] = rTot;
+ }
+
+}
+
+void pokey_device::poly_init_4_5(UINT32 *poly, int size, int xorbit, int invert)
+{
+ int mask = (1 << size) - 1;
+ int i;
+ UINT32 lfsr = 0;
+
+ LOG_POLY(("poly %d\n", size));
+ for( i = 0; i < mask; i++ )
+ {
+ /* calculate next bit */
+ int in = !((lfsr >> 0) & 1) ^ ((lfsr >> xorbit) & 1);
+ lfsr = lfsr >> 1;
+ lfsr = (in << (size-1)) | lfsr;
+ *poly = lfsr ^ invert;
+ LOG_POLY(("%05x: %02x\n", i, *poly));
+ poly++;
+ }
+}
+
+void pokey_device::poly_init_9_17(UINT32 *poly, int size)
+{
+ int mask = (1 << size) - 1;
+ int i;
+ UINT32 lfsr =mask;
+
+ LOG_RAND(("rand %d\n", size));
+
+ if (size == 17)
+ {
+ for( i = 0; i < mask; i++ )
+ {
+ /* calculate next bit @ 7 */
+ int in8 = ((lfsr >> 8) & 1) ^ ((lfsr >> 13) & 1);
+ int in = (lfsr & 1);
+ lfsr = lfsr >> 1;
+ lfsr = (lfsr & 0xff7f) | (in8 << 7);
+ lfsr = (in << 16) | lfsr;
+ *poly = lfsr;
+ LOG_RAND(("%05x: %02x\n", i, *poly));
+ poly++;
+ }
+ }
+ else
+ {
+ for( i = 0; i < mask; i++ )
+ {
+ /* calculate next bit */
+ int in = ((lfsr >> 0) & 1) ^ ((lfsr >> 5) & 1);
+ lfsr = lfsr >> 1;
+ lfsr = (in << 8) | lfsr;
+ *poly = lfsr;
+ LOG_RAND(("%05x: %02x\n", i, *poly));
+ poly++;
+ }
+ }
+
+}
+
+char *pokey_device::audc2str(int val)
+{
+ static char buff[80];
+ if( val & NOTPOLY5 )
+ {
+ if( val & PURE )
+ strcpy(buff,"pure");
+ else
+ if( val & POLY4 )
+ strcpy(buff,"poly4");
+ else
+ strcpy(buff,"poly9/17");
+ }
+ else
+ {
+ if( val & PURE )
+ strcpy(buff,"poly5");
+ else
+ if( val & POLY4 )
+ strcpy(buff,"poly4+poly5");
+ else
+ strcpy(buff,"poly9/17+poly5");
+ }
+ return buff;
+}
+
+char *pokey_device::audctl2str(int val)
+{
+ static char buff[80];
+ if( val & POLY9 )
+ strcpy(buff,"poly9");
+ else
+ strcpy(buff,"poly17");
+ if( val & CH1_HICLK )
+ strcat(buff,"+ch1hi");
+ if( val & CH3_HICLK )
+ strcat(buff,"+ch3hi");
+ if( val & CH12_JOINED )
+ strcat(buff,"+ch1/2");
+ if( val & CH34_JOINED )
+ strcat(buff,"+ch3/4");
+ if( val & CH1_FILTER )
+ strcat(buff,"+ch1filter");
+ if( val & CH2_FILTER )
+ strcat(buff,"+ch2filter");
+ if( val & CLK_15KHZ )
+ strcat(buff,"+clk15");
+ return buff;
+}
+
+pokey_device::pokey_channel::pokey_channel()
+ : m_AUDF(0),
+ m_AUDC(0),
+ m_borrow_cnt(0),
+ m_counter(0),
+ m_output(0),
+ m_filter_sample(0),
+ m_div2(0)
+{
+}
diff --git a/src/devices/sound/pokey.h b/src/devices/sound/pokey.h
new file mode 100644
index 00000000000..96c9f672246
--- /dev/null
+++ b/src/devices/sound/pokey.h
@@ -0,0 +1,381 @@
+// license:BSD-3-Clause
+// copyright-holders:Brad Oliver, Eric Smith, Juergen Buchmueller
+/*****************************************************************************
+ *
+ * POKEY chip emulator 4.6
+ *
+ * Based on original info found in Ron Fries' Pokey emulator,
+ * with additions by Brad Oliver, Eric Smith and Juergen Buchmueller.
+ * paddle (a/d conversion) details from the Atari 400/800 Hardware Manual.
+ * Polynome algorithms according to info supplied by Perry McFarlane.
+ *
+ *****************************************************************************/
+
+#pragma once
+
+#ifndef __POKEY_H__
+#define __POKEY_H__
+
+#include "machine/rescap.h"
+
+/*
+ * ATARI Pokey (CO12294) pin-out
+ *
+ +-----------------+
+ VSS | 1 40 | D2
+ D3 | 2 39 | D1
+ D4 | 3 38 | D0
+ D5 | 4 37 | AUD
+ D6 | 5 36 | A0
+ D7 | 6 35 | A1
+ PHI2 | 7 34 | A2
+ P6 | 8 33 | A3
+ P7 | 9 32 | R / /W
+ P4 | 10 31 | CS1
+ P5 | 11 30 | /CS0
+ P2 | 12 29 | IRQ
+ P3 | 13 28 | SOD
+ P0 | 14 27 | ACLK
+ P1 | 15 26 | BCLK
+ /KR2 | 16 25 | /KR1
+ VCC | 17 24 | SID
+ /K5 | 18 23 | /K0
+ /K4 | 19 22 | /K1
+ /K3 | 20 21 | /K2
+ +-----------------+
+ *
+ */
+
+/* CONSTANT DEFINITIONS */
+
+/* exact 1.79 MHz clock freq (of the Atari 800 that is) */
+#define FREQ_17_EXACT 1789790
+
+//**************************************************************************
+// CALLBACK HANDLERS
+//**************************************************************************
+
+typedef device_delegate<UINT8 (UINT8 k543210)> pokey_kb_cb_delegate;
+typedef device_delegate<void (int mask)> pokey_int_cb_delegate;
+
+#define POKEY_KEYBOARD_CB_MEMBER(_name) UINT8 _name(UINT8 k543210)
+#define POKEY_INTERRUPT_CB_MEMBER(_name) void _name(int mask)
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_POKEY_POT0_R_CB(_devcb) \
+ devcb = &pokey_device::set_pot0_r_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_POKEY_POT1_R_CB(_devcb) \
+ devcb = &pokey_device::set_pot1_r_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_POKEY_POT2_R_CB(_devcb) \
+ devcb = &pokey_device::set_pot2_r_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_POKEY_POT3_R_CB(_devcb) \
+ devcb = &pokey_device::set_pot3_r_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_POKEY_POT4_R_CB(_devcb) \
+ devcb = &pokey_device::set_pot4_r_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_POKEY_POT5_R_CB(_devcb) \
+ devcb = &pokey_device::set_pot5_r_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_POKEY_POT6_R_CB(_devcb) \
+ devcb = &pokey_device::set_pot6_r_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_POKEY_POT7_R_CB(_devcb) \
+ devcb = &pokey_device::set_pot7_r_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_POKEY_ALLPOT_R_CB(_devcb) \
+ devcb = &pokey_device::set_allpot_r_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_POKEY_SERIN_R_CB(_devcb) \
+ devcb = &pokey_device::set_serin_r_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_POKEY_SEROUT_W_CB(_devcb) \
+ devcb = &pokey_device::set_serout_w_callback(*device, DEVCB_##_devcb);
+
+/* k543210 = k5 ... k0 returns bit0: kr1, bit1: kr2 */
+/* all are, in contrast to actual hardware, ACTIVE_HIGH */
+#define MCFG_POKEY_KEYBOARD_CB(_class, _method) \
+ pokey_device::set_keyboard_callback(*device, pokey_kb_cb_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+#define MCFG_POKEY_INTERRUPT_CB(_class, _method) \
+ pokey_device::set_interrupt_callback(*device, pokey_int_cb_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+
+#define MCFG_POKEY_OUTPUT_RC(_R, _C, _V) \
+ (downcast<pokey_device *>(device))->m_output_type = pokey_device::RC_LOWPASS; \
+ (downcast<pokey_device *>(device))->m_r_pullup = (_R); \
+ (downcast<pokey_device *>(device))->m_cap = (_C); \
+ (downcast<pokey_device *>(device))->m_v_ref = (_V);
+
+/* C ignored, please see pokey.c */
+
+#define MCFG_POKEY_OUTPUT_OPAMP(_R, _C, _V) \
+ (downcast<pokey_device *>(device))->m_output_type = pokey_device::OPAMP_C_TO_GROUND; \
+ (downcast<pokey_device *>(device))->m_r_pullup = (_R); \
+ (downcast<pokey_device *>(device))->m_cap = (_C); \
+ (downcast<pokey_device *>(device))->m_v_ref = (_V);
+
+#define MCFG_POKEY_OUTPUT_OPAMP_LOW_PASS(_R, _C, _V) \
+ (downcast<pokey_device *>(device))->m_output_type = pokey_device::OPAMP_LOW_PASS; \
+ (downcast<pokey_device *>(device))->m_r_pullup = (_R); \
+ (downcast<pokey_device *>(device))->m_cap = (_C); \
+ (downcast<pokey_device *>(device))->m_v_ref = (_V);
+
+#define MCFG_POKEY_OUTPUT_DISCRETE() \
+ (downcast<pokey_device *>(device))->m_output_type = pokey_device::DISCRETE_VAR_R;
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> pokey_device
+
+class pokey_device : public device_t,
+ public device_sound_interface,
+ public device_execute_interface,
+ public device_state_interface
+{
+public:
+
+ enum
+ {
+ POK_KEY_BREAK = 0x30,
+ POK_KEY_SHIFT = 0x20,
+ POK_KEY_CTRL = 0x00
+ };
+
+ enum
+ {
+ /* POKEY WRITE LOGICALS */
+ AUDF1_C = 0x00,
+ AUDC1_C = 0x01,
+ AUDF2_C = 0x02,
+ AUDC2_C = 0x03,
+ AUDF3_C = 0x04,
+ AUDC3_C = 0x05,
+ AUDF4_C = 0x06,
+ AUDC4_C = 0x07,
+ AUDCTL_C = 0x08,
+ STIMER_C = 0x09,
+ SKREST_C = 0x0A,
+ POTGO_C = 0x0B,
+ SEROUT_C = 0x0D,
+ IRQEN_C = 0x0E,
+ SKCTL_C = 0x0F
+ };
+
+ enum
+ {
+ /* POKEY READ LOGICALS */
+ POT0_C = 0x00,
+ POT1_C = 0x01,
+ POT2_C = 0x02,
+ POT3_C = 0x03,
+ POT4_C = 0x04,
+ POT5_C = 0x05,
+ POT6_C = 0x06,
+ POT7_C = 0x07,
+ ALLPOT_C = 0x08,
+ KBCODE_C = 0x09,
+ RANDOM_C = 0x0A,
+ SERIN_C = 0x0D,
+ IRQST_C = 0x0E,
+ SKSTAT_C = 0x0F
+ };
+
+ enum /* sync-operations */
+ {
+ SYNC_NOOP = 11,
+ SYNC_SET_IRQST = 12,
+ SYNC_POT = 13,
+ SYNC_WRITE = 14
+ };
+
+ enum output_type
+ {
+ LEGACY_LINEAR = 0,
+ RC_LOWPASS,
+ OPAMP_C_TO_GROUND,
+ OPAMP_LOW_PASS,
+ DISCRETE_VAR_R
+ };
+
+ // construction/destruction
+ pokey_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _Object> static devcb_base &set_pot0_r_callback(device_t &device, _Object object) { return downcast<pokey_device &>(device).m_pot0_r_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_pot1_r_callback(device_t &device, _Object object) { return downcast<pokey_device &>(device).m_pot1_r_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_pot2_r_callback(device_t &device, _Object object) { return downcast<pokey_device &>(device).m_pot2_r_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_pot3_r_callback(device_t &device, _Object object) { return downcast<pokey_device &>(device).m_pot3_r_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_pot4_r_callback(device_t &device, _Object object) { return downcast<pokey_device &>(device).m_pot4_r_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_pot5_r_callback(device_t &device, _Object object) { return downcast<pokey_device &>(device).m_pot5_r_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_pot6_r_callback(device_t &device, _Object object) { return downcast<pokey_device &>(device).m_pot6_r_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_pot7_r_callback(device_t &device, _Object object) { return downcast<pokey_device &>(device).m_pot7_r_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_allpot_r_callback(device_t &device, _Object object) { return downcast<pokey_device &>(device).m_allpot_r_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_serin_r_callback(device_t &device, _Object object) { return downcast<pokey_device &>(device).m_serin_r_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_serout_w_callback(device_t &device, _Object object) { return downcast<pokey_device &>(device).m_serout_w_cb.set_callback(object); }
+
+ static void set_keyboard_callback(device_t &device, pokey_kb_cb_delegate callback) { downcast<pokey_device &>(device).m_keyboard_r = callback; }
+ static void set_interrupt_callback(device_t &device, pokey_int_cb_delegate callback) { downcast<pokey_device &>(device).m_irq_f = callback; }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ UINT8 read(offs_t offset);
+ void write(offs_t offset, UINT8 data);
+
+ DECLARE_WRITE_LINE_MEMBER( sid_w ); // pin 24
+ void serin_ready(int after);
+
+ // analog output configuration
+
+ output_type m_output_type;
+ double m_r_pullup;
+ double m_cap;
+ double m_v_ref;
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_post_load();
+ virtual void device_clock_changed();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ virtual void execute_run();
+
+ //virtual UINT32 execute_min_cycles() const { return 114; }
+ // other internal states
+ int m_icount;
+
+private:
+
+
+ class pokey_channel
+ {
+ public:
+ pokey_channel();
+ pokey_device *m_parent;
+ UINT8 m_INTMask;
+ UINT8 m_AUDF; /* AUDFx (D200, D202, D204, D206) */
+ UINT8 m_AUDC; /* AUDCx (D201, D203, D205, D207) */
+ INT32 m_borrow_cnt; /* borrow counter */
+ INT32 m_counter; /* channel counter */
+ UINT8 m_output; /* channel output signal (1 active, 0 inactive) */
+ UINT8 m_filter_sample; /* high-pass filter sample */
+ UINT8 m_div2; /* division by 2 */
+
+ inline void sample(void) { m_filter_sample = m_output; }
+ inline void reset_channel(void) { m_counter = m_AUDF ^ 0xff; }
+
+ inline void inc_chan()
+ {
+ m_counter = (m_counter + 1) & 0xff;
+ if (m_counter == 0 && m_borrow_cnt == 0)
+ {
+ m_borrow_cnt = 3;
+ if (m_parent->m_IRQEN & m_INTMask)
+ {
+ /* Exposed state has changed: This should only be updated after a resync ... */
+ m_parent->synchronize(SYNC_SET_IRQST, m_INTMask);
+ }
+ }
+ }
+
+ inline int check_borrow()
+ {
+ if (m_borrow_cnt > 0)
+ {
+ m_borrow_cnt--;
+ return (m_borrow_cnt == 0);
+ }
+ return 0;
+ }
+ };
+
+ static const int POKEY_CHANNELS = 4;
+
+ UINT32 step_one_clock();
+ void step_keyboard();
+ void step_pot();
+
+ void poly_init_4_5(UINT32 *poly, int size, int xorbit, int invert);
+ void poly_init_9_17(UINT32 *poly, int size);
+ void vol_init();
+
+ inline void process_channel(int ch);
+ void pokey_potgo(void);
+ char *audc2str(int val);
+ char *audctl2str(int val);
+
+ void write_internal(offs_t offset, UINT8 data);
+
+ // internal state
+ sound_stream* m_stream;
+
+ pokey_channel m_channel[POKEY_CHANNELS];
+
+ UINT32 m_output; /* raw output */
+ double m_out_filter; /* filtered output */
+
+ INT32 m_clock_cnt[3]; /* clock counters */
+ UINT32 m_p4; /* poly4 index */
+ UINT32 m_p5; /* poly5 index */
+ UINT32 m_p9; /* poly9 index */
+ UINT32 m_p17; /* poly17 index */
+
+ devcb_read8 m_pot0_r_cb;
+ devcb_read8 m_pot1_r_cb;
+ devcb_read8 m_pot2_r_cb;
+ devcb_read8 m_pot3_r_cb;
+ devcb_read8 m_pot4_r_cb;
+ devcb_read8 m_pot5_r_cb;
+ devcb_read8 m_pot6_r_cb;
+ devcb_read8 m_pot7_r_cb;
+ devcb_read8 m_allpot_r_cb;
+ devcb_read8 m_serin_r_cb;
+ devcb_write8 m_serout_w_cb;
+
+ pokey_kb_cb_delegate m_keyboard_r;
+ pokey_int_cb_delegate m_irq_f;
+
+ UINT8 m_POTx[8]; /* POTx (R/D200-D207) */
+ UINT8 m_AUDCTL; /* AUDCTL (W/D208) */
+ UINT8 m_ALLPOT; /* ALLPOT (R/D208) */
+ UINT8 m_KBCODE; /* KBCODE (R/D209) */
+ UINT8 m_SERIN; /* SERIN (R/D20D) */
+ UINT8 m_SEROUT; /* SEROUT (W/D20D) */
+ UINT8 m_IRQST; /* IRQST (R/D20E) */
+ UINT8 m_IRQEN; /* IRQEN (W/D20E) */
+ UINT8 m_SKSTAT; /* SKSTAT (R/D20F) */
+ UINT8 m_SKCTL; /* SKCTL (W/D20F) */
+
+ UINT8 m_pot_counter;
+ UINT8 m_kbd_cnt;
+ UINT8 m_kbd_latch;
+ UINT8 m_kbd_state;
+
+ attotime m_clock_period;
+
+ UINT32 m_poly4[0x0f];
+ UINT32 m_poly5[0x1f];
+ UINT32 m_poly9[0x1ff];
+ UINT32 m_poly17[0x1ffff];
+ UINT32 m_voltab[0x10000];
+};
+
+
+// device type definition
+extern const device_type POKEY;
+
+#endif /* __POKEY_H__ */
diff --git a/src/devices/sound/pokey.txt b/src/devices/sound/pokey.txt
new file mode 100644
index 00000000000..e0a30094893
--- /dev/null
+++ b/src/devices/sound/pokey.txt
@@ -0,0 +1,319 @@
+ Atari POKEY Chip Emulator V2.0
+ ==============================
+ by Ron Fries
+ 31 Jan 97
+
+The PokeySound Chip Emulator is designed to emulate the functionality of the
+Atari POKEY Chip Hardware through 'C' Sourcecode. The emulator is able to
+produce sounds which are essentially identical to the original POKEY chip,
+including the exact distortions and pitches.
+
+The emulator is designed to run in a 32-bit environment. Though it can be
+compiled and run in a 16-bit environment, it is slow.
+
+I would like to give special thanks to Neil Bradley. He provided excellent
+testing support and was also the driving force behind the multiple POKEY
+emulation.
+
+New Features:
+-------------
+
+Version 2.0 of the 'PokeySound' adds the following features:
+
+1) Support for multiple POKEY chips. The maximum supported is configured
+ at compile time.
+
+2) An adjustable gain. The previous releases had a built-in gain of 64.
+
+3) A clipping option. Depending on the number of chips emulated and the
+ configured gain, it is possible for the output to exceed 8-bits.
+ Clipping can be enabled to prevent this, though it does increase the
+ processing time.
+
+
+Standard Features:
+------------------
+
+The 'PokeySound' emulator supports the following functions:
+
+1) All polynomial sound generators:
+ a) 4-bit poly - actual bit pattern determined from sampled sound
+ b) 5-bit poly - actual bit pattern determined from sampled sound
+ c) 17-bit poly - simulated random bit pattern
+ d) 9-bit poly - derived from simulated 17-bit poly
+
+2) Full support of all 'Divide by N' counter clocks:
+ a) 1.79 MHz (high limited to playback sample rate)
+ b) 64 KHz (high limited to playback sample rate)
+ c) 15 KHz
+
+3) Full support of all 'Divide by N' resolutions:
+ a) 8-bit - single channel
+ b) 16-bit - double channel
+
+4) Full support of all distortions
+ a) 5-bit poly, then 17-bit poly
+ b) 5-bit poly only
+ c) 5-bit poly, then 4-bit poly
+ d) 17-bit poly only
+ e) no poly counters (pure tone)
+ f) 5-bit poly only
+
+5) Full support of volume control
+
+6) Full support of all pitches - distortions will vary exactly as the
+ original Atari based on different pitches
+
+7) Accurate pitch generation
+
+8) Support of any playback sample rate (e.g. 22050)
+
+
+The 'PokeySound' emulator does not currently support the following functions:
+
+1) High pass filters
+
+
+Though I don't believe adding support for the High-Pass filters is very
+complicated, I decided not to add support right now because I don't
+believe this feature is used much. I'm also not sure how much impact it
+would have on performance. Let me know if you find an application that
+uses it.
+
+In the 2.0 release, I've removed the non-optimized vrersion. It was only
+left in for reference. If you would still like to see the non-optimized
+version, it's available in the 1.2 release.
+
+One of the unique features of the emulator is that the processing time varies
+based on the frequency. Since the routine only calculates new output values
+when a change is sensed, the lower frequencies (which change less frequently)
+will require less processing time.
+
+
+Differences Between the Emulator and the Actual POKEY Chip:
+-----------------------------------------------------------
+
+The biggest difference between the emulator and the original hardware is
+that the emulator emulates an 'ideal' POKEY chip. All output from the
+emulator is a based on a precise square wave, whereas the output from the
+original chip has decay. Though the output is slightly different, I
+don't believe this difference is easily discernible.
+
+Another slight difference is the 17-bit/9-bit poly. Since the polynomial
+is large (2^17 bits), I choose to create the sample using a random number
+generator rather than a table. I don't believe this difference is
+significant.
+
+There are also a few differences which are introduced by aliasing. This is
+a direct result of using an output sampling rate which is not identical to
+the original sound rate. It is most evident with high frequencies.
+
+A final difference is the lack of support for the High-Pass Filter
+functionality. I plan to add this in a future release if necessary.
+
+
+Sample/Test Application:
+------------------------
+
+The test program I've distributed is a 16-bit DOS application created with
+the Borland 'C' compiler. The only reason I used 16-bit was because I
+already had a set of working SB drivers in 16-bit. Since the test system
+is dedicated to generating sounds, the performance in 16-bit is more than
+adequate.
+
+
+POKEY.C
+=======
+
+The POKEY.C file is the heart of the PokeySound Emulation program.
+Although the routines in the file must work together, no other files are
+modules are required for operation. A header file, 'POKEY.H', has
+been included for use in other modules, and provides the necessary
+function prototypes. I've attempted to make the routines as portable as
+possible, so the file should compile on almost any compiler with little
+or no modification.
+
+I have made some attempts at optimizing the routines, though I am sure
+more optimization can be done. They are currently only available in 'C'.
+I'll be happy to convert them to assembly language if desired. Please feel
+free to send me e-mail at rfries@tcmail.frco.com.
+
+The routines are easy to use. Detailed descriptions on the function calls
+are listed below.
+
+The POKEY.C module can be compiled in a 32-bit or 16-bit environment.
+Since these routines are optimized for 32-bit use, the code will default
+to 32-bit. To compile in 16-bits, use a command line option to define
+the variable COMP16.
+
+
+GENERAL OVERVIEW
+----------------
+
+On start-up of the system, a single call should be made to Pokey_sound_init.
+This routine will prepare the structures for sound output. This routine
+can be called again if necessary during warm-start or other reset.
+
+Once in the main loop, there are two other functions that will be used.
+Whenever the system needs to write to either the AUDC or AUDF values,
+a call should be made to the Update_pokey_sound routine. This routine will
+take care of updating the internal registers. It will pre-calculate several
+values to help with optimization.
+
+The only other routine that is called is the Pokey_process function. This
+function will fill a audio buffer with a specified number of bytes. This
+function should be called whenever a new audio buffer is required.
+
+For best results, I recommend using at least two output buffers. Using this
+scheme, the sound card can be playing one buffer while the system is filling
+the other.
+
+
+DETAILED FUNCTION DESCRIPTIONS
+------------------------------
+
+Pokey_sound_init(uint32 freq17, uint16 playback_freq, uint8 num_pokeys)
+-----------------------------------------------------------------------
+
+This function initializes the structures used by the PokeySound routines.
+This function takes three parameters: the main clock frequency, the
+playback frequency and the number of POKEY chips to emulate.
+
+The maximum number of POKEY chips emulated is configured at compile time.
+Though the maximum number of chips can be configured as one, the PokeySound
+1.2 routines are recommended if only a single chip is to be emulated since
+they have will provide better performance.
+
+The main clock frequency is the frequency of the 1.79MHz source clock.
+To provide exact results, freq17 should be set equal to 1789790 Hz. As an
+alternative, freq17 can be set to an approximate frequency of 1787520 Hz.
+Using this approximate frequency will reduce aliasing and thus produce a
+clearer output signal.
+
+A constant has been defined for both of these values for your convenience.
+The names are FREQ_17_EXACT and FREQ_17_APPROX.
+
+The playback frequency is the frequency of the sound playback (the frequency
+used by the sound card). For best results, the playback frequency should
+be an even division of the main clock frequency. Since most of the sounds
+will be generated using the 64kHz clock, I also recommend making the
+playback frequency an even division of the 64kHz clock.
+
+The 64kHz clock is exactly equal to the main clock divided by 28. For
+the playback frequency, I recommend one of the following values:
+
+1) FREQ_17_APPROX / (28*1), which is equal to 63840. Of course, most sound
+ cards can't reproduce this frequency.
+
+2) FREQ_17_APPROX / (28*2), which is equal to 31920. All of the newer cards
+ will support this frequency.
+
+3) FREQ_17_APPROX / (28*3), which is equal to 21280. All of the SB
+ compatibles should support this frequency.
+
+4) FREQ_17_APPROX / (28*4), which is equal to 15960. This may be the
+ best choice, as it offers good sound reproduction with good performance.
+
+Of course, these options also assume you are using the approximate
+frequency for the main clock as well. Any of these choices will offer the
+best results when the main 64kHz clock is used, reasonable results when the
+15kHz clock is selected, and marginal results when the 1.79MHz clock is
+selected (the only way to produce good results in all cases is to set the
+playback frequency to 1.79MHz!)
+
+Feel free to experiment to find other alternatives as well.
+
+This function has no return value (void).
+
+
+Update_pokey_sound (uint16 addr, uint8 val, uint8 chip, uint8 gain)
+-------------------------------------------------------------------
+
+This function should be called each time an AUDC, AUDF or AUDCTL value
+changes. This function takes four parameters: the address to change,
+the new value, the chip to be updated, and the gain to be used.
+The lower four bits of the address should be one of the following values:
+
+ Addr Description
+ ------ -----------
+ 0x00 AUDF1
+ 0x01 AUDC1
+ 0x02 AUDF2
+ 0x03 AUDC2
+ 0x04 AUDF3
+ 0x05 AUDC3
+ 0x06 AUDF4
+ 0x07 AUDC4
+ 0x08 AUDCTL
+
+In order to support multiple POKEY chips, only the lower four bits of
+the address are used. Note that this routine can no longer be called with
+any address as it will affect the operation of the specified chip.
+
+The routine pre-calculates several values that are needed by the
+processing function. This is done to optimize performance.
+
+The output will be amplified (multiplied) by gain/16 (previous releases had
+a built in multiplier of 4, which calculates to a gain value of 64). If the
+output exceeds the maximum value after then gain and clipping is enabled,
+the output will be limited to reduce distortion.
+
+The best value for the gain depends on the number of POKEYs emulated and
+the maximum volume used. The maximum possible output for each channel is 15,
+making the maximum possible output for a single chip to be 60. Assuming all
+four channels on the chip are used at full volume, a gain of 64 can be used
+without distortion. If 4 POKEY chips are emulated and all 16 channels are
+used at full volume, the gain must be no more than 16 to prevent distortion.
+Of course, if only a few of the 16 channels are used or not all channels are
+used at full volume, a larger gain can be used.
+
+To enable clipping, define the logical CLIP before compiling. This is the
+default mode of operation as it has already been included in the POKEY.H file.
+Note that this is only recommended if clipping is necessary since it will
+impact the performance.
+
+This function has no return value (void).
+
+
+Pokey_process (uint8 *buffer, uint16 n)
+---------------------------------------
+
+This function calculates and fills a buffer with unsigned 8-bit mono audio.
+This function takes two parameters: a pointer to the buffer to fill and
+the size of the buffer (limited to 65535). This function fills the
+buffer based on the requested size and returns. It automatically
+updates the pointers for the next call, so subsequent calls to this function
+will provide a continuous stream of data.
+
+The size of the buffer that is needed depends on the playback frequency.
+It is best to keep the buffer as small as possible to maximize response time
+to changes in the sound. Of course, the minimum size is dependent on
+system and emulator performance.
+
+Selecting the correct buffer size is a careful balance. Selecting a buffer
+size that is too small will produce noticeable clicks in the output, though
+selecting a size that is too large will cause a poor response time and
+possible delays in the system when the new buffer is filled.
+
+This function has no return value (void).
+
+
+License Information and Copyright Notice
+========================================
+
+PokeySound is Copyright(c) 1996-1997 by Ron Fries
+
+This library is free software; you can redistribute it and/or modify it under
+the terms of version 2 of the GNU Library General Public License as published
+by the Free Software Foundation.
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more
+details.
+
+To obtain a copy of the GNU Library General Public License, write to the Free
+Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Any permitted reproduction of these routines, in whole or in part, must bear
+this legend.
diff --git a/src/devices/sound/qs1000.c b/src/devices/sound/qs1000.c
new file mode 100644
index 00000000000..3100800acbc
--- /dev/null
+++ b/src/devices/sound/qs1000.c
@@ -0,0 +1,646 @@
+// license:BSD-3-Clause
+// copyright-holders:Philip Bennett
+/***************************************************************************
+
+ qs1000.c
+
+ QS1000 device emulator.
+
+****************************************************************************
+
+ The QS1000 is a 32-voice wavetable synthesizer, believed to be based on
+ the OPTi 82C941. It contains an 8051 core, 256b of RAM and an (undumped)
+ internal program ROM. The internal ROM can be bypassed in favour of an
+ external ROM. Commands are issued to the chip via the 8051 serial port.
+
+ The QS1000 can access 24Mb of sample ROM. To reduce demand on the CPU,
+ instrument parameters such as playback rate, envelope and filter values
+ are encoded in ROM and directly accessed by the wavetable engine.
+ There are table entries for every note of every instrument.
+
+ Registers
+ =========
+
+ [200] = Key on/off
+ 0 = Key on
+ 1 = ?
+ 2 = key off
+ [201] = Address byte 0 (LSB)
+ [202] = Address byte 1
+ [203] = Address byte 2
+ [204] = Pitch
+ [205] = Pitch high byte? (Usually 0)
+ [206] = Left volume
+ [207] = Right volume
+ [208] = Volume
+ [209] = ?
+ [20a] = ?
+ [20b] = ?
+ [20c] = ?
+ [20d] = Velocity
+ [20e] = Channel select
+ [20f] = Modulation
+ [210] = Modulation
+ [211] = 0 - Select global registers?
+ 3 - Select channel registers?
+
+ Velocity register values for MIDI range 0-127:
+
+ 01 01 01 01 01 01 01 02 02 03 03 04 04 05 05 06
+ 06 07 07 08 08 09 09 0A 0A 0B 0B 0C 0C 0D 0D 0E
+ 0E 0F 10 11 11 12 13 14 14 15 16 17 17 18 19 1A
+ 1A 1B 1C 1D 1D 1E 1F 20 20 21 22 23 23 24 25 26
+ 26 27 28 29 29 2A 2B 2C 2C 2D 2E 2F 2F 30 31 32
+ 35 38 3B 3E 41 44 47 4A 4D 50 4F 51 52 53 54 56
+ 57 58 59 5B 5C 5D 5E 60 61 62 63 65 66 67 6A 6B
+ 6C 6E 6F 70 71 73 74 75 76 78 79 7A 7B 7D 7E 7F
+
+ (TODO: Other register values)
+
+ This is the sequence of register writes used to play the Iron Fortress credit sound:
+
+ [211] 0 Select global registers?
+ [200] 1 ?
+ [203] d6 Address byte 2
+ [202] a9 Address byte 1
+ [201] 1 Address byte 0
+ [204] 80 Pitch
+ [205] 0 ?
+ [206] 80 Left volume
+ [207] 80 Right volume
+ [208] b3 Volume
+ [209] 0 ?
+ [20a] ff ?
+ [20b] 0 ?
+ [20c] 0 ?
+ [20d] 78 Velocity
+ [211] 3 Select channel registers
+ [20e] 0 Select channel
+ [200] 0 Key on
+
+
+ Sound Headers
+ =============
+
+ The address registers point to a 6 byte entry in the sound ROM:
+
+ [019be0]
+ 097b 397f 1510
+ ^ ^ ^
+ | | |
+ | | +----- Sound descriptor pointer
+ | +---------- ?
+ +--------------- Playback frequency (fixed point value representing 24MHz clock periods)
+
+ This in turn points to a 24 byte descriptor:
+
+ [1510]:
+ 0 4502D 4508E 45F91 D0 7F 0F 2A 1F 90 00 FF
+ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
+ | | | | | | | | | | | |
+ | | | | | | | | | | | +-- ?
+ | | | | | | | | | | +----- ?
+ | | | | | | | | | +-------- ?
+ | | | | | | | | +----------- ?
+ | | | | | | | +-------------- ?
+ | | | | | | +----------------- Bit 7: Format (0:PCM 1:ADPCM)
+ | | | | | +-------------------- ?
+ | | | | +----------------------- ?
+ | | | +----------------------------- Loop end address
+ | | +----------------------------------- Loop start address
+ | +----------------------------------------- Start address
+ +------------------------------------------- Address most-significant nibble (shared with loop addresses)
+
+ * The unknown parameters are most likely envelope and filter parameters.
+ * Is there a loop flag or do sounds loop indefinitely until stopped?
+
+
+ TODO:
+ * Looping is currently disabled
+ * Figure out unknown sound header parameters
+ * Figure out and implement envelopes and filters
+ * Pitch bending
+ * Dump the internal ROM
+
+***************************************************************************/
+#include "emu.h"
+#include "qs1000.h"
+
+
+#define LOGGING_ENABLED 0
+
+
+// device type definition
+const device_type QS1000 = &device_creator<qs1000_device>;
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+static ADDRESS_MAP_START( qs1000_prg_map, AS_PROGRAM, 8, qs1000_device )
+ AM_RANGE(0x0000, 0x7fff) AM_ROM
+ADDRESS_MAP_END
+
+
+static ADDRESS_MAP_START( qs1000_io_map, AS_IO, 8, qs1000_device )
+ AM_RANGE(0x0000, 0x00ff) AM_RAM
+ AM_RANGE(0x0200, 0x0211) AM_WRITE(wave_w)
+ AM_RANGE(MCS51_PORT_P1, MCS51_PORT_P1) AM_READWRITE(p1_r, p1_w)
+ AM_RANGE(MCS51_PORT_P2, MCS51_PORT_P2) AM_READWRITE(p2_r, p2_w)
+ AM_RANGE(MCS51_PORT_P3, MCS51_PORT_P3) AM_READWRITE(p3_r, p3_w)
+ADDRESS_MAP_END
+
+
+// Machine fragment
+static MACHINE_CONFIG_FRAGMENT( qs1000 )
+ MCFG_CPU_ADD("cpu", I8052, DERIVED_CLOCK(1, 1))
+ MCFG_CPU_PROGRAM_MAP(qs1000_prg_map)
+ MCFG_CPU_IO_MAP(qs1000_io_map)
+MACHINE_CONFIG_END
+
+
+// ROM definition for the QS1000 internal program ROM
+ROM_START( qs1000 )
+ ROM_REGION( 0x10000, "cpu", 0 )
+ ROM_LOAD_OPTIONAL( "qs1000.bin", 0x0000, 0x10000, NO_DUMP )
+ROM_END
+
+
+// Wavetable ROM address map
+static ADDRESS_MAP_START( qs1000, AS_0, 8, qs1000_device )
+ AM_RANGE(0x000000, 0xffffff) AM_ROM AM_REGION("qs1000", 0)
+ADDRESS_MAP_END
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// qs1000_device - constructor
+//-------------------------------------------------
+qs1000_device::qs1000_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, QS1000, "QS1000", tag, owner, clock, "qs1000", __FILE__),
+ device_sound_interface(mconfig, *this),
+ device_memory_interface(mconfig, *this),
+ m_external_rom(false),
+ m_in_p1_cb(*this),
+ m_in_p2_cb(*this),
+ m_in_p3_cb(*this),
+ m_out_p1_cb(*this),
+ m_out_p2_cb(*this),
+ m_out_p3_cb(*this),
+ //m_serial_w_cb(*this),
+ m_space_config("samples", ENDIANNESS_LITTLE, 8, 24, 0, NULL),
+ m_stream(NULL),
+ m_direct(NULL),
+ m_cpu(*this, "cpu")
+{
+ m_address_map[0] = *ADDRESS_MAP_NAME(qs1000);
+}
+
+
+//-------------------------------------------------
+// rom_region - return a pointer to the device's
+// internal ROM region
+//-------------------------------------------------
+const rom_entry *qs1000_device::device_rom_region() const
+{
+ return m_external_rom ? NULL : ROM_NAME( qs1000 );
+}
+
+
+//-------------------------------------------------
+// machine_config_additions - return a pointer to
+// the device's machine fragment
+//-------------------------------------------------
+machine_config_constructor qs1000_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( qs1000 );
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+void qs1000_device::device_start()
+{
+ // Find our direct access
+ m_direct = &space().direct();
+
+ // The QS1000 operates at 24MHz. Creating a stream at that rate
+ // would be overkill so we opt for a fraction of that rate which
+ // gives reasonable results
+ m_stream = stream_alloc(0, 2, clock() / 32);
+
+ // Resolve CPU port callbacks
+ m_in_p1_cb.resolve_safe(0);
+ m_in_p2_cb.resolve_safe(0);
+ m_in_p3_cb.resolve_safe(0);
+
+ m_out_p1_cb.resolve_safe();
+ m_out_p2_cb.resolve_safe();
+ m_out_p3_cb.resolve_safe();
+
+ //m_serial_w_cb.resolve_safe();
+
+ m_cpu->i8051_set_serial_rx_callback(read8_delegate(FUNC(qs1000_device::data_to_i8052),this));
+
+ save_item(NAME(m_serial_data_in));
+ save_item(NAME(m_wave_regs));
+
+ for (int i = 0; i < QS1000_CHANNELS; i++)
+ {
+ save_item(NAME(m_channels[i].m_acc), i);
+ save_item(NAME(m_channels[i].m_adpcm_signal), i);
+ save_item(NAME(m_channels[i].m_start), i);
+ save_item(NAME(m_channels[i].m_addr), i);
+ save_item(NAME(m_channels[i].m_adpcm_addr), i);
+ save_item(NAME(m_channels[i].m_loop_start), i);
+ save_item(NAME(m_channels[i].m_loop_end), i);
+ save_item(NAME(m_channels[i].m_freq), i);
+ save_item(NAME(m_channels[i].m_flags), i);
+ save_item(NAME(m_channels[i].m_regs), i);
+ save_item(NAME(m_channels[i].m_adpcm.m_signal), i);
+ save_item(NAME(m_channels[i].m_adpcm.m_step), i);
+ }
+}
+
+
+//-------------------------------------------------
+// serial_in - send data to the chip
+//-------------------------------------------------
+void qs1000_device::serial_in(UINT8 data)
+{
+ m_serial_data_in = data;
+
+ // Signal to the CPU that data is available
+ m_cpu->set_input_line(MCS51_RX_LINE, ASSERT_LINE);
+ m_cpu->set_input_line(MCS51_RX_LINE, CLEAR_LINE);
+}
+
+
+//-------------------------------------------------
+// set_irq - interrupt the internal CPU
+//-------------------------------------------------
+void qs1000_device::set_irq(int state)
+{
+ // Signal to the CPU that data is available
+ m_cpu->set_input_line(MCS51_INT1_LINE, state ? ASSERT_LINE : CLEAR_LINE);
+}
+
+
+//-------------------------------------------------
+// data_to_i8052 - called by the 8052 core to
+// receive serial data
+//-------------------------------------------------
+READ8_MEMBER(qs1000_device::data_to_i8052)
+{
+ return m_serial_data_in;
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+void qs1000_device::device_reset()
+{
+ for (int ch = 0; ch < QS1000_CHANNELS; ++ch)
+ {
+ m_channels[ch].m_flags = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+const address_space_config *qs1000_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == 0) ? &m_space_config : NULL;
+}
+
+
+//-------------------------------------------------
+// device_timer - handle deferred writes and
+// resets as a timer callback
+//-------------------------------------------------
+void qs1000_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+}
+
+
+//-------------------------------------------------
+// p0_r
+//-------------------------------------------------
+READ8_MEMBER( qs1000_device::p0_r )
+{
+ return 0xff;
+}
+
+
+//-------------------------------------------------
+// p1_r
+//-------------------------------------------------
+READ8_MEMBER( qs1000_device::p1_r )
+{
+ return m_in_p1_cb(0);
+}
+
+
+//-------------------------------------------------
+// p2_r
+//-------------------------------------------------
+READ8_MEMBER( qs1000_device::p2_r )
+{
+ return m_in_p2_cb(0);
+}
+
+
+//-------------------------------------------------
+// p3_r
+//-------------------------------------------------
+READ8_MEMBER( qs1000_device::p3_r )
+{
+ return m_in_p3_cb(0);
+}
+
+
+//-------------------------------------------------
+// p0_w
+//-------------------------------------------------
+WRITE8_MEMBER( qs1000_device::p0_w )
+{
+}
+
+
+//-------------------------------------------------
+// p1_w
+//-------------------------------------------------
+
+WRITE8_MEMBER( qs1000_device::p1_w )
+{
+ m_out_p1_cb((offs_t)0, data);
+}
+
+
+//-------------------------------------------------
+// p2_w
+//-------------------------------------------------
+
+WRITE8_MEMBER( qs1000_device::p2_w )
+{
+ m_out_p2_cb((offs_t)0, data);
+}
+
+
+//-------------------------------------------------
+// p3_w
+//-------------------------------------------------
+
+WRITE8_MEMBER( qs1000_device::p3_w )
+{
+ m_out_p3_cb((offs_t)0, data);
+}
+
+
+//-------------------------------------------------
+// wave_w - process writes to wavetable engine
+//-------------------------------------------------
+
+WRITE8_MEMBER( qs1000_device::wave_w )
+{
+ m_stream->update();
+
+ if (LOGGING_ENABLED)
+ printf("QS1000 W[%x] %x\n", 0x200 + offset, data);
+
+ switch (offset)
+ {
+ case 0x00:
+ {
+ int ch = m_wave_regs[0xe];
+
+ if (data == 0)
+ {
+ // TODO
+ for (int i = 0; i < 16; ++i)
+ m_channels[ch].m_regs[i] = m_wave_regs[i];
+
+ // Key on
+ start_voice(ch);
+ }
+ if (data == 1)
+ {
+ // ?
+ }
+ else if (data == 2)
+ {
+ // Key off
+ m_channels[ch].m_flags &= ~QS1000_KEYON;
+ }
+ break;
+ }
+
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ {
+ if (m_wave_regs[0x11] == 3)
+ {
+ // Channel-specific write?
+ m_channels[m_wave_regs[0xe]].m_regs[offset] = data;
+ }
+ else
+ {
+ // Global write?
+ m_wave_regs[offset] = data;
+ }
+ break;
+ }
+
+ default:
+ m_wave_regs[offset] = data;
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update -
+//-------------------------------------------------
+void qs1000_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ // Rset the output stream
+ memset(outputs[0], 0x0, samples * sizeof(*outputs[0]));
+ memset(outputs[1], 0x0, samples * sizeof(*outputs[1]));
+
+ // Iterate over voices and accumulate sample data
+ for (int ch = 0; ch < QS1000_CHANNELS; ch++)
+ {
+ qs1000_channel &chan = m_channels[ch];
+
+ UINT8 lvol = chan.m_regs[6];
+ UINT8 rvol = chan.m_regs[7];
+ UINT8 vol = chan.m_regs[8];
+
+ if (chan.m_flags & QS1000_PLAYING)
+ {
+ if (chan.m_flags & QS1000_ADPCM)
+ {
+ for (int samp = 0; samp < samples; samp++)
+ {
+ if (chan.m_addr >= chan.m_loop_end)
+ {
+#if 0 // Looping disabled until envelopes work
+ if (chan.m_flags & QS1000_KEYON)
+ {
+ chan.m_addr = chan.m_loop_start;
+ }
+ else
+#endif
+ {
+ chan.m_flags &= ~QS1000_PLAYING;
+ break;
+ }
+ }
+
+ // Not too keen on this but it'll do for now
+ while (chan.m_start + chan.m_adpcm_addr != chan.m_addr)
+ {
+ chan.m_adpcm_addr++;
+
+ if (chan.m_start + chan.m_adpcm_addr >= chan.m_loop_end)
+ chan.m_adpcm_addr = chan.m_loop_start - chan.m_start;
+
+ UINT8 data = m_direct->read_byte(chan.m_start + (chan.m_adpcm_addr >> 1));
+ UINT8 nibble = (chan.m_adpcm_addr & 1 ? data : data >> 4) & 0xf;
+ chan.m_adpcm_signal = chan.m_adpcm.clock(nibble);
+ }
+
+ INT8 result = (chan.m_adpcm_signal >> 4);
+ chan.m_acc += chan.m_freq;
+ chan.m_addr = (chan.m_addr + (chan.m_acc >> 18)) & QS1000_ADDRESS_MASK;
+ chan.m_acc &= ((1 << 18) - 1);
+
+ outputs[0][samp] += (result * 4 * lvol * vol) >> 12;
+ outputs[1][samp] += (result * 4 * rvol * vol) >> 12;
+ }
+ }
+ else
+ {
+ for (int samp = 0; samp < samples; samp++)
+ {
+ if (chan.m_addr >= chan.m_loop_end)
+ {
+#if 0 // Looping disabled until envelopes work
+ if (chan.m_flags & QS1000_KEYON)
+ {
+ chan.m_addr = chan.m_loop_start;
+ }
+ else
+#endif
+ {
+ chan.m_flags &= ~QS1000_PLAYING;
+ break;
+ }
+ }
+
+ INT8 result = m_direct->read_byte(chan.m_addr) - 128;
+
+ chan.m_acc += chan.m_freq;
+ chan.m_addr = (chan.m_addr + (chan.m_acc >> 18)) & QS1000_ADDRESS_MASK;
+ chan.m_acc &= ((1 << 18) - 1);
+
+ outputs[0][samp] += (result * lvol * vol) >> 12;
+ outputs[1][samp] += (result * rvol * vol) >> 12;
+ }
+ }
+ }
+ }
+}
+
+
+void qs1000_device::start_voice(int ch)
+{
+ UINT32 table_addr = (m_channels[ch].m_regs[0x01] << 16) | (m_channels[ch].m_regs[0x02] << 8) | m_channels[ch].m_regs[0x03];
+
+ // Fetch the sound information
+ UINT16 freq = (m_direct->read_byte(table_addr + 0) << 8) | m_direct->read_byte(table_addr + 1);
+ UINT16 word1 = (m_direct->read_byte(table_addr + 2) << 8) | m_direct->read_byte(table_addr + 3);
+ UINT16 base = (m_direct->read_byte(table_addr + 4) << 8) | m_direct->read_byte(table_addr + 5);
+
+ if (LOGGING_ENABLED)
+ printf("[%.6x] Freq:%.4x ????:%.4x Addr:%.4x\n", table_addr, freq, word1, base);
+
+ // See Raccoon World and Wyvern Wings NULL sound
+ if (freq == 0)
+ return;
+
+ // Fetch the sample pointers and flags
+ UINT8 byte0 = m_direct->read_byte(base);
+
+ UINT32 start_addr;
+
+ start_addr = byte0 << 16;
+ start_addr |= m_direct->read_byte(base + 1) << 8;
+ start_addr |= m_direct->read_byte(base + 2) << 0;
+ start_addr &= QS1000_ADDRESS_MASK;
+
+ UINT32 loop_start;
+
+ loop_start = (byte0 & 0xf0) << 16;
+ loop_start |= m_direct->read_byte(base + 3) << 12;
+ loop_start |= m_direct->read_byte(base + 4) << 4;
+ loop_start |= m_direct->read_byte(base + 5) >> 4;
+ loop_start &= QS1000_ADDRESS_MASK;
+
+ UINT32 loop_end;
+
+ loop_end = (byte0 & 0xf0) << 16;
+ loop_end |= (m_direct->read_byte(base + 5) & 0xf) << 16;
+ loop_end |= m_direct->read_byte(base + 6) << 8;
+ loop_end |= m_direct->read_byte(base + 7);
+ loop_end &= QS1000_ADDRESS_MASK;
+
+ UINT8 byte8 = m_direct->read_byte(base + 8);
+
+ if (LOGGING_ENABLED)
+ {
+ UINT8 byte9 = m_direct->read_byte(base + 9);
+ UINT8 byte10 = m_direct->read_byte(base + 10);
+ UINT8 byte11 = m_direct->read_byte(base + 11);
+ UINT8 byte12 = m_direct->read_byte(base + 12);
+ UINT8 byte13 = m_direct->read_byte(base + 13);
+ UINT8 byte14 = m_direct->read_byte(base + 14);
+ UINT8 byte15 = m_direct->read_byte(base + 15);
+
+ printf("[%.6x] Sample Start:%.6x Loop Start:%.6x Loop End:%.6x Params: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", base, start_addr, loop_start, loop_end, byte8, byte9, byte10, byte11, byte12, byte13, byte14, byte15);
+ }
+
+ m_channels[ch].m_acc = 0;
+ m_channels[ch].m_start = start_addr;
+ m_channels[ch].m_addr = start_addr;
+ m_channels[ch].m_loop_start = loop_start;
+ m_channels[ch].m_loop_end = loop_end;
+ m_channels[ch].m_freq = freq;
+ m_channels[ch].m_flags = QS1000_PLAYING | QS1000_KEYON;
+
+ if (byte8 & 0x08)
+ {
+ m_channels[ch].m_adpcm.reset();
+ m_channels[ch].m_adpcm_addr = -1;
+// m_channels[ch].m_adpcm_signal = -2;
+ m_channels[ch].m_flags |= QS1000_ADPCM;
+ }
+}
diff --git a/src/devices/sound/qs1000.h b/src/devices/sound/qs1000.h
new file mode 100644
index 00000000000..24b843184b4
--- /dev/null
+++ b/src/devices/sound/qs1000.h
@@ -0,0 +1,165 @@
+// license:BSD-3-Clause
+// copyright-holders:Philip Bennett
+/***************************************************************************
+
+ qs1000.h
+
+ QS1000 device emulator.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __QS1000_H__
+#define __QS1000_H__
+
+#include "cpu/mcs51/mcs51.h"
+#include "sound/okiadpcm.h"
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_QS1000_EXTERNAL_ROM(_bool) \
+ qs1000_device::set_external_rom(*device, _bool);
+
+#define MCFG_QS1000_IN_P1_CB(_devcb) \
+ devcb = &qs1000_device::set_in_p1_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_QS1000_IN_P2_CB(_devcb) \
+ devcb = &qs1000_device::set_in_p2_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_QS1000_IN_P3_CB(_devcb) \
+ devcb = &qs1000_device::set_in_p3_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_QS1000_OUT_P1_CB(_devcb) \
+ devcb = &qs1000_device::set_out_p1_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_QS1000_OUT_P2_CB(_devcb) \
+ devcb = &qs1000_device::set_out_p2_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_QS1000_OUT_P3_CB(_devcb) \
+ devcb = &qs1000_device::set_out_p3_callback(*device, DEVCB_##_devcb);
+
+/*#define MCFG_QS1000_SERIAL_W_CB(_devcb) \
+ devcb = &qs1000_device::set_serial_w_callback(*device, DEVCB_##_devcb);*/
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+#define QS1000_CHANNELS 32
+#define QS1000_ADDRESS_MASK 0xffffff
+
+// ======================> qs1000_device
+
+class qs1000_device : public device_t,
+ public device_sound_interface,
+ public device_memory_interface
+{
+public:
+ // construction/destruction
+ qs1000_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void set_external_rom(device_t &device, bool external_rom) { downcast<qs1000_device &>(device).m_external_rom = external_rom; }
+ template<class _Object> static devcb_base &set_in_p1_callback(device_t &device, _Object object) { return downcast<qs1000_device &>(device).m_in_p1_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_in_p2_callback(device_t &device, _Object object) { return downcast<qs1000_device &>(device).m_in_p2_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_in_p3_callback(device_t &device, _Object object) { return downcast<qs1000_device &>(device).m_in_p3_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_p1_callback(device_t &device, _Object object) { return downcast<qs1000_device &>(device).m_out_p1_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_p2_callback(device_t &device, _Object object) { return downcast<qs1000_device &>(device).m_out_p2_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_out_p3_callback(device_t &device, _Object object) { return downcast<qs1000_device &>(device).m_out_p3_cb.set_callback(object); }
+ //template<class _Object> static devcb_base &set_serial_w_callback(device_t &device, _Object object) { return downcast<qs1000_device &>(device).m_serial_w_cb.set_callback(object); }
+
+ // external
+ void serial_in(UINT8 data);
+ void set_irq(int state);
+
+protected:
+ // device-level overrides
+ virtual const rom_entry *device_rom_region() const;
+ virtual machine_config_constructor device_mconfig_additions() const;
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ DECLARE_READ8_MEMBER( data_to_i8052 );
+public:
+ DECLARE_WRITE8_MEMBER( wave_w );
+
+ DECLARE_READ8_MEMBER( p0_r );
+ DECLARE_WRITE8_MEMBER( p0_w );
+
+ DECLARE_READ8_MEMBER( p1_r );
+ DECLARE_WRITE8_MEMBER( p1_w );
+
+ DECLARE_READ8_MEMBER( p2_r );
+ DECLARE_WRITE8_MEMBER( p2_w );
+
+ DECLARE_READ8_MEMBER( p3_r );
+ DECLARE_WRITE8_MEMBER( p3_w );
+
+ enum
+ {
+ QS1000_KEYON = 1,
+ QS1000_PLAYING = 2,
+ QS1000_ADPCM = 4
+ };
+
+ void start_voice(int ch);
+ void set_voice_regs(int ch);
+
+ bool m_external_rom;
+
+ // Callbacks
+ devcb_read8 m_in_p1_cb;
+ devcb_read8 m_in_p2_cb;
+ devcb_read8 m_in_p3_cb;
+
+ devcb_write8 m_out_p1_cb;
+ devcb_write8 m_out_p2_cb;
+ devcb_write8 m_out_p3_cb;
+
+ //devcb_write8 m_serial_w_cb;
+
+ // Internal state
+ const address_space_config m_space_config;
+ sound_stream * m_stream;
+ direct_read_data * m_direct;
+ required_device<i8052_device> m_cpu;
+
+ // Wavetable engine
+ UINT8 m_serial_data_in;
+ UINT8 m_wave_regs[18];
+
+ struct qs1000_channel
+ {
+ UINT32 m_acc;
+ INT32 m_adpcm_signal;
+ UINT32 m_start;
+ UINT32 m_addr;
+ UINT32 m_adpcm_addr;
+ UINT32 m_loop_start;
+ UINT32 m_loop_end;
+ UINT16 m_freq;
+ UINT16 m_flags;
+
+ UINT8 m_regs[16]; // FIXME
+
+ oki_adpcm_state m_adpcm;
+ };
+
+ qs1000_channel m_channels[QS1000_CHANNELS];
+};
+
+
+// device type definition
+extern const device_type QS1000;
+
+
+#endif /* __QS1000_H__ */
diff --git a/src/devices/sound/qsound.c b/src/devices/sound/qsound.c
new file mode 100644
index 00000000000..c519da454a5
--- /dev/null
+++ b/src/devices/sound/qsound.c
@@ -0,0 +1,324 @@
+// license:???
+// copyright-holders:Paul Leaman, Miguel Angel Horna
+/***************************************************************************
+
+ Capcom System QSound(tm)
+ ========================
+
+ Driver by Paul Leaman and Miguel Angel Horna
+
+ A 16 channel stereo sample player.
+
+ QSpace position is simulated by panning the sound in the stereo space.
+
+ Many thanks to CAB (the author of Amuse), without whom this probably would
+ never have been finished.
+
+ TODO:
+ - hook up the DSP!
+ - is master volume really linear?
+ - understand higher bits of reg 0
+ - understand reg 9
+ - understand other writes to $90-$ff area
+
+***************************************************************************/
+
+#include "emu.h"
+#include "qsound.h"
+
+// device type definition
+const device_type QSOUND = &device_creator<qsound_device>;
+
+
+// program map for the DSP (points to internal 4096 words of internal ROM)
+static ADDRESS_MAP_START( dsp16_program_map, AS_PROGRAM, 16, qsound_device )
+ AM_RANGE(0x0000, 0x0fff) AM_ROM
+ADDRESS_MAP_END
+
+
+// data map for the DSP (the dsp16 appears to use 2048 words of internal RAM)
+static ADDRESS_MAP_START( dsp16_data_map, AS_DATA, 16, qsound_device )
+ ADDRESS_MAP_UNMAP_HIGH
+ AM_RANGE(0x0000, 0x07ff) AM_RAM
+ADDRESS_MAP_END
+
+
+// machine fragment
+static MACHINE_CONFIG_FRAGMENT( qsound )
+ MCFG_CPU_ADD("qsound", DSP16, QSOUND_CLOCK)
+ MCFG_CPU_PROGRAM_MAP(dsp16_program_map)
+ MCFG_CPU_DATA_MAP(dsp16_data_map)
+MACHINE_CONFIG_END
+
+
+// ROM definition for the Qsound program ROM
+// NOTE: ROM is marked as bad since a handful of questionable bits haven't been fully examined.
+ROM_START( qsound )
+ ROM_REGION( 0x2000, "qsound", 0 )
+ ROM_LOAD16_WORD( "qsound.bin", 0x0000, 0x2000, BAD_DUMP CRC(059c847d) SHA1(229cead1be2f86733dd80573d4983ba482355ece) )
+ROM_END
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// qsound_device - constructor
+//-------------------------------------------------
+
+qsound_device::qsound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, QSOUND, "Q-Sound", tag, owner, clock, "qsound", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_cpu(*this, "qsound"),
+ m_sample_rom(*this, DEVICE_SELF),
+ m_data(0),
+ m_stream(NULL)
+{
+}
+
+
+//-------------------------------------------------
+// rom_region - return a pointer to the device's
+// internal ROM region
+//-------------------------------------------------
+
+const rom_entry *qsound_device::device_rom_region() const
+{
+ return ROM_NAME( qsound );
+}
+
+
+//-------------------------------------------------
+// machine_config_additions - return a pointer to
+// the device's machine fragment
+//-------------------------------------------------
+
+machine_config_constructor qsound_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( qsound );
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void qsound_device::device_start()
+{
+ m_stream = stream_alloc(0, 2, clock() / 166); // /166 clock divider?
+
+ // create pan table
+ for (int i = 0; i < 33; i++)
+ m_pan_table[i] = (int)((256 / sqrt(32.0)) * sqrt((double)i));
+
+ // init sound regs
+ memset(m_channel, 0, sizeof(m_channel));
+
+ for (int adr = 0x7f; adr >= 0; adr--)
+ write_data(adr, 0);
+ for (int adr = 0x80; adr < 0x90; adr++)
+ write_data(adr, 0x120);
+
+ // state save
+ for (int i = 0; i < 16; i++)
+ {
+ save_item(NAME(m_channel[i].bank), i);
+ save_item(NAME(m_channel[i].address), i);
+ save_item(NAME(m_channel[i].freq), i);
+ save_item(NAME(m_channel[i].loop), i);
+ save_item(NAME(m_channel[i].end), i);
+ save_item(NAME(m_channel[i].vol), i);
+ save_item(NAME(m_channel[i].enabled), i);
+ save_item(NAME(m_channel[i].lvol), i);
+ save_item(NAME(m_channel[i].rvol), i);
+ save_item(NAME(m_channel[i].step_ptr), i);
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void qsound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ // Clear the buffers
+ memset(outputs[0], 0, samples * sizeof(*outputs[0]));
+ memset(outputs[1], 0, samples * sizeof(*outputs[1]));
+
+ for (int ch = 0; ch < 16; ch++)
+ {
+ if (m_channel[ch].enabled)
+ {
+ stream_sample_t *lmix=outputs[0];
+ stream_sample_t *rmix=outputs[1];
+
+ // Go through the buffer and add voice contributions
+ for (int i = 0; i < samples; i++)
+ {
+ m_channel[ch].address += (m_channel[ch].step_ptr >> 12);
+ m_channel[ch].step_ptr &= 0xfff;
+ m_channel[ch].step_ptr += m_channel[ch].freq;
+
+ if (m_channel[ch].address >= m_channel[ch].end)
+ {
+ if (m_channel[ch].loop)
+ {
+ // Reached the end, restart the loop
+ m_channel[ch].address -= m_channel[ch].loop;
+
+ // Make sure we don't overflow (what does the real chip do in this case?)
+ if (m_channel[ch].address >= m_channel[ch].end)
+ m_channel[ch].address = m_channel[ch].end - m_channel[ch].loop;
+
+ m_channel[ch].address &= 0xffff;
+ }
+ else
+ {
+ // Reached the end of a non-looped sample
+ m_channel[ch].enabled = false;
+ break;
+ }
+ }
+
+ INT8 sample = read_sample(m_channel[ch].bank | m_channel[ch].address);
+ *lmix++ += ((sample * m_channel[ch].lvol * m_channel[ch].vol) >> 14);
+ *rmix++ += ((sample * m_channel[ch].rvol * m_channel[ch].vol) >> 14);
+ }
+ }
+ }
+}
+
+
+WRITE8_MEMBER(qsound_device::qsound_w)
+{
+ switch (offset)
+ {
+ case 0:
+ m_data = (m_data & 0x00ff) | (data << 8);
+ break;
+
+ case 1:
+ m_data = (m_data & 0xff00) | data;
+ break;
+
+ case 2:
+ m_stream->update();
+ write_data(data, m_data);
+ break;
+
+ default:
+ logerror("%s: qsound_w %d = %02x\n", machine().describe_context(), offset, data);
+ break;
+ }
+}
+
+
+READ8_MEMBER(qsound_device::qsound_r)
+{
+ /* Port ready bit (0x80 if ready) */
+ return 0x80;
+}
+
+
+void qsound_device::write_data(UINT8 address, UINT16 data)
+{
+ int ch = 0, reg = 0;
+
+ // direct sound reg
+ if (address < 0x80)
+ {
+ ch = address >> 3;
+ reg = address & 7;
+ }
+
+ // >= 0x80 is probably for the dsp?
+ else if (address < 0x90)
+ {
+ ch = address & 0xf;
+ reg = 8;
+ }
+ else if (address >= 0xba && address < 0xca)
+ {
+ ch = address - 0xba;
+ reg = 9;
+ }
+ else
+ {
+ // unknown
+ reg = address;
+ }
+
+ switch (reg)
+ {
+ case 0:
+ // bank, high bits unknown
+ ch = (ch + 1) & 0xf; // strange ...
+ m_channel[ch].bank = data << 16;
+ break;
+
+ case 1:
+ // start/cur address
+ m_channel[ch].address = data;
+ break;
+
+ case 2:
+ // frequency
+ m_channel[ch].freq = data;
+ if (data == 0)
+ {
+ // key off
+ m_channel[ch].enabled = false;
+ }
+ break;
+
+ case 3:
+ // key on (does the value matter? it always writes 0x8000)
+ m_channel[ch].enabled = true;
+ m_channel[ch].step_ptr = 0;
+ break;
+
+ case 4:
+ // loop address
+ m_channel[ch].loop = data;
+ break;
+
+ case 5:
+ // end address
+ m_channel[ch].end = data;
+ break;
+
+ case 6:
+ // master volume
+ m_channel[ch].vol = data;
+ break;
+
+ case 7:
+ // unused?
+ break;
+
+ case 8:
+ {
+ // panning (left=0x0110, centre=0x0120, right=0x0130)
+ // looks like it doesn't write other values than that
+ int pan = (data & 0x3f) - 0x10;
+ if (pan > 0x20)
+ pan = 0x20;
+ if (pan < 0)
+ pan = 0;
+
+ m_channel[ch].rvol = m_pan_table[pan];
+ m_channel[ch].lvol = m_pan_table[0x20 - pan];
+ break;
+ }
+
+ case 9:
+ // unknown
+ break;
+
+ default:
+ //logerror("%s: write_data %02x = %04x\n", machine().describe_context(), address, data);
+ break;
+ }
+}
diff --git a/src/devices/sound/qsound.h b/src/devices/sound/qsound.h
new file mode 100644
index 00000000000..208fdac41e9
--- /dev/null
+++ b/src/devices/sound/qsound.h
@@ -0,0 +1,81 @@
+// license:???
+// copyright-holders:Paul Leaman, Miguel Angel Horna
+/*********************************************************
+
+ Capcom Q-Sound system
+
+*********************************************************/
+
+#pragma once
+
+#ifndef __QSOUND_H__
+#define __QSOUND_H__
+
+#include "cpu/dsp16/dsp16.h"
+
+#define QSOUND_CLOCK 4000000 /* default 4MHz clock (60MHz/15?) */
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_QSOUND_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, QSOUND, _clock)
+#define MCFG_QSOUND_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, QSOUND, _clock)
+
+
+// ======================> qsound_device
+
+class qsound_device : public device_t,
+ public device_sound_interface
+{
+public:
+ qsound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~qsound_device() { }
+
+ DECLARE_WRITE8_MEMBER(qsound_w);
+ DECLARE_READ8_MEMBER(qsound_r);
+
+protected:
+ // device-level overrides
+ const rom_entry *device_rom_region() const;
+ machine_config_constructor device_mconfig_additions() const;
+ 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 qsound_channel
+ {
+ UINT32 bank; // bank
+ UINT32 address; // start/cur address
+ UINT16 loop; // loop address
+ UINT16 end; // end address
+ UINT32 freq; // frequency
+ UINT16 vol; // master volume
+
+ // work variables
+ bool enabled; // key on / key off
+ int lvol; // left volume
+ int rvol; // right volume
+ UINT32 step_ptr; // current offset counter
+ } m_channel[16];
+
+ required_device<dsp16_device> m_cpu;
+ required_region_ptr<INT8> m_sample_rom;
+
+ int m_pan_table[33]; // pan volume table
+ UINT16 m_data; // register latch data
+ sound_stream *m_stream; // audio stream
+
+ inline INT8 read_sample(UINT32 offset) { return m_sample_rom[offset & m_sample_rom.mask()]; }
+ void write_data(UINT8 address, UINT16 data);
+};
+
+extern const device_type QSOUND;
+
+
+#endif /* __QSOUND_H__ */
diff --git a/src/devices/sound/rf5c400.c b/src/devices/sound/rf5c400.c
new file mode 100644
index 00000000000..2e3b823c013
--- /dev/null
+++ b/src/devices/sound/rf5c400.c
@@ -0,0 +1,551 @@
+// license:BSD-3-Clause
+// copyright-holders:Ville Linde
+/*
+ Ricoh RF5C400 emulator
+
+ Written by Ville Linde
+ Improvements by the hoot development team
+
+ history -
+ 2007-02-08 hoot development team
+ looping
+ stereo panning
+ 8-bit sample support
+
+ 2007-02-16 hoot development team
+ envelope
+ fixed volume table
+*/
+
+#include "emu.h"
+#include "rf5c400.h"
+
+static int volume_table[256];
+static double pan_table[0x64];
+
+/* envelope parameter (experimental) */
+#define ENV_AR_SPEED 0.1
+#define ENV_MIN_AR 0x02
+#define ENV_MAX_AR 0x80
+#define ENV_DR_SPEED 2.0
+#define ENV_MIN_DR 0x20
+#define ENV_MAX_DR 0x73
+#define ENV_RR_SPEED 0.7
+#define ENV_MIN_RR 0x20
+#define ENV_MAX_RR 0x54
+
+/* PCM type */
+enum
+{
+ TYPE_MASK = 0x00C0,
+ TYPE_16 = 0x0000,
+ TYPE_8LOW = 0x0040,
+ TYPE_8HIGH = 0x0080
+};
+
+/* envelope phase */
+enum
+{
+ PHASE_NONE = 0,
+ PHASE_ATTACK,
+ PHASE_DECAY,
+ PHASE_RELEASE
+};
+
+
+// device type definition
+const device_type RF5C400 = &device_creator<rf5c400_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// rf5c400_device - constructor
+//-------------------------------------------------
+
+rf5c400_device::rf5c400_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, RF5C400, "RF5C400", tag, owner, clock, "rf5c400", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_rom(*this, DEVICE_SELF),
+ m_stream(NULL)
+{
+ memset(m_env_ar_table, 0, sizeof(double)*0x9f);
+ memset(m_env_dr_table, 0, sizeof(double)*0x9f);
+ memset(m_env_rr_table, 0, sizeof(double)*0x9f);
+}
+
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void rf5c400_device::device_start()
+{
+ rf5c400_init_chip();
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void rf5c400_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int i, ch;
+ INT16 *rom = m_rom;
+ UINT32 end, loop;
+ UINT64 pos;
+ UINT8 vol, lvol, rvol, type;
+ UINT8 env_phase;
+ double env_level, env_step, env_rstep;
+
+ memset(outputs[0], 0, samples * sizeof(*outputs[0]));
+ memset(outputs[1], 0, samples * sizeof(*outputs[1]));
+
+ for (ch=0; ch < 32; ch++)
+ {
+ rf5c400_channel *channel = &m_channels[ch];
+ stream_sample_t *buf0 = outputs[0];
+ stream_sample_t *buf1 = outputs[1];
+
+// start = ((channel->startH & 0xFF00) << 8) | channel->startL;
+ end = ((channel->endHloopH & 0xFF) << 16) | channel->endL;
+ loop = ((channel->endHloopH & 0xFF00) << 8) | channel->loopL;
+ pos = channel->pos;
+ vol = channel->volume & 0xFF;
+ lvol = channel->pan & 0xFF;
+ rvol = channel->pan >> 8;
+ type = (channel->volume >> 8) & TYPE_MASK;
+
+ env_phase = channel->env_phase;
+ env_level = channel->env_level;
+ env_step = channel->env_step;
+ env_rstep = env_step * channel->env_scale;
+
+ for (i=0; i < samples; i++)
+ {
+ INT16 tmp;
+ INT32 sample;
+
+ if (env_phase == PHASE_NONE) break;
+
+ tmp = rom[(pos>>16) & m_rommask];
+ switch ( type )
+ {
+ case TYPE_16:
+ sample = tmp;
+ break;
+ case TYPE_8LOW:
+ sample = (INT16)(tmp << 8);
+ break;
+ case TYPE_8HIGH:
+ sample = (INT16)(tmp & 0xFF00);
+ break;
+ default:
+ sample = 0;
+ break;
+ }
+
+ if ( sample & 0x8000 )
+ {
+ sample ^= 0x7FFF;
+ }
+
+ env_level += env_rstep;
+ switch (env_phase)
+ {
+ case PHASE_ATTACK:
+ if (env_level >= 1.0)
+ {
+ env_phase = PHASE_DECAY;
+ env_level = 1.0;
+ if (channel->decay & 0x0080)
+ {
+ env_step = 0.0;
+ }
+ else
+ {
+ env_step =
+ m_env_dr_table[decode80(channel->decay >> 8)];
+ }
+ env_rstep = env_step * channel->env_scale;
+ }
+ break;
+ case PHASE_DECAY:
+ if (env_level <= 0.0)
+ {
+ env_phase = PHASE_NONE;
+ env_level = 0.0;
+ env_step = 0.0;
+ env_rstep = 0.0;
+ }
+ break;
+ case PHASE_RELEASE:
+ if (env_level <= 0.0)
+ {
+ env_phase = PHASE_NONE;
+ env_level = 0.0;
+ env_step = 0.0;
+ env_rstep = 0.0;
+ }
+ break;
+ }
+
+ sample *= volume_table[vol];
+ sample = (sample >> 9) * env_level;
+ *buf0++ += sample * pan_table[lvol];
+ *buf1++ += sample * pan_table[rvol];
+
+ pos += channel->step;
+ if ( (pos>>16) > m_rom.length() || (pos>>16) > end)
+ {
+ pos -= loop<<16;
+ pos &= U64(0xFFFFFF0000);
+ }
+
+ }
+ channel->pos = pos;
+
+ channel->env_phase = env_phase;
+ channel->env_level = env_level;
+ channel->env_step = env_step;
+ }
+}
+
+
+/*****************************************************************************/
+
+UINT8 rf5c400_device::decode80(UINT8 val)
+{
+ if (val & 0x80)
+ {
+ val = (val & 0x7f) + 0x1f;
+ }
+
+ return val;
+}
+
+void rf5c400_device::rf5c400_init_chip()
+{
+ int i;
+
+ // init volume table
+ {
+ double max=255.0;
+ for (i = 0; i < 256; i++) {
+ volume_table[i]=(UINT16)max;
+ max /= pow(10.0,(double)((4.5/(256.0/16.0))/20));
+ }
+ for(i = 0; i < 0x48; i++) {
+ pan_table[i] = sqrt( (double)(0x47 - i) ) / sqrt( (double)0x47 );
+ }
+ for(i = 0x48; i < 0x64; i++) {
+ pan_table[i] = 0.0;
+ }
+ }
+
+ // init envelope table
+ {
+ double r;
+
+ // attack
+ r = 1.0 / (ENV_AR_SPEED * (clock() / 384));
+ for (i = 0; i < ENV_MIN_AR; i++)
+ {
+ m_env_ar_table[i] = 1.0;
+ }
+ for (i = ENV_MIN_AR; i < ENV_MAX_AR; i++)
+ {
+ m_env_ar_table[i] =
+ r * (ENV_MAX_AR - i) / (ENV_MAX_AR - ENV_MIN_AR);
+ }
+ for (i = ENV_MAX_AR; i < 0x9f; i++)
+ {
+ m_env_ar_table[i] = 0.0;
+ }
+
+ // decay
+ r = -5.0 / (ENV_DR_SPEED * (clock() / 384));
+ for (i = 0; i < ENV_MIN_DR; i++)
+ {
+ m_env_dr_table[i] = r;
+ }
+ for (i = ENV_MIN_DR; i < ENV_MAX_DR; i++)
+ {
+ m_env_dr_table[i] =
+ r * (ENV_MAX_DR - i) / (ENV_MAX_DR - ENV_MIN_DR);
+ }
+ for (i = ENV_MAX_DR; i < 0x9f; i++)
+ {
+ m_env_dr_table[i] = 0.0;
+ }
+
+ // release
+ r = -5.0 / (ENV_RR_SPEED * (clock() / 384));
+ for (i = 0; i < ENV_MIN_RR; i++)
+ {
+ m_env_rr_table[i] = r;
+ }
+ for (i = ENV_MIN_RR; i < ENV_MAX_RR; i++)
+ {
+ m_env_rr_table[i] =
+ r * (ENV_MAX_RR - i) / (ENV_MAX_RR - ENV_MIN_RR);
+ }
+ for (i = ENV_MAX_RR; i < 0x9f; i++)
+ {
+ m_env_rr_table[i] = 0.0;
+ }
+ }
+
+ // init channel info
+ for (i = 0; i < 32; i++)
+ {
+ m_channels[i].env_phase = PHASE_NONE;
+ m_channels[i].env_level = 0.0;
+ m_channels[i].env_step = 0.0;
+ m_channels[i].env_scale = 1.0;
+ }
+
+ for (i = 0; i < ARRAY_LENGTH(m_channels); i++)
+ {
+ save_item(NAME(m_channels[i].startH), i);
+ save_item(NAME(m_channels[i].startL), i);
+ save_item(NAME(m_channels[i].freq), i);
+ save_item(NAME(m_channels[i].endL), i);
+ save_item(NAME(m_channels[i].endHloopH), i);
+ save_item(NAME(m_channels[i].loopL), i);
+ save_item(NAME(m_channels[i].pan), i);
+ save_item(NAME(m_channels[i].effect), i);
+ save_item(NAME(m_channels[i].volume), i);
+ save_item(NAME(m_channels[i].attack), i);
+ save_item(NAME(m_channels[i].decay), i);
+ save_item(NAME(m_channels[i].release), i);
+ save_item(NAME(m_channels[i].cutoff), i);
+ save_item(NAME(m_channels[i].pos), i);
+ save_item(NAME(m_channels[i].step), i);
+ save_item(NAME(m_channels[i].keyon), i);
+ save_item(NAME(m_channels[i].env_phase), i);
+ save_item(NAME(m_channels[i].env_level), i);
+ save_item(NAME(m_channels[i].env_step), i);
+ save_item(NAME(m_channels[i].env_scale), i);
+ }
+
+ m_stream = stream_alloc(0, 2, clock()/384);
+
+ m_rommask = m_rom.length() - 1;
+}
+
+
+/*****************************************************************************/
+
+static UINT16 rf5c400_status = 0;
+READ16_MEMBER( rf5c400_device::rf5c400_r )
+{
+ switch(offset)
+ {
+ case 0x00:
+ {
+ return rf5c400_status;
+ }
+
+ case 0x04:
+ {
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+WRITE16_MEMBER( rf5c400_device::rf5c400_w )
+{
+ if (offset < 0x400)
+ {
+ switch(offset)
+ {
+ case 0x00:
+ {
+ rf5c400_status = data;
+ break;
+ }
+
+ case 0x01: // channel control
+ {
+ int ch = data & 0x1f;
+ switch ( data & 0x60 )
+ {
+ case 0x60:
+ m_channels[ch].pos =
+ ((m_channels[ch].startH & 0xFF00) << 8) | m_channels[ch].startL;
+ m_channels[ch].pos <<= 16;
+
+ m_channels[ch].env_phase = PHASE_ATTACK;
+ m_channels[ch].env_level = 0.0;
+ m_channels[ch].env_step =
+ m_env_ar_table[decode80(m_channels[ch].attack >> 8)];
+ break;
+ case 0x40:
+ if (m_channels[ch].env_phase != PHASE_NONE)
+ {
+ m_channels[ch].env_phase = PHASE_RELEASE;
+ if (m_channels[ch].release & 0x0080)
+ {
+ m_channels[ch].env_step = 0.0;
+ }
+ else
+ {
+ m_channels[ch].env_step =
+ m_env_rr_table[decode80(m_channels[ch].release >> 8)];
+ }
+ }
+ break;
+ default:
+ m_channels[ch].env_phase = PHASE_NONE;
+ m_channels[ch].env_level = 0.0;
+ m_channels[ch].env_step = 0.0;
+ break;
+ }
+ break;
+ }
+
+ case 0x08: // relative to env attack (channel no)
+ case 0x09: // relative to env attack (0x0c00/ 0x1c00)
+
+ case 0x11: // ? counter for 0x13?
+ {
+ break;
+ }
+ case 0x13: // ? bujutsu writes sample data here
+ {
+ break;
+ }
+
+ case 0x14: // ? related to 0x11/0x13 ?
+ break;
+
+ case 0x21: // reverb(character).w
+ case 0x32: // reverb(pre-lpf).w
+ case 0x2B: // reverb(level).w
+ case 0x20: // ???.b : reverb(time).b
+
+ case 0x2C: // chorus(level).w
+ case 0x30: // chorus(rate).w
+ case 0x22: // chorus(macro).w
+ case 0x23: // chorus(depth).w
+ case 0x24: // chorus(macro).w
+ case 0x2F: // chorus(depth).w
+ case 0x27: // chorus(send level to reverb).w
+
+ default:
+ {
+ //osd_printf_debug("%s:rf5c400_w: %08X, %08X, %08X\n", machine().describe_context(), data, offset, mem_mask);
+ break;
+ }
+ }
+ //osd_printf_debug("%s:rf5c400_w: %08X, %08X, %08X at %08X\n", machine().describe_context(), data, offset, mem_mask);
+ }
+ else
+ {
+ // channel registers
+ int ch = (offset >> 5) & 0x1f;
+ int reg = (offset & 0x1f);
+
+ rf5c400_channel *channel = &m_channels[ch];
+
+ switch (reg)
+ {
+ case 0x00: // sample start address, bits 23 - 16
+ {
+ channel->startH = data;
+ break;
+ }
+ case 0x01: // sample start address, bits 15 - 0
+ {
+ channel->startL = data;
+ break;
+ }
+ case 0x02: // sample playing frequency
+ {
+ channel->step = ((data & 0x1fff) << (data >> 13)) * 4;
+ channel->freq = data;
+ break;
+ }
+ case 0x03: // sample end address, bits 15 - 0
+ {
+ channel->endL = data;
+ break;
+ }
+ case 0x04: // sample end address, bits 23 - 16 , sample loop 23 - 16
+ {
+ channel->endHloopH = data;
+ break;
+ }
+ case 0x05: // sample loop offset, bits 15 - 0
+ {
+ channel->loopL = data;
+ break;
+ }
+ case 0x06: // channel volume
+ {
+ channel->pan = data;
+ break;
+ }
+ case 0x07: // effect depth
+ {
+ // 0xCCRR: CC = chorus send depth, RR = reverb send depth
+ channel->effect = data;
+ break;
+ }
+ case 0x08: // volume, flag
+ {
+ channel->volume = data;
+ break;
+ }
+ case 0x09: // env attack
+ {
+ // 0x0100: max speed (in case of attack <= 0x40)
+ // 0xXX40: XX = attack-0x3f (encoded) (in case of attack > 0x40)
+ //
+ channel->attack = data;
+ break;
+ }
+ case 0x0A: // relative to env attack ?
+ {
+ // always 0x0100
+ break;
+ }
+ case 0x0B: // relative to env decay ?
+ {
+ // always 0x0100
+ break;
+ }
+ case 0x0C: // env decay
+ {
+ // 0xXX70: XX = decay (encoded) (in case of decay > 0x71)
+ // 0xXX80: XX = decay (encoded) (in case of decay <= 0x71)
+ channel->decay = data;
+ break;
+ }
+ case 0x0D: // relative to env release ?
+ {
+ // always 0x0100
+ break;
+ }
+ case 0x0E: // env release
+ {
+ // 0xXX70: XX = release-0x1f (encoded) (0x01 if release <= 0x20)
+ channel->release = data;
+ break;
+ }
+ case 0x10: // resonance, cutoff freq.
+ {
+ // bit 15-12: resonance
+ // bit 11-0 : cutoff frequency
+ channel->cutoff = data;
+ break;
+ }
+ }
+ }
+}
diff --git a/src/devices/sound/rf5c400.h b/src/devices/sound/rf5c400.h
new file mode 100644
index 00000000000..4781f8893d9
--- /dev/null
+++ b/src/devices/sound/rf5c400.h
@@ -0,0 +1,117 @@
+// license:BSD-3-Clause
+// copyright-holders:Ville Linde
+/* Ricoh RF5C400 emulator */
+
+#pragma once
+
+#ifndef __RF5C400_H__
+#define __RF5C400_H__
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_RF5C400_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, RF5C400, _clock)
+#define MCFG_RF5C400_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, RF5C400, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+struct rf5c400_channel
+{
+ rf5c400_channel() :
+ startH(0),
+ startL(0),
+ freq(0),
+ endL(0),
+ endHloopH(0),
+ loopL(0),
+ pan(0),
+ effect(0),
+ volume(0),
+ attack(0),
+ decay(0),
+ release(0),
+ cutoff(0),
+ pos(0),
+ step(0),
+ keyon(0),
+ env_phase(0),
+ env_level(0.0),
+ env_step(0.0),
+ env_scale(0.0)
+ { }
+
+ UINT16 startH;
+ UINT16 startL;
+ UINT16 freq;
+ UINT16 endL;
+ UINT16 endHloopH;
+ UINT16 loopL;
+ UINT16 pan;
+ UINT16 effect;
+ UINT16 volume;
+
+ UINT16 attack;
+ UINT16 decay;
+ UINT16 release;
+
+ UINT16 cutoff;
+
+ UINT64 pos;
+ UINT64 step;
+ UINT16 keyon;
+
+ UINT8 env_phase;
+ double env_level;
+ double env_step;
+ double env_scale;
+};
+
+
+// ======================> rf5c400_device
+
+class rf5c400_device : public device_t,
+ public device_sound_interface
+{
+public:
+ rf5c400_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~rf5c400_device() { }
+
+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);
+
+public:
+ DECLARE_READ16_MEMBER( rf5c400_r );
+ DECLARE_WRITE16_MEMBER( rf5c400_w );
+
+private:
+ void rf5c400_init_chip();
+ UINT8 decode80(UINT8 val);
+
+private:
+ required_region_ptr<INT16> m_rom;
+
+ UINT32 m_rommask;
+
+ sound_stream *m_stream;
+
+ double m_env_ar_table[0x9f];
+ double m_env_dr_table[0x9f];
+ double m_env_rr_table[0x9f];
+
+ rf5c400_channel m_channels[32];
+};
+
+extern const device_type RF5C400;
+
+
+#endif /* __RF5C400_H__ */
diff --git a/src/devices/sound/rf5c68.c b/src/devices/sound/rf5c68.c
new file mode 100644
index 00000000000..af99edd197a
--- /dev/null
+++ b/src/devices/sound/rf5c68.c
@@ -0,0 +1,233 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert,Aaron Giles
+/*********************************************************/
+/* ricoh RF5C68(or clone) PCM controller */
+/*********************************************************/
+
+#include "emu.h"
+#include "rf5c68.h"
+
+
+// device type definition
+const device_type RF5C68 = &device_creator<rf5c68_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// rf5c68_device - constructor
+//-------------------------------------------------
+
+rf5c68_device::rf5c68_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, RF5C68, "RF5C68", tag, owner, clock, "rf5c68", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_cbank(0),
+ m_wbank(0),
+ m_enable(0)
+{
+ memset(m_data, 0, sizeof(UINT8)*0x10000);
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void rf5c68_device::device_start()
+{
+ m_sample_end_cb.bind_relative_to(*owner());
+
+ /* allocate memory for the chip */
+ memset(m_data, 0xff, sizeof(m_data));
+
+ /* allocate the stream */
+ m_stream = stream_alloc(0, 2, clock() / 384);
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void rf5c68_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *left = outputs[0];
+ stream_sample_t *right = outputs[1];
+ int i, j;
+
+ /* start with clean buffers */
+ memset(left, 0, samples * sizeof(*left));
+ memset(right, 0, samples * sizeof(*right));
+
+ /* bail if not enabled */
+ if (!m_enable)
+ return;
+
+ /* loop over channels */
+ for (i = 0; i < RF5C68_NUM_CHANNELS; i++)
+ {
+ rf5c68_pcm_channel *chan = &m_chan[i];
+
+ /* if this channel is active, accumulate samples */
+ if (chan->enable)
+ {
+ int lv = (chan->pan & 0x0f) * chan->env;
+ int rv = ((chan->pan >> 4) & 0x0f) * chan->env;
+
+ /* loop over the sample buffer */
+ for (j = 0; j < samples; j++)
+ {
+ int sample;
+
+ /* trigger sample callback */
+ if(!m_sample_end_cb.isnull())
+ {
+ if(((chan->addr >> 11) & 0xfff) == 0xfff)
+ m_sample_end_cb((chan->addr >> 11)/0x2000);
+ }
+
+ /* fetch the sample and handle looping */
+ sample = m_data[(chan->addr >> 11) & 0xffff];
+ if (sample == 0xff)
+ {
+ chan->addr = chan->loopst << 11;
+ sample = m_data[(chan->addr >> 11) & 0xffff];
+
+ /* if we loop to a loop point, we're effectively dead */
+ if (sample == 0xff)
+ break;
+ }
+ chan->addr += chan->step;
+
+ /* add to the buffer */
+ if (sample & 0x80)
+ {
+ sample &= 0x7f;
+ left[j] += (sample * lv) >> 5;
+ right[j] += (sample * rv) >> 5;
+ }
+ else
+ {
+ left[j] -= (sample * lv) >> 5;
+ right[j] -= (sample * rv) >> 5;
+ }
+ }
+ }
+ }
+
+ /* now clamp and shift the result (output is only 10 bits) */
+ for (j = 0; j < samples; j++)
+ {
+ stream_sample_t temp;
+
+ temp = left[j];
+ if (temp > 32767) temp = 32767;
+ else if (temp < -32768) temp = -32768;
+ left[j] = temp & ~0x3f;
+
+ temp = right[j];
+ if (temp > 32767) temp = 32767;
+ else if (temp < -32768) temp = -32768;
+ right[j] = temp & ~0x3f;
+ }
+}
+
+
+//-------------------------------------------------
+// RF5C68 write register
+//-------------------------------------------------
+
+READ8_MEMBER( rf5c68_device::rf5c68_r )
+{
+ UINT8 shift;
+
+ m_stream->update();
+ shift = (offset & 1) ? 11 + 8 : 11;
+
+// printf("%08x\n",(m_chan[(offset & 0x0e) >> 1].addr));
+
+ return (m_chan[(offset & 0x0e) >> 1].addr) >> (shift);
+}
+
+WRITE8_MEMBER( rf5c68_device::rf5c68_w )
+{
+ rf5c68_pcm_channel *chan = &m_chan[m_cbank];
+ int i;
+
+ /* force the stream to update first */
+ m_stream->update();
+
+ /* switch off the address */
+ switch (offset)
+ {
+ case 0x00: /* envelope */
+ chan->env = data;
+ break;
+
+ case 0x01: /* pan */
+ chan->pan = data;
+ break;
+
+ case 0x02: /* FDL */
+ chan->step = (chan->step & 0xff00) | (data & 0x00ff);
+ break;
+
+ case 0x03: /* FDH */
+ chan->step = (chan->step & 0x00ff) | ((data << 8) & 0xff00);
+ break;
+
+ case 0x04: /* LSL */
+ chan->loopst = (chan->loopst & 0xff00) | (data & 0x00ff);
+ break;
+
+ case 0x05: /* LSH */
+ chan->loopst = (chan->loopst & 0x00ff) | ((data << 8) & 0xff00);
+ break;
+
+ case 0x06: /* ST */
+ chan->start = data;
+ if (!chan->enable)
+ chan->addr = chan->start << (8 + 11);
+ break;
+
+ case 0x07: /* control reg */
+ m_enable = (data >> 7) & 1;
+ if (data & 0x40)
+ m_cbank = data & 7;
+ else
+ m_wbank = data & 15;
+ break;
+
+ case 0x08: /* channel on/off reg */
+ for (i = 0; i < 8; i++)
+ {
+ m_chan[i].enable = (~data >> i) & 1;
+ if (!m_chan[i].enable)
+ m_chan[i].addr = m_chan[i].start << (8 + 11);
+ }
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// RF5C68 read memory
+//-------------------------------------------------
+
+READ8_MEMBER( rf5c68_device::rf5c68_mem_r )
+{
+ return m_data[m_wbank * 0x1000 + offset];
+}
+
+
+//-------------------------------------------------
+// RF5C68 write memory
+//-------------------------------------------------
+
+WRITE8_MEMBER( rf5c68_device::rf5c68_mem_w )
+{
+ m_data[m_wbank * 0x1000 + offset] = data;
+}
diff --git a/src/devices/sound/rf5c68.h b/src/devices/sound/rf5c68.h
new file mode 100644
index 00000000000..8dafb114f21
--- /dev/null
+++ b/src/devices/sound/rf5c68.h
@@ -0,0 +1,96 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert,Aaron Giles
+/*********************************************************/
+/* ricoh RF5C68(or clone) PCM controller */
+/*********************************************************/
+
+#pragma once
+
+#ifndef __RF5C68_H__
+#define __RF5C68_H__
+
+#define RF5C68_NUM_CHANNELS (8)
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_RF5C68_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, RF5C68, _clock)
+#define MCFG_RF5C68_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, RF5C68, _clock)
+
+#define MCFG_RF5C68_SAMPLE_END_CB(_class, _method) \
+ rf5c68_device::set_end_callback(*device, rf5c68_sample_end_cb_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+typedef device_delegate<void (int channel)> rf5c68_sample_end_cb_delegate;
+
+#define RF5C68_SAMPLE_END_CB_MEMBER(_name) void _name(int channel)
+
+
+struct rf5c68_pcm_channel
+{
+ rf5c68_pcm_channel() :
+ enable(0),
+ env(0),
+ pan(0),
+ start(0),
+ addr(0),
+ step(0),
+ loopst(0) {}
+
+ UINT8 enable;
+ UINT8 env;
+ UINT8 pan;
+ UINT8 start;
+ UINT32 addr;
+ UINT16 step;
+ UINT16 loopst;
+};
+
+
+// ======================> rf5c68_device
+
+class rf5c68_device : public device_t,
+ public device_sound_interface
+{
+public:
+ rf5c68_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~rf5c68_device() { }
+
+ static void set_end_callback(device_t &device, rf5c68_sample_end_cb_delegate callback) { downcast<rf5c68_device &>(device).m_sample_end_cb = callback; }
+
+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);
+
+public:
+ DECLARE_READ8_MEMBER( rf5c68_r );
+ DECLARE_WRITE8_MEMBER( rf5c68_w );
+
+ DECLARE_READ8_MEMBER( rf5c68_mem_r );
+ DECLARE_WRITE8_MEMBER( rf5c68_mem_w );
+
+private:
+ sound_stream* m_stream;
+ rf5c68_pcm_channel m_chan[RF5C68_NUM_CHANNELS];
+ UINT8 m_cbank;
+ UINT8 m_wbank;
+ UINT8 m_enable;
+ UINT8 m_data[0x10000];
+
+ rf5c68_sample_end_cb_delegate m_sample_end_cb;
+};
+
+extern const device_type RF5C68;
+
+
+#endif /* __RF5C68_H__ */
diff --git a/src/devices/sound/s14001a.c b/src/devices/sound/s14001a.c
new file mode 100644
index 00000000000..50cca284241
--- /dev/null
+++ b/src/devices/sound/s14001a.c
@@ -0,0 +1,651 @@
+// license:BSD-3-Clause
+// copyright-holders:Jonathan Gevaryahu,R. Belmont,Zsolt Vasvari
+/*
+
+ TSI S14001A emulator v1.32
+ By Jonathan Gevaryahu ("Lord Nightmare") with help from Kevin Horton ("kevtris")
+ MAME conversion and integration by R. Belmont
+ Clock Frequency control updated by Zsolt Vasvari
+ Other fixes by AtariAce
+
+ Copyright (C) 2006-2013 Jonathan Gevaryahu aka Lord Nightmare
+
+ Version history:
+ 0.8 initial version - LN
+ 0.9 MAME conversion, glue code added - R. Belmont
+ 1.0 partly fixed stream update - LN (0.111u4)
+ 1.01 fixed clipping problem - LN (0.111u5)
+ 1.1 add VSU-1000 features, fully fixed stream update by fixing word latching - LN (0.111u6)
+ 1.11 fix signedness of output, pre-multiply, fixes clicking on VSU-1000 volume change - LN (0.111u7)
+ 1.20 supports setting the clock freq directly - reset is done by external hardware,
+ the chip has no reset line ZV (0.122)
+ 1.30 move main dac to 4 bits only with no extension (4->16 bit range extension is now done by output).
+ Added a somewhat better, but still not perfect, filtering system - LN
+ 1.31 fix a minor bug with the dac range. wolfpack clips again, and I'm almost sure its an encoding error on the original speech - LN (0.125u9)
+ 1.31a Add chip pinout and other notes - LN (0.128u4)
+ 1.31b slight update to notes to clarify input bus stuff, mostly finish the state map in the comments - LN
+ 1.31c remove usage of deprecat lib - AtariAce (0.128u5)
+ 1.32 fix the squealing noise using a define; it isn't accurate to the chip exactly, but there are other issues which need to be fixed too. see TODO. - LN (0.136u2)
+
+ TODO:
+ * increase accuracy of internal S14001A 'filter' for both driven and undriven cycles (its not terribly inaccurate for undriven cycles, but the dc sliding of driven cycles is not emulated)
+ * add option for and attach Frank P.'s emulation of the Analog external filter from the vsu-1000 using the discrete core. (with the direction of independent sound core and analog stuff, this should actually be attached in the main berzerk/frenzy driver and not here)
+ * fix the local and global silence stuff to not force the dac to a specific level, but cease doing deltas (i.e. force all deltas to 0) after the last sample; this should fix the clipping in wolfpack and in the fidelity games in mess.
+*/
+
+/* Chip Pinout:
+The original datasheet (which is lost as far as I know) clearly called the
+s14001a chip the 'CRC chip', or 'Custom Rom Controller', as it appears with
+this name on the Stern and Canon schematics, as well as on some TSI speech
+print advertisements.
+Labels are not based on the labels used by the Atari wolf pack and Stern
+schematics, as these are inconsistent. Atari calls the word select/speech address
+input pins SAx while Stern calls them Cx. Also Atari and Canon both have the bit
+ordering for the word select/speech address bus backwards, which may indicate it
+was so on the original datasheet. Stern has it correct, and I've used their Cx
+labeling.
+
+ ______ ______
+ _|o \__/ |_
+ +5V -- |_|1 40|_| -> /BUSY*
+ _| |_
+ ?TEST ?? |_|2 39|_| <- ROM D7
+ _| |_
+ XTAL CLOCK/CKC -> |_|3 38|_| -> ROM A11
+ _| |_
+ ROM CLOCK/CKR <- |_|4 37|_| <- ROM D6
+ _| |_
+ DIGITAL OUT 0 <- |_|5 36|_| -> ROM A10
+ _| |_
+ DIGITAL OUT 1 <- |_|6 35|_| -> ROM A9
+ _| |_
+ DIGITAL OUT 2 <- |_|7 34|_| <- ROM D5
+ _| |_
+ DIGITAL OUT 3 <- |_|8 33|_| -> ROM A8
+ _| |_
+ ROM /EN <- |_|9 32|_| <- ROM D4
+ _| S |_
+ START -> |_|10 7 1 T 31|_| -> ROM A7
+ _| 7 4 S |_
+ AUDIO OUT <- |_|11 3 0 I 30|_| <- ROM D3
+ _| 7 0 |_
+ ROM A0 <- |_|12 1 29|_| -> ROM A6
+ _| A |_
+SPCH ADR BUS C0 -> |_|13 28|_| <- SPCH ADR BUS C5
+ _| |_
+ ROM A1 <- |_|14 27|_| <- ROM D2
+ _| |_
+SPCH ADR BUS C1 -> |_|15 26|_| <- SPCH ADR BUS C4
+ _| |_
+ ROM A2 <- |_|16 25|_| <- ROM D1
+ _| |_
+SPCH ADR BUS C2 -> |_|17 24|_| <- SPCH ADR BUS C3
+ _| |_
+ ROM A3 <- |_|18 23|_| <- ROM D0
+ _| |_
+ ROM A4 <- |_|19 22|_| -> ROM A5
+ _| |_
+ GND -- |_|20 21|_| -- -10V
+ |________________|
+
+*Note from Kevin Horton when testing the hookup of the S14001A: the /BUSY line
+is not a standard voltage line: when it is in its HIGH state (i.e. not busy) it
+puts out a voltage of -10 volts, so it needs to be dropped back to a sane
+voltage level before it can be passed to any sort of modern IC. The address
+lines for the speech rom (A0-A11) do not have this problem, they output at a
+TTL/CMOS compatible voltage. The AUDIO OUT pin also outputs a voltage below GND,
+and the TEST pins may do so too.
+
+START is pulled high when a word is to be said and the word number is on the
+word select/speech address input lines. The Canon 'Canola' uses a separate 'rom
+strobe' signal independent of the chip to either enable or clock the speech rom.
+Its likely that they did this to be able to force the speech chip to stop talking,
+which is normally impossible. The later 'version 3' TSI speech board as featured in
+an advertisement in the John Cater book probably also has this feature, in addition
+to external speech rom banking.
+
+The Digital out pins supply a copy of the 4-bit waveform which also goes to the
+internal DAC. They are only valid every other clock cycle. It is possible that
+on 'invalid' cycles they act as a 4 bit input to drive the dac.
+
+Because it requires -10V to operate, the chip manufacturing process must be PMOS.
+
+/-----------\
+> Operation <
+\-----------/
+Put the 6-bit address of the word to be said onto the C0-C5 word select/speech
+address bus lines. Next, clock the START line low-high-low. As long as the START
+line is held high, the first address byte of the first word will be read repeatedly
+every clock, with the rom enable line enabled constantly (i.e. it doesn't toggle on
+and off as it normally does during speech). Once START has gone low-high-low, the
+/BUSY line will go low until 3 clocks after the chip is done speaking.
+*/
+
+
+/* state map:
+
+ * state machine 1: odd/even clock state
+ * on even clocks, audio output is floating, /romen is low so rom data bus is driven, input is latched?
+ * on odd clocks, audio output is driven, /romen is high, state machine 2 is clocked
+ * *****
+ * state machine 2: decoder state
+ * NOTE: holding the start line high forces the state machine 2 state to go to or remain in state 1!
+ * state 0(Idle): Idle (no sample rom bus activity, output at 0), next state is 0(Idle)
+
+ * state 1(GetHiWord):
+ * grab byte at (wordinput<<1) -> register_WH
+ * reset output DAC accumulator to 0x8 <- ???
+ * reset OldValHi to 1
+ * reset OldValLo to 0
+ * next state is 2(GetLoWord) UNLESS the PLAY line is still high, in which case the state remains at 1
+
+ * state 2(GetLoWord):
+ * grab byte at (wordinput<<1)+1 -> register_WL
+ * next state is 3(GetHiPhon)
+
+ * state 3(GetHiPhon):
+ * grab byte at ((register_WH<<8) + (register_WL))>>4 -> phoneaddress
+ * next state is 4(GetLoPhon)
+
+ * state 4(GetLoPhon):
+ * grab byte at (((register_WH<<8) + (register_WL))>>4)+1 -> playparams
+ * set phonepos register to 0
+ * set oddphone register to 0
+ * next state is 5(PlayForward1)
+ * playparams:
+ * 7 6 5 4 3 2 1 0
+ * G G = LastPhone
+ * B B = PlayMode
+ * Y Y = Silenceflag
+ * S S S S = Length count load value
+ * R R R = Repeat count reload value (upon carry/overflow of 3 bits)
+ * load the repeat counter with the bits 'R R 0'
+ * load the length counter with the bits 'S S S 0'
+ * NOTE: though only three bits of the length counter load value are controllable, there is a fourth lower bit which is assumed 0 on start and controls the direction of playback, i.e. forwards or backwards within a phone.
+ * NOTE: though only two bits of the repeat counter reload value are controllable, there is a third bit which is loaded to 0 on phoneme start, and this hidden low-order bit of the counter itself is what controls whether the output is forced to silence in mirrored mode. the 'carry' from the highest bit of the 3 bit counter is what increments the address pointer for pointing to the next phoneme in mirrored mode
+
+
+ * shift register diagram:
+ * F E D C B A 9 8 7 6 5 4 3 2 1 0
+ * <new byte here>
+ * C C C = Current delta sample read point
+ * O O O = Old delta sample read point
+ * I *OPTIMIZED OUT* the shift register by making use of the fact that the device reads each rom byte 4 times
+
+ * state 5(PlayForward1):
+ * grab byte at (((phoneaddress<<8)+(oddphone*8))+(phonepos>>2)) -> PlayRegister high end, bits F to 8
+ * if Playmode is mirrored, set OldValHi and OldValLo to 1 and 0 respectively, otherwise leave them with whatever was in them before.
+ * Put OldValHi in bit 7 of PlayRegister
+ * Put OldValLo in bit 6 of PlayRegister
+ * Get new OldValHi from bit 9
+ * Get new OldValLo from bit 8
+ * feed current delta (bits 9 and 8) and olddelta (bits 7 and 6) to delta demodulator table, delta demodulator table applies a delta to the accumulator, accumulator goes to enable/disable latch which Silenceflag enables or disables (forces output to 0x8 on disable), then to DAC to output.
+ * next state: state 6(PlayForward2)
+
+ * state 6(PlayForward2):
+ * grab byte at (((phoneaddress<<8)+oddphone)+(phonepos>>2)) -> PlayRegister bits D to 6.
+ * Put OldValHi in bit 7 of PlayRegister\____already done by above operation
+ * Put OldValLo in bit 6 of PlayRegister/
+ * Get new OldValHi from bit 9
+ * Get new OldValLo from bit 8
+ * feed current delta (bits 9 and 8) and olddelta (bits 7 and 6) to delta demodulator table, delta demodulator table applies a delta to the accumulator, accumulator goes to enable/disable latch which Silenceflag enables or disables (forces output to 0x8 on disable), then to DAC to output.
+ * next state: state 7(PlayForward3)
+
+ * state 7(PlayForward3):
+ * grab byte at (((phoneaddress<<8)+oddphone)+(phonepos>>2)) -> PlayRegister bits B to 4.
+ * Put OldValHi in bit 7 of PlayRegister\____already done by above operation
+ * Put OldValLo in bit 6 of PlayRegister/
+ * Get new OldValHi from bit 9
+ * Get new OldValLo from bit 8
+ * feed current delta (bits 9 and 8) and olddelta (bits 7 and 6) to delta demodulator table, delta demodulator table applies a delta to the accumulator, accumulator goes to enable/disable latch which Silenceflag enables or disables (forces output to 0x8 on disable), then to DAC to output.
+ * next state: state 8(PlayForward4)
+
+ * state 8(PlayForward4):
+ * grab byte at (((phoneaddress<<8)+oddphone)+(phonepos>>2)) -> PlayRegister bits 9 to 2.
+ * Put OldValHi in bit 7 of PlayRegister\____already done by above operation
+ * Put OldValLo in bit 6 of PlayRegister/
+ * Get new OldValHi from bit 9
+ * Get new OldValLo from bit 8
+ * feed current delta (bits 9 and 8) and olddelta (bits 7 and 6) to delta demodulator table, delta demodulator table applies a delta to the accumulator, accumulator goes to enable/disable latch which Silenceflag enables or disables (forces output to 0x8 on disable), then to DAC to output.
+ * if offset < 8, increment offset within 8-byte phone
+ * if offset = 8: (see PostPhoneme code to understand how this part works, its a bit complicated)
+
+ * next state: depends on playparams:
+ * if we're in mirrored mode, next will be LoadAndPlayBackward1
+ * if we're in nonmirrored mode, next will be PlayForward1
+
+ * state 9(LoadAndPlayBackward1)
+ * grab byte at (((phoneaddress<<8)+(oddphone*8))+(phonepos>>2)) -> PlayRegister high end, bits F to 8 <- check code on this, I think its backwards here but its correct in the code
+ * see code for this, its basically the same as state 8 but with the byte grab mentioned above, and the values fed to the delta demod table are switched
+ * state 10(PlayBackward2)
+ * see code for this, its basically the same as state 7 but the values fed to the delta demod table are switched
+ * state 11(PlayBackward3)
+ * see code for this, its basically the same as state 6 but the values fed to the delta demod table are switched
+ * state 12(PlayBackward4)
+ * see code for this, its basically the same as state 5 but with no byte grab, and the values fed to the delta demod table are switched, and a bit below similar to state 5
+ * if offset > -1, decrement offset within 8-byte phone
+ * if offset = -1: (see PostPhoneme code to understand how this part works, its a bit complicated)
+*/
+
+/* increment address function:
+ * increment repeat counter
+ if repeat counter produces a carry, do two things:
+ 1. if mirrored mode is ON, increment oddphone. if oddphone carries out (i.e. if it was 1), increment phoneaddress and zero oddphone
+ 2. increment lengthcounter. if lengthcounter carries out, we're done this phone.
+ * increment output counter
+ * if mirrored mode is on, output direction is
+ * if mirrored mode is OFF, increment oddphone. if not, don't touch it here. if oddphone was 1 before the increment, increment phoneaddress and set oddphone to 0
+ *
+ */
+
+#undef ACCURATE_SQUEAL
+
+#include "emu.h"
+#include "s14001a.h"
+
+
+//#define DEBUGSTATE
+
+#define SILENCE 0x7 // value output when silent
+#define ALTFLAG 0xFF // value to tell renderer that this frame's output is the average of the 8 prior frames and not directly used.
+
+#define LASTSYLLABLE ((m_PlayParams & 0x80)>>7)
+#define MIRRORMODE ((m_PlayParams & 0x40)>>6)
+#define SILENCEFLAG ((m_PlayParams & 0x20)>>5)
+#define LENGTHCOUNT ((m_PlayParams & 0x1C)>>1) // remember: its 4 bits and the bottom bit is always zero!
+#define REPEATCOUNT ((m_PlayParams<<1)&0x6) // remember: its 3 bits and the bottom bit is always zero!
+#define LOCALSILENCESTATE ((m_OutputCounter & 0x2) && (MIRRORMODE)) // 1 when silent output, 0 when DAC output.
+
+static const INT8 DeltaTable[4][4] =
+{
+ { -3, -3, -1, -1, },
+ { -1, -1, 0, 0, },
+ { 0, 0, 1, 1, },
+ { 1, 1, 3, 3 },
+};
+
+#ifdef ACCURATE_SQUEAL
+INT16 s14001a_device::audiofilter() /* rewrite me to better match the real filter! */
+{
+ UINT8 temp1;
+ INT16 temp2 = 0;
+ /* mean averaging filter! 1/n exponential *would* be somewhat better, but I'm lazy... */
+ for (temp1 = 0; temp1 < 8; temp1++) { temp2 += m_filtervals[temp1]; }
+ temp2 >>= 3;
+ return temp2;
+}
+
+void s14001a_device::shiftIntoFilter(INT16 inputvalue)
+{
+ UINT8 temp1;
+ for (temp1 = 7; temp1 > 0; temp1--)
+ {
+ m_filtervals[temp1] = m_filtervals[(temp1 - 1)];
+ }
+ m_filtervals[0] = inputvalue;
+}
+#endif
+
+void s14001a_device::PostPhoneme() /* figure out what the heck to do after playing a phoneme */
+{
+#ifdef DEBUGSTATE
+ fprintf(stderr,"0: entered PostPhoneme\n");
+#endif
+ m_RepeatCounter++; // increment the repeat counter
+ m_OutputCounter++; // increment the output counter
+ if (MIRRORMODE) // if mirroring is enabled
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"1: MIRRORMODE was on\n");
+#endif
+ if (m_RepeatCounter == 0x8) // exceeded 3 bits?
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"2: RepeatCounter was == 8\n");
+#endif
+ // reset repeat counter, increment length counter
+ // but first check if lowest bit is set
+ m_RepeatCounter = REPEATCOUNT; // reload repeat counter with reload value
+ if (m_LengthCounter & 0x1) // if low bit is 1 (will carry after increment)
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"3: LengthCounter's low bit was 1\n");
+#endif
+ m_PhoneAddress+=8; // go to next phone in this syllable
+ }
+ m_LengthCounter++;
+ if (m_LengthCounter == 0x10) // if Length counter carried out of 4 bits
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"3: LengthCounter overflowed\n");
+#endif
+ m_SyllableAddress += 2; // go to next syllable
+ m_nextstate = LASTSYLLABLE ? 13 : 3; // if we're on the last syllable, go to end state, otherwise go and load the next syllable.
+ }
+ else
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"3: LengthCounter's low bit wasn't 1 and it didn't overflow\n");
+#endif
+ m_PhoneOffset = (m_OutputCounter&1) ? 7 : 0;
+ m_nextstate = (m_OutputCounter&1) ? 9 : 5;
+ }
+ }
+ else // repeatcounter did NOT carry out of 3 bits so leave length counter alone
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"2: RepeatCounter is less than 8 (its actually %d)\n", m_RepeatCounter);
+#endif
+ m_PhoneOffset = (m_OutputCounter&1) ? 7 : 0;
+ m_nextstate = (m_OutputCounter&1) ? 9 : 5;
+ }
+ }
+ else // if mirroring is NOT enabled
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"1: MIRRORMODE was off\n");
+#endif
+ if (m_RepeatCounter == 0x8) // exceeded 3 bits?
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"2: RepeatCounter was == 8\n");
+#endif
+ // reset repeat counter, increment length counter
+ m_RepeatCounter = REPEATCOUNT; // reload repeat counter with reload value
+ m_LengthCounter++;
+ if (m_LengthCounter == 0x10) // if Length counter carried out of 4 bits
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"3: LengthCounter overflowed\n");
+#endif
+ m_SyllableAddress += 2; // go to next syllable
+ m_nextstate = LASTSYLLABLE ? 13 : 3; // if we're on the last syllable, go to end state, otherwise go and load the next syllable.
+#ifdef DEBUGSTATE
+ fprintf(stderr,"nextstate is now %d\n", m_nextstate); // see line below, same reason.
+#endif
+ return; // need a return here so we don't hit the 'nextstate = 5' line below
+ }
+ }
+ m_PhoneAddress += 8; // regardless of counters, the phone address always increments in non-mirrored mode
+ m_PhoneOffset = 0;
+ m_nextstate = 5;
+ }
+#ifdef DEBUGSTATE
+ fprintf(stderr,"nextstate is now %d\n", m_nextstate);
+#endif
+}
+
+void s14001a_device::s14001a_clock() /* called once per clock */
+{
+ UINT8 CurDelta; // Current delta
+
+ /* on even clocks, audio output is floating, /romen is low so rom data bus is driven
+ * on odd clocks, audio output is driven, /romen is high, state machine 2 is clocked
+ */
+ m_oddeven = !(m_oddeven); // invert the clock
+ if (m_oddeven == 0) // even clock
+ {
+#ifdef ACCURATE_SQUEAL
+ m_audioout = ALTFLAG; // flag to the renderer that this output should be the average of the last 8
+#endif
+ // DIGITAL INPUT *MIGHT* occur on the test pins occurs on this cycle?
+ }
+ else // odd clock
+ {
+ // fix dac output between samples. theoretically this might be unnecessary but it would require some messy logic in state 5 on the first sample load.
+ // Note: this behavior is NOT accurate, and needs to be fixed. see TODO.
+ if (m_GlobalSilenceState || LOCALSILENCESTATE)
+ {
+ m_DACOutput = SILENCE;
+ m_OldDelta = 2;
+ }
+ m_audioout = (m_GlobalSilenceState || LOCALSILENCESTATE) ? SILENCE : m_DACOutput; // when either silence state is 1, output silence.
+ // DIGITAL OUTPUT *might* be driven onto the test pins on this cycle?
+ switch(m_machineState) // HUUUUUGE switch statement
+ {
+ case 0: // idle state
+ m_nextstate = 0;
+ break;
+ case 1: // read starting syllable high byte from word table
+ m_SyllableAddress = 0; // clear syllable address
+ m_SyllableAddress |= m_SpeechRom[(m_LatchedWord<<1)]<<4;
+ m_nextstate = m_resetState ? 1 : 2;
+ break;
+ case 2: // read starting syllable low byte from word table
+ m_SyllableAddress |= m_SpeechRom[(m_LatchedWord<<1)+1]>>4;
+ m_nextstate = 3;
+ break;
+ case 3: // read starting phone address
+ m_PhoneAddress = m_SpeechRom[m_SyllableAddress]<<4;
+ m_nextstate = 4;
+ break;
+ case 4: // read playback parameters and prepare for play
+ m_PlayParams = m_SpeechRom[m_SyllableAddress+1];
+ m_GlobalSilenceState = SILENCEFLAG; // load phone silence flag
+ m_LengthCounter = LENGTHCOUNT; // load length counter
+ m_RepeatCounter = REPEATCOUNT; // load repeat counter
+ m_OutputCounter = 0; // clear output counter and disable mirrored phoneme silence indirectly via LOCALSILENCESTATE
+ m_PhoneOffset = 0; // set offset within phone to zero
+ m_OldDelta = 0x2; // set old delta to 2 <- is this right?
+ m_DACOutput = SILENCE ; // set DAC output to center/silence position
+ m_nextstate = 5;
+ break;
+ case 5: // Play phone forward, shift = 0 (also load)
+ CurDelta = (m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0xc0)>>6; // grab current delta from high 2 bits of high nybble
+ m_DACOutput += DeltaTable[CurDelta][m_OldDelta]; // send data to forward delta table and add result to accumulator
+ m_OldDelta = CurDelta; // Move current delta to old
+ m_nextstate = 6;
+ break;
+ case 6: // Play phone forward, shift = 2
+ CurDelta = (m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0x30)>>4; // grab current delta from low 2 bits of high nybble
+ m_DACOutput += DeltaTable[CurDelta][m_OldDelta]; // send data to forward delta table and add result to accumulator
+ m_OldDelta = CurDelta; // Move current delta to old
+ m_nextstate = 7;
+ break;
+ case 7: // Play phone forward, shift = 4
+ CurDelta = (m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0xc)>>2; // grab current delta from high 2 bits of low nybble
+ m_DACOutput += DeltaTable[CurDelta][m_OldDelta]; // send data to forward delta table and add result to accumulator
+ m_OldDelta = CurDelta; // Move current delta to old
+ m_nextstate = 8;
+ break;
+ case 8: // Play phone forward, shift = 6 (increment address if needed)
+ CurDelta = m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0x3; // grab current delta from low 2 bits of low nybble
+ m_DACOutput += DeltaTable[CurDelta][m_OldDelta]; // send data to forward delta table and add result to accumulator
+ m_OldDelta = CurDelta; // Move current delta to old
+ m_PhoneOffset++; // increment phone offset
+ if (m_PhoneOffset == 0x8) // if we're now done this phone
+ {
+ /* call the PostPhoneme Function */
+ PostPhoneme();
+ }
+ else
+ {
+ m_nextstate = 5;
+ }
+ break;
+ case 9: // Play phone backward, shift = 6 (also load)
+ CurDelta = (m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0x3); // grab current delta from low 2 bits of low nybble
+ if (m_laststate != 8) // ignore first (bogus) dac change in mirrored backwards mode. observations and the patent show this.
+ {
+ m_DACOutput -= DeltaTable[m_OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator
+ }
+ m_OldDelta = CurDelta; // Move current delta to old
+ m_nextstate = 10;
+ break;
+ case 10: // Play phone backward, shift = 4
+ CurDelta = (m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0xc)>>2; // grab current delta from high 2 bits of low nybble
+ m_DACOutput -= DeltaTable[m_OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator
+ m_OldDelta = CurDelta; // Move current delta to old
+ m_nextstate = 11;
+ break;
+ case 11: // Play phone backward, shift = 2
+ CurDelta = (m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0x30)>>4; // grab current delta from low 2 bits of high nybble
+ m_DACOutput -= DeltaTable[m_OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator
+ m_OldDelta = CurDelta; // Move current delta to old
+ m_nextstate = 12;
+ break;
+ case 12: // Play phone backward, shift = 0 (increment address if needed)
+ CurDelta = (m_SpeechRom[(m_PhoneAddress)+m_PhoneOffset]&0xc0)>>6; // grab current delta from high 2 bits of high nybble
+ m_DACOutput -= DeltaTable[m_OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator
+ m_OldDelta = CurDelta; // Move current delta to old
+ m_PhoneOffset--; // decrement phone offset
+ if (m_PhoneOffset == 0xFF) // if we're now done this phone
+ {
+ /* call the PostPhoneme() function */
+ PostPhoneme();
+ }
+ else
+ {
+ m_nextstate = 9;
+ }
+ break;
+ case 13: // For those pedantic among us, consume an extra two clocks like the real chip does.
+ m_nextstate = 0;
+ break;
+ }
+#ifdef DEBUGSTATE
+ fprintf(stderr, "Machine state is now %d, was %d, PhoneOffset is %d\n", m_nextstate, m_machineState, m_PhoneOffset);
+#endif
+ m_laststate = m_machineState;
+ m_machineState = m_nextstate;
+
+ /* the dac is 4 bits wide. if a delta step forced it outside of 4 bits, mask it back over here */
+ m_DACOutput &= 0xF;
+ }
+}
+
+/**************************************************************************
+ MAME glue code
+ **************************************************************************/
+
+int s14001a_device::bsy_r()
+{
+ m_stream->update();
+#ifdef DEBUGSTATE
+ fprintf(stderr,"busy state checked: %d\n",(m_machineState != 0) );
+#endif
+ return (m_machineState != 0);
+}
+
+void s14001a_device::reg_w(int data)
+{
+ m_stream->update();
+ m_WordInput = data;
+}
+
+void s14001a_device::rst_w(int data)
+{
+ m_stream->update();
+ m_LatchedWord = m_WordInput;
+ m_resetState = (data==1);
+ m_machineState = m_resetState ? 1 : m_machineState;
+}
+
+void s14001a_device::set_clock(int clock)
+{
+ m_stream->set_sample_rate(clock);
+}
+
+void s14001a_device::set_volume(int volume)
+{
+ m_stream->update();
+ m_VSU1000_amp = volume;
+}
+
+const device_type S14001A = &device_creator<s14001a_device>;
+
+s14001a_device::s14001a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, S14001A, "S14001A", tag, owner, clock, "s14001a", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_SpeechRom(*this, DEVICE_SELF),
+ m_stream(NULL),
+ m_WordInput(0),
+ m_LatchedWord(0),
+ m_SyllableAddress(0),
+ m_PhoneAddress(0),
+ m_PlayParams(0),
+ m_PhoneOffset(0),
+ m_LengthCounter(0),
+ m_RepeatCounter(0),
+ m_OutputCounter(0),
+ m_machineState(0),
+ m_nextstate(0),
+ m_laststate(0),
+ m_resetState(0),
+ m_oddeven(0),
+ m_GlobalSilenceState(1),
+ m_OldDelta(0x02),
+ m_DACOutput(SILENCE),
+ m_audioout(0),
+ m_VSU1000_amp(0)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void s14001a_device::device_start()
+{
+ int i;
+
+ m_GlobalSilenceState = 1;
+ m_OldDelta = 0x02;
+ m_DACOutput = SILENCE;
+
+ for (i = 0; i < 8; i++)
+ {
+ m_filtervals[i] = SILENCE;
+ }
+
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() ? clock() : machine().sample_rate());
+
+ save_item(NAME(m_WordInput));
+ save_item(NAME(m_LatchedWord));
+ save_item(NAME(m_SyllableAddress));
+ save_item(NAME(m_PhoneAddress));
+ save_item(NAME(m_PlayParams));
+ save_item(NAME(m_PhoneOffset));
+ save_item(NAME(m_LengthCounter));
+ save_item(NAME(m_RepeatCounter));
+ save_item(NAME(m_OutputCounter));
+ save_item(NAME(m_machineState));
+ save_item(NAME(m_nextstate));
+ save_item(NAME(m_laststate));
+ save_item(NAME(m_resetState));
+ save_item(NAME(m_oddeven));
+ save_item(NAME(m_GlobalSilenceState));
+ save_item(NAME(m_OldDelta));
+ save_item(NAME(m_DACOutput));
+ save_item(NAME(m_audioout));
+ save_item(NAME(m_filtervals));
+ save_item(NAME(m_VSU1000_amp));
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void s14001a_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int i;
+
+ for (i = 0; i < samples; i++)
+ {
+ s14001a_clock();
+ #ifdef ACCURATE_SQUEAL
+ if (m_audioout == ALTFLAG) // input from test pins -> output
+ {
+ shiftIntoFilter(chip, audiofilter(chip)); // shift over the previous outputs and stick in audioout.
+ outputs[0][i] = audiofilter(chip)*m_VSU1000_amp;
+ }
+ else // normal, dac-driven output
+ {
+ shiftIntoFilter(chip, ((((INT16)m_audioout)-8)<<9)); // shift over the previous outputs and stick in audioout 4 times. note <<9 instead of <<10, to prevent clipping, and to simulate that the filtered output normally has a somewhat lower amplitude than the driven one.
+ #endif
+ outputs[0][i] = ((((INT16)m_audioout)-8)<<10)*m_VSU1000_amp;
+ #ifdef ACCURATE_SQUEAL
+ }
+ #endif
+ }
+}
diff --git a/src/devices/sound/s14001a.h b/src/devices/sound/s14001a.h
new file mode 100644
index 00000000000..4427a59f8b7
--- /dev/null
+++ b/src/devices/sound/s14001a.h
@@ -0,0 +1,67 @@
+// license:LGPL-2.1+
+// copyright-holders:Jonathan Gevaryahu,R. Belmont,Zsolt Vasvari
+#pragma once
+/*
+ Copyright (C) 2006-2013 Jonathan Gevaryahu AKA Lord Nightmare
+
+*/
+#ifndef __S14001A_H__
+#define __S14001A_H__
+
+
+class s14001a_device : public device_t,
+ public device_sound_interface
+{
+public:
+ s14001a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~s14001a_device() {}
+
+ int bsy_r(); /* read BUSY pin */
+ void reg_w(int data); /* write to input latch */
+ void rst_w(int data); /* write to RESET pin */
+ void set_clock(int clock); /* set VSU-1000 clock */
+ void set_volume(int volume); /* set VSU-1000 volume control */
+
+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:
+ // internal state
+ required_region_ptr<UINT8> m_SpeechRom;
+ sound_stream * m_stream;
+
+ UINT8 m_WordInput; // value on word input bus
+ UINT8 m_LatchedWord; // value latched from input bus
+ UINT16 m_SyllableAddress; // address read from word table
+ UINT16 m_PhoneAddress; // starting/current phone address from syllable table
+ UINT8 m_PlayParams; // playback parameters from syllable table
+ UINT8 m_PhoneOffset; // offset within phone
+ UINT8 m_LengthCounter; // 4-bit counter which holds the inverted length of the word in phones, leftshifted by 1
+ UINT8 m_RepeatCounter; // 3-bit counter which holds the inverted number of repeats per phone, leftshifted by 1
+ UINT8 m_OutputCounter; // 2-bit counter to determine forward/backward and output/silence state.
+ UINT8 m_machineState; // chip state machine state
+ UINT8 m_nextstate; // chip state machine's new state
+ UINT8 m_laststate; // chip state machine's previous state, needed for mirror increment masking
+ UINT8 m_resetState; // reset line state
+ UINT8 m_oddeven; // odd versus even cycle toggle
+ UINT8 m_GlobalSilenceState; // same as above but for silent syllables instead of silent portions of mirrored syllables
+ UINT8 m_OldDelta; // 2-bit old delta value
+ UINT8 m_DACOutput; // 4-bit DAC Accumulator/output
+ UINT8 m_audioout; // filtered audio output
+ INT16 m_filtervals[8];
+ UINT8 m_VSU1000_amp; // amplitude setting on VSU-1000 board
+
+ INT16 audiofilter();
+ void shiftIntoFilter(INT16 inputvalue);
+ void PostPhoneme();
+ void s14001a_clock();
+};
+
+extern const device_type S14001A;
+
+
+#endif /* __S14001A_H__ */
diff --git a/src/devices/sound/saa1099.c b/src/devices/sound/saa1099.c
new file mode 100644
index 00000000000..a915fa6ed38
--- /dev/null
+++ b/src/devices/sound/saa1099.c
@@ -0,0 +1,452 @@
+// 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
+
+ Version History:
+ ================
+ ??-??-200? - First version of the driver submitted for MESS (GPL/MESS license)
+ ??-??-200? - Submitted to DOSBOX for Creative Music System/Game Blaster emulation
+ ??-??-201? - Driver relicensed to BSD 3 Clause (GPL+ compatible)
+ 06-27-2015 - Applied clock divisor fix from DOSBOX SVN, http://www.vogons.org/viewtopic.php?p=344227#p344227
+
+***************************************************************************/
+
+#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 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
+const device_type SAA1099 = &device_creator<saa1099_device>;
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// saa1099_device - constructor
+//-------------------------------------------------
+
+saa1099_device::saa1099_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, SAA1099, "SAA1099", tag, owner, clock, "saa1099", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_all_ch_enable(0),
+ m_sync_state(0),
+ m_selected_reg(0),
+ m_sample_rate(0.0)
+{
+ memset(m_noise_params, 0, sizeof(int)*2);
+ memset(m_env_enable, 0, sizeof(int)*2);
+ memset(m_env_reverse_right, 0, sizeof(int)*2);
+ memset(m_env_mode, 0, sizeof(int)*2);
+ memset(m_env_bits, 0, sizeof(int)*2);
+ memset(m_env_clock, 0, sizeof(int)*2);
+ memset(m_env_step, 0, sizeof(int)*2);
+}
+
+
+//-------------------------------------------------
+// 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;
+ }
+ }
+
+ /* 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++)
+ {
+ /* check the actual position in noise generator */
+ 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 & 0x4000) == 0) == ((m_noise[ch].level & 0x0040) == 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! */
+ logerror("%s: (SAA1099 '%s') Unknown operation (reg:%02x, data:%02x)\n", machine().describe_context(), tag(), reg, data);
+ }
+}
diff --git a/src/devices/sound/saa1099.h b/src/devices/sound/saa1099.h
new file mode 100644
index 00000000000..1909efa1c52
--- /dev/null
+++ b/src/devices/sound/saa1099.h
@@ -0,0 +1,112 @@
+// license:BSD-3-Clause
+// copyright-holders:Juergen Buchmueller, Manuel Abadia
+/**********************************************
+ Philips SAA1099 Sound driver
+**********************************************/
+
+#pragma once
+
+#ifndef __SAA1099_H__
+#define __SAA1099_H__
+
+//**************************************************************************
+// 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
+//**************************************************************************
+
+struct saa1099_channel
+{
+ saa1099_channel() :
+ frequency(0),
+ freq_enable(0),
+ noise_enable(0),
+ octave(0),
+ counter(0.0),
+ freq(0.0),
+ level(0)
+ {
+ memset(amplitude, 0, sizeof(int)*2);
+ memset(envelope, 0, sizeof(int)*2);
+ }
+
+ 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.0),
+ freq(0.0),
+ level(0) {}
+
+ /* vars to simulate the noise generator output */
+ double counter;
+ double freq;
+ int level; /* noise polynomal shifter */
+};
+
+
+// ======================> 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 clock);
+ ~saa1099_device() { }
+
+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);
+
+public:
+ DECLARE_WRITE8_MEMBER( control_w );
+ DECLARE_WRITE8_MEMBER( data_w );
+
+private:
+ void envelope_w(int ch);
+
+private:
+ 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;
+};
+
+extern const device_type SAA1099;
+
+
+#endif /* __SAA1099_H__ */
diff --git a/src/devices/sound/samples.c b/src/devices/sound/samples.c
new file mode 100644
index 00000000000..863e30f0832
--- /dev/null
+++ b/src/devices/sound/samples.c
@@ -0,0 +1,642 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ samples.c
+
+ Sound device for sample playback.
+
+****************************************************************************
+
+ Playback of pre-recorded samples. Used for high-level simulation of
+ discrete sound circuits where proper low-level simulation isn't
+ available. Also used for tape loops and similar.
+
+ Current limitations
+ - Only supports single channel samples!
+
+ Considerations
+ - Maybe this should be part of the presentation layer
+ (artwork etc.) with samples specified in .lay files instead of
+ in drivers?
+
+***************************************************************************/
+
+#include "emu.h"
+#include "emuopts.h"
+#include "samples.h"
+#include "flac.h"
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+const device_type SAMPLES = &device_creator<samples_device>;
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// samples_device - constructors
+//-------------------------------------------------
+
+samples_device::samples_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, SAMPLES, "Samples", tag, owner, clock, "samples", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_channels(0),
+ m_names(NULL)
+{
+}
+
+samples_device::samples_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_sound_interface(mconfig, *this),
+ m_channels(0),
+ m_names(NULL)
+{
+}
+
+
+//**************************************************************************
+// PUBLIC INTERFACE
+//**************************************************************************
+
+//-------------------------------------------------
+// start - start playing a loaded sample
+//-------------------------------------------------
+
+void samples_device::start(UINT8 channel, UINT32 samplenum, bool loop)
+{
+ // if samples are disabled, just return quietly
+ if (m_sample.empty())
+ return;
+
+ assert(samplenum < m_sample.size());
+ assert(channel < m_channels);
+
+ // force an update before we start
+ channel_t &chan = m_channel[channel];
+ chan.stream->update();
+
+ // update the parameters
+ sample_t &sample = m_sample[samplenum];
+ chan.source = &sample.data[0];
+ chan.source_length = sample.data.size();
+ chan.source_num = (chan.source_length > 0) ? samplenum : -1;
+ chan.pos = 0;
+ chan.frac = 0;
+ chan.basefreq = sample.frequency;
+ chan.step = (INT64(chan.basefreq) << FRAC_BITS) / machine().sample_rate();
+ chan.loop = loop;
+}
+
+
+//-------------------------------------------------
+// start_raw - start playing an externally
+// provided sample
+//-------------------------------------------------
+
+void samples_device::start_raw(UINT8 channel, const INT16 *sampledata, UINT32 samples, UINT32 frequency, bool loop)
+{
+ assert(channel < m_channels);
+
+ // force an update before we start
+ channel_t &chan = m_channel[channel];
+ chan.stream->update();
+
+ // update the parameters
+ chan.source = sampledata;
+ chan.source_length = samples;
+ chan.source_num = -1;
+ chan.pos = 0;
+ chan.frac = 0;
+ chan.basefreq = frequency;
+ chan.step = (INT64(chan.basefreq) << FRAC_BITS) / machine().sample_rate();
+ chan.loop = loop;
+}
+
+
+//-------------------------------------------------
+// set_frequency - set the playback frequency of
+// a sample
+//-------------------------------------------------
+
+void samples_device::set_frequency(UINT8 channel, UINT32 freq)
+{
+ assert(channel < m_channels);
+
+ // force an update before we start
+ channel_t &chan = m_channel[channel];
+ chan.stream->update();
+ chan.step = (INT64(freq) << FRAC_BITS) / machine().sample_rate();
+}
+
+
+//-------------------------------------------------
+// set_volume - set the playback volume of a
+// sample
+//-------------------------------------------------
+
+void samples_device::set_volume(UINT8 channel, float volume)
+{
+ assert(channel < m_channels);
+
+ // force an update before we start
+ channel_t &chan = m_channel[channel];
+ chan.stream->set_output_gain(0, volume);
+}
+
+
+//-------------------------------------------------
+// pause - pause playback on a channel
+//-------------------------------------------------
+
+void samples_device::pause(UINT8 channel, bool pause)
+{
+ assert(channel < m_channels);
+
+ // force an update before we start
+ channel_t &chan = m_channel[channel];
+ chan.paused = pause;
+}
+
+
+//-------------------------------------------------
+// stop - stop playback on a channel
+//-------------------------------------------------
+
+void samples_device::stop(UINT8 channel)
+{
+ assert(channel < m_channels);
+
+ // force an update before we start
+ channel_t &chan = m_channel[channel];
+ chan.source = NULL;
+ chan.source_num = -1;
+}
+
+
+//-------------------------------------------------
+// stop_all - stop playback on all channels
+//-------------------------------------------------
+
+void samples_device::stop_all()
+{
+ // just iterate over channels and stop them
+ for (UINT8 channel = 0; channel < m_channels; channel++)
+ stop(channel);
+}
+
+
+//-------------------------------------------------
+// base_frequency - return the base frequency of
+// a channel being played
+//-------------------------------------------------
+
+UINT32 samples_device::base_frequency(UINT8 channel) const
+{
+ assert(channel < m_channels);
+
+ // force an update before we start
+ const channel_t &chan = m_channel[channel];
+ chan.stream->update();
+ return chan.basefreq;
+}
+
+
+//-------------------------------------------------
+// playing - return true if a sample is still
+// playing on the given channel
+//-------------------------------------------------
+
+bool samples_device::playing(UINT8 channel) const
+{
+ assert(channel < m_channels);
+
+ // force an update before we start
+ const channel_t &chan = m_channel[channel];
+ chan.stream->update();
+ return (chan.source != NULL);
+}
+
+
+
+//**************************************************************************
+// DEVICE INTERFACE
+//**************************************************************************
+
+//-------------------------------------------------
+// device_start - handle device startup
+//-------------------------------------------------
+
+void samples_device::device_start()
+{
+ // read audio samples
+ load_samples();
+
+ // allocate channels
+ m_channel.resize(m_channels);
+ for (int channel = 0; channel < m_channels; channel++)
+ {
+ // initialize channel
+ channel_t &chan = m_channel[channel];
+ chan.stream = stream_alloc(0, 1, machine().sample_rate());
+ chan.source = NULL;
+ chan.source_num = -1;
+ chan.step = 0;
+ chan.loop = 0;
+ chan.paused = 0;
+
+ // register with the save state system
+ save_item(NAME(chan.source_length), channel);
+ save_item(NAME(chan.source_num), channel);
+ save_item(NAME(chan.pos), channel);
+ save_item(NAME(chan.frac), channel);
+ save_item(NAME(chan.step), channel);
+ save_item(NAME(chan.loop), channel);
+ save_item(NAME(chan.paused), channel);
+ }
+
+ // initialize any custom handlers
+ m_samples_start_cb.bind_relative_to(*owner());
+
+ if (!m_samples_start_cb.isnull())
+ m_samples_start_cb();
+}
+
+
+//-------------------------------------------------
+// device_reset - handle device reset
+//-------------------------------------------------
+
+void samples_device::device_reset()
+{
+ stop_all();
+}
+
+
+//-------------------------------------------------
+// device_post_load - handle updating after a
+// restore
+//-------------------------------------------------
+
+void samples_device::device_post_load()
+{
+ // loop over channels
+ for (int channel = 0; channel < m_channels; channel++)
+ {
+ // attach any samples that were loaded and playing
+ channel_t &chan = m_channel[channel];
+ if (chan.source_num >= 0 && chan.source_num < m_sample.size())
+ {
+ sample_t &sample = m_sample[chan.source_num];
+ chan.source = &sample.data[0];
+ chan.source_length = sample.data.size();
+ if (sample.data.empty())
+ chan.source_num = -1;
+ }
+
+ // validate the position against the length in case the sample is smaller
+ if (chan.source != NULL && chan.pos >= chan.source_length)
+ {
+ if (chan.loop)
+ chan.pos %= chan.source_length;
+ else
+ {
+ chan.source = NULL;
+ chan.source_num = -1;
+ }
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - update a sound stream
+//-------------------------------------------------
+
+void samples_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ // find the channel with this stream
+ for (int channel = 0; channel < m_channels; channel++)
+ if (&stream == m_channel[channel].stream)
+ {
+ channel_t &chan = m_channel[channel];
+ stream_sample_t *buffer = outputs[0];
+
+ // process if we still have a source and we're not paused
+ if (chan.source != NULL && !chan.paused)
+ {
+ // load some info locally
+ UINT32 pos = chan.pos;
+ UINT32 frac = chan.frac;
+ UINT32 step = chan.step;
+ const INT16 *sample = chan.source;
+ UINT32 sample_length = chan.source_length;
+
+ while (samples--)
+ {
+ // do a linear interp on the sample
+ INT32 sample1 = sample[pos];
+ INT32 sample2 = sample[(pos + 1) % sample_length];
+ INT32 fracmult = frac >> (FRAC_BITS - 14);
+ *buffer++ = ((0x4000 - fracmult) * sample1 + fracmult * sample2) >> 14;
+
+ // advance
+ frac += step;
+ pos += frac >> FRAC_BITS;
+ frac = frac & ((1 << FRAC_BITS) - 1);
+
+ // handle looping/ending
+ if (pos >= sample_length)
+ {
+ if (chan.loop)
+ pos %= sample_length;
+ else
+ {
+ chan.source = NULL;
+ chan.source_num = -1;
+ if (samples > 0)
+ memset(buffer, 0, samples * sizeof(*buffer));
+ samples = 0;
+ break;
+ }
+ }
+ }
+
+ // push position back out
+ chan.pos = pos;
+ chan.frac = frac;
+ }
+ else
+ memset(buffer, 0, samples * sizeof(*buffer));
+ break;
+ }
+}
+
+
+
+//**************************************************************************
+// INTERNAL HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// read_sample - read a WAV or FLAC file as a
+// sample
+//-------------------------------------------------
+
+bool samples_device::read_sample(emu_file &file, sample_t &sample)
+{
+ // read the core header and make sure it's a proper file
+ UINT8 buf[4];
+ UINT32 offset = file.read(buf, 4);
+ if (offset < 4)
+ {
+ osd_printf_warning("Unable to read %s, 0-byte file?\n", file.filename());
+ return false;
+ }
+
+ // look for the appropriate RIFF tag
+ if (memcmp(&buf[0], "RIFF", 4) == 0)
+ return read_wav_sample(file, sample);
+ else if (memcmp(&buf[0], "fLaC", 4) == 0)
+ return read_flac_sample(file, sample);
+
+ // if nothing appropriate, emit a warning
+ osd_printf_warning("Unable to read %s, corrupt file?\n", file.filename());
+ return false;
+}
+
+
+//-------------------------------------------------
+// read_wav_sample - read a WAV file as a sample
+//-------------------------------------------------
+
+bool samples_device::read_wav_sample(emu_file &file, sample_t &sample)
+{
+ // we already read the opening 'RIFF' tag
+ UINT32 offset = 4;
+
+ // get the total size
+ UINT32 filesize;
+ offset += file.read(&filesize, 4);
+ if (offset < 8)
+ {
+ osd_printf_warning("Unexpected size offset %u (%s)\n", offset, file.filename());
+ return false;
+ }
+ filesize = LITTLE_ENDIANIZE_INT32(filesize);
+
+ // read the RIFF file type and make sure it's a WAVE file
+ char buf[32];
+ offset += file.read(buf, 4);
+ if (offset < 12)
+ {
+ osd_printf_warning("Unexpected WAVE offset %u (%s)\n", offset, file.filename());
+ return false;
+ }
+ if (memcmp(&buf[0], "WAVE", 4) != 0)
+ {
+ osd_printf_warning("Could not find WAVE header (%s)\n", file.filename());
+ return false;
+ }
+
+ // seek until we find a format tag
+ UINT32 length;
+ while (1)
+ {
+ offset += file.read(buf, 4);
+ offset += file.read(&length, 4);
+ length = LITTLE_ENDIANIZE_INT32(length);
+ if (memcmp(&buf[0], "fmt ", 4) == 0)
+ break;
+
+ // seek to the next block
+ file.seek(length, SEEK_CUR);
+ offset += length;
+ if (offset >= filesize)
+ {
+ osd_printf_warning("Could not find fmt tag (%s)\n", file.filename());
+ return false;
+ }
+ }
+
+ // read the format -- make sure it is PCM
+ UINT16 temp16;
+ offset += file.read(&temp16, 2);
+ temp16 = LITTLE_ENDIANIZE_INT16(temp16);
+ if (temp16 != 1)
+ {
+ osd_printf_warning("unsupported format %u - only PCM is supported (%s)\n", temp16, file.filename());
+ return false;
+ }
+
+ // number of channels -- only mono is supported
+ offset += file.read(&temp16, 2);
+ temp16 = LITTLE_ENDIANIZE_INT16(temp16);
+ if (temp16 != 1)
+ {
+ osd_printf_warning("unsupported number of channels %u - only mono is supported (%s)\n", temp16, file.filename());
+ return false;
+ }
+
+ // sample rate
+ UINT32 rate;
+ offset += file.read(&rate, 4);
+ rate = LITTLE_ENDIANIZE_INT32(rate);
+
+ // bytes/second and block alignment are ignored
+ offset += file.read(buf, 6);
+
+ // bits/sample
+ UINT16 bits;
+ offset += file.read(&bits, 2);
+ bits = LITTLE_ENDIANIZE_INT16(bits);
+ if (bits != 8 && bits != 16)
+ {
+ osd_printf_warning("unsupported bits/sample %u - only 8 and 16 are supported (%s)\n", bits, file.filename());
+ return false;
+ }
+
+ // seek past any extra data
+ file.seek(length - 16, SEEK_CUR);
+ offset += length - 16;
+
+ // seek until we find a data tag
+ while (1)
+ {
+ offset += file.read(buf, 4);
+ offset += file.read(&length, 4);
+ length = LITTLE_ENDIANIZE_INT32(length);
+ if (memcmp(&buf[0], "data", 4) == 0)
+ break;
+
+ // seek to the next block
+ file.seek(length, SEEK_CUR);
+ offset += length;
+ if (offset >= filesize)
+ {
+ osd_printf_warning("Could not find data tag (%s)\n", file.filename());
+ return false;
+ }
+ }
+
+ // if there was a 0 length data block, we're done
+ if (length == 0)
+ {
+ osd_printf_warning("empty data block (%s)\n", file.filename());
+ return false;
+ }
+
+ // fill in the sample data
+ sample.frequency = rate;
+
+ // read the data in
+ if (bits == 8)
+ {
+ sample.data.resize(length);
+ file.read(&sample.data[0], length);
+
+ // convert 8-bit data to signed samples
+ UINT8 *tempptr = reinterpret_cast<UINT8 *>(&sample.data[0]);
+ for (INT32 sindex = length - 1; sindex >= 0; sindex--)
+ sample.data[sindex] = INT8(tempptr[sindex] ^ 0x80) * 256;
+ }
+ else
+ {
+ // 16-bit data is fine as-is
+ sample.data.resize(length / 2);
+ file.read(&sample.data[0], length);
+
+ // swap high/low on big-endian systems
+ if (ENDIANNESS_NATIVE != ENDIANNESS_LITTLE)
+ for (UINT32 sindex = 0; sindex < length / 2; sindex++)
+ sample.data[sindex] = LITTLE_ENDIANIZE_INT16(sample.data[sindex]);
+ }
+ return true;
+}
+
+
+//-------------------------------------------------
+// read_flac_sample - read a FLAC file as a sample
+//-------------------------------------------------
+
+bool samples_device::read_flac_sample(emu_file &file, sample_t &sample)
+{
+ // seek back to the start of the file
+ file.seek(0, SEEK_SET);
+
+ // create the FLAC decoder and fill in the sample data
+ flac_decoder decoder(file);
+ sample.frequency = decoder.sample_rate();
+
+ // error if more than 1 channel or not 16bpp
+ if (decoder.channels() != 1)
+ return false;
+ if (decoder.bits_per_sample() != 16)
+ return false;
+
+ // resize the array and read
+ sample.data.resize(decoder.total_samples());
+ if (!decoder.decode_interleaved(&sample.data[0], sample.data.size()))
+ return false;
+
+ // finish up and clean up
+ decoder.finish();
+ return true;
+}
+
+
+//-------------------------------------------------
+// load_samples - load all the samples in our
+// attached interface
+// Returns true when all samples were successfully read, else false
+//-------------------------------------------------
+
+bool samples_device::load_samples()
+{
+ bool ok = true;
+ // if the user doesn't want to use samples, bail
+ if (!machine().options().samples())
+ return false;
+
+ // iterate over ourself
+ const char *basename = machine().basename();
+ samples_iterator iter(*this);
+ const char *altbasename = iter.altbasename();
+
+ // pre-size the array
+ m_sample.resize(iter.count());
+
+ // load the samples
+ int index = 0;
+ for (const char *samplename = iter.first(); samplename != NULL; index++, samplename = iter.next())
+ {
+ // attempt to open as FLAC first
+ emu_file file(machine().options().sample_path(), OPEN_FLAG_READ);
+ file_error filerr = file.open(basename, PATH_SEPARATOR, samplename, ".flac");
+ if (filerr != FILERR_NONE && altbasename != NULL)
+ filerr = file.open(altbasename, PATH_SEPARATOR, samplename, ".flac");
+
+ // if not, try as WAV
+ if (filerr != FILERR_NONE)
+ filerr = file.open(basename, PATH_SEPARATOR, samplename, ".wav");
+ if (filerr != FILERR_NONE && altbasename != NULL)
+ filerr = file.open(altbasename, PATH_SEPARATOR, samplename, ".wav");
+
+ // if opened, read it
+ if (filerr == FILERR_NONE)
+ read_sample(file, m_sample[index]);
+ else if (filerr == FILERR_NOT_FOUND)
+ {
+ logerror("%s: Sample '%s' NOT FOUND\n", tag(), samplename);
+ ok = false;
+ }
+ }
+ return ok;
+}
diff --git a/src/devices/sound/samples.h b/src/devices/sound/samples.h
new file mode 100644
index 00000000000..24bb4541766
--- /dev/null
+++ b/src/devices/sound/samples.h
@@ -0,0 +1,187 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ samples.h
+
+ Sound device for sample playback.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __SAMPLES_H__
+#define __SAMPLES_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_SAMPLES_CHANNELS(_channels) \
+ samples_device::static_set_channels(*device, _channels);
+
+#define MCFG_SAMPLES_NAMES(_names) \
+ samples_device::static_set_samples_names(*device, _names);
+
+typedef device_delegate<void ()> samples_start_cb_delegate;
+
+#define SAMPLES_START_CB_MEMBER(_name) void _name()
+
+#define MCFG_SAMPLES_START_CB(_class, _method) \
+ samples_device::set_samples_start_callback(*device, samples_start_cb_delegate(&_class::_method, #_class "::" #_method, downcast<_class *>(owner)));
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> samples_device
+
+class samples_device : public device_t,
+ public device_sound_interface
+{
+public:
+ // construction/destruction
+ samples_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ static void static_set_channels(device_t &device, UINT8 channels) { downcast<samples_device &>(device).m_channels = channels; }
+ static void static_set_samples_names(device_t &device, const char *const *names) { downcast<samples_device &>(device).m_names = names; }
+ static void set_samples_start_callback(device_t &device, samples_start_cb_delegate callback) { downcast<samples_device &>(device).m_samples_start_cb = callback; }
+
+ // getters
+ bool playing(UINT8 channel) const;
+ UINT32 base_frequency(UINT8 channel) const;
+
+ // start/stop helpers
+ void start(UINT8 channel, UINT32 samplenum, bool loop = false);
+ void start_raw(UINT8 channel, const INT16 *sampledata, UINT32 samples, UINT32 frequency, bool loop = false);
+ void pause(UINT8 channel, bool pause = true);
+ void stop(UINT8 channel);
+ void stop_all();
+
+ // dynamic control
+ void set_frequency(UINT8 channel, UINT32 frequency);
+ void set_volume(UINT8 channel, float volume);
+
+ // helpers
+ struct sample_t
+ {
+ // shouldn't need a copy, but in case it happens, catch it here
+ sample_t &operator=(const sample_t &rhs) { assert(false); return *this; }
+
+ UINT32 frequency; // frequency of the sample
+ std::vector<INT16> data; // 16-bit signed data
+ };
+ static bool read_sample(emu_file &file, sample_t &sample);
+
+ // interface
+ UINT8 m_channels; // number of discrete audio channels needed
+ const char *const *m_names; // array of sample names
+ samples_start_cb_delegate m_samples_start_cb; // optional callback
+
+protected:
+ // subclasses can do it this way
+ samples_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_post_load();
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ // internal classes
+ struct channel_t
+ {
+ sound_stream * stream;
+ const INT16 * source;
+ INT32 source_length;
+ INT32 source_num;
+ UINT32 pos;
+ UINT32 frac;
+ UINT32 step;
+ UINT32 basefreq;
+ bool loop;
+ bool paused;
+ };
+
+ // internal helpers
+ static bool read_wav_sample(emu_file &file, sample_t &sample);
+ static bool read_flac_sample(emu_file &file, sample_t &sample);
+ bool load_samples();
+
+ // internal state
+ std::vector<channel_t> m_channel;
+ std::vector<sample_t> m_sample;
+
+ // internal constants
+ static const UINT8 FRAC_BITS = 24;
+ static const UINT32 FRAC_ONE = 1 << FRAC_BITS;
+ static const UINT32 FRAC_MASK = FRAC_ONE - 1;
+};
+
+// iterator, since lots of people are interested in these devices
+typedef device_type_iterator<&device_creator<samples_device>, samples_device> samples_device_iterator;
+
+
+// ======================> samples_iterator
+
+class samples_iterator
+{
+public:
+ // construction/destruction
+ samples_iterator(samples_device &device)
+ : m_samples(device),
+ m_current(-1) { }
+
+ // getters
+ const char *altbasename() const { return (m_samples.m_names != NULL && m_samples.m_names[0] != NULL && m_samples.m_names[0][0] == '*') ? &m_samples.m_names[0][1] : NULL; }
+
+ // iteration
+ const char *first()
+ {
+ if (m_samples.m_names == NULL || m_samples.m_names[0] == NULL)
+ return NULL;
+ m_current = 0;
+ if (m_samples.m_names[0][0] == '*')
+ m_current++;
+ return m_samples.m_names[m_current++];
+ }
+
+ const char *next()
+ {
+ if (m_current == -1 || m_samples.m_names[m_current] == NULL)
+ return NULL;
+ return m_samples.m_names[m_current++];
+ }
+
+ // counting
+ int count()
+ {
+ int save = m_current;
+ int result = 0;
+ for (const char *scan = first(); scan != NULL; scan = next())
+ result++;
+ m_current = save;
+ return result;
+ }
+
+private:
+ // internal state
+ samples_device &m_samples;
+ int m_current;
+};
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+extern const device_type SAMPLES;
+
+
+#endif
diff --git a/src/devices/sound/sb0400.c b/src/devices/sound/sb0400.c
new file mode 100644
index 00000000000..343cb6aecaa
--- /dev/null
+++ b/src/devices/sound/sb0400.c
@@ -0,0 +1,24 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+#include "sb0400.h"
+
+const device_type SB0400 = &device_creator<sb0400_device>;
+
+DEVICE_ADDRESS_MAP_START(map, 32, sb0400_device)
+ADDRESS_MAP_END
+
+sb0400_device::sb0400_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : pci_device(mconfig, SB0400, "Creative Labs SB0400 Audigy2 Value", tag, owner, clock, "sb0400", __FILE__)
+{
+}
+
+void sb0400_device::device_start()
+{
+ pci_device::device_start();
+ add_map(64, M_IO, FUNC(sb0400_device::map));
+}
+
+void sb0400_device::device_reset()
+{
+ pci_device::device_reset();
+}
diff --git a/src/devices/sound/sb0400.h b/src/devices/sound/sb0400.h
new file mode 100644
index 00000000000..aeb1ec43e61
--- /dev/null
+++ b/src/devices/sound/sb0400.h
@@ -0,0 +1,27 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+// Creative labs SB0400 Audigy2 Value
+
+#ifndef SB0400_H
+#define SB0400_H
+
+#include "machine/pci.h"
+
+#define MCFG_SB0400_ADD(_tag, _subdevice_id) \
+ MCFG_PCI_DEVICE_ADD(_tag, SB0400, 0x11020008, 0x00, 0x040100, _subdevice_id)
+
+class sb0400_device : public pci_device {
+public:
+ sb0400_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual void device_start();
+ virtual void device_reset();
+
+private:
+ DECLARE_ADDRESS_MAP(map, 32);
+};
+
+extern const device_type SB0400;
+
+#endif
diff --git a/src/devices/sound/scsp.c b/src/devices/sound/scsp.c
new file mode 100644
index 00000000000..df2a5ef0143
--- /dev/null
+++ b/src/devices/sound/scsp.c
@@ -0,0 +1,1574 @@
+// license:BSD-3-Clause
+// copyright-holders:ElSemi, R. Belmont
+/*
+ Sega/Yamaha YMF292-F (SCSP = Saturn Custom Sound Processor) emulation
+ By ElSemi
+ MAME/M1 conversion and cleanup by R. Belmont
+ Additional code and bugfixes by kingshriek
+
+ This chip has 32 voices. Each voice can play a sample or be part of
+ an FM construct. Unlike traditional Yamaha FM chips, the base waveform
+ for the FM still comes from the wavetable RAM.
+
+ ChangeLog:
+ * November 25, 2003 (ES) Fixed buggy timers and envelope overflows.
+ (RB) Improved sample rates other than 44100, multiple
+ chips now works properly.
+ * December 02, 2003 (ES) Added DISDL register support, improves mix.
+ * April 28, 2004 (ES) Corrected envelope rates, added key-rate scaling,
+ added ringbuffer support.
+ * January 8, 2005 (RB) Added ability to specify region offset for RAM.
+ * January 26, 2007 (ES) Added on-board DSP capability
+ * September 24, 2007 (RB+ES) Removed fake reverb. Rewrote timers and IRQ handling.
+ Fixed case where voice frequency is updated while looping.
+ Enabled DSP again.
+ * December 16, 2007 (kingshriek) Many EG bug fixes, implemented effects mixer,
+ implemented FM.
+ * January 5, 2008 (kingshriek+RB) Working, good-sounding FM, removed obsolete non-USEDSP code.
+ * April 22, 2009 ("PluginNinja") Improved slot monitor, misc cleanups
+ * June 6, 2011 (AS) Rewrote DMA from scratch, Darius 2 relies on it.
+*/
+
+#include "emu.h"
+#include "sound/cdda.h"
+#include "scsp.h"
+
+
+#define ICLIP16(x) (x<-32768)?-32768:((x>32767)?32767:x)
+
+#define SHIFT 12
+#define LFO_SHIFT 8
+#define FIX(v) ((UINT32) ((float) (1<<SHIFT)*(v)))
+
+
+#define EG_SHIFT 16
+
+
+/*
+ SCSP features 32 programmable slots
+ that can generate FM and PCM (from ROM/RAM) sound
+*/
+
+//SLOT PARAMETERS
+#define KEYONEX(slot) ((slot->udata.data[0x0]>>0x0)&0x1000)
+#define KEYONB(slot) ((slot->udata.data[0x0]>>0x0)&0x0800)
+#define SBCTL(slot) ((slot->udata.data[0x0]>>0x9)&0x0003)
+#define SSCTL(slot) ((slot->udata.data[0x0]>>0x7)&0x0003)
+#define LPCTL(slot) ((slot->udata.data[0x0]>>0x5)&0x0003)
+#define PCM8B(slot) ((slot->udata.data[0x0]>>0x0)&0x0010)
+
+#define SA(slot) (((slot->udata.data[0x0]&0xF)<<16)|(slot->udata.data[0x1]))
+
+#define LSA(slot) (slot->udata.data[0x2])
+
+#define LEA(slot) (slot->udata.data[0x3])
+
+#define D2R(slot) ((slot->udata.data[0x4]>>0xB)&0x001F)
+#define D1R(slot) ((slot->udata.data[0x4]>>0x6)&0x001F)
+#define EGHOLD(slot) ((slot->udata.data[0x4]>>0x0)&0x0020)
+#define AR(slot) ((slot->udata.data[0x4]>>0x0)&0x001F)
+
+#define LPSLNK(slot) ((slot->udata.data[0x5]>>0x0)&0x4000)
+#define KRS(slot) ((slot->udata.data[0x5]>>0xA)&0x000F)
+#define DL(slot) ((slot->udata.data[0x5]>>0x5)&0x001F)
+#define RR(slot) ((slot->udata.data[0x5]>>0x0)&0x001F)
+
+#define STWINH(slot) ((slot->udata.data[0x6]>>0x0)&0x0200)
+#define SDIR(slot) ((slot->udata.data[0x6]>>0x0)&0x0100)
+#define TL(slot) ((slot->udata.data[0x6]>>0x0)&0x00FF)
+
+#define MDL(slot) ((slot->udata.data[0x7]>>0xC)&0x000F)
+#define MDXSL(slot) ((slot->udata.data[0x7]>>0x6)&0x003F)
+#define MDYSL(slot) ((slot->udata.data[0x7]>>0x0)&0x003F)
+
+#define OCT(slot) ((slot->udata.data[0x8]>>0xB)&0x000F)
+#define FNS(slot) ((slot->udata.data[0x8]>>0x0)&0x03FF)
+
+#define LFORE(slot) ((slot->udata.data[0x9]>>0x0)&0x8000)
+#define LFOF(slot) ((slot->udata.data[0x9]>>0xA)&0x001F)
+#define PLFOWS(slot) ((slot->udata.data[0x9]>>0x8)&0x0003)
+#define PLFOS(slot) ((slot->udata.data[0x9]>>0x5)&0x0007)
+#define ALFOWS(slot) ((slot->udata.data[0x9]>>0x3)&0x0003)
+#define ALFOS(slot) ((slot->udata.data[0x9]>>0x0)&0x0007)
+
+#define ISEL(slot) ((slot->udata.data[0xA]>>0x3)&0x000F)
+#define IMXL(slot) ((slot->udata.data[0xA]>>0x0)&0x0007)
+
+#define DISDL(slot) ((slot->udata.data[0xB]>>0xD)&0x0007)
+#define DIPAN(slot) ((slot->udata.data[0xB]>>0x8)&0x001F)
+#define EFSDL(slot) ((slot->udata.data[0xB]>>0x5)&0x0007)
+#define EFPAN(slot) ((slot->udata.data[0xB]>>0x0)&0x001F)
+
+//Envelope times in ms
+static const double ARTimes[64]={100000/*infinity*/,100000/*infinity*/,8100.0,6900.0,6000.0,4800.0,4000.0,3400.0,3000.0,2400.0,2000.0,1700.0,1500.0,
+ 1200.0,1000.0,860.0,760.0,600.0,500.0,430.0,380.0,300.0,250.0,220.0,190.0,150.0,130.0,110.0,95.0,
+ 76.0,63.0,55.0,47.0,38.0,31.0,27.0,24.0,19.0,15.0,13.0,12.0,9.4,7.9,6.8,6.0,4.7,3.8,3.4,3.0,2.4,
+ 2.0,1.8,1.6,1.3,1.1,0.93,0.85,0.65,0.53,0.44,0.40,0.35,0.0,0.0};
+static const double DRTimes[64]={100000/*infinity*/,100000/*infinity*/,118200.0,101300.0,88600.0,70900.0,59100.0,50700.0,44300.0,35500.0,29600.0,25300.0,22200.0,17700.0,
+ 14800.0,12700.0,11100.0,8900.0,7400.0,6300.0,5500.0,4400.0,3700.0,3200.0,2800.0,2200.0,1800.0,1600.0,1400.0,1100.0,
+ 920.0,790.0,690.0,550.0,460.0,390.0,340.0,270.0,230.0,200.0,170.0,140.0,110.0,98.0,85.0,68.0,57.0,49.0,43.0,34.0,
+ 28.0,25.0,22.0,18.0,14.0,12.0,11.0,8.5,7.1,6.1,5.4,4.3,3.6,3.1};
+
+#define MEM4B() ((m_udata.data[0]>>0x0)&0x0200)
+#define DAC18B() ((m_udata.data[0]>>0x0)&0x0100)
+#define MVOL() ((m_udata.data[0]>>0x0)&0x000F)
+#define RBL() ((m_udata.data[1]>>0x7)&0x0003)
+#define RBP() ((m_udata.data[1]>>0x0)&0x003F)
+#define MOFULL() ((m_udata.data[2]>>0x0)&0x1000)
+#define MOEMPTY() ((m_udata.data[2]>>0x0)&0x0800)
+#define MIOVF() ((m_udata.data[2]>>0x0)&0x0400)
+#define MIFULL() ((m_udata.data[2]>>0x0)&0x0200)
+#define MIEMPTY() ((m_udata.data[2]>>0x0)&0x0100)
+
+#define SCILV0() ((m_udata.data[0x24/2]>>0x0)&0xff)
+#define SCILV1() ((m_udata.data[0x26/2]>>0x0)&0xff)
+#define SCILV2() ((m_udata.data[0x28/2]>>0x0)&0xff)
+
+#define SCIEX0 0
+#define SCIEX1 1
+#define SCIEX2 2
+#define SCIMID 3
+#define SCIDMA 4
+#define SCIIRQ 5
+#define SCITMA 6
+#define SCITMB 7
+
+#define USEDSP
+
+/* TODO */
+//#define dma_transfer_end ((scsp_regs[0x24/2] & 0x10)>>4)|(((scsp_regs[0x26/2] & 0x10)>>4)<<1)|(((scsp_regs[0x28/2] & 0x10)>>4)<<2)
+
+static const float SDLT[8]={-1000000.0f,-36.0f,-30.0f,-24.0f,-18.0f,-12.0f,-6.0f,0.0f};
+
+const device_type SCSP = &device_creator<scsp_device>;
+
+scsp_device::scsp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, SCSP, "SCSP", tag, owner, clock, "scsp", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_roffset(0),
+ m_irq_cb(*this),
+ m_main_irq_cb(*this),
+ m_BUFPTR(0),
+ m_SCSPRAM(NULL),
+ m_SCSPRAM_LENGTH(0),
+ m_Master(0),
+ m_stream(NULL),
+ m_buffertmpl(NULL),
+ m_buffertmpr(NULL),
+ m_IrqTimA(0),
+ m_IrqTimBC(0),
+ m_IrqMidi(0),
+ m_MidiOutW(0),
+ m_MidiOutR(0),
+ m_MidiW(0),
+ m_MidiR(0),
+ m_timerA(NULL),
+ m_timerB(NULL),
+ m_timerC(NULL),
+ m_mcieb(0),
+ m_mcipd(0),
+ m_bufferl(NULL),
+ m_bufferr(NULL),
+ m_length(0),
+ m_RBUFDST(NULL)
+{
+ memset(m_RINGBUF, 0, sizeof(m_RINGBUF));
+ memset(m_MidiStack, 0, sizeof(m_MidiStack));
+ memset(m_LPANTABLE, 0, sizeof(m_LPANTABLE));
+ memset(m_RPANTABLE, 0, sizeof(m_RPANTABLE));
+ memset(m_TimPris, 0, sizeof(m_TimPris));
+ memset(m_ARTABLE, 0, sizeof(m_ARTABLE));
+ memset(m_DRTABLE, 0, sizeof(m_DRTABLE));
+ memset(m_EG_TABLE, 0, sizeof(m_EG_TABLE));
+ memset(m_PLFO_TRI, 0, sizeof(m_PLFO_TRI));
+ memset(m_PLFO_SQR, 0, sizeof(m_PLFO_SQR));
+ memset(m_PLFO_SAW, 0, sizeof(m_PLFO_SAW));
+ memset(m_PLFO_NOI, 0, sizeof(m_PLFO_NOI));
+ memset(m_ALFO_TRI, 0, sizeof(m_ALFO_TRI));
+ memset(m_ALFO_SQR, 0, sizeof(m_ALFO_SQR));
+ memset(m_ALFO_SAW, 0, sizeof(m_ALFO_SAW));
+ memset(m_ALFO_NOI, 0, sizeof(m_ALFO_NOI));
+ memset(m_PSCALES, 0, sizeof(m_PSCALES));
+ memset(m_ASCALES, 0, sizeof(m_ASCALES));
+ memset(&m_Slots, 0, sizeof(m_Slots));
+ memset(&m_udata.data, 0, sizeof(m_udata.data));
+ m_TimCnt[0] = 0;
+ m_TimCnt[1] = 0;
+ m_TimCnt[2] = 0;
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void scsp_device::device_start()
+{
+ // init the emulation
+ init();
+
+ // set up the IRQ callbacks
+ m_irq_cb.resolve_safe();
+ m_main_irq_cb.resolve_safe();
+
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, 44100);
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void scsp_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ m_bufferl = outputs[0];
+ m_bufferr = outputs[1];
+ m_length = samples;
+ DoMasterSamples(samples);
+}
+
+unsigned char scsp_device::DecodeSCI(unsigned char irq)
+{
+ unsigned char SCI=0;
+ unsigned char v;
+ v=(SCILV0()&(1<<irq))?1:0;
+ SCI|=v;
+ v=(SCILV1()&(1<<irq))?1:0;
+ SCI|=v<<1;
+ v=(SCILV2()&(1<<irq))?1:0;
+ SCI|=v<<2;
+ return SCI;
+}
+
+void scsp_device::CheckPendingIRQ()
+{
+ UINT32 pend=m_udata.data[0x20/2];
+ UINT32 en=m_udata.data[0x1e/2];
+ if(m_MidiW!=m_MidiR)
+ {
+ m_udata.data[0x20/2] |= 8;
+ pend |= 8;
+ }
+ if(!pend)
+ return;
+ if(pend&0x40)
+ if(en&0x40)
+ {
+ m_irq_cb(m_IrqTimA, ASSERT_LINE);
+ return;
+ }
+ if(pend&0x80)
+ if(en&0x80)
+ {
+ m_irq_cb(m_IrqTimBC, ASSERT_LINE);
+ return;
+ }
+ if(pend&0x100)
+ if(en&0x100)
+ {
+ m_irq_cb(m_IrqTimBC, ASSERT_LINE);
+ return;
+ }
+ if(pend&8)
+ if (en&8)
+ {
+ m_irq_cb(m_IrqMidi, ASSERT_LINE);
+ m_udata.data[0x20/2] &= ~8;
+ return;
+ }
+
+ m_irq_cb((offs_t)0, CLEAR_LINE);
+}
+
+void scsp_device::MainCheckPendingIRQ(UINT16 irq_type)
+{
+ m_mcipd |= irq_type;
+
+ //machine().scheduler().synchronize(); // force resync
+
+ if(m_mcipd & m_mcieb)
+ m_main_irq_cb(1);
+ else
+ m_main_irq_cb(0);
+}
+
+void scsp_device::ResetInterrupts()
+{
+ UINT32 reset = m_udata.data[0x22/2];
+
+ if (reset & 0x40)
+ {
+ m_irq_cb(m_IrqTimA, CLEAR_LINE);
+ }
+ if (reset & 0x180)
+ {
+ m_irq_cb(m_IrqTimBC, CLEAR_LINE);
+ }
+ if (reset & 0x8)
+ {
+ m_irq_cb(m_IrqMidi, CLEAR_LINE);
+ }
+
+ CheckPendingIRQ();
+}
+
+TIMER_CALLBACK_MEMBER( scsp_device::timerA_cb )
+{
+ m_TimCnt[0] = 0xFFFF;
+ m_udata.data[0x20/2]|=0x40;
+ m_udata.data[0x18/2]&=0xff00;
+ m_udata.data[0x18/2]|=m_TimCnt[0]>>8;
+
+ CheckPendingIRQ();
+ MainCheckPendingIRQ(0x40);
+}
+
+TIMER_CALLBACK_MEMBER( scsp_device::timerB_cb )
+{
+ m_TimCnt[1] = 0xFFFF;
+ m_udata.data[0x20/2]|=0x80;
+ m_udata.data[0x1a/2]&=0xff00;
+ m_udata.data[0x1a/2]|=m_TimCnt[1]>>8;
+
+ CheckPendingIRQ();
+}
+
+TIMER_CALLBACK_MEMBER( scsp_device::timerC_cb )
+{
+ m_TimCnt[2] = 0xFFFF;
+ m_udata.data[0x20/2]|=0x100;
+ m_udata.data[0x1c/2]&=0xff00;
+ m_udata.data[0x1c/2]|=m_TimCnt[2]>>8;
+
+ CheckPendingIRQ();
+}
+
+int scsp_device::Get_AR(int base, int R)
+{
+ int Rate=base+(R<<1);
+ if(Rate>63) Rate=63;
+ if(Rate<0) Rate=0;
+ return m_ARTABLE[Rate];
+}
+
+int scsp_device::Get_DR(int base, int R)
+{
+ int Rate=base+(R<<1);
+ if(Rate>63) Rate=63;
+ if(Rate<0) Rate=0;
+ return m_DRTABLE[Rate];
+}
+
+int scsp_device::Get_RR(int base, int R)
+{
+ int Rate=base+(R<<1);
+ if(Rate>63) Rate=63;
+ if(Rate<0) Rate=0;
+ return m_DRTABLE[Rate];
+}
+
+void scsp_device::Compute_EG(SCSP_SLOT *slot)
+{
+ int octave=(OCT(slot)^8)-8;
+ int rate;
+ if(KRS(slot)!=0xf)
+ rate=octave+2*KRS(slot)+((FNS(slot)>>9)&1);
+ else
+ rate=0; //rate=((FNS(slot)>>9)&1);
+
+ slot->EG.volume=0x17F<<EG_SHIFT;
+ slot->EG.AR=Get_AR(rate,AR(slot));
+ slot->EG.D1R=Get_DR(rate,D1R(slot));
+ slot->EG.D2R=Get_DR(rate,D2R(slot));
+ slot->EG.RR=Get_RR(rate,RR(slot));
+ slot->EG.DL=0x1f-DL(slot);
+ slot->EG.EGHOLD=EGHOLD(slot);
+}
+
+int scsp_device::EG_Update(SCSP_SLOT *slot)
+{
+ switch(slot->EG.state)
+ {
+ case SCSP_ATTACK:
+ slot->EG.volume+=slot->EG.AR;
+ if(slot->EG.volume>=(0x3ff<<EG_SHIFT))
+ {
+ if (!LPSLNK(slot))
+ {
+ slot->EG.state=SCSP_DECAY1;
+ if(slot->EG.D1R>=(1024<<EG_SHIFT)) //Skip SCSP_DECAY1, go directly to SCSP_DECAY2
+ slot->EG.state=SCSP_DECAY2;
+ }
+ slot->EG.volume=0x3ff<<EG_SHIFT;
+ }
+ if(slot->EG.EGHOLD)
+ return 0x3ff<<(SHIFT-10);
+ break;
+ case SCSP_DECAY1:
+ slot->EG.volume-=slot->EG.D1R;
+ if(slot->EG.volume<=0)
+ slot->EG.volume=0;
+ if(slot->EG.volume>>(EG_SHIFT+5)<=slot->EG.DL)
+ slot->EG.state=SCSP_DECAY2;
+ break;
+ case SCSP_DECAY2:
+ if(D2R(slot)==0)
+ return (slot->EG.volume>>EG_SHIFT)<<(SHIFT-10);
+ slot->EG.volume-=slot->EG.D2R;
+ if(slot->EG.volume<=0)
+ slot->EG.volume=0;
+
+ break;
+ case SCSP_RELEASE:
+ slot->EG.volume-=slot->EG.RR;
+ if(slot->EG.volume<=0)
+ {
+ slot->EG.volume=0;
+ StopSlot(slot,0);
+ //slot->EG.volume=0x17F<<EG_SHIFT;
+ //slot->EG.state=SCSP_ATTACK;
+ }
+ break;
+ default:
+ return 1<<SHIFT;
+ }
+ return (slot->EG.volume>>EG_SHIFT)<<(SHIFT-10);
+}
+
+UINT32 scsp_device::Step(SCSP_SLOT *slot)
+{
+ int octave=(OCT(slot)^8)-8+SHIFT-10;
+ UINT32 Fn=FNS(slot)+(1 << 10);
+ if (octave >= 0)
+ {
+ Fn<<=octave;
+ }
+ else
+ {
+ Fn>>=-octave;
+ }
+
+ return Fn;
+}
+
+
+void scsp_device::Compute_LFO(SCSP_SLOT *slot)
+{
+ if(PLFOS(slot)!=0)
+ LFO_ComputeStep(&(slot->PLFO),LFOF(slot),PLFOWS(slot),PLFOS(slot),0);
+ if(ALFOS(slot)!=0)
+ LFO_ComputeStep(&(slot->ALFO),LFOF(slot),ALFOWS(slot),ALFOS(slot),1);
+}
+
+void scsp_device::StartSlot(SCSP_SLOT *slot)
+{
+ UINT32 start_offset;
+
+ slot->active=1;
+ start_offset = PCM8B(slot) ? SA(slot) : SA(slot) & 0x7FFFE;
+ slot->base=m_SCSPRAM + start_offset;
+ slot->cur_addr=0;
+ slot->nxt_addr=1<<SHIFT;
+ slot->step=Step(slot);
+ Compute_EG(slot);
+ slot->EG.state=SCSP_ATTACK;
+ slot->EG.volume=0x17F<<EG_SHIFT;
+ slot->Prev=0;
+ slot->Backwards=0;
+
+ Compute_LFO(slot);
+
+// printf("StartSlot[%p]: SA %x PCM8B %x LPCTL %x ALFOS %x STWINH %x TL %x EFSDL %x\n", slot, SA(slot), PCM8B(slot), LPCTL(slot), ALFOS(slot), STWINH(slot), TL(slot), EFSDL(slot));
+}
+
+void scsp_device::StopSlot(SCSP_SLOT *slot,int keyoff)
+{
+ if(keyoff /*&& slot->EG.state!=SCSP_RELEASE*/)
+ {
+ slot->EG.state=SCSP_RELEASE;
+ }
+ else
+ {
+ slot->active=0;
+ }
+ slot->udata.data[0]&=~0x800;
+}
+
+#define log_base_2(n) (log((double)(n))/log(2.0))
+
+void scsp_device::init()
+{
+ int i;
+
+ SCSPDSP_Init(&m_DSP);
+
+ m_IrqTimA = m_IrqTimBC = m_IrqMidi = 0;
+ m_MidiR=m_MidiW=0;
+ m_MidiOutR=m_MidiOutW=0;
+
+ // get SCSP RAM
+ if (strcmp(tag(), ":scsp") == 0 || strcmp(tag(), ":scsp1") == 0)
+ {
+ m_Master=1;
+ }
+ else
+ {
+ m_Master=0;
+ }
+
+ m_SCSPRAM = region()->base();
+ if (m_SCSPRAM)
+ {
+ m_SCSPRAM_LENGTH = region()->bytes();
+ m_DSP.SCSPRAM = (UINT16 *)m_SCSPRAM;
+ m_DSP.SCSPRAM_LENGTH = m_SCSPRAM_LENGTH/2;
+ m_SCSPRAM += m_roffset;
+ }
+
+ m_timerA = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(scsp_device::timerA_cb), this));
+ m_timerB = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(scsp_device::timerB_cb), this));
+ m_timerC = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(scsp_device::timerC_cb), this));
+
+ for(i=0;i<0x400;++i)
+ {
+ float envDB=((float)(3*(i-0x3ff)))/32.0f;
+ float scale=(float)(1<<SHIFT);
+ m_EG_TABLE[i]=(INT32)(powf(10.0f,envDB/20.0f)*scale);
+ }
+
+ for(i=0;i<0x10000;++i)
+ {
+ int iTL =(i>>0x0)&0xff;
+ int iPAN=(i>>0x8)&0x1f;
+ int iSDL=(i>>0xD)&0x07;
+ float TL=1.0f;
+ float SegaDB=0.0f;
+ float fSDL=1.0f;
+ float PAN=1.0f;
+ float LPAN,RPAN;
+
+ if(iTL&0x01) SegaDB-=0.4f;
+ if(iTL&0x02) SegaDB-=0.8f;
+ if(iTL&0x04) SegaDB-=1.5f;
+ if(iTL&0x08) SegaDB-=3.0f;
+ if(iTL&0x10) SegaDB-=6.0f;
+ if(iTL&0x20) SegaDB-=12.0f;
+ if(iTL&0x40) SegaDB-=24.0f;
+ if(iTL&0x80) SegaDB-=48.0f;
+
+ TL=powf(10.0f,SegaDB/20.0f);
+
+ SegaDB=0;
+ if(iPAN&0x1) SegaDB-=3.0f;
+ if(iPAN&0x2) SegaDB-=6.0f;
+ if(iPAN&0x4) SegaDB-=12.0f;
+ if(iPAN&0x8) SegaDB-=24.0f;
+
+ if((iPAN&0xf)==0xf) PAN=0.0;
+ else PAN=powf(10.0f,SegaDB/20.0f);
+
+ if(iPAN<0x10)
+ {
+ LPAN=PAN;
+ RPAN=1.0;
+ }
+ else
+ {
+ RPAN=PAN;
+ LPAN=1.0;
+ }
+
+ if(iSDL)
+ fSDL=powf(10.0f,(SDLT[iSDL])/20.0f);
+ else
+ fSDL=0.0;
+
+ m_LPANTABLE[i]=FIX((4.0f*LPAN*TL*fSDL));
+ m_RPANTABLE[i]=FIX((4.0f*RPAN*TL*fSDL));
+ }
+
+ m_ARTABLE[0]=m_DRTABLE[0]=0; //Infinite time
+ m_ARTABLE[1]=m_DRTABLE[1]=0; //Infinite time
+ for(i=2;i<64;++i)
+ {
+ double t,step,scale;
+ t=ARTimes[i]; //In ms
+ if(t!=0.0)
+ {
+ step=(1023*1000.0)/( 44100.0*t);
+ scale=(double) (1<<EG_SHIFT);
+ m_ARTABLE[i]=(int) (step*scale);
+ }
+ else
+ m_ARTABLE[i]=1024<<EG_SHIFT;
+
+ t=DRTimes[i]; //In ms
+ step=(1023*1000.0)/( 44100.0*t);
+ scale=(double) (1<<EG_SHIFT);
+ m_DRTABLE[i]=(int) (step*scale);
+ }
+
+ // make sure all the slots are off
+ for(i=0;i<32;++i)
+ {
+ m_Slots[i].slot=i;
+ m_Slots[i].active=0;
+ m_Slots[i].base=NULL;
+ m_Slots[i].EG.state=SCSP_RELEASE;
+ }
+
+ LFO_Init();
+ m_buffertmpl=auto_alloc_array_clear(machine(), signed int, 44100);
+ m_buffertmpr=auto_alloc_array_clear(machine(), signed int, 44100);
+
+ // no "pend"
+ m_udata.data[0x20/2] = 0;
+ m_TimCnt[0] = 0xffff;
+ m_TimCnt[1] = 0xffff;
+ m_TimCnt[2] = 0xffff;
+}
+
+void scsp_device::UpdateSlotReg(int s,int r)
+{
+ SCSP_SLOT *slot=m_Slots+s;
+ int sl;
+ switch(r&0x3f)
+ {
+ case 0:
+ case 1:
+ if(KEYONEX(slot))
+ {
+ for(sl=0;sl<32;++sl)
+ {
+ SCSP_SLOT *s2=m_Slots+sl;
+ {
+ if(KEYONB(s2) && s2->EG.state==SCSP_RELEASE/*&& !s2->active*/)
+ {
+ StartSlot(s2);
+ }
+ if(!KEYONB(s2) /*&& s2->active*/)
+ {
+ StopSlot(s2,1);
+ }
+ }
+ }
+ slot->udata.data[0]&=~0x1000;
+ }
+ break;
+ case 0x10:
+ case 0x11:
+ slot->step=Step(slot);
+ break;
+ case 0xA:
+ case 0xB:
+ slot->EG.RR=Get_RR(0,RR(slot));
+ slot->EG.DL=0x1f-DL(slot);
+ break;
+ case 0x12:
+ case 0x13:
+ Compute_LFO(slot);
+ break;
+ }
+}
+
+void scsp_device::UpdateReg(address_space &space, int reg)
+{
+ switch(reg&0x3f)
+ {
+ case 0x0:
+ m_stream->set_output_gain(0,MVOL() / 15.0);
+ m_stream->set_output_gain(1,MVOL() / 15.0);
+ break;
+ case 0x2:
+ case 0x3:
+ {
+ unsigned int v=RBL();
+ m_DSP.RBP=RBP();
+ if(v==0)
+ m_DSP.RBL=8*1024;
+ else if(v==1)
+ m_DSP.RBL=16*1024;
+ if(v==2)
+ m_DSP.RBL=32*1024;
+ if(v==3)
+ m_DSP.RBL=64*1024;
+ }
+ break;
+ case 0x6:
+ case 0x7:
+ midi_in(space, 0, m_udata.data[0x6/2]&0xff, 0);
+ break;
+ case 8:
+ case 9:
+ /* Only MSLC could be written. */
+ m_udata.data[0x8/2] &= 0xf800; /**< @todo Docs claims MSLC to be 0x7800, but Jikkyou Parodius doesn't agree. */
+ break;
+ case 0x12:
+ case 0x13:
+ m_dma.dmea = (m_udata.data[0x12/2] & 0xfffe) | (m_dma.dmea & 0xf0000);
+ break;
+ case 0x14:
+ case 0x15:
+ m_dma.dmea = ((m_udata.data[0x14/2] & 0xf000) << 4) | (m_dma.dmea & 0xfffe);
+ m_dma.drga = (m_udata.data[0x14/2] & 0x0ffe);
+ break;
+ case 0x16:
+ case 0x17:
+ m_dma.dtlg = (m_udata.data[0x16/2] & 0x0ffe);
+ m_dma.ddir = (m_udata.data[0x16/2] & 0x2000) >> 13;
+ m_dma.dgate = (m_udata.data[0x16/2] & 0x4000) >> 14;
+ if(m_udata.data[0x16/2] & 0x1000) // dexe
+ exec_dma(space);
+ break;
+ case 0x18:
+ case 0x19:
+ if(m_Master)
+ {
+ UINT32 time;
+
+ m_TimPris[0]=1<<((m_udata.data[0x18/2]>>8)&0x7);
+ m_TimCnt[0]=(m_udata.data[0x18/2]&0xff)<<8;
+
+ if ((m_udata.data[0x18/2]&0xff) != 255)
+ {
+ time = (44100 / m_TimPris[0]) / (255-(m_udata.data[0x18/2]&0xff));
+ if (time)
+ {
+ m_timerA->adjust(attotime::from_hz(time));
+ }
+ }
+ }
+ break;
+ case 0x1a:
+ case 0x1b:
+ if(m_Master)
+ {
+ UINT32 time;
+
+ m_TimPris[1]=1<<((m_udata.data[0x1A/2]>>8)&0x7);
+ m_TimCnt[1]=(m_udata.data[0x1A/2]&0xff)<<8;
+
+ if ((m_udata.data[0x1A/2]&0xff) != 255)
+ {
+ time = (44100 / m_TimPris[1]) / (255-(m_udata.data[0x1A/2]&0xff));
+ if (time)
+ {
+ m_timerB->adjust(attotime::from_hz(time));
+ }
+ }
+ }
+ break;
+ case 0x1C:
+ case 0x1D:
+ if(m_Master)
+ {
+ UINT32 time;
+
+ m_TimPris[2]=1<<((m_udata.data[0x1C/2]>>8)&0x7);
+ m_TimCnt[2]=(m_udata.data[0x1C/2]&0xff)<<8;
+
+ if ((m_udata.data[0x1C/2]&0xff) != 255)
+ {
+ time = (44100 / m_TimPris[2]) / (255-(m_udata.data[0x1C/2]&0xff));
+ if (time)
+ {
+ m_timerC->adjust(attotime::from_hz(time));
+ }
+ }
+ }
+ break;
+ case 0x1e: // SCIEB
+ case 0x1f:
+ if(m_Master)
+ {
+ CheckPendingIRQ();
+
+ if(m_udata.data[0x1e/2] & 0x610)
+ popmessage("SCSP SCIEB enabled %04x, contact MAMEdev",m_udata.data[0x1e/2]);
+ }
+ break;
+ case 0x20: // SCIPD
+ case 0x21:
+ if(m_Master)
+ {
+ if(m_udata.data[0x1e/2] & m_udata.data[0x20/2] & 0x20)
+ popmessage("SCSP SCIPD write %04x, contact MAMEdev",m_udata.data[0x20/2]);
+ }
+ break;
+ case 0x22: //SCIRE
+ case 0x23:
+
+ if(m_Master)
+ {
+ m_udata.data[0x20/2]&=~m_udata.data[0x22/2];
+ ResetInterrupts();
+
+ // behavior from real hardware: if you SCIRE a timer that's expired,
+ // it'll immediately pop up again in SCIPD. ask Sakura Taisen on the Saturn...
+ if (m_TimCnt[0] == 0xffff)
+ {
+ m_udata.data[0x20/2] |= 0x40;
+ }
+ if (m_TimCnt[1] == 0xffff)
+ {
+ m_udata.data[0x20/2] |= 0x80;
+ }
+ if (m_TimCnt[2] == 0xffff)
+ {
+ m_udata.data[0x20/2] |= 0x100;
+ }
+ }
+ break;
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ if(m_Master)
+ {
+ m_IrqTimA=DecodeSCI(SCITMA);
+ m_IrqTimBC=DecodeSCI(SCITMB);
+ m_IrqMidi=DecodeSCI(SCIMID);
+ }
+ break;
+ case 0x2a:
+ case 0x2b:
+ m_mcieb = m_udata.data[0x2a/2];
+
+ MainCheckPendingIRQ(0);
+ if(m_mcieb & ~0x60)
+ popmessage("SCSP MCIEB enabled %04x, contact MAMEdev",m_mcieb);
+ break;
+ case 0x2c:
+ case 0x2d:
+ if(m_udata.data[0x2c/2] & 0x20)
+ MainCheckPendingIRQ(0x20);
+ break;
+ case 0x2e:
+ case 0x2f:
+ m_mcipd &= ~m_udata.data[0x2e/2];
+ MainCheckPendingIRQ(0);
+ break;
+
+ }
+}
+
+void scsp_device::UpdateSlotRegR(int slot,int reg)
+{
+}
+
+void scsp_device::UpdateRegR(address_space &space, int reg)
+{
+ switch(reg&0x3f)
+ {
+ case 4:
+ case 5:
+ {
+ unsigned short v=m_udata.data[0x5/2];
+ v&=0xff00;
+ v|=m_MidiStack[m_MidiR];
+ m_irq_cb(m_IrqMidi, CLEAR_LINE); // cancel the IRQ
+ logerror("Read %x from SCSP MIDI\n", v);
+ if(m_MidiR!=m_MidiW)
+ {
+ ++m_MidiR;
+ m_MidiR&=31;
+ }
+ m_udata.data[0x5/2]=v;
+ }
+ break;
+ case 8:
+ case 9:
+ {
+ // MSLC | CA |SGC|EG
+ // f e d c b a 9 8 7 6 5 4 3 2 1 0
+ unsigned char MSLC=(m_udata.data[0x8/2]>>11)&0x1f;
+ SCSP_SLOT *slot=m_Slots + MSLC;
+ unsigned int SGC = (slot->EG.state) & 3;
+ unsigned int CA = (slot->cur_addr>>(SHIFT+12)) & 0xf;
+ unsigned int EG = (0x1f - (slot->EG.volume>>(EG_SHIFT+5))) & 0x1f;
+ /* note: according to the manual MSLC is write only, CA, SGC and EG read only. */
+ m_udata.data[0x8/2] = /*(MSLC << 11) |*/ (CA << 7) | (SGC << 5) | EG;
+ }
+ break;
+
+ case 0x18:
+ case 0x19:
+ break;
+
+ case 0x1a:
+ case 0x1b:
+ break;
+
+ case 0x1c:
+ case 0x1d:
+ break;
+
+ case 0x2a:
+ case 0x2b:
+ m_udata.data[0x2a/2] = m_mcieb;
+ break;
+
+ case 0x2c:
+ case 0x2d:
+ m_udata.data[0x2c/2] = m_mcipd;
+ break;
+ }
+}
+
+void scsp_device::w16(address_space &space,unsigned int addr,unsigned short val)
+{
+ addr&=0xffff;
+ if(addr<0x400)
+ {
+ int slot=addr/0x20;
+ addr&=0x1f;
+ *((unsigned short *) (m_Slots[slot].udata.datab+(addr))) = val;
+ UpdateSlotReg(slot,addr&0x1f);
+ }
+ else if(addr<0x600)
+ {
+ if (addr < 0x430)
+ {
+ *((unsigned short *) (m_udata.datab+((addr&0x3f)))) = val;
+ UpdateReg(space, addr&0x3f);
+ }
+ }
+ else if(addr<0x700)
+ m_RINGBUF[(addr-0x600)/2]=val;
+ else
+ {
+ //DSP
+ if(addr<0x780) //COEF
+ *((unsigned short *) (m_DSP.COEF+(addr-0x700)/2))=val;
+ else if(addr<0x7c0)
+ *((unsigned short *) (m_DSP.MADRS+(addr-0x780)/2))=val;
+ else if(addr<0x800) // MADRS is mirrored twice
+ *((unsigned short *) (m_DSP.MADRS+(addr-0x7c0)/2))=val;
+ else if(addr<0xC00)
+ {
+ *((unsigned short *) (m_DSP.MPRO+(addr-0x800)/2))=val;
+
+ if(addr==0xBF0)
+ {
+ SCSPDSP_Start(&m_DSP);
+ }
+ }
+ }
+}
+
+unsigned short scsp_device::r16(address_space &space, unsigned int addr)
+{
+ unsigned short v=0;
+ addr&=0xffff;
+ if(addr<0x400)
+ {
+ int slot=addr/0x20;
+ addr&=0x1f;
+ UpdateSlotRegR(slot,addr&0x1f);
+ v=*((unsigned short *) (m_Slots[slot].udata.datab+(addr)));
+ }
+ else if(addr<0x600)
+ {
+ if (addr < 0x430)
+ {
+ UpdateRegR(space, addr&0x3f);
+ v= *((unsigned short *) (m_udata.datab+((addr&0x3f))));
+ }
+ }
+ else if(addr<0x700)
+ v=m_RINGBUF[(addr-0x600)/2];
+ else
+ {
+ //DSP
+ if(addr<0x780) //COEF
+ v= *((unsigned short *) (m_DSP.COEF+(addr-0x700)/2));
+ else if(addr<0x7c0)
+ v= *((unsigned short *) (m_DSP.MADRS+(addr-0x780)/2));
+ else if(addr<0x800)
+ v= *((unsigned short *) (m_DSP.MADRS+(addr-0x7c0)/2));
+ else if(addr<0xC00)
+ v= *((unsigned short *) (m_DSP.MPRO+(addr-0x800)/2));
+ else if(addr<0xE00)
+ {
+ if(addr & 2)
+ v= m_DSP.TEMP[(addr >> 2) & 0x7f] & 0xffff;
+ else
+ v= m_DSP.TEMP[(addr >> 2) & 0x7f] >> 16;
+ }
+ else if(addr<0xE80)
+ {
+ if(addr & 2)
+ v= m_DSP.MEMS[(addr >> 2) & 0x1f] & 0xffff;
+ else
+ v= m_DSP.MEMS[(addr >> 2) & 0x1f] >> 16;
+ }
+ else if(addr<0xEC0)
+ {
+ if(addr & 2)
+ v= m_DSP.MIXS[(addr >> 2) & 0xf] & 0xffff;
+ else
+ v= m_DSP.MIXS[(addr >> 2) & 0xf] >> 16;
+ }
+ else if(addr<0xEE0)
+ v= *((unsigned short *) (m_DSP.EFREG+(addr-0xec0)/2));
+ else
+ {
+ /**!
+ @todo Kyuutenkai reads from 0xee0/0xee2, it's tied with EXTS register(s) also used for CD-Rom Player equalizer.
+ This port is actually an external parallel port, directly connected from the CD Block device, hence code is a bit of an hack.
+ Kyuutenkai code snippet for reference:
+ 004A3A: 207C 0010 0EE0 movea.l #$100ee0, A0
+ 004A40: 43EA 0090 lea ($90,A2), A1 ;A2=0x700
+ 004A44: 6100 0254 bsr $4c9a
+ 004A48: 207C 0010 0EE2 movea.l #$100ee2, A0
+ 004A4E: 43EA 0092 lea ($92,A2), A1
+ 004A52: 6100 0246 bsr $4c9a
+ 004A56: 207C 0010 0ED2 movea.l #$100ed2, A0
+ 004A5C: 43EA 0094 lea ($94,A2), A1
+ 004A60: 6100 0238 bsr $4c9a
+ 004A64: 3540 0096 move.w D0, ($96,A2)
+ 004A68: 207C 0010 0ED4 movea.l #$100ed4, A0
+ 004A6E: 43EA 0098 lea ($98,A2), A1
+ 004A72: 6100 0226 bsr $4c9a
+ 004A76: 3540 009A move.w D0, ($9a,A2)
+ 004A7A: 207C 0010 0ED6 movea.l #$100ed6, A0
+ 004A80: 43EA 009C lea ($9c,A2), A1
+ 004A84: 6100 0214 bsr $4c9a
+ 004A88: 3540 009E move.w D0, ($9e,A2)
+ 004A8C: 4E75 rts
+
+ 004C9A: 48E7 4000 movem.l D1, -(A7)
+ 004C9E: 3010 move.w (A0), D0 ;reads from 0x100ee0/ee2
+ 004CA0: 4A40 tst.w D0
+ 004CA2: 6A00 0004 bpl $4ca8
+ 004CA6: 4440 neg.w D0
+ 004CA8: 3211 move.w (A1), D1
+ 004CAA: D041 add.w D1, D0
+ 004CAC: E248 lsr.w #1, D0
+ 004CAE: 3280 move.w D0, (A1) ;writes to RAM buffer 0x790/0x792
+ 004CB0: 4CDF 0002 movem.l (A7)+, D1
+ 004CB4: 4E75 rts
+ */
+ logerror("SCSP: Reading from EXTS register %08x\n",addr);
+ if(addr == 0xee0)
+ v = space.machine().device<cdda_device>("cdda")->get_channel_volume(0);
+ if(addr == 0xee2)
+ v = space.machine().device<cdda_device>("cdda")->get_channel_volume(1);
+ }
+ }
+ return v;
+}
+
+
+#define REVSIGN(v) ((~v)+1)
+
+inline INT32 scsp_device::UpdateSlot(SCSP_SLOT *slot)
+{
+ INT32 sample;
+ int step=slot->step;
+ UINT32 addr1,addr2,addr_select; // current and next sample addresses
+ UINT32 *addr[2] = {&addr1, &addr2}; // used for linear interpolation
+ UINT32 *slot_addr[2] = {&(slot->cur_addr), &(slot->nxt_addr)}; //
+
+ if(SSCTL(slot)!=0) //no FM or noise yet
+ return 0;
+
+ if(PLFOS(slot)!=0)
+ {
+ step=step*PLFO_Step(&(slot->PLFO));
+ step>>=SHIFT;
+ }
+
+ if(PCM8B(slot))
+ {
+ addr1=slot->cur_addr>>SHIFT;
+ addr2=slot->nxt_addr>>SHIFT;
+ }
+ else
+ {
+ addr1=(slot->cur_addr>>(SHIFT-1))&0x7fffe;
+ addr2=(slot->nxt_addr>>(SHIFT-1))&0x7fffe;
+ }
+
+ if(MDL(slot)!=0 || MDXSL(slot)!=0 || MDYSL(slot)!=0)
+ {
+ INT32 smp=(m_RINGBUF[(m_BUFPTR+MDXSL(slot))&63]+m_RINGBUF[(m_BUFPTR+MDYSL(slot))&63])/2;
+
+ smp<<=0xA; // associate cycle with 1024
+ smp>>=0x1A-MDL(slot); // ex. for MDL=0xF, sample range corresponds to +/- 64 pi (32=2^5 cycles) so shift by 11 (16-5 == 0x1A-0xF)
+ if(!PCM8B(slot)) smp<<=1;
+
+ addr1+=smp; addr2+=smp;
+ }
+
+ if(PCM8B(slot)) //8 bit signed
+ {
+ INT8 *p1=(signed char *) (m_SCSPRAM+BYTE_XOR_BE(((SA(slot)+addr1))&0x7FFFF));
+ INT8 *p2=(signed char *) (m_SCSPRAM+BYTE_XOR_BE(((SA(slot)+addr2))&0x7FFFF));
+ //sample=(p[0])<<8;
+ INT32 s;
+ INT32 fpart=slot->cur_addr&((1<<SHIFT)-1);
+ s=(int) (p1[0]<<8)*((1<<SHIFT)-fpart)+(int) (p2[0]<<8)*fpart;
+ sample=(s>>SHIFT);
+ }
+ else //16 bit signed (endianness?)
+ {
+ INT16 *p1=(signed short *) (m_SCSPRAM+((SA(slot)+addr1)&0x7FFFE));
+ INT16 *p2=(signed short *) (m_SCSPRAM+((SA(slot)+addr2)&0x7FFFE));
+ INT32 s;
+ INT32 fpart=slot->cur_addr&((1<<SHIFT)-1);
+ s=(int)(p1[0])*((1<<SHIFT)-fpart)+(int)(p2[0])*fpart;
+ sample=(s>>SHIFT);
+ }
+
+ if(SBCTL(slot)&0x1)
+ sample ^= 0x7FFF;
+ if(SBCTL(slot)&0x2)
+ sample = (INT16)(sample^0x8000);
+
+ if(slot->Backwards)
+ slot->cur_addr-=step;
+ else
+ slot->cur_addr+=step;
+ slot->nxt_addr=slot->cur_addr+(1<<SHIFT);
+
+ addr1=slot->cur_addr>>SHIFT;
+ addr2=slot->nxt_addr>>SHIFT;
+
+ if(addr1>=LSA(slot) && !(slot->Backwards))
+ {
+ if(LPSLNK(slot) && slot->EG.state==SCSP_ATTACK)
+ slot->EG.state = SCSP_DECAY1;
+ }
+
+ for (addr_select=0;addr_select<2;addr_select++)
+ {
+ INT32 rem_addr;
+ switch(LPCTL(slot))
+ {
+ case 0: //no loop
+ if(*addr[addr_select]>=LSA(slot) && *addr[addr_select]>=LEA(slot))
+ {
+ //slot->active=0;
+ StopSlot(slot,0);
+ }
+ break;
+ case 1: //normal loop
+ if(*addr[addr_select]>=LEA(slot))
+ {
+ rem_addr = *slot_addr[addr_select] - (LEA(slot)<<SHIFT);
+ *slot_addr[addr_select]=(LSA(slot)<<SHIFT) + rem_addr;
+ }
+ break;
+ case 2: //reverse loop
+ if((*addr[addr_select]>=LSA(slot)) && !(slot->Backwards))
+ {
+ rem_addr = *slot_addr[addr_select] - (LSA(slot)<<SHIFT);
+ *slot_addr[addr_select]=(LEA(slot)<<SHIFT) - rem_addr;
+ slot->Backwards=1;
+ }
+ else if((*addr[addr_select]<LSA(slot) || (*slot_addr[addr_select]&0x80000000)) && slot->Backwards)
+ {
+ rem_addr = (LSA(slot)<<SHIFT) - *slot_addr[addr_select];
+ *slot_addr[addr_select]=(LEA(slot)<<SHIFT) - rem_addr;
+ }
+ break;
+ case 3: //ping-pong
+ if(*addr[addr_select]>=LEA(slot)) //reached end, reverse till start
+ {
+ rem_addr = *slot_addr[addr_select] - (LEA(slot)<<SHIFT);
+ *slot_addr[addr_select]=(LEA(slot)<<SHIFT) - rem_addr;
+ slot->Backwards=1;
+ }
+ else if((*addr[addr_select]<LSA(slot) || (*slot_addr[addr_select]&0x80000000)) && slot->Backwards)//reached start or negative
+ {
+ rem_addr = (LSA(slot)<<SHIFT) - *slot_addr[addr_select];
+ *slot_addr[addr_select]=(LSA(slot)<<SHIFT) + rem_addr;
+ slot->Backwards=0;
+ }
+ break;
+ }
+ }
+
+ if(!SDIR(slot))
+ {
+ if(ALFOS(slot)!=0)
+ {
+ sample=sample*ALFO_Step(&(slot->ALFO));
+ sample>>=SHIFT;
+ }
+
+ if(slot->EG.state==SCSP_ATTACK)
+ sample=(sample*EG_Update(slot))>>SHIFT;
+ else
+ sample=(sample*m_EG_TABLE[EG_Update(slot)>>(SHIFT-10)])>>SHIFT;
+ }
+
+ if(!STWINH(slot))
+ {
+ if(!SDIR(slot))
+ {
+ unsigned short Enc=((TL(slot))<<0x0)|(0x7<<0xd);
+ *m_RBUFDST=(sample*m_LPANTABLE[Enc])>>(SHIFT+1);
+ }
+ else
+ {
+ unsigned short Enc=(0<<0x0)|(0x7<<0xd);
+ *m_RBUFDST=(sample*m_LPANTABLE[Enc])>>(SHIFT+1);
+ }
+ }
+
+ return sample;
+}
+
+void scsp_device::DoMasterSamples(int nsamples)
+{
+ stream_sample_t *bufr,*bufl;
+ int sl, s, i;
+
+ bufr=m_bufferr;
+ bufl=m_bufferl;
+
+ for(s=0;s<nsamples;++s)
+ {
+ INT32 smpl, smpr;
+
+ smpl = smpr = 0;
+
+ for(sl=0;sl<32;++sl)
+ {
+#if FM_DELAY
+ m_RBUFDST=m_DELAYBUF+m_DELAYPTR;
+#else
+ m_RBUFDST=m_RINGBUF+m_BUFPTR;
+#endif
+ if(m_Slots[sl].active)
+ {
+ SCSP_SLOT *slot=m_Slots+sl;
+ unsigned short Enc;
+ signed int sample;
+
+ sample=UpdateSlot(slot);
+
+ Enc=((TL(slot))<<0x0)|((IMXL(slot))<<0xd);
+ SCSPDSP_SetSample(&m_DSP,(sample*m_LPANTABLE[Enc])>>(SHIFT-2),ISEL(slot),IMXL(slot));
+ Enc=((TL(slot))<<0x0)|((DIPAN(slot))<<0x8)|((DISDL(slot))<<0xd);
+ {
+ smpl+=(sample*m_LPANTABLE[Enc])>>SHIFT;
+ smpr+=(sample*m_RPANTABLE[Enc])>>SHIFT;
+ }
+ }
+
+#if FM_DELAY
+ m_RINGBUF[(m_BUFPTR+64-(FM_DELAY-1))&63] = m_DELAYBUF[(m_DELAYPTR+FM_DELAY-(FM_DELAY-1))%FM_DELAY];
+#endif
+ ++m_BUFPTR;
+ m_BUFPTR&=63;
+#if FM_DELAY
+ ++m_DELAYPTR;
+ if(m_DELAYPTR>FM_DELAY-1) m_DELAYPTR=0;
+#endif
+ }
+
+ SCSPDSP_Step(&m_DSP);
+
+ for(i=0;i<16;++i)
+ {
+ SCSP_SLOT *slot=m_Slots+i;
+ if(EFSDL(slot))
+ {
+ unsigned short Enc=((EFPAN(slot))<<0x8)|((EFSDL(slot))<<0xd);
+ smpl+=(m_DSP.EFREG[i]*m_LPANTABLE[Enc])>>SHIFT;
+ smpr+=(m_DSP.EFREG[i]*m_RPANTABLE[Enc])>>SHIFT;
+ }
+ }
+
+ *bufl++ = ICLIP16(smpl>>2);
+ *bufr++ = ICLIP16(smpr>>2);
+ }
+}
+
+/* TODO: this needs to be timer-ized */
+void scsp_device::exec_dma(address_space &space)
+{
+ static UINT16 tmp_dma[3];
+ int i;
+
+ logerror("SCSP: DMA transfer START\n"
+ "DMEA: %04x DRGA: %04x DTLG: %04x\n"
+ "DGATE: %d DDIR: %d\n",m_dma.dmea,m_dma.drga,m_dma.dtlg,m_dma.dgate ? 1 : 0,m_dma.ddir ? 1 : 0);
+
+ /* Copy the dma values in a temp storage for resuming later */
+ /* (DMA *can't* overwrite its parameters). */
+ if(!(m_dma.ddir))
+ {
+ for(i=0;i<3;i++)
+ tmp_dma[i] = m_udata.data[(0x12+(i*2))/2];
+ }
+
+ /* note: we don't use space.read_word / write_word because it can happen that SH-2 enables the DMA instead of m68k. */
+ /* TODO: don't know if params auto-updates, I guess not ... */
+ if(m_dma.ddir)
+ {
+ if(m_dma.dgate)
+ {
+ popmessage("Check: SCSP DMA DGATE enabled, contact MAME/MESSdev");
+ for(i=0;i < m_dma.dtlg;i+=2)
+ {
+ m_SCSPRAM[m_dma.dmea] = 0;
+ m_SCSPRAM[m_dma.dmea+1] = 0;
+ m_dma.dmea+=2;
+ }
+ }
+ else
+ {
+ for(i=0;i < m_dma.dtlg;i+=2)
+ {
+ UINT16 tmp;
+ tmp = r16(space, m_dma.drga);
+ m_SCSPRAM[m_dma.dmea] = tmp & 0xff;
+ m_SCSPRAM[m_dma.dmea+1] = tmp>>8;
+ m_dma.dmea+=2;
+ m_dma.drga+=2;
+ }
+ }
+ }
+ else
+ {
+ if(m_dma.dgate)
+ {
+ popmessage("Check: SCSP DMA DGATE enabled, contact MAME/MESSdev");
+ for(i=0;i < m_dma.dtlg;i+=2)
+ {
+ w16(space, m_dma.drga, 0);
+ m_dma.drga+=2;
+ }
+ }
+ else
+ {
+ for(i=0;i < m_dma.dtlg;i+=2)
+ {
+ UINT16 tmp;
+ tmp = m_SCSPRAM[m_dma.dmea];
+ tmp|= m_SCSPRAM[m_dma.dmea+1]<<8;
+ w16(space, m_dma.drga, tmp);
+ m_dma.dmea+=2;
+ m_dma.drga+=2;
+ }
+ }
+ }
+
+ /*Resume the values*/
+ if(!(m_dma.ddir))
+ {
+ for(i=0;i<3;i++)
+ m_udata.data[(0x12+(i*2))/2] = tmp_dma[i];
+ }
+
+ /* Job done */
+ m_udata.data[0x16/2] &= ~0x1000;
+ /* request a dma end irq (TODO: make it inside the interface) */
+ if(m_udata.data[0x1e/2] & 0x10)
+ {
+ popmessage("SCSP DMA IRQ triggered, contact MAMEdev");
+ space.machine().device("audiocpu")->execute().set_input_line(DecodeSCI(SCIDMA),HOLD_LINE);
+ }
+}
+
+#ifdef UNUSED_FUNCTION
+int IRQCB(void *param)
+{
+ CheckPendingIRQ(param);
+ return -1;
+}
+#endif
+
+
+void scsp_device::set_ram_base(void *base)
+{
+ if (this)
+ {
+ m_SCSPRAM = (unsigned char *)base;
+ m_DSP.SCSPRAM = (UINT16 *)base;
+ m_SCSPRAM_LENGTH = 0x80000;
+ m_DSP.SCSPRAM_LENGTH = 0x80000/2;
+ }
+}
+
+
+READ16_MEMBER( scsp_device::read )
+{
+ m_stream->update();
+ return r16(space, offset*2);
+}
+
+WRITE16_MEMBER( scsp_device::write )
+{
+ UINT16 tmp;
+
+ m_stream->update();
+
+ tmp = r16(space, offset*2);
+ COMBINE_DATA(&tmp);
+ w16(space,offset*2, tmp);
+}
+
+WRITE16_MEMBER( scsp_device::midi_in )
+{
+ // printf("scsp_midi_in: %02x\n", data);
+
+ m_MidiStack[m_MidiW++]=data;
+ m_MidiW &= 31;
+
+ CheckPendingIRQ();
+}
+
+READ16_MEMBER( scsp_device::midi_out_r )
+{
+ unsigned char val;
+
+ val=m_MidiStack[m_MidiR++];
+ m_MidiR&=31;
+ return val;
+}
+
+//LFO handling
+
+#define LFIX(v) ((unsigned int) ((float) (1<<LFO_SHIFT)*(v)))
+
+//Convert DB to multiply amplitude
+#define DB(v) LFIX(powf(10.0f,v/20.0f))
+
+//Convert cents to step increment
+#define CENTS(v) LFIX(powf(2.0f,v/1200.0f))
+
+
+static const float LFOFreq[32]=
+{
+ 0.17f,0.19f,0.23f,0.27f,0.34f,0.39f,0.45f,0.55f,0.68f,0.78f,0.92f,1.10f,1.39f,1.60f,1.87f,2.27f,
+ 2.87f,3.31f,3.92f,4.79f,6.15f,7.18f,8.60f,10.8f,14.4f,17.2f,21.5f,28.7f,43.1f,57.4f,86.1f,172.3f
+};
+static const float ASCALE[8]={0.0f,0.4f,0.8f,1.5f,3.0f,6.0f,12.0f,24.0f};
+static const float PSCALE[8]={0.0f,7.0f,13.5f,27.0f,55.0f,112.0f,230.0f,494.0f};
+
+
+void scsp_device::LFO_Init()
+{
+ int i,s;
+ for(i=0;i<256;++i)
+ {
+ int a,p;
+// float TL;
+ //Saw
+ a=255-i;
+ if(i<128)
+ p=i;
+ else
+ p=i-256;
+ m_ALFO_SAW[i]=a;
+ m_PLFO_SAW[i]=p;
+
+ //Square
+ if(i<128)
+ {
+ a=255;
+ p=127;
+ }
+ else
+ {
+ a=0;
+ p=-128;
+ }
+ m_ALFO_SQR[i]=a;
+ m_PLFO_SQR[i]=p;
+
+ //Tri
+ if(i<128)
+ a=255-(i*2);
+ else
+ a=(i*2)-256;
+ if(i<64)
+ p=i*2;
+ else if(i<128)
+ p=255-i*2;
+ else if(i<192)
+ p=256-i*2;
+ else
+ p=i*2-511;
+ m_ALFO_TRI[i]=a;
+ m_PLFO_TRI[i]=p;
+
+ //noise
+ //a=lfo_noise[i];
+ a=machine().rand()&0xff;
+ p=128-a;
+ m_ALFO_NOI[i]=a;
+ m_PLFO_NOI[i]=p;
+ }
+
+ for(s=0;s<8;++s)
+ {
+ float limit=PSCALE[s];
+ for(i=-128;i<128;++i)
+ {
+ m_PSCALES[s][i+128]=CENTS(((limit*(float) i)/128.0f));
+ }
+ limit=-ASCALE[s];
+ for(i=0;i<256;++i)
+ {
+ m_ASCALES[s][i]=DB(((limit*(float) i)/256.0f));
+ }
+ }
+}
+
+signed int scsp_device::PLFO_Step(SCSP_LFO_t *LFO)
+{
+ int p;
+ LFO->phase+=LFO->phase_step;
+#if LFO_SHIFT!=8
+ LFO->phase&=(1<<(LFO_SHIFT+8))-1;
+#endif
+ p=LFO->table[LFO->phase>>LFO_SHIFT];
+ p=LFO->scale[p+128];
+ return p<<(SHIFT-LFO_SHIFT);
+}
+
+signed int scsp_device::ALFO_Step(SCSP_LFO_t *LFO)
+{
+ int p;
+ LFO->phase+=LFO->phase_step;
+#if LFO_SHIFT!=8
+ LFO->phase&=(1<<(LFO_SHIFT+8))-1;
+#endif
+ p=LFO->table[LFO->phase>>LFO_SHIFT];
+ p=LFO->scale[p];
+ return p<<(SHIFT-LFO_SHIFT);
+}
+
+void scsp_device::LFO_ComputeStep(SCSP_LFO_t *LFO,UINT32 LFOF,UINT32 LFOWS,UINT32 LFOS,int ALFO)
+{
+ float step=(float) LFOFreq[LFOF]*256.0f/(float)44100;
+ LFO->phase_step=(unsigned int) ((float) (1<<LFO_SHIFT)*step);
+ if(ALFO)
+ {
+ switch(LFOWS)
+ {
+ case 0: LFO->table=m_ALFO_SAW; break;
+ case 1: LFO->table=m_ALFO_SQR; break;
+ case 2: LFO->table=m_ALFO_TRI; break;
+ case 3: LFO->table=m_ALFO_NOI; break;
+ }
+ LFO->scale=m_ASCALES[LFOS];
+ }
+ else
+ {
+ switch(LFOWS)
+ {
+ case 0: LFO->table=m_PLFO_SAW; break;
+ case 1: LFO->table=m_PLFO_SQR; break;
+ case 2: LFO->table=m_PLFO_TRI; break;
+ case 3: LFO->table=m_PLFO_NOI; break;
+ }
+ LFO->scale=m_PSCALES[LFOS];
+ }
+}
diff --git a/src/devices/sound/scsp.h b/src/devices/sound/scsp.h
new file mode 100644
index 00000000000..bc104a85b68
--- /dev/null
+++ b/src/devices/sound/scsp.h
@@ -0,0 +1,214 @@
+// license:BSD-3-Clause
+// copyright-holders:ElSemi, R. Belmont
+/*
+ SCSP (YMF292-F) header
+*/
+
+#pragma once
+
+#ifndef __SCSP_H__
+#define __SCSP_H__
+
+#include "scspdsp.h"
+
+#define FM_DELAY 0 // delay in number of slots processed before samples are written to the FM ring buffer
+ // driver code indicates should be 4, but sounds distorted then
+
+
+#define MCFG_SCSP_ROFFSET(_offs) \
+ scsp_device::set_roffset(*device, _offs);
+
+#define MCFG_SCSP_IRQ_CB(_devcb) \
+ devcb = &scsp_device::set_irq_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_SCSP_MAIN_IRQ_CB(_devcb) \
+ devcb = &scsp_device::set_main_irq_callback(*device, DEVCB_##_devcb);
+
+
+enum SCSP_STATE {SCSP_ATTACK,SCSP_DECAY1,SCSP_DECAY2,SCSP_RELEASE};
+
+struct SCSP_EG_t
+{
+ int volume; //
+ SCSP_STATE state;
+ int step;
+ //step vals
+ int AR; //Attack
+ int D1R; //Decay1
+ int D2R; //Decay2
+ int RR; //Release
+
+ int DL; //Decay level
+ UINT8 EGHOLD;
+ UINT8 LPLINK;
+};
+
+struct SCSP_LFO_t
+{
+ unsigned short phase;
+ UINT32 phase_step;
+ int *table;
+ int *scale;
+};
+
+struct SCSP_SLOT
+{
+ union
+ {
+ UINT16 data[0x10]; //only 0x1a bytes used
+ UINT8 datab[0x20];
+ } udata;
+
+ UINT8 Backwards; //the wave is playing backwards
+ UINT8 active; //this slot is currently playing
+ UINT8 *base; //samples base address
+ UINT32 cur_addr; //current play address (24.8)
+ UINT32 nxt_addr; //next play address
+ UINT32 step; //pitch step (24.8)
+ SCSP_EG_t EG; //Envelope
+ SCSP_LFO_t PLFO; //Phase LFO
+ SCSP_LFO_t ALFO; //Amplitude LFO
+ int slot;
+ signed short Prev; //Previous sample (for interpolation)
+};
+
+
+class scsp_device : public device_t,
+ public device_sound_interface
+{
+public:
+ scsp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void set_roffset(device_t &device, int roffset) { downcast<scsp_device &>(device).m_roffset = roffset; }
+ template<class _Object> static devcb_base &set_irq_callback(device_t &device, _Object object) { return downcast<scsp_device &>(device).m_irq_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_main_irq_callback(device_t &device, _Object object) { return downcast<scsp_device &>(device).m_main_irq_cb.set_callback(object); }
+
+ // SCSP register access
+ DECLARE_READ16_MEMBER( read );
+ DECLARE_WRITE16_MEMBER( write );
+
+ // MIDI I/O access (used for comms on Model 2/3)
+ DECLARE_WRITE16_MEMBER( midi_in );
+ DECLARE_READ16_MEMBER( midi_out_r );
+
+ void set_ram_base(void *base);
+
+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:
+ int m_roffset; /* offset in the region */
+ devcb_write8 m_irq_cb; /* irq callback */
+ devcb_write_line m_main_irq_cb;
+
+ union
+ {
+ UINT16 data[0x30/2];
+ UINT8 datab[0x30];
+ } m_udata;
+
+ SCSP_SLOT m_Slots[32];
+ signed short m_RINGBUF[128];
+ unsigned char m_BUFPTR;
+#if FM_DELAY
+ signed short m_DELAYBUF[FM_DELAY];
+ unsigned char m_DELAYPTR;
+#endif
+ unsigned char *m_SCSPRAM;
+ UINT32 m_SCSPRAM_LENGTH;
+ char m_Master;
+ sound_stream * m_stream;
+
+ INT32 *m_buffertmpl,*m_buffertmpr;
+
+ UINT32 m_IrqTimA;
+ UINT32 m_IrqTimBC;
+ UINT32 m_IrqMidi;
+
+ UINT8 m_MidiOutW, m_MidiOutR;
+ UINT8 m_MidiStack[32];
+ UINT8 m_MidiW, m_MidiR;
+
+ INT32 m_EG_TABLE[0x400];
+
+ int m_LPANTABLE[0x10000];
+ int m_RPANTABLE[0x10000];
+
+ int m_TimPris[3];
+ int m_TimCnt[3];
+
+ // timers
+ emu_timer *m_timerA, *m_timerB, *m_timerC;
+
+ // DMA stuff
+ struct
+ {
+ UINT32 dmea;
+ UINT16 drga;
+ UINT16 dtlg;
+ UINT8 dgate;
+ UINT8 ddir;
+ } m_dma;
+
+ UINT16 m_mcieb;
+ UINT16 m_mcipd;
+
+ int m_ARTABLE[64], m_DRTABLE[64];
+
+ SCSPDSP m_DSP;
+
+ stream_sample_t *m_bufferl;
+ stream_sample_t *m_bufferr;
+
+ int m_length;
+
+ signed short *m_RBUFDST; //this points to where the sample will be stored in the RingBuf
+
+ //LFO
+ int m_PLFO_TRI[256], m_PLFO_SQR[256], m_PLFO_SAW[256], m_PLFO_NOI[256];
+ int m_ALFO_TRI[256], m_ALFO_SQR[256], m_ALFO_SAW[256], m_ALFO_NOI[256];
+ int m_PSCALES[8][256];
+ int m_ASCALES[8][256];
+
+ void exec_dma(address_space &space); /*state DMA transfer function*/
+ unsigned char DecodeSCI(unsigned char irq);
+ void CheckPendingIRQ();
+ void MainCheckPendingIRQ(UINT16 irq_type);
+ void ResetInterrupts();
+ TIMER_CALLBACK_MEMBER( timerA_cb );
+ TIMER_CALLBACK_MEMBER( timerB_cb );
+ TIMER_CALLBACK_MEMBER( timerC_cb );
+ int Get_AR(int base, int R);
+ int Get_DR(int base, int R);
+ int Get_RR(int base, int R);
+ void Compute_EG(SCSP_SLOT *slot);
+ int EG_Update(SCSP_SLOT *slot);
+ UINT32 Step(SCSP_SLOT *slot);
+ void Compute_LFO(SCSP_SLOT *slot);
+ void StartSlot(SCSP_SLOT *slot);
+ void StopSlot(SCSP_SLOT *slot,int keyoff);
+ void init();
+ void UpdateSlotReg(int s,int r);
+ void UpdateReg(address_space &space, int reg);
+ void UpdateSlotRegR(int slot,int reg);
+ void UpdateRegR(address_space &space, int reg);
+ void w16(address_space &space,unsigned int addr,unsigned short val);
+ unsigned short r16(address_space &space, unsigned int addr);
+ inline INT32 UpdateSlot(SCSP_SLOT *slot);
+ void DoMasterSamples(int nsamples);
+
+ //LFO
+ void LFO_Init();
+ signed int PLFO_Step(SCSP_LFO_t *LFO);
+ signed int ALFO_Step(SCSP_LFO_t *LFO);
+ void LFO_ComputeStep(SCSP_LFO_t *LFO,UINT32 LFOF,UINT32 LFOWS,UINT32 LFOS,int ALFO);
+};
+
+extern const device_type SCSP;
+
+
+#endif /* __SCSP_H__ */
diff --git a/src/devices/sound/scspdsp.c b/src/devices/sound/scspdsp.c
new file mode 100644
index 00000000000..75b30cfe85a
--- /dev/null
+++ b/src/devices/sound/scspdsp.c
@@ -0,0 +1,355 @@
+// license:BSD-3-Clause
+// copyright-holders:ElSemi, R. Belmont
+#include "emu.h"
+#include "scspdsp.h"
+
+static UINT16 PACK(INT32 val)
+{
+ UINT32 temp;
+ int sign,exponent,k;
+
+ sign = (val >> 23) & 0x1;
+ temp = (val ^ (val << 1)) & 0xFFFFFF;
+ exponent = 0;
+ for (k=0; k<12; k++)
+ {
+ if (temp & 0x800000)
+ break;
+ temp <<= 1;
+ exponent += 1;
+ }
+ if (exponent < 12)
+ val = (val << exponent) & 0x3FFFFF;
+ else
+ val <<= 11;
+ val >>= 11;
+ val &= 0x7FF;
+ val |= sign << 15;
+ val |= exponent << 11;
+
+ return (UINT16)val;
+}
+
+static INT32 UNPACK(UINT16 val)
+{
+ int sign,exponent,mantissa;
+ INT32 uval;
+
+ sign = (val >> 15) & 0x1;
+ exponent = (val >> 11) & 0xF;
+ mantissa = val & 0x7FF;
+ uval = mantissa << 11;
+ if (exponent > 11)
+ {
+ exponent = 11;
+ uval |= sign << 22;
+ }
+ else
+ uval |= (sign ^ 1) << 22;
+ uval |= sign << 23;
+ uval <<= 8;
+ uval >>= 8;
+ uval >>= exponent;
+
+ return uval;
+}
+
+void SCSPDSP_Init(SCSPDSP *DSP)
+{
+ memset(DSP,0,sizeof(SCSPDSP));
+ DSP->RBL=0x8000;
+ DSP->Stopped=1;
+}
+
+void SCSPDSP_Step(SCSPDSP *DSP)
+{
+ INT32 ACC=0; //26 bit
+ INT32 SHIFTED=0; //24 bit
+ INT32 X=0; //24 bit
+ INT32 Y=0; //13 bit
+ INT32 B=0; //26 bit
+ INT32 INPUTS=0; //24 bit
+ INT32 MEMVAL=0;
+ INT32 FRC_REG=0; //13 bit
+ INT32 Y_REG=0; //24 bit
+ UINT32 ADDR=0;
+ UINT32 ADRS_REG=0; //13 bit
+ int step;
+
+ if(DSP->Stopped)
+ return;
+
+ memset(DSP->EFREG,0,2*16);
+#if 0
+ int dump=0;
+ FILE *f=NULL;
+ if(dump)
+ f=fopen("dsp.txt","wt");
+#endif
+ for(step=0;step</*128*/DSP->LastStep;++step)
+ {
+ UINT16 *IPtr=DSP->MPRO+step*4;
+
+// if(IPtr[0]==0 && IPtr[1]==0 && IPtr[2]==0 && IPtr[3]==0)
+// break;
+
+ UINT32 TRA=(IPtr[0]>>8)&0x7F;
+ UINT32 TWT=(IPtr[0]>>7)&0x01;
+ UINT32 TWA=(IPtr[0]>>0)&0x7F;
+
+ UINT32 XSEL=(IPtr[1]>>15)&0x01;
+ UINT32 YSEL=(IPtr[1]>>13)&0x03;
+ UINT32 IRA=(IPtr[1]>>6)&0x3F;
+ UINT32 IWT=(IPtr[1]>>5)&0x01;
+ UINT32 IWA=(IPtr[1]>>0)&0x1F;
+
+ UINT32 TABLE=(IPtr[2]>>15)&0x01;
+ UINT32 MWT=(IPtr[2]>>14)&0x01;
+ UINT32 MRD=(IPtr[2]>>13)&0x01;
+ UINT32 EWT=(IPtr[2]>>12)&0x01;
+ UINT32 EWA=(IPtr[2]>>8)&0x0F;
+ UINT32 ADRL=(IPtr[2]>>7)&0x01;
+ UINT32 FRCL=(IPtr[2]>>6)&0x01;
+ UINT32 SHIFT=(IPtr[2]>>4)&0x03;
+ UINT32 YRL=(IPtr[2]>>3)&0x01;
+ UINT32 NEGB=(IPtr[2]>>2)&0x01;
+ UINT32 ZERO=(IPtr[2]>>1)&0x01;
+ UINT32 BSEL=(IPtr[2]>>0)&0x01;
+
+ UINT32 NOFL=(IPtr[3]>>15)&1; //????
+ UINT32 COEF=(IPtr[3]>>9)&0x3f;
+
+ UINT32 MASA=(IPtr[3]>>2)&0x1f; //???
+ UINT32 ADREB=(IPtr[3]>>1)&0x1;
+ UINT32 NXADR=(IPtr[3]>>0)&0x1;
+
+ INT64 v;
+
+ //operations are done at 24 bit precision
+#if 0
+ if(MASA)
+ int a=1;
+ if(NOFL)
+ int a=1;
+
+// int dump=0;
+
+ if(f)
+ {
+#define DUMP(v) fprintf(f," " #v ": %04X",v);
+
+ fprintf(f,"%d: ",step);
+ DUMP(ACC);
+ DUMP(SHIFTED);
+ DUMP(X);
+ DUMP(Y);
+ DUMP(B);
+ DUMP(INPUTS);
+ DUMP(MEMVAL);
+ DUMP(FRC_REG);
+ DUMP(Y_REG);
+ DUMP(ADDR);
+ DUMP(ADRS_REG);
+ fprintf(f,"\n");
+ }
+#endif
+ //INPUTS RW
+// colmns97 hits this
+// assert(IRA<0x32);
+ if(IRA<=0x1f)
+ INPUTS=DSP->MEMS[IRA];
+ else if(IRA<=0x2F)
+ INPUTS=DSP->MIXS[IRA-0x20]<<4; //MIXS is 20 bit
+ else if(IRA<=0x31)
+ INPUTS=0;
+ else
+ return;
+
+ INPUTS<<=8;
+ INPUTS>>=8;
+ //if(INPUTS&0x00800000)
+ // INPUTS|=0xFF000000;
+
+ if(IWT)
+ {
+ DSP->MEMS[IWA]=MEMVAL; //MEMVAL was selected in previous MRD
+ if(IRA==IWA)
+ INPUTS=MEMVAL;
+ }
+
+ //Operand sel
+ //B
+ if(!ZERO)
+ {
+ if(BSEL)
+ B=ACC;
+ else
+ {
+ B=DSP->TEMP[(TRA+DSP->DEC)&0x7F];
+ B<<=8;
+ B>>=8;
+ //if(B&0x00800000)
+ // B|=0xFF000000; //Sign extend
+ }
+ if(NEGB)
+ B=0-B;
+ }
+ else
+ B=0;
+
+ //X
+ if(XSEL)
+ X=INPUTS;
+ else
+ {
+ X=DSP->TEMP[(TRA+DSP->DEC)&0x7F];
+ X<<=8;
+ X>>=8;
+ //if(X&0x00800000)
+ // X|=0xFF000000;
+ }
+
+ //Y
+ if(YSEL==0)
+ Y=FRC_REG;
+ else if(YSEL==1)
+ Y=DSP->COEF[COEF]>>3; //COEF is 16 bits
+ else if(YSEL==2)
+ Y=(Y_REG>>11)&0x1FFF;
+ else if(YSEL==3)
+ Y=(Y_REG>>4)&0x0FFF;
+
+ if(YRL)
+ Y_REG=INPUTS;
+
+ //Shifter
+ if(SHIFT==0)
+ {
+ SHIFTED=ACC;
+ if(SHIFTED>0x007FFFFF)
+ SHIFTED=0x007FFFFF;
+ if(SHIFTED<(-0x00800000))
+ SHIFTED=-0x00800000;
+ }
+ else if(SHIFT==1)
+ {
+ SHIFTED=ACC*2;
+ if(SHIFTED>0x007FFFFF)
+ SHIFTED=0x007FFFFF;
+ if(SHIFTED<(-0x00800000))
+ SHIFTED=-0x00800000;
+ }
+ else if(SHIFT==2)
+ {
+ SHIFTED=ACC*2;
+ SHIFTED<<=8;
+ SHIFTED>>=8;
+ //SHIFTED&=0x00FFFFFF;
+ //if(SHIFTED&0x00800000)
+ // SHIFTED|=0xFF000000;
+ }
+ else if(SHIFT==3)
+ {
+ SHIFTED=ACC;
+ SHIFTED<<=8;
+ SHIFTED>>=8;
+ //SHIFTED&=0x00FFFFFF;
+ //if(SHIFTED&0x00800000)
+ // SHIFTED|=0xFF000000;
+ }
+
+ //ACCUM
+ Y<<=19;
+ Y>>=19;
+ //if(Y&0x1000)
+ // Y|=0xFFFFF000;
+
+ v=(((INT64) X*(INT64) Y)>>12);
+ ACC=(int) v+B;
+
+ if(TWT)
+ DSP->TEMP[(TWA+DSP->DEC)&0x7F]=SHIFTED;
+
+ if(FRCL)
+ {
+ if(SHIFT==3)
+ FRC_REG=SHIFTED&0x0FFF;
+ else
+ FRC_REG=(SHIFTED>>11)&0x1FFF;
+ }
+
+ if(MRD || MWT)
+ //if(0)
+ {
+ ADDR=DSP->MADRS[MASA];
+ if(!TABLE)
+ ADDR+=DSP->DEC;
+ if(ADREB)
+ ADDR+=ADRS_REG&0x0FFF;
+ if(NXADR)
+ ADDR++;
+ if(!TABLE)
+ ADDR&=DSP->RBL-1;
+ else
+ ADDR&=0xFFFF;
+ //ADDR<<=1;
+ //ADDR+=DSP->RBP<<13;
+ //MEMVAL=DSP->SCSPRAM[ADDR>>1];
+ ADDR+=DSP->RBP<<12;
+ if (ADDR > 0x7ffff) ADDR = 0;
+ if(MRD && (step&1)) //memory only allowed on odd? DoA inserts NOPs on even
+ {
+ if(NOFL)
+ MEMVAL=DSP->SCSPRAM[ADDR]<<8;
+ else
+ MEMVAL=UNPACK(DSP->SCSPRAM[ADDR]);
+ }
+ if(MWT && (step&1))
+ {
+ if(NOFL)
+ DSP->SCSPRAM[ADDR]=SHIFTED>>8;
+ else
+ DSP->SCSPRAM[ADDR]=PACK(SHIFTED);
+ }
+ }
+
+ if(ADRL)
+ {
+ if(SHIFT==3)
+ ADRS_REG=(SHIFTED>>12)&0xFFF;
+ else
+ ADRS_REG=(INPUTS>>16);
+ }
+
+ if(EWT)
+ DSP->EFREG[EWA]+=SHIFTED>>8;
+
+ }
+ --DSP->DEC;
+ memset(DSP->MIXS,0,4*16);
+// if(f)
+// fclose(f);
+}
+
+void SCSPDSP_SetSample(SCSPDSP *DSP,INT32 sample,int SEL,int MXL)
+{
+ //DSP->MIXS[SEL]+=sample<<(MXL+1)/*7*/;
+ DSP->MIXS[SEL]+=sample;
+// if(MXL)
+// int a=1;
+}
+
+void SCSPDSP_Start(SCSPDSP *DSP)
+{
+ int i;
+ DSP->Stopped=0;
+ for(i=127;i>=0;--i)
+ {
+ UINT16 *IPtr=DSP->MPRO+i*4;
+
+ if(IPtr[0]!=0 || IPtr[1]!=0 || IPtr[2]!=0 || IPtr[3]!=0)
+ break;
+ }
+ DSP->LastStep=i+1;
+
+}
diff --git a/src/devices/sound/scspdsp.h b/src/devices/sound/scspdsp.h
new file mode 100644
index 00000000000..319b3ebe099
--- /dev/null
+++ b/src/devices/sound/scspdsp.h
@@ -0,0 +1,42 @@
+// license:BSD-3-Clause
+// copyright-holders:ElSemi, R. Belmont
+#pragma once
+
+#ifndef __SCSPDSP_H__
+#define __SCSPDSP_H__
+
+//the DSP Context
+struct SCSPDSP
+{
+//Config
+ UINT16 *SCSPRAM;
+ UINT32 SCSPRAM_LENGTH;
+ UINT32 RBP; //Ring buf pointer
+ UINT32 RBL; //Delay ram (Ring buffer) size in words
+
+//context
+
+ INT16 COEF[64]; //16 bit signed
+ UINT16 MADRS[32]; //offsets (in words), 16 bit
+ UINT16 MPRO[128*4]; //128 steps 64 bit
+ INT32 TEMP[128]; //TEMP regs,24 bit signed
+ INT32 MEMS[32]; //MEMS regs,24 bit signed
+ UINT32 DEC;
+
+//input
+ INT32 MIXS[16]; //MIXS, 24 bit signed
+ INT16 EXTS[2]; //External inputs (CDDA) 16 bit signed
+
+//output
+ INT16 EFREG[16]; //EFREG, 16 bit signed
+
+ int Stopped;
+ int LastStep;
+};
+
+void SCSPDSP_Init(SCSPDSP *DSP);
+void SCSPDSP_SetSample(SCSPDSP *DSP, INT32 sample, INT32 SEL, INT32 MXL);
+void SCSPDSP_Step(SCSPDSP *DSP);
+void SCSPDSP_Start(SCSPDSP *DSP);
+
+#endif /* __SCSPDSP_H__ */
diff --git a/src/devices/sound/segapcm.c b/src/devices/sound/segapcm.c
new file mode 100644
index 00000000000..50e875bc474
--- /dev/null
+++ b/src/devices/sound/segapcm.c
@@ -0,0 +1,151 @@
+// license:BSD-3-Clause
+// copyright-holders:Hiromitsu Shioya, Olivier Galibert
+/*********************************************************/
+/* SEGA 16ch 8bit PCM */
+/*********************************************************/
+
+#include "emu.h"
+#include "segapcm.h"
+
+
+// device type definition
+const device_type SEGAPCM = &device_creator<segapcm_device>;
+
+
+//-------------------------------------------------
+// segapcm_device - constructor
+//-------------------------------------------------
+
+segapcm_device::segapcm_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, SEGAPCM, "Sega PCM", tag, owner, clock, "segapcm", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_rom(*this, DEVICE_SELF),
+ m_ram(NULL),
+ m_bank(0),
+ m_bankshift(0),
+ m_bankmask(0),
+ m_stream(NULL)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void segapcm_device::device_start()
+{
+ int mask, rom_mask;
+
+ m_ram = auto_alloc_array(machine(), UINT8, 0x800);
+
+ memset(m_ram, 0xff, 0x800);
+
+ m_bankshift = (UINT8) m_bank;
+ mask = m_bank >> 16;
+ if (!mask)
+ mask = BANK_MASK7 >> 16;
+
+ for(rom_mask = 1; rom_mask < m_rom.length(); rom_mask *= 2);
+ rom_mask--;
+
+ m_bankmask = mask & (rom_mask >> m_bankshift);
+
+ m_stream = stream_alloc(0, 2, clock() / 128);
+
+ save_item(NAME(m_low));
+ save_pointer(NAME(m_ram), 0x800);
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void segapcm_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ /* clear the buffers */
+ memset(outputs[0], 0, samples*sizeof(*outputs[0]));
+ memset(outputs[1], 0, samples*sizeof(*outputs[1]));
+
+ // reg function
+ // ------------------------------------------------
+ // 0x00 ?
+ // 0x01 ?
+ // 0x02 volume left
+ // 0x03 volume right
+ // 0x04 loop address (08-15)
+ // 0x05 loop address (16-23)
+ // 0x06 end address
+ // 0x07 address delta
+ // 0x80 ?
+ // 0x81 ?
+ // 0x82 ?
+ // 0x83 ?
+ // 0x84 current address (08-15), 00-07 is internal?
+ // 0x85 current address (16-23)
+ // 0x86 bit 0: channel disable?
+ // bit 1: loop disable
+ // other bits: bank
+ // 0x87 ?
+
+ /* loop over channels */
+ for (int ch = 0; ch < 16; ch++)
+ {
+ UINT8 *regs = m_ram+8*ch;
+
+ /* only process active channels */
+ if (!(regs[0x86]&1))
+ {
+ const UINT8 *rom = m_rom + ((regs[0x86] & m_bankmask) << m_bankshift);
+ UINT32 addr = (regs[0x85] << 16) | (regs[0x84] << 8) | m_low[ch];
+ UINT32 loop = (regs[0x05] << 16) | (regs[0x04] << 8);
+ UINT8 end = regs[6] + 1;
+ int i;
+
+ /* loop over samples on this channel */
+ for (i = 0; i < samples; i++)
+ {
+ INT8 v = 0;
+
+ /* handle looping if we've hit the end */
+ if ((addr >> 16) == end)
+ {
+ if (regs[0x86] & 2)
+ {
+ regs[0x86] |= 1;
+ break;
+ }
+ else addr = loop;
+ }
+
+ /* fetch the sample */
+ v = rom[(addr >> 8) & m_rom.mask()] - 0x80;
+
+ /* apply panning and advance */
+ outputs[0][i] += v * (regs[2] & 0x7f);
+ outputs[1][i] += v * (regs[3] & 0x7f);
+ addr = (addr + regs[7]) & 0xffffff;
+ }
+
+ /* store back the updated address */
+ regs[0x84] = addr >> 8;
+ regs[0x85] = addr >> 16;
+ m_low[ch] = regs[0x86] & 1 ? 0 : addr;
+ }
+ }
+}
+
+
+WRITE8_MEMBER( segapcm_device::sega_pcm_w )
+{
+ m_stream->update();
+ m_ram[offset & 0x07ff] = data;
+}
+
+
+READ8_MEMBER( segapcm_device::sega_pcm_r )
+{
+ m_stream->update();
+ return m_ram[offset & 0x07ff];
+}
diff --git a/src/devices/sound/segapcm.h b/src/devices/sound/segapcm.h
new file mode 100644
index 00000000000..df7110bb7b2
--- /dev/null
+++ b/src/devices/sound/segapcm.h
@@ -0,0 +1,70 @@
+// license:BSD-3-Clause
+// copyright-holders:Hiromitsu Shioya, Olivier Galibert
+/*********************************************************/
+/* SEGA 8bit PCM */
+/*********************************************************/
+
+#pragma once
+
+#ifndef __SEGAPCM_H__
+#define __SEGAPCM_H__
+
+#define BANK_256 (11)
+#define BANK_512 (12)
+#define BANK_12M (13)
+#define BANK_MASK7 (0x70<<16)
+#define BANK_MASKF (0xf0<<16)
+#define BANK_MASKF8 (0xf8<<16)
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_SEGAPCM_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, SEGAPCM, _clock)
+#define MCFG_SEGAPCM_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, SEGAPCM, _clock)
+
+#define MCFG_SEGAPCM_BANK(_bank) \
+ segapcm_device::set_bank(*device, _bank);
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+class segapcm_device : public device_t,
+ public device_sound_interface
+{
+public:
+ segapcm_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~segapcm_device() { }
+
+ // static configuration
+ static void set_bank(device_t &device, int bank) { downcast<segapcm_device &>(device).m_bank = bank; }
+
+ DECLARE_WRITE8_MEMBER( sega_pcm_w );
+ DECLARE_READ8_MEMBER( sega_pcm_r );
+
+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:
+ required_region_ptr<UINT8> m_rom;
+ UINT8* m_ram;
+ UINT8 m_low[16];
+ int m_bank;
+ int m_bankshift;
+ int m_bankmask;
+ sound_stream* m_stream;
+};
+
+extern const device_type SEGAPCM;
+
+
+#endif /* __SEGAPCM_H__ */
diff --git a/src/devices/sound/sid.c b/src/devices/sound/sid.c
new file mode 100644
index 00000000000..375095e7401
--- /dev/null
+++ b/src/devices/sound/sid.c
@@ -0,0 +1,352 @@
+// license:BSD-3-Clause
+// copyright-holders:Peter Trauner
+/*
+ copyright peter trauner
+
+ based on michael schwend's sid play
+
+ Noise generation algorithm is used courtesy of Asger Alstrup Nielsen.
+ His original publication can be found on the SID home page.
+
+ Noise table optimization proposed by Phillip Wooller. The output of
+ each table does not differ.
+
+ MOS-8580 R5 combined waveforms recorded by Dennis "Deadman" Lindroos.
+*/
+
+#include "emu.h"
+#include "sidvoice.h"
+#include "sidenvel.h"
+#include "sid.h"
+
+static float *filterTable;
+static float *bandPassParam;
+#define lowPassParam filterTable
+static float filterResTable[16];
+
+#define maxLogicalVoices 4
+
+static const int mix16monoMiddleIndex = 256*maxLogicalVoices/2;
+static UINT16 mix16mono[256*maxLogicalVoices];
+
+static UINT16 zero16bit=0; /* either signed or unsigned */
+//UINT32 splitBufferLen;
+
+static void MixerInit(int threeVoiceAmplify)
+{
+ long si;
+ UINT16 ui;
+ long ampDiv = maxLogicalVoices;
+
+ if (threeVoiceAmplify)
+ {
+ ampDiv = (maxLogicalVoices-1);
+ }
+
+ /* Mixing formulas are optimized by sample input value. */
+
+ si = (-128*maxLogicalVoices) * 256;
+ for (ui = 0; ui < sizeof(mix16mono)/sizeof(UINT16); ui++ )
+ {
+ mix16mono[ui] = (UINT16)(si/ampDiv) + zero16bit;
+ si+=256;
+ }
+
+}
+
+
+INLINE void syncEm(SID6581_t *This)
+{
+ int sync1 = (This->optr1.modulator->cycleLenCount <= 0);
+ int sync2 = (This->optr2.modulator->cycleLenCount <= 0);
+ int sync3 = (This->optr3.modulator->cycleLenCount <= 0);
+
+ This->optr1.cycleLenCount--;
+ This->optr2.cycleLenCount--;
+ This->optr3.cycleLenCount--;
+
+ if (This->optr1.sync && sync1)
+ {
+ This->optr1.cycleLenCount = 0;
+ This->optr1.outProc = &sidWaveCalcNormal;
+#if defined(DIRECT_FIXPOINT)
+ optr1.waveStep.l = 0;
+#else
+ This->optr1.waveStep = (This->optr1.waveStepPnt = 0);
+#endif
+ }
+ if (This->optr2.sync && sync2)
+ {
+ This->optr2.cycleLenCount = 0;
+ This->optr2.outProc = &sidWaveCalcNormal;
+#if defined(DIRECT_FIXPOINT)
+ This->optr2.waveStep.l = 0;
+#else
+ This->optr2.waveStep = (This->optr2.waveStepPnt = 0);
+#endif
+ }
+ if (This->optr3.sync && sync3)
+ {
+ This->optr3.cycleLenCount = 0;
+ This->optr3.outProc = &sidWaveCalcNormal;
+#if defined(DIRECT_FIXPOINT)
+ optr3.waveStep.l = 0;
+#else
+ This->optr3.waveStep = (This->optr3.waveStepPnt = 0);
+#endif
+ }
+}
+
+
+void sidEmuFillBuffer(SID6581_t *This, stream_sample_t *buffer, UINT32 bufferLen )
+{
+//void* fill16bitMono( SID6581_t *This, void* buffer, UINT32 numberOfSamples )
+
+ for ( ; bufferLen > 0; bufferLen-- )
+ {
+ *buffer++ = (INT16) mix16mono[(unsigned)(mix16monoMiddleIndex
+ +(*This->optr1.outProc)(&This->optr1)
+ +(*This->optr2.outProc)(&This->optr2)
+ +(This->optr3.outProc(&This->optr3)&This->optr3_outputmask)
+/* hack for digi sounds
+ does n't seam to come from a tone operator
+ ghostbusters and goldrunner everything except volume zeroed */
+ +(This->masterVolume<<2)
+// +(*sampleEmuRout)()
+ )];
+ syncEm(This);
+ }
+}
+
+/* --------------------------------------------------------------------- Init */
+
+
+/* Reset. */
+
+int sidEmuReset(SID6581_t *This)
+{
+ sidClearOperator( &This->optr1 );
+ enveEmuResetOperator( &This->optr1 );
+ sidClearOperator( &This->optr2 );
+ enveEmuResetOperator( &This->optr2 );
+ sidClearOperator( &This->optr3 );
+ enveEmuResetOperator( &This->optr3 );
+ This->optr3_outputmask = ~0; /* on */
+
+// sampleEmuReset();
+
+ This->filter.Type = (This->filter.CurType = 0);
+ This->filter.Value = 0;
+ This->filter.Dy = (This->filter.ResDy = 0);
+
+ sidEmuSet( &This->optr1 );
+ sidEmuSet( &This->optr2 );
+ sidEmuSet( &This->optr3 );
+
+ sidEmuSet2( &This->optr1 );
+ sidEmuSet2( &This->optr2 );
+ sidEmuSet2( &This->optr3 );
+
+ return TRUE;
+}
+
+
+static void filterTableInit(running_machine &machine)
+{
+ int sample_rate = machine.sample_rate();
+ UINT16 uk;
+ /* Parameter calculation has not been moved to a separate function */
+ /* by purpose. */
+ const float filterRefFreq = 44100.0f;
+
+ float yMax = 1.0f;
+ float yMin = 0.01f;
+ float yAdd;
+ float yTmp, rk, rk2;
+
+ float resDyMax;
+ float resDyMin;
+ float resDy;
+
+ filterTable = auto_alloc_array(machine, float, 0x800);
+ bandPassParam = auto_alloc_array(machine, float, 0x800);
+
+ uk = 0;
+ for ( rk = 0; rk < 0x800; rk++ )
+ {
+ filterTable[uk] = (((expf(rk/0x800*logf(400.0f))/60.0f)+0.05f)
+ *filterRefFreq) / sample_rate;
+ if ( filterTable[uk] < yMin )
+ filterTable[uk] = yMin;
+ if ( filterTable[uk] > yMax )
+ filterTable[uk] = yMax;
+ uk++;
+ }
+
+ /*extern float bandPassParam[0x800]; */
+ yMax = 0.22f;
+ yMin = 0.05f; /* less for some R1/R4 chips */
+ yAdd = (yMax-yMin)/2048.0f;
+ yTmp = yMin;
+ uk = 0;
+ /* Some C++ compilers still have non-local scope! */
+ for ( rk2 = 0; rk2 < 0x800; rk2++ )
+ {
+ bandPassParam[uk] = (yTmp*filterRefFreq) / sample_rate;
+ yTmp += yAdd;
+ uk++;
+ }
+
+ /*extern float filterResTable[16]; */
+ resDyMax = 1.0f;
+ resDyMin = 2.0f;
+ resDy = resDyMin;
+ for ( uk = 0; uk < 16; uk++ )
+ {
+ filterResTable[uk] = resDy;
+ resDy -= (( resDyMin - resDyMax ) / 15 );
+ }
+ filterResTable[0] = resDyMin;
+ filterResTable[15] = resDyMax;
+}
+
+void sid6581_init (SID6581_t *This)
+{
+ This->optr1.sid=This;
+ This->optr2.sid=This;
+ This->optr3.sid=This;
+
+ This->optr1.modulator = &This->optr3;
+ This->optr3.carrier = &This->optr1;
+ This->optr1.filtVoiceMask = 1;
+
+ This->optr2.modulator = &This->optr1;
+ This->optr1.carrier = &This->optr2;
+ This->optr2.filtVoiceMask = 2;
+
+ This->optr3.modulator = &This->optr2;
+ This->optr2.carrier = &This->optr3;
+ This->optr3.filtVoiceMask = 4;
+
+
+
+ This->PCMsid = (UINT32)(This->PCMfreq * (16777216.0 / This->clock));
+ This->PCMsidNoise = (UINT32)((This->clock*256.0)/This->PCMfreq);
+
+ This->filter.Enabled = TRUE;
+
+ sidInitMixerEngine(This->device->machine());
+ filterTableInit(This->device->machine());
+
+ sidInitWaveformTables(This->type);
+
+ enveEmuInit(This->PCMfreq, TRUE);
+
+ MixerInit(0);
+
+ sidEmuReset(This);
+}
+
+void sid6581_port_w (SID6581_t *This, int offset, int data)
+{
+ offset &= 0x1f;
+
+ switch (offset)
+ {
+ case 0x19: case 0x1a: case 0x1b: case 0x1c:
+ case 0x1d:
+ case 0x1e:
+ case 0x1f:
+ break;
+ case 0x15: case 0x16: case 0x17:
+ case 0x18:
+ This->mixer_channel->update();
+ This->reg[offset] = data;
+ This->masterVolume = ( This->reg[0x18] & 15 );
+ This->masterVolumeAmplIndex = This->masterVolume << 8;
+
+ if ((This->reg[0x18]&0x80) &&
+ ((This->reg[0x17]&This->optr3.filtVoiceMask)==0))
+ This->optr3_outputmask = 0; /* off */
+ else
+ This->optr3_outputmask = ~0; /* on */
+
+ This->filter.Type = This->reg[0x18] & 0x70;
+ if (This->filter.Type != This->filter.CurType)
+ {
+ This->filter.CurType = This->filter.Type;
+ This->optr1.filtLow = (This->optr1.filtRef = 0);
+ This->optr2.filtLow = (This->optr2.filtRef = 0);
+ This->optr3.filtLow = (This->optr3.filtRef = 0);
+ }
+ if ( This->filter.Enabled )
+ {
+ This->filter.Value = 0x7ff & ( (This->reg[0x15]&7) | ( (UINT16)This->reg[0x16] << 3 ));
+ if (This->filter.Type == 0x20)
+ This->filter.Dy = bandPassParam ? bandPassParam[This->filter.Value] : 0.0f;
+ else
+ This->filter.Dy = lowPassParam ? lowPassParam[This->filter.Value] : 0.0f;
+ This->filter.ResDy = filterResTable[This->reg[0x17] >> 4] - This->filter.Dy;
+ if ( This->filter.ResDy < 1.0f )
+ This->filter.ResDy = 1.0f;
+ }
+
+ sidEmuSet( &This->optr1 );
+ sidEmuSet( &This->optr3 );
+ sidEmuSet( &This->optr2 );
+
+ // relies on sidEmuSet also for other channels!
+ sidEmuSet2( &This->optr1 );
+ sidEmuSet2( &This->optr2 );
+ sidEmuSet2( &This->optr3 );
+ break;
+
+ default:
+ This->mixer_channel->update();
+ This->reg[offset] = data;
+
+ if (offset<7) {
+ This->optr1.reg[offset] = data;
+ } else if (offset<14) {
+ This->optr2.reg[offset-7] = data;
+ } else if (offset<21) {
+ This->optr3.reg[offset-14] = data;
+ }
+
+ sidEmuSet( &This->optr1 );
+ sidEmuSet( &This->optr3 );
+ sidEmuSet( &This->optr2 );
+
+ // relies on sidEmuSet also for other channels!
+ sidEmuSet2( &This->optr1 );
+ sidEmuSet2( &This->optr2 );
+ sidEmuSet2( &This->optr3 );
+ break;
+ }
+}
+
+int sid6581_port_r (running_machine &machine, SID6581_t *This, int offset)
+{
+ int data;
+/* SIDPLAY reads last written at a sid address value */
+ offset &= 0x1f;
+ switch (offset)
+ {
+ case 0x1d:
+ case 0x1e:
+ case 0x1f:
+ data=0xff;
+ break;
+ case 0x1b:
+ This->mixer_channel->update();
+ data = This->optr3.output;
+ break;
+ case 0x1c:
+ This->mixer_channel->update();
+ data = This->optr3.enveVol;
+ break;
+ default:
+ data=This->reg[offset];
+ }
+ return data;
+}
diff --git a/src/devices/sound/sid.h b/src/devices/sound/sid.h
new file mode 100644
index 00000000000..9fb8a6dc5bb
--- /dev/null
+++ b/src/devices/sound/sid.h
@@ -0,0 +1,66 @@
+// license:BSD-3-Clause
+// copyright-holders:Peter Trauner
+#pragma once
+
+#ifndef __SID_H__
+#define __SID_H__
+
+/*
+ approximation of the sid6581 chip
+ this part is for one chip,
+*/
+
+#include "sidvoice.h"
+
+/* private area */
+struct SID6581_t
+{
+ device_t *device;
+ sound_stream *mixer_channel; // mame stream/ mixer channel
+
+ int type;
+ UINT32 clock;
+
+ UINT16 PCMfreq; // samplerate of the current systems soundcard/DAC
+ UINT32 PCMsid, PCMsidNoise;
+
+#if 0
+ /* following depends on type */
+ ptr2sidVoidFunc ModeNormalTable[16];
+ ptr2sidVoidFunc ModeRingTable[16];
+ // for speed reason it could be better to make them global!
+ UINT8* waveform30;
+ UINT8* waveform50;
+ UINT8* waveform60;
+ UINT8* waveform70;
+#endif
+ int reg[0x20];
+
+// bool sidKeysOn[0x20], sidKeysOff[0x20];
+
+ UINT8 masterVolume;
+ UINT16 masterVolumeAmplIndex;
+
+
+ struct
+ {
+ int Enabled;
+ UINT8 Type, CurType;
+ float Dy, ResDy;
+ UINT16 Value;
+ } filter;
+
+ sidOperator optr1, optr2, optr3;
+ int optr3_outputmask;
+};
+
+void sid6581_init (SID6581_t *This);
+
+int sidEmuReset(SID6581_t *This);
+
+int sid6581_port_r (running_machine &machine, SID6581_t *This, int offset);
+void sid6581_port_w (SID6581_t *This, int offset, int data);
+
+void sidEmuFillBuffer(SID6581_t *This, stream_sample_t *buffer, UINT32 bufferLen );
+
+#endif /* __SID_H__ */
diff --git a/src/devices/sound/side6581.h b/src/devices/sound/side6581.h
new file mode 100644
index 00000000000..9fb28ffd6d0
--- /dev/null
+++ b/src/devices/sound/side6581.h
@@ -0,0 +1,128 @@
+// license:BSD-3-Clause
+// copyright-holders:Peter Trauner
+//
+// /home/ms/source/sidplay/libsidplay/emu/RCS/enve_dl.h,v
+//
+
+#pragma once
+
+#ifndef __SIDE6581_H__
+#define __SIDE6581_H__
+
+
+//
+// Hardware:
+// C64 C PAL, MOS 6581R4 1686 S
+//
+
+static const UINT8 releaseTab[] =
+{
+ 255, 255, 254, 254, 253, 253, 252, 252, 251, 251, 250, 250, 249, 249, 248,
+ 248, 247, 247, 246, 246, 245, 245, 244, 244, 243, 243, 242, 242, 241, 241,
+ 240, 240, 239, 239, 238, 238, 237, 237, 236, 236, 235, 235, 234, 234, 233,
+ 233, 232, 232, 231, 231, 230, 230, 229, 229, 228, 228, 227, 227, 226, 226,
+ 225, 225, 224, 224, 223, 223, 222, 222, 221, 221, 220, 220, 219, 219, 218,
+ 218, 217, 217, 216, 216, 215, 215, 214, 214, 213, 213, 212, 212, 211, 211,
+ 210, 210, 209, 209, 208, 208, 207, 207, 206, 206, 205, 205, 204, 204, 203,
+ 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196,
+ 195, 195, 194, 194, 193, 193, 192, 192, 191, 191, 190, 190, 189, 189, 188,
+ 188, 187, 187, 186, 186, 185, 185, 184, 184, 183, 183, 182, 182, 181, 181,
+ 180, 180, 179, 179, 178, 178, 177, 177, 176, 176, 175, 175, 174, 174, 173,
+ 173, 172, 172, 171, 171, 170, 170, 169, 169, 168, 168, 167, 167, 166, 166,
+ 165, 165, 164, 164, 163, 163, 162, 162, 161, 161, 160, 160, 159, 159, 158,
+ 158, 157, 157, 156, 156, 155, 155, 154, 154, 153, 153, 152, 152, 151, 151,
+ 150, 150, 149, 149, 148, 148, 147, 147, 146, 146, 145, 145, 144, 144, 143,
+ 143, 142, 142, 141, 141, 140, 140, 139, 139, 138, 138, 137, 137, 136, 136,
+ 135, 135, 134, 134, 133, 133, 132, 132, 131, 131, 130, 130, 129, 129, 128,
+ 128, 127, 127, 126, 126, 125, 125, 124, 124, 123, 123, 122, 122, 121, 121,
+ 120, 120, 119, 119, 118, 118, 117, 117, 116, 116, 115, 115, 114, 114, 113,
+ 113, 112, 112, 111, 111, 110, 110, 109, 109, 108, 108, 107, 107, 106, 106,
+ 105, 105, 104, 104, 103, 103, 102, 102, 101, 101, 100, 100, 99, 99, 98,
+ 98, 97, 97, 96, 96, 95, 95, 94, 94, 94, 94, 93, 93, 93, 93,
+ 92, 92, 92, 92, 91, 91, 91, 91, 90, 90, 90, 90, 89, 89, 89,
+ 89, 88, 88, 88, 88, 87, 87, 87, 87, 86, 86, 86, 86, 85, 85,
+ 85, 85, 84, 84, 84, 84, 83, 83, 83, 83, 82, 82, 82, 82, 81,
+ 81, 81, 81, 80, 80, 80, 80, 79, 79, 79, 79, 78, 78, 78, 78,
+ 77, 77, 77, 77, 76, 76, 76, 76, 75, 75, 75, 75, 74, 74, 74,
+ 74, 73, 73, 73, 73, 72, 72, 72, 72, 71, 71, 71, 71, 70, 70,
+ 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66,
+ 66, 66, 66, 65, 65, 65, 65, 64, 64, 64, 64, 63, 63, 63, 63,
+ 62, 62, 62, 62, 61, 61, 61, 61, 60, 60, 60, 60, 59, 59, 59,
+ 59, 58, 58, 58, 58, 57, 57, 57, 57, 56, 56, 56, 56, 55, 55,
+ 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 54, 54, 54, 54, 53,
+ 53, 53, 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52,
+ 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50,
+ 50, 49, 49, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, 48,
+ 48, 48, 47, 47, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46,
+ 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44,
+ 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42,
+ 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40,
+ 40, 40, 40, 40, 40, 40, 39, 39, 39, 39, 39, 39, 39, 39, 38,
+ 38, 38, 38, 38, 38, 38, 38, 37, 37, 37, 37, 37, 37, 37, 37,
+ 36, 36, 36, 36, 36, 36, 36, 36, 35, 35, 35, 35, 35, 35, 35,
+ 35, 34, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 33, 33,
+ 33, 33, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31,
+ 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 30, 29, 29, 29, 29,
+ 29, 29, 29, 29, 28, 28, 28, 28, 28, 28, 28, 28, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 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, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0
+};
+
+
+#endif /* __SIDE6581_H__ */
diff --git a/src/devices/sound/sidenvel.c b/src/devices/sound/sidenvel.c
new file mode 100644
index 00000000000..9b375c37b1f
--- /dev/null
+++ b/src/devices/sound/sidenvel.c
@@ -0,0 +1,588 @@
+// license:BSD-3-Clause
+// copyright-holders:Peter Trauner
+/*========================================================================= */
+/* This source implements the ADSR volume envelope of the SID-chip. */
+/* Two different envelope shapes are implemented, an exponential */
+/* approximation and the linear shape, which can easily be determined */
+/* by reading the registers of the third SID operator. */
+/* */
+/* Accurate volume envelope times as of November 1994 are used */
+/* courtesy of George W. Taylor <aa601@cfn.cs.dal.ca>, <yurik@io.org> */
+/* They are slightly modified. */
+/* */
+/* To use the rounded envelope times from the C64 Programmers Reference */
+/* Book define SID_REFTIMES at the Makefile level. */
+/* */
+/* To perform realtime calculations with floating point precision define */
+/* SID_FPUENVE at the Makefile level. On high-end FPUs (not Pentium !), */
+/* this can result in speed improvement. Default is integer fixpoint. */
+/* */
+/* Global Makefile definables: */
+/* */
+/* DIRECT_FIXPOINT - use a union to access integer fixpoint operands */
+/* in memory. This makes an assumption about the */
+/* hardware and software architecture and therefore */
+/* is considered a hack ! */
+/* */
+/* Local (or Makefile) definables: */
+/* */
+/* SID_REFTIMES - use rounded envelope times */
+/* SID_FPUENVE - use floating point precision for calculations */
+/* (will override the global DIRECT_FIXPOINT setting !) */
+/* */
+/*========================================================================= */
+
+#include "emu.h"
+#include "sidvoice.h"
+#include "sid.h"
+
+#include "side6581.h"
+#include "sidenvel.h"
+
+const UINT8 masterVolumeLevels[16] =
+{
+ 0, 17, 34, 51, 68, 85, 102, 119,
+ 136, 153, 170, 187, 204, 221, 238, 255
+};
+
+static UINT16 masterAmplModTable[16*256];
+
+static const float attackTimes[16] =
+{
+ /* milliseconds */
+#if defined(SID_REFTIMES)
+ 2.0f, 8.0f, 16.0f, 24.0f, 38.0f, 56.0f, 68.0f, 80.0f,
+ 100.0f, 250.0f, 500.0f, 800.0f, 1000.0f, 3000.0f, 5000.0f, 8000.0f
+#else
+ 2.2528606f, 8.0099577f, 15.7696042f, 23.7795619f, 37.2963655f, 55.0684591f,
+ 66.8330845f, 78.3473987f,
+ 98.1219818f, 244.554021f, 489.108042f, 782.472742f, 977.715461f, 2933.64701f,
+ 4889.07793f, 7822.72493f
+#endif
+};
+
+static const float decayReleaseTimes[16] =
+{
+ /* milliseconds */
+#if defined(SID_REFTIMES)
+ 8.0f, 24.0f, 48.0f, 72.0f, 114.0f, 168.0f, 204.0f, 240.0f,
+ 300.0f, 750.0f, 1500.0f, 2400.0f, 3000.0f, 9000.0f, 15000.0f, 24000.0f
+#else
+ 8.91777693f, 24.594051f, 48.4185907f, 73.0116639f, 114.512475f, 169.078356f,
+ 205.199432f, 240.551975f,
+ 301.266125f, 750.858245f, 1501.71551f, 2402.43682f, 3001.89298f, 9007.21405f,
+ 15010.998f, 24018.2111f
+#endif
+};
+
+#ifdef SID_FPUENVE
+ static float attackRates[16];
+ static float decayReleaseRates[16];
+#elif defined(DIRECT_FIXPOINT)
+ static UINT32 attackRates[16];
+ static UINT32 decayReleaseRates[16];
+#else
+ static UINT32 attackRates[16];
+ static UINT32 attackRatesP[16];
+ static UINT32 decayReleaseRates[16];
+ static UINT32 decayReleaseRatesP[16];
+#endif
+
+static const UINT32 attackTabLen = 255;
+static UINT32 releaseTabLen;
+static UINT32 releasePos[256];
+
+
+void enveEmuInit( UINT32 updateFreq, int measuredValues )
+{
+ UINT32 i, j, k;
+
+ releaseTabLen = sizeof(releaseTab);
+ for ( i = 0; i < 256; i++ )
+ {
+ j = 0;
+ while (( j < releaseTabLen ) && (releaseTab[j] > i) )
+ {
+ j++;
+ }
+ if ( j < releaseTabLen )
+ {
+ releasePos[i] = j;
+ }
+ else
+ {
+ releasePos[i] = releaseTabLen -1;
+ }
+ }
+
+ k = 0;
+ for ( i = 0; i < 16; i++ )
+ {
+ for ( j = 0; j < 256; j++ )
+ {
+ UINT16 tmpVol = j;
+ if (measuredValues)
+ {
+ tmpVol = (UINT16) ((293.0*(1-exp(j/-130.0)))+4.0);
+ if (j == 0)
+ tmpVol = 0;
+ if (tmpVol > 255)
+ tmpVol = 255;
+ }
+ /* Want the modulated volume value in the high byte. */
+ masterAmplModTable[k++] = ((tmpVol * masterVolumeLevels[i]) / 255) << 8;
+ }
+ }
+
+ for ( i = 0; i < 16; i++ )
+ {
+#ifdef SID_FPUENVE
+ double scaledenvelen = floor(( attackTimes[i] * updateFreq ) / 1000UL );
+ if (scaledenvelen == 0)
+ scaledenvelen = 1;
+ attackRates[i] = attackTabLen / scaledenvelen;
+
+ scaledenvelen = floor(( decayReleaseTimes[i] * updateFreq ) / 1000UL );
+ if (scaledenvelen == 0)
+ scaledenvelen = 1;
+ decayReleaseRates[i] = releaseTabLen / scaledenvelen;
+#elif defined(DIRECT_FIXPOINT)
+ UINT32 scaledenvelen = (UINT32)floor(( attackTimes[i] * updateFreq ) / 1000UL );
+ if (scaledenvelen == 0)
+ scaledenvelen = 1;
+ attackRates[i] = (attackTabLen << 16) / scaledenvelen;
+
+ scaledenvelen = (UINT32)floor(( decayReleaseTimes[i] * updateFreq ) / 1000UL );
+ if (scaledenvelen == 0)
+ scaledenvelen = 1;
+ decayReleaseRates[i] = (releaseTabLen << 16) / scaledenvelen;
+#else
+ UINT32 scaledenvelen = (UINT32)(/*floor*/(( attackTimes[i] * updateFreq ) / 1000UL ));
+
+ if (scaledenvelen == 0)
+ scaledenvelen = 1;
+ attackRates[i] = attackTabLen / scaledenvelen;
+ attackRatesP[i] = (( attackTabLen % scaledenvelen ) * 65536UL ) / scaledenvelen;
+
+ scaledenvelen = (UINT32)(/*floor*/(( decayReleaseTimes[i] * updateFreq ) / 1000UL ));
+ if (scaledenvelen == 0)
+ scaledenvelen = 1;
+ decayReleaseRates[i] = releaseTabLen / scaledenvelen;
+ decayReleaseRatesP[i] = (( releaseTabLen % scaledenvelen ) * 65536UL ) / scaledenvelen;
+#endif
+ }
+}
+
+/* Reset op. */
+
+void enveEmuResetOperator(sidOperator* pVoice)
+{
+ /* mute, end of R-phase */
+ pVoice->ADSRctrl = ENVE_MUTE;
+// pVoice->gateOnCtrl = (pVoice->gateOffCtrl = false);
+
+#ifdef SID_FPUENVE
+ pVoice->fenveStep = (pVoice->fenveStepAdd = 0);
+ pVoice->enveStep = 0;
+#elif defined(DIRECT_FIXPOINT)
+ pVoice->enveStep.l = (pVoice->enveStepAdd.l = 0);
+#else
+ pVoice->enveStep = (pVoice->enveStepPnt = 0);
+ pVoice->enveStepAdd = (pVoice->enveStepAddPnt = 0);
+#endif
+ pVoice->enveSusVol = 0;
+ pVoice->enveVol = 0;
+ pVoice->enveShortAttackCount = 0;
+}
+
+INLINE UINT16 enveEmuStartAttack(sidOperator*);
+INLINE UINT16 enveEmuStartDecay(sidOperator*);
+INLINE UINT16 enveEmuStartRelease(sidOperator*);
+INLINE UINT16 enveEmuAlterAttack(sidOperator*);
+INLINE UINT16 enveEmuAlterDecay(sidOperator*);
+INLINE UINT16 enveEmuAlterSustain(sidOperator*);
+INLINE UINT16 enveEmuAlterSustainDecay(sidOperator*);
+INLINE UINT16 enveEmuAlterRelease(sidOperator*);
+INLINE UINT16 enveEmuAttack(sidOperator*);
+INLINE UINT16 enveEmuDecay(sidOperator*);
+INLINE UINT16 enveEmuSustain(sidOperator*);
+INLINE UINT16 enveEmuSustainDecay(sidOperator*);
+INLINE UINT16 enveEmuRelease(sidOperator*);
+INLINE UINT16 enveEmuMute(sidOperator*);
+
+INLINE UINT16 enveEmuStartShortAttack(sidOperator*);
+INLINE UINT16 enveEmuAlterShortAttack(sidOperator*);
+INLINE UINT16 enveEmuShortAttack(sidOperator*);
+
+
+const ptr2sidUwordFunc enveModeTable[] =
+{
+ /* 0 */
+ &enveEmuStartAttack, &enveEmuStartRelease,
+ &enveEmuAttack, &enveEmuDecay, &enveEmuSustain, &enveEmuRelease,
+ &enveEmuSustainDecay, &enveEmuMute,
+ /* 16 */
+ &enveEmuStartShortAttack,
+ &enveEmuMute, &enveEmuMute, &enveEmuMute,
+ &enveEmuMute, &enveEmuMute, &enveEmuMute, &enveEmuMute,
+ /* 32 */
+ &enveEmuStartAttack, &enveEmuStartRelease,
+ &enveEmuAlterAttack, &enveEmuAlterDecay, &enveEmuAlterSustain, &enveEmuAlterRelease,
+ &enveEmuAlterSustainDecay, &enveEmuMute,
+ /* 48 */
+ &enveEmuStartShortAttack,
+ &enveEmuMute, &enveEmuMute, &enveEmuMute,
+ &enveEmuMute, &enveEmuMute, &enveEmuMute, &enveEmuMute
+};
+
+/* Real-time functions. */
+/* Order is important because of inline optimizations. */
+/* */
+/* ADSRctrl is (index*2) to enveModeTable[], because of KEY-bit. */
+
+INLINE void enveEmuEnveAdvance(sidOperator* pVoice)
+{
+#ifdef SID_FPUENVE
+ pVoice->fenveStep += pVoice->fenveStepAdd;
+#elif defined(DIRECT_FIXPOINT)
+ pVoice->enveStep.l += pVoice->enveStepAdd.l;
+#else
+ pVoice->enveStepPnt += pVoice->enveStepAddPnt;
+ pVoice->enveStep += pVoice->enveStepAdd + ( pVoice->enveStepPnt > 65535 );
+ pVoice->enveStepPnt &= 0xFFFF;
+#endif
+}
+
+/* */
+/* Mute/Idle. */
+/* */
+
+/* Only used in the beginning. */
+INLINE UINT16 enveEmuMute(sidOperator* pVoice)
+{
+ return 0;
+}
+
+/* */
+/* Release */
+/* */
+
+INLINE UINT16 enveEmuRelease(sidOperator* pVoice)
+{
+#ifdef SID_FPUENVE
+ pVoice->enveStep = (UINT16)pVoice->fenveStep;
+#endif
+#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
+ if ( pVoice->enveStep.w[HI] >= releaseTabLen )
+#else
+ if ( pVoice->enveStep >= releaseTabLen )
+#endif
+ {
+ pVoice->enveVol = releaseTab[releaseTabLen -1];
+ return masterAmplModTable[ pVoice->sid->masterVolumeAmplIndex + pVoice->enveVol ];
+ }
+ else
+ {
+#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
+ pVoice->enveVol = releaseTab[pVoice->enveStep.w[HI]];
+#else
+ pVoice->enveVol = releaseTab[pVoice->enveStep];
+#endif
+ enveEmuEnveAdvance(pVoice);
+ return masterAmplModTable[ pVoice->sid->masterVolumeAmplIndex + pVoice->enveVol ];
+ }
+}
+
+INLINE UINT16 enveEmuAlterRelease(sidOperator* pVoice)
+{
+ UINT8 release = pVoice->SIDSR & 0x0F;
+#ifdef SID_FPUENVE
+ pVoice->fenveStepAdd = decayReleaseRates[release];
+#elif defined(DIRECT_FIXPOINT)
+ pVoice->enveStepAdd.l = decayReleaseRates[release];
+#else
+ pVoice->enveStepAdd = decayReleaseRates[release];
+ pVoice->enveStepAddPnt = decayReleaseRatesP[release];
+#endif
+ pVoice->ADSRproc = &enveEmuRelease;
+ return enveEmuRelease(pVoice);
+}
+
+INLINE UINT16 enveEmuStartRelease(sidOperator* pVoice)
+{
+ pVoice->ADSRctrl = ENVE_RELEASE;
+#ifdef SID_FPUENVE
+ pVoice->fenveStep = releasePos[pVoice->enveVol];
+#elif defined(DIRECT_FIXPOINT)
+ pVoice->enveStep.w[HI] = releasePos[pVoice->enveVol];
+ pVoice->enveStep.w[LO] = 0;
+#else
+ pVoice->enveStep = releasePos[pVoice->enveVol];
+ pVoice->enveStepPnt = 0;
+#endif
+ return enveEmuAlterRelease(pVoice);
+}
+
+/* */
+/* Sustain */
+/* */
+
+INLINE UINT16 enveEmuSustain(sidOperator* pVoice)
+{
+ return masterAmplModTable[pVoice->sid->masterVolumeAmplIndex+pVoice->enveVol];
+}
+
+INLINE UINT16 enveEmuSustainDecay(sidOperator* pVoice)
+{
+#ifdef SID_FPUENVE
+ pVoice->enveStep = (UINT16)pVoice->fenveStep;
+#endif
+#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
+ if ( pVoice->enveStep.w[HI] >= releaseTabLen )
+#else
+ if ( pVoice->enveStep >= releaseTabLen )
+#endif
+ {
+ pVoice->enveVol = releaseTab[releaseTabLen-1];
+ return enveEmuAlterSustain(pVoice);
+ }
+ else
+ {
+#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
+ pVoice->enveVol = releaseTab[pVoice->enveStep.w[HI]];
+#else
+ pVoice->enveVol = releaseTab[pVoice->enveStep];
+#endif
+ /* Will be controlled from sidEmuSet2(). */
+ if ( pVoice->enveVol <= pVoice->enveSusVol )
+ {
+ pVoice->enveVol = pVoice->enveSusVol;
+ return enveEmuAlterSustain(pVoice);
+ }
+ else
+ {
+ enveEmuEnveAdvance(pVoice);
+ return masterAmplModTable[ pVoice->sid->masterVolumeAmplIndex + pVoice->enveVol ];
+ }
+ }
+}
+
+/* This is the same as enveEmuStartSustainDecay(). */
+INLINE UINT16 enveEmuAlterSustainDecay(sidOperator* pVoice)
+{
+ UINT8 decay = pVoice->SIDAD & 0x0F ;
+#ifdef SID_FPUENVE
+ pVoice->fenveStepAdd = decayReleaseRates[decay];
+#elif defined(DIRECT_FIXPOINT)
+ pVoice->enveStepAdd.l = decayReleaseRates[decay];
+#else
+ pVoice->enveStepAdd = decayReleaseRates[decay];
+ pVoice->enveStepAddPnt = decayReleaseRatesP[decay];
+#endif
+ pVoice->ADSRproc = &enveEmuSustainDecay;
+ return enveEmuSustainDecay(pVoice);
+}
+
+/* This is the same as enveEmuStartSustain(). */
+INLINE UINT16 enveEmuAlterSustain(sidOperator* pVoice)
+{
+ if ( pVoice->enveVol > pVoice->enveSusVol )
+ {
+ pVoice->ADSRctrl = ENVE_SUSTAINDECAY;
+ pVoice->ADSRproc = &enveEmuSustainDecay;
+ return enveEmuAlterSustainDecay(pVoice);
+ }
+ else
+ {
+ pVoice->ADSRctrl = ENVE_SUSTAIN;
+ pVoice->ADSRproc = &enveEmuSustain;
+ return enveEmuSustain(pVoice);
+ }
+}
+
+/* */
+/* Decay */
+/* */
+
+INLINE UINT16 enveEmuDecay(sidOperator* pVoice)
+{
+#ifdef SID_FPUENVE
+ pVoice->enveStep = (UINT16)pVoice->fenveStep;
+#endif
+#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
+ if ( pVoice->enveStep.w[HI] >= releaseTabLen )
+#else
+ if ( pVoice->enveStep >= releaseTabLen )
+#endif
+ {
+ pVoice->enveVol = pVoice->enveSusVol;
+ return enveEmuAlterSustain(pVoice); /* start sustain */
+ }
+ else
+ {
+#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
+ pVoice->enveVol = releaseTab[pVoice->enveStep.w[HI]];
+#else
+ pVoice->enveVol = releaseTab[pVoice->enveStep];
+#endif
+ /* Will be controlled from sidEmuSet2(). */
+ if ( pVoice->enveVol <= pVoice->enveSusVol )
+ {
+ pVoice->enveVol = pVoice->enveSusVol;
+ return enveEmuAlterSustain(pVoice); /* start sustain */
+ }
+ else
+ {
+ enveEmuEnveAdvance(pVoice);
+ return masterAmplModTable[ pVoice->sid->masterVolumeAmplIndex + pVoice->enveVol ];
+ }
+ }
+}
+
+INLINE UINT16 enveEmuAlterDecay(sidOperator* pVoice)
+{
+ UINT8 decay = pVoice->SIDAD & 0x0F ;
+#ifdef SID_FPUENVE
+ pVoice->fenveStepAdd = decayReleaseRates[decay];
+#elif defined(DIRECT_FIXPOINT)
+ pVoice->enveStepAdd.l = decayReleaseRates[decay];
+#else
+ pVoice->enveStepAdd = decayReleaseRates[decay];
+ pVoice->enveStepAddPnt = decayReleaseRatesP[decay];
+#endif
+ pVoice->ADSRproc = &enveEmuDecay;
+ return enveEmuDecay(pVoice);
+}
+
+INLINE UINT16 enveEmuStartDecay(sidOperator* pVoice)
+{
+ pVoice->ADSRctrl = ENVE_DECAY;
+#ifdef SID_FPUENVE
+ pVoice->fenveStep = 0;
+#elif defined(DIRECT_FIXPOINT)
+ pVoice->enveStep.l = 0;
+#else
+ pVoice->enveStep = (pVoice->enveStepPnt = 0);
+#endif
+ return enveEmuAlterDecay(pVoice);
+}
+
+/* */
+/* Attack */
+/* */
+
+INLINE UINT16 enveEmuAttack(sidOperator* pVoice)
+{
+#ifdef SID_FPUENVE
+ pVoice->enveStep = (UINT16)pVoice->fenveStep;
+#endif
+#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
+ if ( pVoice->enveStep.w[HI] > attackTabLen )
+#else
+ if ( pVoice->enveStep >= attackTabLen )
+#endif
+ return enveEmuStartDecay(pVoice);
+ else
+ {
+#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
+ pVoice->enveVol = pVoice->enveStep.w[HI];
+#else
+ pVoice->enveVol = pVoice->enveStep;
+#endif
+ enveEmuEnveAdvance(pVoice);
+ return masterAmplModTable[ pVoice->sid->masterVolumeAmplIndex + pVoice->enveVol ];
+ }
+}
+
+INLINE UINT16 enveEmuAlterAttack(sidOperator* pVoice)
+{
+ UINT8 attack = pVoice->SIDAD >> 4;
+#ifdef SID_FPUENVE
+ pVoice->fenveStepAdd = attackRates[attack];
+#elif defined(DIRECT_FIXPOINT)
+ pVoice->enveStepAdd.l = attackRates[attack];
+#else
+ pVoice->enveStepAdd = attackRates[attack];
+ pVoice->enveStepAddPnt = attackRatesP[attack];
+#endif
+ pVoice->ADSRproc = &enveEmuAttack;
+ return enveEmuAttack(pVoice);
+}
+
+INLINE UINT16 enveEmuStartAttack(sidOperator* pVoice)
+{
+ pVoice->ADSRctrl = ENVE_ATTACK;
+#ifdef SID_FPUENVE
+ pVoice->fenveStep = (float)pVoice->enveVol;
+#elif defined(DIRECT_FIXPOINT)
+ pVoice->enveStep.w[HI] = pVoice->enveVol;
+ pVoice->enveStep.w[LO] = 0;
+#else
+ pVoice->enveStep = pVoice->enveVol;
+ pVoice->enveStepPnt = 0;
+#endif
+ return enveEmuAlterAttack(pVoice);
+}
+
+/* */
+/* Experimental. */
+/* */
+
+/*#include <iostream.h> */
+/*#include <iomanip.h> */
+
+INLINE UINT16 enveEmuShortAttack(sidOperator* pVoice)
+{
+#ifdef SID_FPUENVE
+ pVoice->enveStep = (UINT16)pVoice->fenveStep;
+#endif
+#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
+ if ((pVoice->enveStep.w[HI] > attackTabLen) ||
+ (pVoice->enveShortAttackCount == 0))
+#else
+ if ((pVoice->enveStep >= attackTabLen) ||
+ (pVoice->enveShortAttackCount == 0))
+#endif
+/* return enveEmuStartRelease(pVoice); */
+ return enveEmuStartDecay(pVoice);
+#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
+ pVoice->enveVol = pVoice->enveStep.w[HI];
+#else
+ pVoice->enveVol = pVoice->enveStep;
+#endif
+ pVoice->enveShortAttackCount--;
+/* cout << hex << pVoice->enveShortAttackCount << " / " << pVoice->enveVol << endl; */
+ enveEmuEnveAdvance(pVoice);
+ return masterAmplModTable[ pVoice->sid->masterVolumeAmplIndex + pVoice->enveVol ];
+}
+
+INLINE UINT16 enveEmuAlterShortAttack(sidOperator* pVoice)
+{
+ UINT8 attack = pVoice->SIDAD >> 4;
+#ifdef SID_FPUENVE
+ pVoice->fenveStepAdd = attackRates[attack];
+#elif defined(DIRECT_FIXPOINT)
+ pVoice->enveStepAdd.l = attackRates[attack];
+#else
+ pVoice->enveStepAdd = attackRates[attack];
+ pVoice->enveStepAddPnt = attackRatesP[attack];
+#endif
+ pVoice->ADSRproc = &enveEmuShortAttack;
+ return enveEmuShortAttack(pVoice);
+}
+
+INLINE UINT16 enveEmuStartShortAttack(sidOperator* pVoice)
+{
+ pVoice->ADSRctrl = ENVE_SHORTATTACK;
+#ifdef SID_FPUENVE
+ pVoice->fenveStep = (float)pVoice->enveVol;
+#elif defined(DIRECT_FIXPOINT)
+ pVoice->enveStep.w[HI] = pVoice->enveVol;
+ pVoice->enveStep.w[LO] = 0;
+#else
+ pVoice->enveStep = pVoice->enveVol;
+ pVoice->enveStepPnt = 0;
+#endif
+ pVoice->enveShortAttackCount = 65535; /* unused */
+ return enveEmuAlterShortAttack(pVoice);
+}
diff --git a/src/devices/sound/sidenvel.h b/src/devices/sound/sidenvel.h
new file mode 100644
index 00000000000..72f303b8110
--- /dev/null
+++ b/src/devices/sound/sidenvel.h
@@ -0,0 +1,39 @@
+// license:BSD-3-Clause
+// copyright-holders:Peter Trauner
+//
+// /home/ms/source/sidplay/libsidplay/emu/RCS/envelope.h,v
+//
+
+#pragma once
+
+#ifndef __SIDENVEL_H__
+#define __SIDENVEL_H__
+
+
+extern void enveEmuInit(UINT32 updateFreq, int measuredValues);
+void enveEmuResetOperator(sidOperator* pVoice);
+
+
+extern const ptr2sidUwordFunc enveModeTable[]; // -> envelope.cpp
+extern const UINT8 masterVolumeLevels[16]; // -> envelope.cpp
+
+enum
+{
+ ENVE_STARTATTACK = 0,
+ ENVE_STARTRELEASE = 2,
+
+ ENVE_ATTACK = 4,
+ ENVE_DECAY = 6,
+ ENVE_SUSTAIN = 8,
+ ENVE_RELEASE = 10,
+ ENVE_SUSTAINDECAY = 12,
+ ENVE_MUTE = 14,
+
+ ENVE_STARTSHORTATTACK = 16,
+ ENVE_SHORTATTACK = 16,
+
+ ENVE_ALTER = 32
+};
+
+
+#endif /* __SIDENVEL_H__ */
diff --git a/src/devices/sound/sidvoice.c b/src/devices/sound/sidvoice.c
new file mode 100644
index 00000000000..2dd42bcf3c4
--- /dev/null
+++ b/src/devices/sound/sidvoice.c
@@ -0,0 +1,810 @@
+// license:BSD-3-Clause
+// copyright-holders:Peter Trauner
+#include "emu.h"
+#include "sidvoice.h"
+#include "sid.h"
+#include "sidenvel.h"
+#include "sidw6581.h"
+#include "sidw8580.h"
+
+static UINT8 triangleTable[4096];
+static UINT8 sawtoothTable[4096];
+static UINT8 squareTable[2*4096];
+static const UINT8* waveform30;
+static const UINT8* waveform50;
+static const UINT8* waveform60;
+static const UINT8* waveform70;
+#if defined(LARGE_NOISE_TABLE)
+ static UINT8 noiseTableMSB[1<<8];
+ static UINT8 noiseTableLSB[1L<<16];
+#else
+ static UINT8 noiseTableMSB[1<<8];
+ static UINT8 noiseTableMID[1<<8];
+ static UINT8 noiseTableLSB[1<<8];
+#endif
+
+static INT8* ampMod1x8;
+
+static const UINT32 noiseSeed = 0x7ffff8;
+
+void sidInitMixerEngine(running_machine &machine)
+{
+ UINT16 uk;
+ INT32 si, sj ;
+
+ /* 8-bit volume modulation tables. */
+ float filterAmpl = 0.7f;
+
+ ampMod1x8=auto_alloc_array(machine, INT8, 256*256);
+
+ uk = 0;
+ for ( si = 0; si < 256; si++ )
+ {
+ for ( sj = -128; sj < 128; sj++, uk++ )
+ {
+ ampMod1x8[uk] = (INT8)(((si*sj)/255)*filterAmpl);
+ }
+ }
+
+}
+
+INLINE void waveAdvance(sidOperator* pVoice)
+{
+#if defined(DIRECT_FIXPOINT)
+ pVoice->waveStep.l += pVoice->waveStepAdd.l;
+ pVoice->waveStep.w[HI] &= 4095;
+#else
+ pVoice->waveStepPnt += pVoice->waveStepAddPnt;
+ pVoice->waveStep += pVoice->waveStepAdd;
+ if (pVoice->waveStepPnt > 65535 ) pVoice->waveStep++;
+ pVoice->waveStepPnt &= 0xFFFF;
+ pVoice->waveStep &= 4095;
+#endif
+}
+
+INLINE void noiseAdvance(sidOperator* pVoice)
+{
+ pVoice->noiseStep += pVoice->noiseStepAdd;
+ if (pVoice->noiseStep >= (1L<<20))
+ {
+ pVoice->noiseStep -= (1L<<20);
+#if defined(DIRECT_FIXPOINT)
+ pVoice->noiseReg.l = (pVoice->noiseReg.l << 1) |
+ (((pVoice->noiseReg.l >> 22) ^ (pVoice->noiseReg.l >> 17)) & 1);
+#else
+ pVoice->noiseReg = (pVoice->noiseReg << 1) |
+ (((pVoice->noiseReg >> 22) ^ (pVoice->noiseReg >> 17)) & 1);
+#endif
+#if defined(DIRECT_FIXPOINT) && defined(LARGE_NOISE_TABLE)
+ pVoice->noiseOutput = (noiseTableLSB[pVoice->noiseReg.w[LO]]
+ |noiseTableMSB[pVoice->noiseReg.w[HI]&0xff]);
+#elif defined(DIRECT_FIXPOINT)
+ pVoice->noiseOutput = (noiseTableLSB[pVoice->noiseReg.b[LOLO]]
+ |noiseTableMID[pVoice->noiseReg.b[LOHI]]
+ |noiseTableMSB[pVoice->noiseReg.b[HILO]]);
+#else
+ pVoice->noiseOutput = (noiseTableLSB[pVoice->noiseReg&0xff]
+ |noiseTableMID[pVoice->noiseReg>>8&0xff]
+ |noiseTableMSB[pVoice->noiseReg>>16&0xff]);
+#endif
+ }
+}
+
+INLINE void noiseAdvanceHp(sidOperator* pVoice)
+{
+ UINT32 tmp = pVoice->noiseStepAdd;
+ while (tmp >= (1L<<20))
+ {
+ tmp -= (1L<<20);
+#if defined(DIRECT_FIXPOINT)
+ pVoice->noiseReg.l = (pVoice->noiseReg.l << 1) |
+ (((pVoice->noiseReg.l >> 22) ^ (pVoice->noiseReg.l >> 17)) & 1);
+#else
+ pVoice->noiseReg = (pVoice->noiseReg << 1) |
+ (((pVoice->noiseReg >> 22) ^ (pVoice->noiseReg >> 17)) & 1);
+#endif
+ }
+ pVoice->noiseStep += tmp;
+ if (pVoice->noiseStep >= (1L<<20))
+ {
+ pVoice->noiseStep -= (1L<<20);
+#if defined(DIRECT_FIXPOINT)
+ pVoice->noiseReg.l = (pVoice->noiseReg.l << 1) |
+ (((pVoice->noiseReg.l >> 22) ^ (pVoice->noiseReg.l >> 17)) & 1);
+#else
+ pVoice->noiseReg = (pVoice->noiseReg << 1) |
+ (((pVoice->noiseReg >> 22) ^ (pVoice->noiseReg >> 17)) & 1);
+#endif
+ }
+#if defined(DIRECT_FIXPOINT) && defined(LARGE_NOISE_TABLE)
+ pVoice->noiseOutput = (noiseTableLSB[pVoice->noiseReg.w[LO]]
+ |noiseTableMSB[pVoice->noiseReg.w[HI]&0xff]);
+#elif defined(DIRECT_FIXPOINT)
+ pVoice->noiseOutput = (noiseTableLSB[pVoice->noiseReg.b[LOLO]]
+ |noiseTableMID[pVoice->noiseReg.b[LOHI]]
+ |noiseTableMSB[pVoice->noiseReg.b[HILO]]);
+#else
+ pVoice->noiseOutput = (noiseTableLSB[pVoice->noiseReg&0xff]
+ |noiseTableMID[pVoice->noiseReg>>8&0xff]
+ |noiseTableMSB[pVoice->noiseReg>>16&0xff]);
+#endif
+}
+
+
+#if defined(DIRECT_FIXPOINT)
+ #define triangle triangleTable[pVoice->waveStep.w[HI]]
+ #define sawtooth sawtoothTable[pVoice->waveStep.w[HI]]
+ #define square squareTable[pVoice->waveStep.w[HI] + pVoice->pulseIndex]
+ #define triSaw waveform30[pVoice->waveStep.w[HI]]
+ #define triSquare waveform50[pVoice->waveStep.w[HI] + pVoice->SIDpulseWidth]
+ #define sawSquare waveform60[pVoice->waveStep.w[HI] + pVoice->SIDpulseWidth]
+ #define triSawSquare waveform70[pVoice->waveStep.w[HI] + pVoice->SIDpulseWidth]
+#else
+ #define triangle triangleTable[pVoice->waveStep]
+ #define sawtooth sawtoothTable[pVoice->waveStep]
+ #define square squareTable[pVoice->waveStep + pVoice->pulseIndex]
+ #define triSaw waveform30[pVoice->waveStep]
+ #define triSquare waveform50[pVoice->waveStep + pVoice->SIDpulseWidth]
+ #define sawSquare waveform60[pVoice->waveStep + pVoice->SIDpulseWidth]
+ #define triSawSquare waveform70[pVoice->waveStep + pVoice->SIDpulseWidth]
+#endif
+
+
+static void sidMode00(sidOperator* pVoice) {
+ pVoice->output = (pVoice->filtIO-0x80);
+ waveAdvance(pVoice);
+}
+
+#if 0
+/* not used */
+static void sidModeReal00(sidOperator* pVoice) {
+ pVoice->output = 0;
+ waveAdvance(pVoice);
+}
+#endif
+
+static void sidMode10(sidOperator* pVoice) {
+ pVoice->output = triangle;
+ waveAdvance(pVoice);
+}
+
+static void sidMode20(sidOperator* pVoice) {
+ pVoice->output = sawtooth;
+ waveAdvance(pVoice);
+}
+
+static void sidMode30(sidOperator* pVoice) {
+ pVoice->output = triSaw;
+ waveAdvance(pVoice);
+}
+
+static void sidMode40(sidOperator* pVoice) {
+ pVoice->output = square;
+ waveAdvance(pVoice);
+}
+
+static void sidMode50(sidOperator* pVoice) {
+ pVoice->output = triSquare;
+ waveAdvance(pVoice);
+}
+
+static void sidMode60(sidOperator* pVoice) {
+ pVoice->output = sawSquare;
+ waveAdvance(pVoice);
+}
+
+static void sidMode70(sidOperator* pVoice) {
+ pVoice->output = triSawSquare;
+ waveAdvance(pVoice);
+}
+
+static void sidMode80(sidOperator* pVoice) {
+ pVoice->output = pVoice->noiseOutput;
+ waveAdvance(pVoice);
+ noiseAdvance(pVoice);
+}
+
+static void sidMode80hp(sidOperator* pVoice) {
+ pVoice->output = pVoice->noiseOutput;
+ waveAdvance(pVoice);
+ noiseAdvanceHp(pVoice);
+}
+
+static void sidModeLock(sidOperator* pVoice)
+{
+ pVoice->noiseIsLocked = TRUE;
+ pVoice->output = (pVoice->filtIO-0x80);
+ waveAdvance(pVoice);
+}
+
+/* */
+/* */
+/* */
+
+static void sidMode14(sidOperator* pVoice)
+{
+#if defined(DIRECT_FIXPOINT)
+ if ( pVoice->modulator->waveStep.w[HI] < 2048 )
+#else
+ if ( pVoice->modulator->waveStep < 2048 )
+#endif
+ pVoice->output = triangle;
+ else
+ pVoice->output = 0xFF ^ triangle;
+ waveAdvance(pVoice);
+}
+
+static void sidMode34(sidOperator* pVoice) {
+#if defined(DIRECT_FIXPOINT)
+ if ( pVoice->modulator->waveStep.w[HI] < 2048 )
+#else
+ if ( pVoice->modulator->waveStep < 2048 )
+#endif
+ pVoice->output = triSaw;
+ else
+ pVoice->output = 0xFF ^ triSaw;
+ waveAdvance(pVoice);
+}
+
+static void sidMode54(sidOperator* pVoice) {
+#if defined(DIRECT_FIXPOINT)
+ if ( pVoice->modulator->waveStep.w[HI] < 2048 )
+#else
+ if ( pVoice->modulator->waveStep < 2048 )
+#endif
+ pVoice->output = triSquare;
+ else
+ pVoice->output = 0xFF ^ triSquare;
+ waveAdvance(pVoice);
+}
+
+static void sidMode74(sidOperator* pVoice) {
+#if defined(DIRECT_FIXPOINT)
+ if ( pVoice->modulator->waveStep.w[HI] < 2048 )
+#else
+ if ( pVoice->modulator->waveStep < 2048 )
+#endif
+ pVoice->output = triSawSquare;
+ else
+ pVoice->output = 0xFF ^ triSawSquare;
+ waveAdvance(pVoice);
+}
+
+/* */
+/* */
+/* */
+
+INLINE void waveCalcCycleLen(sidOperator* pVoice)
+{
+#if defined(DIRECT_FIXPOINT)
+ pVoice->cycleAddLen.w[HI] = 0;
+ pVoice->cycleAddLen.l += pVoice->cycleLen.l;
+ pVoice->cycleLenCount = pVoice->cycleAddLen.w[HI];
+#else
+ pVoice->cycleAddLenPnt += pVoice->cycleLenPnt;
+ pVoice->cycleLenCount = pVoice->cycleLen;
+ if ( pVoice->cycleAddLenPnt > 65535 ) pVoice->cycleLenCount++;
+ pVoice->cycleAddLenPnt &= 0xFFFF;
+#endif
+ /* If we keep the value cycleLen between 1 <= x <= 65535, */
+ /* the following check is not required. */
+/* if ( pVoice->cycleLenCount == 0 ) */
+/* { */
+/*#if defined(DIRECT_FIXPOINT) */
+/* pVoice->waveStep.l = 0; */
+/*#else */
+/* pVoice->waveStep = (pVoice->waveStepPnt = 0); */
+/*#endif */
+/* pVoice->cycleLenCount = 0; */
+/* } */
+/* else */
+ {
+#if defined(DIRECT_FIXPOINT)
+ register UINT16 diff = pVoice->cycleLenCount - pVoice->cycleLen.w[HI];
+#else
+ register UINT16 diff = pVoice->cycleLenCount - pVoice->cycleLen;
+#endif
+ if ( pVoice->wavePre[diff].len != pVoice->cycleLenCount )
+ {
+ pVoice->wavePre[diff].len = pVoice->cycleLenCount;
+#if defined(DIRECT_FIXPOINT)
+ pVoice->wavePre[diff].stp = (pVoice->waveStepAdd.l = (4096UL*65536UL) / pVoice->cycleLenCount);
+#else
+ pVoice->wavePre[diff].stp = (pVoice->waveStepAdd = 4096UL / pVoice->cycleLenCount);
+ pVoice->wavePre[diff].pnt = (pVoice->waveStepAddPnt = ((4096UL % pVoice->cycleLenCount) * 65536UL) / pVoice->cycleLenCount);
+#endif
+ }
+ else
+ {
+#if defined(DIRECT_FIXPOINT)
+ pVoice->waveStepAdd.l = pVoice->wavePre[diff].stp;
+#else
+ pVoice->waveStepAdd = pVoice->wavePre[diff].stp;
+ pVoice->waveStepAddPnt = pVoice->wavePre[diff].pnt;
+#endif
+ }
+ } /* see above (opening bracket) */
+}
+
+INLINE void waveCalcFilter(sidOperator* pVoice)
+{
+ if ( pVoice->filtEnabled )
+ {
+ if ( pVoice->sid->filter.Type != 0 )
+ {
+ if ( pVoice->sid->filter.Type == 0x20 )
+ {
+ float tmp;
+ pVoice->filtLow += ( pVoice->filtRef * pVoice->sid->filter.Dy );
+ tmp = (float)pVoice->filtIO - pVoice->filtLow;
+ tmp -= pVoice->filtRef * pVoice->sid->filter.ResDy;
+ pVoice->filtRef += ( tmp * (pVoice->sid->filter.Dy) );
+ pVoice->filtIO = (INT8)(pVoice->filtRef-pVoice->filtLow/4);
+ }
+ else if (pVoice->sid->filter.Type == 0x40)
+ {
+ float tmp, tmp2;
+ pVoice->filtLow += ( pVoice->filtRef * pVoice->sid->filter.Dy * 0.1f );
+ tmp = (float)pVoice->filtIO - pVoice->filtLow;
+ tmp -= pVoice->filtRef * pVoice->sid->filter.ResDy;
+ pVoice->filtRef += ( tmp * (pVoice->sid->filter.Dy) );
+ tmp2 = pVoice->filtRef - pVoice->filtIO/8;
+ if (tmp2 < -128)
+ tmp2 = -128;
+ if (tmp2 > 127)
+ tmp2 = 127;
+ pVoice->filtIO = (INT8)tmp2;
+ }
+ else
+ {
+ float sample, sample2;
+ int tmp;
+ pVoice->filtLow += ( pVoice->filtRef * pVoice->sid->filter.Dy );
+ sample = pVoice->filtIO;
+ sample2 = sample - pVoice->filtLow;
+ tmp = (int)sample2;
+ sample2 -= pVoice->filtRef * pVoice->sid->filter.ResDy;
+ pVoice->filtRef += ( sample2 * pVoice->sid->filter.Dy );
+
+ if ( pVoice->sid->filter.Type == 0x10 )
+ {
+ pVoice->filtIO = (INT8)pVoice->filtLow;
+ }
+ else if ( pVoice->sid->filter.Type == 0x30 )
+ {
+ pVoice->filtIO = (INT8)pVoice->filtLow;
+ }
+ else if ( pVoice->sid->filter.Type == 0x50 )
+ {
+ pVoice->filtIO = (INT8)(sample - (tmp >> 1));
+ }
+ else if ( pVoice->sid->filter.Type == 0x60 )
+ {
+ pVoice->filtIO = (INT8)tmp;
+ }
+ else if ( pVoice->sid->filter.Type == 0x70 )
+ {
+ pVoice->filtIO = (INT8)(sample - (tmp >> 1));
+ }
+ }
+ }
+ else /* pVoice->sid->filter.Type == 0x00 */
+ {
+ pVoice->filtIO = 0;
+ }
+ }
+}
+
+static INT8 waveCalcMute(sidOperator* pVoice)
+{
+ (*pVoice->ADSRproc)(pVoice); /* just process envelope */
+ return pVoice->filtIO;//&pVoice->outputMask;
+}
+
+
+INT8 sidWaveCalcNormal(sidOperator* pVoice)
+{
+ if ( pVoice->cycleLenCount <= 0 )
+ {
+ waveCalcCycleLen(pVoice);
+ if (( pVoice->SIDctrl & 0x40 ) == 0x40 )
+ {
+ pVoice->pulseIndex = pVoice->newPulseIndex;
+ if ( pVoice->pulseIndex > 2048 )
+ {
+#if defined(DIRECT_FIXPOINT)
+ pVoice->waveStep.w[HI] = 0;
+#else
+ pVoice->waveStep = 0;
+#endif
+ }
+ }
+ }
+ (*pVoice->waveProc)(pVoice);
+ pVoice->filtIO = ampMod1x8[(*pVoice->ADSRproc)(pVoice)|pVoice->output];
+// pVoice->filtIO = pVoice->sid->masterVolume; // test for digi sound
+ waveCalcFilter(pVoice);
+ return pVoice->filtIO;//&pVoice->outputMask;
+}
+
+
+static INT8 waveCalcRangeCheck(sidOperator* pVoice)
+{
+#if defined(DIRECT_FIXPOINT)
+ pVoice->waveStepOld = pVoice->waveStep.w[HI];
+ (*pVoice->waveProc)(pVoice);
+ if (pVoice->waveStep.w[HI] < pVoice->waveStepOld)
+#else
+ pVoice->waveStepOld = pVoice->waveStep;
+ (*pVoice->waveProc)(pVoice);
+ if (pVoice->waveStep < pVoice->waveStepOld)
+#endif
+ {
+ /* Next step switch back to normal calculation. */
+ pVoice->cycleLenCount = 0;
+ pVoice->outProc = &sidWaveCalcNormal;
+#if defined(DIRECT_FIXPOINT)
+ pVoice->waveStep.w[HI] = 4095;
+#else
+ pVoice->waveStep = 4095;
+#endif
+ }
+ pVoice->filtIO = ampMod1x8[(*pVoice->ADSRproc)(pVoice)|pVoice->output];
+ waveCalcFilter(pVoice);
+ return pVoice->filtIO;//&pVoice->outputMask;
+}
+
+/* -------------------------------------------------- Operator frame set-up 1 */
+
+void sidEmuSet(sidOperator* pVoice)
+{
+ UINT8 enveTemp, newWave, oldWave;
+ UINT8 ADtemp;
+ UINT8 SRtemp;
+ UINT8 tmpSusVol;
+
+ pVoice->SIDfreq = pVoice->reg[0]|(pVoice->reg[1]<<8);
+
+ pVoice->SIDpulseWidth = (pVoice->reg[2]|(pVoice->reg[3]<<8)) & 0x0FFF;
+ pVoice->newPulseIndex = 4096 - pVoice->SIDpulseWidth;
+#if defined(DIRECT_FIXPOINT)
+ if ( ((pVoice->waveStep.w[HI] + pVoice->pulseIndex) >= 0x1000)
+ && ((pVoice->waveStep.w[HI] + pVoice->newPulseIndex) >= 0x1000) )
+ {
+ pVoice->pulseIndex = pVoice->newPulseIndex;
+ }
+ else if ( ((pVoice->waveStep.w[HI] + pVoice->pulseIndex) < 0x1000)
+ && ((pVoice->waveStep.w[HI] + pVoice->newPulseIndex) < 0x1000) )
+ {
+ pVoice->pulseIndex = pVoice->newPulseIndex;
+ }
+#else
+ if ( ((pVoice->waveStep + pVoice->pulseIndex) >= 0x1000)
+ && ((pVoice->waveStep + pVoice->newPulseIndex) >= 0x1000) )
+ {
+ pVoice->pulseIndex = pVoice->newPulseIndex;
+ }
+ else if ( ((pVoice->waveStep + pVoice->pulseIndex) < 0x1000)
+ && ((pVoice->waveStep + pVoice->newPulseIndex) < 0x1000) )
+ {
+ pVoice->pulseIndex = pVoice->newPulseIndex;
+ }
+#endif
+
+
+ oldWave = pVoice->SIDctrl;
+ enveTemp = pVoice->ADSRctrl;
+ pVoice->SIDctrl = (newWave = pVoice->reg[4]|(pVoice->reg[5]<<8));
+
+ if (( newWave & 1 ) ==0 )
+ {
+ if (( oldWave & 1 ) !=0 )
+ enveTemp = ENVE_STARTRELEASE;
+/* else if ( pVoice->gateOnCtrl ) */
+/* { */
+/* enveTemp = ENVE_STARTSHORTATTACK; */
+/* } */
+ }
+ else if ( /*pVoice->gateOffCtrl || */((oldWave&1)==0) )
+ {
+ enveTemp = ENVE_STARTATTACK;
+ }
+
+ if ((( oldWave ^ newWave ) & 0xF0 ) != 0 )
+ {
+ pVoice->cycleLenCount = 0;
+ }
+
+ ADtemp = pVoice->reg[5];
+ SRtemp = pVoice->reg[6];
+ if ( pVoice->SIDAD != ADtemp )
+ {
+ enveTemp |= ENVE_ALTER;
+ }
+ else if ( pVoice->SIDSR != SRtemp )
+ {
+ enveTemp |= ENVE_ALTER;
+ }
+ pVoice->SIDAD = ADtemp;
+ pVoice->SIDSR = SRtemp;
+ tmpSusVol = masterVolumeLevels[SRtemp >> 4];
+ if (pVoice->ADSRctrl != ENVE_SUSTAIN) /* !!! */
+ {
+ pVoice->enveSusVol = tmpSusVol;
+ }
+ else
+ {
+ if ( pVoice->enveSusVol > pVoice->enveVol )
+ pVoice->enveSusVol = 0;
+ else
+ pVoice->enveSusVol = tmpSusVol;
+ }
+
+ pVoice->ADSRproc = enveModeTable[enveTemp>>1]; /* shifting out the KEY-bit */
+ pVoice->ADSRctrl = enveTemp & (255-ENVE_ALTER-1);
+
+ pVoice->filtEnabled = pVoice->sid->filter.Enabled &&
+ ((pVoice->sid->reg[0x17] & pVoice->filtVoiceMask)!=0);
+}
+
+/* -------------------------------------------------- Operator frame set-up 2 */
+
+/* MOS-8580, MOS-6581 (no 70) */
+static ptr2sidVoidFunc sidModeNormalTable[16] =
+{
+ sidMode00, sidMode10, sidMode20, sidMode30, sidMode40, sidMode50, sidMode60, sidMode70,
+ sidMode80, sidModeLock, sidModeLock, sidModeLock, sidModeLock, sidModeLock, sidModeLock, sidModeLock
+};
+
+/* MOS-8580, MOS-6581 (no 74) */
+static ptr2sidVoidFunc sidModeRingTable[16] =
+{
+ sidMode00, sidMode14, sidMode00, sidMode34, sidMode00, sidMode54, sidMode00, sidMode74,
+ sidModeLock, sidModeLock, sidModeLock, sidModeLock, sidModeLock, sidModeLock, sidModeLock, sidModeLock
+};
+
+void sidClearOperator( sidOperator* pVoice )
+{
+ pVoice->SIDfreq = 0;
+ pVoice->SIDctrl = 0;
+ pVoice->SIDAD = 0;
+ pVoice->SIDSR = 0;
+
+ pVoice->sync = FALSE;
+
+ pVoice->pulseIndex = (pVoice->newPulseIndex = (pVoice->SIDpulseWidth = 0));
+ pVoice->curSIDfreq = (pVoice->curNoiseFreq = 0);
+
+ pVoice->output = (pVoice->noiseOutput = 0);
+ pVoice->filtIO = 0;
+
+ pVoice->filtEnabled = FALSE;
+ pVoice->filtLow = (pVoice->filtRef = 0);
+
+ pVoice->cycleLenCount = 0;
+#if defined(DIRECT_FIXPOINT)
+ pVoice->cycleLen.l = (pVoice->cycleAddLen.l = 0);
+#else
+ pVoice->cycleLen = (pVoice->cycleLenPnt = 0);
+ pVoice->cycleAddLenPnt = 0;
+#endif
+
+ pVoice->outProc = waveCalcMute;
+
+#if defined(DIRECT_FIXPOINT)
+ pVoice->waveStepAdd.l = (pVoice->waveStep.l = 0);
+ pVoice->wavePre[0].len = (pVoice->wavePre[0].stp = 0);
+ pVoice->wavePre[1].len = (pVoice->wavePre[1].stp = 0);
+#else
+ pVoice->waveStepAdd = (pVoice->waveStepAddPnt = 0);
+ pVoice->waveStep = (pVoice->waveStepPnt = 0);
+ pVoice->wavePre[0].len = 0;
+ pVoice->wavePre[0].stp = (pVoice->wavePre[0].pnt = 0);
+ pVoice->wavePre[1].len = 0;
+ pVoice->wavePre[1].stp = (pVoice->wavePre[1].pnt = 0);
+#endif
+ pVoice->waveStepOld = 0;
+
+#if defined(DIRECT_FIXPOINT)
+ pVoice->noiseReg.l = noiseSeed;
+#else
+ pVoice->noiseReg = noiseSeed;
+#endif
+ pVoice->noiseStepAdd = (pVoice->noiseStep = 0);
+ pVoice->noiseIsLocked = FALSE;
+}
+
+void sidEmuSet2(sidOperator* pVoice)
+{
+ pVoice->outProc = &sidWaveCalcNormal;
+ pVoice->sync = FALSE;
+
+ if ( (pVoice->SIDfreq < 16) || ((pVoice->SIDctrl & 8) != 0) )
+// if ( /*(pVoice->SIDfreq < 16) || */((pVoice->SIDctrl & 8) != 0) )
+ {
+ pVoice->outProc = waveCalcMute;
+ if (pVoice->SIDfreq == 0)
+ {
+#if defined(DIRECT_FIXPOINT)
+ pVoice->cycleLen.l = (pVoice->cycleAddLen.l = 0);
+ pVoice->waveStep.l = 0;
+#else
+ pVoice->cycleLen = (pVoice->cycleLenPnt = 0);
+ pVoice->cycleAddLenPnt = 0;
+ pVoice->waveStep = 0;
+ pVoice->waveStepPnt = 0;
+#endif
+ pVoice->curSIDfreq = (pVoice->curNoiseFreq = 0);
+ pVoice->noiseStepAdd = 0;
+ pVoice->cycleLenCount = 0;
+ }
+ if ((pVoice->SIDctrl & 8) != 0)
+ {
+ if (pVoice->noiseIsLocked)
+ {
+ pVoice->noiseIsLocked = FALSE;
+#if defined(DIRECT_FIXPOINT)
+ pVoice->noiseReg.l = noiseSeed;
+#else
+ pVoice->noiseReg = noiseSeed;
+#endif
+ }
+ }
+ }
+ else
+ {
+ if ( pVoice->curSIDfreq != pVoice->SIDfreq )
+ {
+ pVoice->curSIDfreq = pVoice->SIDfreq;
+ /* We keep the value cycleLen between 1 <= x <= 65535. */
+ /* This makes a range-check in waveCalcCycleLen() unrequired. */
+#if defined(DIRECT_FIXPOINT)
+ pVoice->cycleLen.l = ((pVoice->sid->PCMsid << 12) / pVoice->SIDfreq) << 4;
+ if (pVoice->cycleLenCount > 0)
+ {
+ waveCalcCycleLen(pVoice);
+ pVoice->outProc = &waveCalcRangeCheck;
+ }
+#else
+ pVoice->cycleLen = pVoice->sid->PCMsid / pVoice->SIDfreq;
+ pVoice->cycleLenPnt = (( pVoice->sid->PCMsid % pVoice->SIDfreq ) * 65536UL ) / pVoice->SIDfreq;
+ if (pVoice->cycleLenCount > 0)
+ {
+ waveCalcCycleLen(pVoice);
+ pVoice->outProc = &waveCalcRangeCheck;
+ }
+#endif
+ }
+
+ if ((( pVoice->SIDctrl & 0x80 ) == 0x80 ) && ( pVoice->curNoiseFreq != pVoice->SIDfreq ))
+ {
+ pVoice->curNoiseFreq = pVoice->SIDfreq;
+ pVoice->noiseStepAdd = (pVoice->sid->PCMsidNoise * pVoice->SIDfreq) >> 8;
+ if (pVoice->noiseStepAdd >= (1L<<21))
+ sidModeNormalTable[8] = sidMode80hp;
+ else
+ sidModeNormalTable[8] = sidMode80;
+ }
+
+ if (( pVoice->SIDctrl & 2 ) != 0 )
+ {
+ if ( ( pVoice->modulator->SIDfreq == 0 ) || (( pVoice->modulator->SIDctrl & 8 ) != 0 ) )
+ {
+ ;
+ }
+ else if ( (( pVoice->carrier->SIDctrl & 2 ) != 0 ) &&
+ ( pVoice->modulator->SIDfreq >= ( pVoice->SIDfreq << 1 )) )
+ {
+ ;
+ }
+ else
+ {
+ pVoice->sync = TRUE;
+ }
+ }
+
+ if ((( pVoice->SIDctrl & 0x14 ) == 0x14 ) && ( pVoice->modulator->SIDfreq != 0 ))
+ pVoice->waveProc = sidModeRingTable[pVoice->SIDctrl >> 4];
+ else
+ pVoice->waveProc = sidModeNormalTable[pVoice->SIDctrl >> 4];
+ }
+}
+
+void sidInitWaveformTables(int type)
+{
+ int i,j;
+ UINT16 k;
+
+ k = 0;
+ for ( i = 0; i < 256; i++ )
+ for ( j = 0; j < 8; j++ )
+ triangleTable[k++] = i;
+ for ( i = 255; i >= 0; i-- )
+ for ( j = 0; j < 8; j++ )
+ triangleTable[k++] = i;
+
+ k = 0;
+ for ( i = 0; i < 256; i++ )
+ for ( j = 0; j < 16; j++ )
+ sawtoothTable[k++] = i;
+
+ k = 0;
+ for ( i = 0; i < 4096; i++ )
+ squareTable[k++] = 255; //0; my estimation; especial for digi sound
+ for ( i = 0; i < 4096; i++ )
+ squareTable[k++] = 0; //255;
+
+ if ( type==mos6581_device::TYPE_8580 )
+ {
+ waveform30 = waveform30_8580;
+ waveform50 = waveform50_8580;
+ waveform60 = waveform60_8580;
+ waveform70 = waveform70_8580;
+ }
+ else
+ {
+ waveform30 = waveform30_6581;
+ waveform50 = waveform50_6581;
+ waveform60 = waveform60_6581;
+ waveform70 = waveform70_6581; /* really audible? */
+ }
+
+ if ( type==mos6581_device::TYPE_8580 )
+ {
+ sidModeNormalTable[3] = sidMode30;
+ sidModeNormalTable[6] = sidMode60;
+ sidModeNormalTable[7] = sidMode70;
+ sidModeRingTable[7] = sidMode74;
+ }
+ else
+ {
+ sidModeNormalTable[3] = sidMode30;
+ sidModeNormalTable[6] = sidMode60;
+ sidModeNormalTable[7] = sidMode00; /* really audible? */
+ sidModeRingTable[7] = sidMode00; /* */
+ }
+
+ {
+#if defined(LARGE_NOISE_TABLE)
+ UINT32 ni;
+ for (ni = 0; ni < sizeof(noiseTableLSB); ni++)
+ {
+ noiseTableLSB[ni] = (UINT8)
+ (((ni >> (13-4)) & 0x10) |
+ ((ni >> (11-3)) & 0x08) |
+ ((ni >> (7-2)) & 0x04) |
+ ((ni >> (4-1)) & 0x02) |
+ ((ni >> (2-0)) & 0x01));
+ }
+ for (ni = 0; ni < sizeof(noiseTableMSB); ni++)
+ {
+ noiseTableMSB[ni] = (UINT8)
+ (((ni << (7-(22-16))) & 0x80) |
+ ((ni << (6-(20-16))) & 0x40) |
+ ((ni << (5-(16-16))) & 0x20));
+ }
+#else
+ UINT32 ni;
+ for (ni = 0; ni < sizeof(noiseTableLSB); ni++)
+ {
+ noiseTableLSB[ni] = (UINT8)
+ (((ni >> (7-2)) & 0x04) |
+ ((ni >> (4-1)) & 0x02) |
+ ((ni >> (2-0)) & 0x01));
+ }
+ for (ni = 0; ni < sizeof(noiseTableMID); ni++)
+ {
+ noiseTableMID[ni] = (UINT8)
+ (((ni >> (13-8-4)) & 0x10) |
+ ((ni << (3-(11-8))) & 0x08));
+ }
+ for (ni = 0; ni < sizeof(noiseTableMSB); ni++)
+ {
+ noiseTableMSB[ni] = (UINT8)
+ (((ni << (7-(22-16))) & 0x80) |
+ ((ni << (6-(20-16))) & 0x40) |
+ ((ni << (5-(16-16))) & 0x20));
+ }
+#endif
+ }
+}
diff --git a/src/devices/sound/sidvoice.h b/src/devices/sound/sidvoice.h
new file mode 100644
index 00000000000..f0aebf50f08
--- /dev/null
+++ b/src/devices/sound/sidvoice.h
@@ -0,0 +1,120 @@
+// license:BSD-3-Clause
+// copyright-holders:Peter Trauner
+#pragma once
+
+#ifndef __SIDVOICE_H__
+#define __SIDVOICE_H__
+
+
+/*
+ approximation of the sid6581 chip
+ this part is for 1 (of the 3) voices of a chip
+*/
+#include "sound/mos6581.h"
+
+struct sw_storage
+{
+ UINT16 len;
+#if defined(DIRECT_FIXPOINT)
+ UINT32 stp;
+#else
+ UINT32 pnt;
+ INT16 stp;
+#endif
+};
+
+struct SID6581_t;
+
+struct sidOperator
+{
+ SID6581_t *sid;
+ UINT8 reg[7];
+ UINT32 SIDfreq;
+ UINT16 SIDpulseWidth;
+ UINT8 SIDctrl;
+ UINT8 SIDAD, SIDSR;
+
+ sidOperator* carrier;
+ sidOperator* modulator;
+ int sync;
+
+ UINT16 pulseIndex, newPulseIndex;
+ UINT16 curSIDfreq;
+ UINT16 curNoiseFreq;
+
+ UINT8 output;//, outputMask;
+
+ char filtVoiceMask;
+ int filtEnabled;
+ float filtLow, filtRef;
+ INT8 filtIO;
+
+ INT32 cycleLenCount;
+#if defined(DIRECT_FIXPOINT)
+ cpuLword cycleLen, cycleAddLen;
+#else
+ UINT32 cycleAddLenPnt;
+ UINT16 cycleLen, cycleLenPnt;
+#endif
+
+ INT8(*outProc)(sidOperator *);
+ void(*waveProc)(sidOperator *);
+
+#if defined(DIRECT_FIXPOINT)
+ cpuLword waveStep, waveStepAdd;
+#else
+ UINT16 waveStep, waveStepAdd;
+ UINT32 waveStepPnt, waveStepAddPnt;
+#endif
+ UINT16 waveStepOld;
+ struct sw_storage wavePre[2];
+
+#if defined(DIRECT_FIXPOINT) && defined(LARGE_NOISE_TABLE)
+ cpuLword noiseReg;
+#elif defined(DIRECT_FIXPOINT)
+ cpuLBword noiseReg;
+#else
+ UINT32 noiseReg;
+#endif
+ UINT32 noiseStep, noiseStepAdd;
+ UINT8 noiseOutput;
+ int noiseIsLocked;
+
+ UINT8 ADSRctrl;
+// int gateOnCtrl, gateOffCtrl;
+ UINT16 (*ADSRproc)(sidOperator *);
+
+#ifdef SID_FPUENVE
+ float fenveStep, fenveStepAdd;
+ UINT32 enveStep;
+#elif defined(DIRECT_FIXPOINT)
+ cpuLword enveStep, enveStepAdd;
+#else
+ UINT16 enveStep, enveStepAdd;
+ UINT32 enveStepPnt, enveStepAddPnt;
+#endif
+ UINT8 enveVol, enveSusVol;
+ UINT16 enveShortAttackCount;
+};
+
+typedef INT8 (*ptr2sidFunc)(sidOperator *);
+typedef UINT16 (*ptr2sidUwordFunc)(sidOperator *);
+typedef void (*ptr2sidVoidFunc)(sidOperator *);
+
+void sidClearOperator( sidOperator* pVoice );
+
+void sidEmuSet(sidOperator* pVoice);
+void sidEmuSet2(sidOperator* pVoice);
+INT8 sidWaveCalcNormal(sidOperator* pVoice);
+
+void sidInitWaveformTables(int type);
+void sidInitMixerEngine(running_machine &machine);
+
+#if 0
+extern ptr2sidVoidFunc sid6581ModeNormalTable[16];
+extern ptr2sidVoidFunc sid6581ModeRingTable[16];
+extern ptr2sidVoidFunc sid8580ModeNormalTable[16];
+extern ptr2sidVoidFunc sid8580ModeRingTable[16];
+#endif
+
+#endif /* __SIDVOICE_H__ */
diff --git a/src/devices/sound/sidw6581.h b/src/devices/sound/sidw6581.h
new file mode 100644
index 00000000000..b37f7693c54
--- /dev/null
+++ b/src/devices/sound/sidw6581.h
@@ -0,0 +1,1053 @@
+// license:GPL-2.0+
+// copyright-holders:Dag Lem
+#ifndef WAVE6581_h
+#define WAVE6581_h
+
+/*
+ * MOS-6581 R1, R3, R4
+ *
+ * Read-out combined waveforms taken from reSID 0.5.
+ * Copyright Dag Lem <resid@nimrod.no>
+ */
+
+static const UINT8 waveform30_6581[4096] =
+{
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x7,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0xe, 0xe, 0xf, 0xf, 0xf, 0xf,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x7,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1e,0x1e,0x1e,0x1e,0x1f,0x1f,0x3f,0x3f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x7,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0xe, 0xe, 0xf, 0xf, 0xf,0x1f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x7,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,
+ 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3e,0x3e,0x3f,0x3f,0x7f,0x7f,0x7f,0x7f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x7,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0xe, 0xe, 0xf, 0xf, 0xf, 0xf,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x7,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1e,0x1e,0x1e,0x1e,0x1f,0x1f,0x3f,0x3f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x7,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0xe, 0xe, 0xf, 0xf, 0xf,0x1f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0x7, 0x7,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,
+ 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3e,0x3e,0x3f,0x3f,0x7f,0x7f,0x7f,0x7f
+};
+
+static const UINT8 waveform50_6581[4096+4096] =
+{
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x20,0x38,0x3f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40, 0x0,0x40,0x40,0x5f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40, 0x0, 0x0, 0x0,0x60, 0x0,0x60,0x60,0x6f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x60,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x60, 0x0, 0x0, 0x0,0x60, 0x0,0x60,0x70,0x77,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x60,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x60, 0x0, 0x0, 0x0,0x70,0x40,0x70,0x70,0x7b,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,0x70, 0x0,0x40,0x40,0x70,0x60,0x70,0x78,0x7d,
+ 0x0,0x40,0x60,0x78,0x60,0x78,0x78,0x7e,0x70,0x7c,0x7c,0x7f,0x7e,0x7f,0x7f,0x7f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80, 0x0, 0x0, 0x0,0x80,0x80,0x80,0x80,0x9f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80, 0x0,0x80,0x80,0x80,0x80,0xa0,0xa0,0xaf,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80,0x80,0x80,
+ 0x0, 0x0, 0x0,0x80, 0x0,0x80,0x80,0xa0, 0x0,0x80,0x80,0xa0,0x80,0xa0,0xb0,0xb7,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80, 0x0, 0x0, 0x0,0x80, 0x0,0x80,0x80,0xa0,
+ 0x0, 0x0, 0x0,0x80, 0x0,0x80,0x80,0xa0, 0x0,0x80,0x80,0xa0,0x80,0xb0,0xb0,0xbb,
+ 0x0, 0x0, 0x0,0x80,0x80,0x80,0x80,0xb0,0x80,0x80,0x80,0xb0,0x80,0xb0,0xb8,0xbd,
+ 0x80,0x80,0x80,0xb8,0xa0,0xb8,0xb8,0xbe,0xa0,0xb8,0xbc,0xbf,0xbe,0xbf,0xbf,0xbf,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0xc0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80,0x80,0xc0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80, 0x0,0x80,0x80,0xc0,
+ 0x0, 0x0, 0x0,0x80, 0x0,0x80,0x80,0xc0, 0x0,0x80,0x80,0xc0,0xc0,0xc0,0xc0,0xcf,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80, 0x0, 0x0, 0x0,0x80, 0x0,0x80,0x80,0xc0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80, 0x0, 0x0, 0x0,0x80, 0x0,0x80,0x80,0xc0,
+ 0x0, 0x0, 0x0,0x80, 0x0,0x80,0xc0,0xc0,0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xd0,0xd7,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80, 0x0, 0x0, 0x0,0x80,0x80,0x80,0xc0,0xc0,
+ 0x0,0x80,0x80,0xc0,0x80,0xc0,0xc0,0xc0,0x80,0xc0,0xc0,0xc0,0xc0,0xd0,0xd0,0xdb,
+ 0x0,0x80,0x80,0xc0,0x80,0xc0,0xc0,0xd0,0x80,0xc0,0xc0,0xd0,0xc0,0xd0,0xd8,0xdd,
+ 0xc0,0xc0,0xc0,0xd0,0xc0,0xd8,0xd8,0xde,0xc0,0xd8,0xdc,0xdf,0xdc,0xdf,0xdf,0xdf,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80, 0x0, 0x0, 0x0,0x80,0x80,0xc0,0xc0,0xe0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x80, 0x0,0x80,0x80,0xc0,0x80,0xc0,0xc0,0xe0,
+ 0x0,0x80,0x80,0xc0,0x80,0xc0,0xc0,0xe0,0x80,0xc0,0xc0,0xe0,0xc0,0xe0,0xe0,0xe7,
+ 0x0, 0x0, 0x0,0x80, 0x0,0x80,0x80,0xc0, 0x0,0x80,0x80,0xc0,0x80,0xc0,0xc0,0xe0,
+ 0x0,0x80,0x80,0xc0,0x80,0xc0,0xc0,0xe0,0xc0,0xc0,0xc0,0xe0,0xe0,0xe0,0xe0,0xeb,
+ 0x80,0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xe0,0xc0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xed,
+ 0xc0,0xe0,0xe0,0xe0,0xe0,0xe8,0xe8,0xee,0xe0,0xe8,0xec,0xef,0xec,0xef,0xef,0xef,
+ 0x0, 0x0, 0x0,0x80,0x80,0x80,0x80,0xc0,0x80,0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xf0,
+ 0x80,0xc0,0xc0,0xc0,0xc0,0xe0,0xe0,0xf0,0xc0,0xe0,0xe0,0xf0,0xe0,0xf0,0xf0,0xf3,
+ 0x80,0xc0,0xc0,0xe0,0xc0,0xe0,0xe0,0xf0,0xc0,0xe0,0xe0,0xf0,0xe0,0xf0,0xf0,0xf5,
+ 0xe0,0xe0,0xe0,0xf0,0xf0,0xf0,0xf0,0xf6,0xf0,0xf0,0xf4,0xf7,0xf4,0xf7,0xf7,0xf7,
+ 0xc0,0xc0,0xc0,0xe0,0xe0,0xe0,0xe0,0xf0,0xe0,0xe0,0xe0,0xf8,0xf0,0xf8,0xf8,0xf9,
+ 0xe0,0xf0,0xf0,0xf8,0xf0,0xf8,0xf8,0xfa,0xf0,0xf8,0xf8,0xfb,0xf8,0xfb,0xfb,0xfb,
+ 0xe0,0xf0,0xf0,0xf8,0xf0,0xf8,0xfc,0xfc,0xf8,0xfc,0xfc,0xfd,0xfc,0xfd,0xfd,0xfd,
+ 0xf8,0xfc,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfc,0xf8,
+ 0xfd,0xfd,0xfd,0xfc,0xfd,0xfc,0xfc,0xf8,0xfc,0xfc,0xfc,0xf0,0xf8,0xf0,0xf0,0xe0,
+ 0xfb,0xfb,0xfb,0xf8,0xfb,0xf8,0xf8,0xf0,0xfa,0xf8,0xf8,0xf0,0xf8,0xf0,0xf0,0xe0,
+ 0xf9,0xf8,0xf8,0xf0,0xf8,0xf0,0xe0,0xe0,0xf0,0xe0,0xe0,0xe0,0xe0,0xc0,0xc0,0xc0,
+ 0xf7,0xf7,0xf7,0xf4,0xf7,0xf4,0xf0,0xf0,0xf6,0xf0,0xf0,0xf0,0xf0,0xe0,0xe0,0xe0,
+ 0xf5,0xf0,0xf0,0xe0,0xf0,0xe0,0xe0,0xc0,0xf0,0xe0,0xe0,0xc0,0xe0,0xc0,0xc0,0x80,
+ 0xf3,0xf0,0xf0,0xe0,0xf0,0xe0,0xe0,0xc0,0xf0,0xe0,0xe0,0xc0,0xc0,0xc0,0xc0,0x80,
+ 0xf0,0xe0,0xc0,0xc0,0xc0,0xc0,0x80,0x80,0xc0,0x80,0x80,0x80,0x80, 0x0, 0x0, 0x0,
+ 0xef,0xef,0xef,0xec,0xef,0xec,0xe8,0xe0,0xee,0xe8,0xe8,0xe0,0xe0,0xe0,0xe0,0xc0,
+ 0xed,0xe8,0xe0,0xe0,0xe0,0xe0,0xe0,0xc0,0xe0,0xe0,0xc0,0xc0,0xc0,0xc0,0x80,0x80,
+ 0xeb,0xe0,0xe0,0xe0,0xe0,0xc0,0xc0,0xc0,0xe0,0xc0,0xc0,0x80,0xc0,0x80,0x80, 0x0,
+ 0xe0,0xc0,0xc0,0x80,0xc0,0x80,0x80, 0x0,0xc0,0x80,0x80, 0x0,0x80, 0x0, 0x0, 0x0,
+ 0xe7,0xe0,0xe0,0xc0,0xe0,0xc0,0xc0,0x80,0xe0,0xc0,0xc0,0x80,0xc0,0x80,0x80, 0x0,
+ 0xe0,0xc0,0xc0,0x80,0xc0,0x80,0x80, 0x0,0x80,0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xe0,0xc0,0xc0,0x80,0x80, 0x0, 0x0, 0x0,0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xdf,0xdf,0xdf,0xdc,0xdf,0xdc,0xd8,0xc0,0xde,0xd8,0xd8,0xc0,0xd8,0xc0,0xc0,0xc0,
+ 0xdd,0xd8,0xd0,0xc0,0xd0,0xc0,0xc0,0x80,0xd0,0xc0,0xc0,0x80,0xc0,0x80,0x80, 0x0,
+ 0xdb,0xd0,0xd0,0xc0,0xc0,0xc0,0xc0,0x80,0xc0,0xc0,0xc0,0x80,0xc0,0x80,0x80, 0x0,
+ 0xc0,0xc0,0x80,0x80,0x80, 0x0, 0x0, 0x0,0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xd7,0xd0,0xc0,0xc0,0xc0,0xc0,0xc0,0x80,0xc0,0xc0,0x80, 0x0,0x80, 0x0, 0x0, 0x0,
+ 0xc0,0x80,0x80, 0x0,0x80, 0x0, 0x0, 0x0,0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xc0,0x80,0x80, 0x0,0x80, 0x0, 0x0, 0x0,0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xcf,0xc0,0xc0,0xc0,0xc0,0x80,0x80, 0x0,0xc0,0x80,0x80, 0x0,0x80, 0x0, 0x0, 0x0,
+ 0xc0,0x80,0x80, 0x0,0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xc0,0x80,0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xc0,0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xbf,0xbf,0xbf,0xbe,0xbf,0xbc,0xbc,0xa0,0xbe,0xbc,0xb8,0xa0,0xb8,0xa0,0x80,0x80,
+ 0xbd,0xb8,0xb0,0x80,0xb0,0x80,0x80,0x80,0xb0,0x80,0x80,0x80,0x80, 0x0, 0x0, 0x0,
+ 0xbb,0xb0,0xb0,0x80,0xa0,0x80,0x80, 0x0,0xa0,0x80,0x80, 0x0,0x80, 0x0, 0x0, 0x0,
+ 0xa0,0x80,0x80, 0x0,0x80, 0x0, 0x0, 0x0,0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xb7,0xb0,0xa0,0x80,0xa0,0x80,0x80, 0x0,0xa0,0x80,0x80, 0x0,0x80, 0x0, 0x0, 0x0,
+ 0x80,0x80,0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x80,0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xaf,0xa0,0xa0,0x80,0x80,0x80,0x80, 0x0,0x80,0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x9f,0x90,0x80,0x80,0x80, 0x0, 0x0, 0x0,0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x7f,0x7f,0x7f,0x7e,0x7f,0x7c,0x7c,0x70,0x7e,0x7c,0x78,0x60,0x78,0x60,0x60, 0x0,
+ 0x7d,0x78,0x78,0x60,0x70,0x40,0x40, 0x0,0x70,0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x7b,0x78,0x70,0x40,0x70,0x40, 0x0, 0x0,0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x77,0x70,0x70, 0x0,0x60, 0x0, 0x0, 0x0,0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x6f,0x60,0x60, 0x0,0x60, 0x0, 0x0, 0x0,0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x5f,0x58,0x40, 0x0,0x40, 0x0, 0x0, 0x0,0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x3f,0x3c,0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
+};
+
+static const UINT8 waveform60_6581[4096+4096] =
+{
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,0x1f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x2f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x37,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x3b,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x3d,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x3e,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x30,0x3f, 0x0,0x30,0x38,0x3f,0x3e,0x3f,0x3f,0x3f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x4f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x57,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x5b,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x5d,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x5e,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,0x5f, 0x0,0x40,0x40,0x5f,0x5c,0x5f,0x5f,0x5f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x67,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,0x6b,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,0x40,0x6d,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40, 0x0, 0x0, 0x0,0x40, 0x0,0x40,0x40,0x6e,
+ 0x0, 0x0, 0x0,0x40, 0x0,0x60,0x60,0x6f, 0x0,0x60,0x60,0x6f,0x60,0x6f,0x6f,0x6f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40, 0x0, 0x0, 0x0,0x40, 0x0,0x40,0x60,0x73,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40, 0x0, 0x0, 0x0,0x40, 0x0,0x60,0x60,0x75,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x60, 0x0, 0x0, 0x0,0x60, 0x0,0x60,0x60,0x76,
+ 0x0, 0x0, 0x0,0x60, 0x0,0x60,0x60,0x77, 0x0,0x70,0x70,0x77,0x70,0x77,0x77,0x77,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x60,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x60, 0x0, 0x0, 0x0,0x60, 0x0,0x60,0x60,0x79,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x60, 0x0, 0x0, 0x0,0x60, 0x0,0x70,0x70,0x7a,
+ 0x0, 0x0, 0x0,0x70, 0x0,0x70,0x70,0x7b,0x40,0x70,0x70,0x7b,0x78,0x7b,0x7b,0x7b,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x70, 0x0, 0x0, 0x0,0x70, 0x0,0x70,0x70,0x7c,
+ 0x0, 0x0, 0x0,0x70,0x40,0x70,0x70,0x7d,0x40,0x70,0x78,0x7d,0x78,0x7d,0x7d,0x7d,
+ 0x0,0x40,0x40,0x78,0x60,0x78,0x78,0x7e,0x60,0x78,0x78,0x7e,0x7c,0x7e,0x7e,0x7e,
+ 0x70,0x7c,0x7c,0x7f,0x7e,0x7f,0x7f,0x7f,0x7e,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,0x1f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x2f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x37,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x3b,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x3d,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x3e,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x30,0x3f, 0x0,0x30,0x38,0x3f,0x3e,0x3f,0x3f,0x3f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x4f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x57,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x5b,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x5d,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x5e,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,0x5f, 0x0,0x40,0x40,0x5f,0x5c,0x5f,0x5f,0x5f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x67,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,0x6b,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,0x6d,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40, 0x0, 0x0, 0x0,0x40, 0x0,0x40,0x40,0x6e,
+ 0x0, 0x0, 0x0,0x40, 0x0,0x60,0x60,0x6f, 0x0,0x60,0x60,0x6f,0x60,0x6f,0x6f,0x6f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40, 0x0, 0x0, 0x0,0x40, 0x0,0x40,0x60,0x73,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x40, 0x0, 0x0, 0x0,0x40, 0x0,0x60,0x60,0x75,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x60, 0x0, 0x0, 0x0,0x60, 0x0,0x60,0x60,0x76,
+ 0x0, 0x0, 0x0,0x60, 0x0,0x60,0x60,0x77, 0x0,0x70,0x70,0x77,0x70,0x77,0x77,0x77,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x60,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x60, 0x0, 0x0, 0x0,0x60, 0x0,0x60,0x60,0x79,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x60, 0x0, 0x0, 0x0,0x60, 0x0,0x70,0x70,0x7a,
+ 0x0, 0x0, 0x0,0x70, 0x0,0x70,0x70,0x7b,0x40,0x70,0x70,0x7b,0x78,0x7b,0x7b,0x7b,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x70, 0x0, 0x0, 0x0,0x70, 0x0,0x70,0x70,0x7c,
+ 0x0, 0x0, 0x0,0x70,0x40,0x70,0x70,0x7d,0x40,0x70,0x78,0x7d,0x78,0x7d,0x7d,0x7d,
+ 0x0,0x40,0x40,0x78,0x60,0x78,0x78,0x7e,0x60,0x78,0x78,0x7e,0x7c,0x7e,0x7e,0x7e,
+ 0x70,0x7c,0x7c,0x7f,0x7c,0x7f,0x7f,0x7f,0x7e,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f
+};
+
+static const UINT8 waveform70_6581[4096+4096] =
+{
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x3f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x20,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x30, 0x0, 0x0, 0x0,0x78,0x78,0x7e,0x7f,0x7f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x3f,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x20,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,0x30, 0x0, 0x0, 0x0,0x78,0x78,0x7e,0x7f,0x7f
+};
+
+#endif // WAVE6581_h
diff --git a/src/devices/sound/sidw8580.h b/src/devices/sound/sidw8580.h
new file mode 100644
index 00000000000..e1f0c10ec8e
--- /dev/null
+++ b/src/devices/sound/sidw8580.h
@@ -0,0 +1,1399 @@
+// license:GPL-2.0+
+// copyright-holders:Dag Lem
+#pragma once
+
+#ifndef __SIDW8580_H__
+#define __SIDW8580_H__
+
+
+/* MOS-8580 R5 waveforms $30,$50,$60,$70
+ *
+ * Created with Deadman's Raw Data to C Header converter
+ */
+
+static const UINT8 waveform30_8580[4096] =
+{
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x07,0x07,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x0e,0x0e,0x0e,0x0e,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x07,0x07,0x07,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1e,0x1e,0x1e,0x1e,
+ 0x1f,0x1f,0x1f,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x07,0x07,0x07,0x07,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x0e,0x0e,0x0e,0x0e,0x0f,0x0f,0x0f,0x0f,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x07,0x07,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,
+ 0x38,0x38,0x38,0x38,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,
+ 0x3e,0x3e,0x3f,0x3f,0x7f,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x07,0x07,0x07,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x0e,0x0e,0x0e,
+ 0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x07,0x07,0x07,0x07,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x1c,0x1c,0x1c,
+ 0x1c,0x1c,0x1c,0x1c,0x1e,0x1e,0x1e,0x1e,0x1f,0x1f,0x1f,0x1f,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x07,0x07,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x0e,0x0e,0x0e,0x0e,0x0f,0x0f,0x1f,0x1f,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x81,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x87,0x87,0x87,0x8f,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,
+ 0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,
+ 0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,
+ 0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,
+ 0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,
+ 0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe3,0xe3,0xe0,0xf0,0xf0,0xf0,
+ 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xe0,0xe0,0xf0,0xf0,
+ 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
+ 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
+ 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
+ 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf1,
+ 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf8,0xf0,0xf0,
+ 0xf0,0xf0,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,
+ 0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xfc,0xfc,0xfc,0xfc,
+ 0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,
+ 0xfc,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff
+};
+
+static const UINT8 waveform50_8580[4096+4096] =
+{
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x3f,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x40,0x00,0x40,0x40,0x6f,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x40,0x60,0x60,0x77,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x60,
+ 0x00,0x40,0x40,0x60,0x40,0x70,0x70,0x7b,0x00,0x00,0x40,0x40,
+ 0x40,0x40,0x40,0x70,0x40,0x60,0x60,0x70,0x60,0x70,0x78,0x7c,
+ 0x60,0x70,0x70,0x78,0x70,0x78,0x7c,0x7e,0x78,0x7c,0x7c,0x7f,
+ 0x7e,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,
+ 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x9f,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x80,
+ 0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0xaf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xb7,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xa0,
+ 0x80,0xa0,0xb0,0xbb,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xa0,
+ 0x80,0x80,0x80,0xb0,0xa0,0xb0,0xb0,0xbc,0x80,0xa0,0xa0,0xb0,
+ 0xb0,0xb8,0xb8,0xbe,0xb8,0xbc,0xbc,0xbf,0xbc,0xbf,0xbf,0xbf,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x00,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
+ 0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc0,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0xc0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc0,
+ 0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xcf,0x00,0x00,0x80,0x80,
+ 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0xc0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0xc0,0xc0,0x80,0x80,0x80,0xc0,
+ 0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xd7,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc0,0x80,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xd0,0xd8,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xd0,0xd0,0xdc,
+ 0xc0,0xc0,0xc0,0xd0,0xd0,0xd8,0xd8,0xde,0xd0,0xd8,0xdc,0xdf,
+ 0xdc,0xdf,0xdf,0xdf,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc0,0x80,0x80,0x80,0x80,
+ 0x80,0xc0,0xc0,0xc0,0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0x80,0x80,0x80,0xc0,0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xe0,
+ 0xc0,0xc0,0xc0,0xe0,0xc0,0xe0,0xe0,0xe7,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xe0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xe0,0xe0,0xc0,0xe0,0xe0,0xe0,
+ 0xe0,0xe0,0xe0,0xe8,0xc0,0xc0,0xc0,0xe0,0xe0,0xe0,0xe0,0xe0,
+ 0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xec,0xe0,0xe0,0xe0,0xe0,
+ 0xe0,0xe0,0xe8,0xee,0xe0,0xe8,0xec,0xee,0xec,0xef,0xef,0xef,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xe0,0xc0,0xc0,0xe0,0xe0,
+ 0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xf0,
+ 0xe0,0xe0,0xe0,0xf0,0xe0,0xf0,0xf0,0xf0,0xe0,0xe0,0xe0,0xe0,
+ 0xe0,0xe0,0xe0,0xf0,0xe0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf4,
+ 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf4,0xf0,0xf0,0xf0,0xf6,
+ 0xf4,0xf7,0xf7,0xf7,0xe0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
+ 0xf0,0xf0,0xf0,0xf8,0xf0,0xf8,0xf8,0xf8,0xf0,0xf0,0xf0,0xf8,
+ 0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xfa,0xf8,0xfb,0xfb,0xfb,
+ 0xf8,0xf8,0xf8,0xf8,0xf8,0xfc,0xfc,0xfc,0xf8,0xfc,0xfc,0xfc,
+ 0xfc,0xfd,0xfd,0xfd,0xfc,0xfc,0xfc,0xfe,0xfe,0xfe,0xfe,0xfe,
+ 0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfc,0xfc,0xfc,
+ 0xfd,0xfd,0xfc,0xfc,0xfc,0xfc,0xfc,0xf8,0xfc,0xfc,0xfc,0xf8,
+ 0xf8,0xf8,0xf8,0xf8,0xfb,0xfb,0xfb,0xf8,0xfa,0xf8,0xf8,0xf8,
+ 0xf8,0xf8,0xf8,0xf8,0xf8,0xf0,0xf0,0xf0,0xf8,0xf8,0xf8,0xf0,
+ 0xf8,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xe0,
+ 0xf7,0xf7,0xf7,0xf4,0xf6,0xf0,0xf0,0xf0,0xf4,0xf0,0xf0,0xf0,
+ 0xf0,0xf0,0xf0,0xf0,0xf4,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xe0,
+ 0xf0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xf0,0xf0,0xf0,0xe0,
+ 0xf0,0xe0,0xe0,0xe0,0xf0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xc0,
+ 0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xc0,0xc0,0xe0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xef,0xef,0xef,0xec,0xee,0xec,0xe8,0xe0,
+ 0xee,0xe8,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xec,0xe0,0xe0,0xe0,
+ 0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xc0,0xc0,0xc0,
+ 0xe8,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xc0,0xe0,0xe0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xe0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xe7,0xe0,0xe0,0xc0,
+ 0xe0,0xc0,0xc0,0xc0,0xe0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x80,
+ 0xc0,0x80,0x80,0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x80,
+ 0xc0,0xc0,0x80,0x80,0x80,0x80,0x80,0x80,0xc0,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0xdf,0xdf,0xdf,0xdc,0xdf,0xdc,0xdc,0xd0,0xde,0xd8,0xd8,0xd0,
+ 0xd0,0xc0,0xc0,0xc0,0xdc,0xd0,0xd0,0xc0,0xd0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xd8,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x80,0xc0,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0xd7,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0x80,0xc0,0x80,0x80,0x80,0xc0,0xc0,0xc0,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0xc0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0xcf,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0x80,0xc0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0xc0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0xc0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,
+ 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x80,0x80,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0xbf,0xbf,0xbf,0xbc,0xbf,0xbc,0xbc,0xb8,
+ 0xbe,0xb8,0xb8,0xb0,0xb8,0xa0,0xa0,0x80,0xbc,0xb0,0xb0,0xa0,
+ 0xb0,0x80,0x80,0x80,0xa0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0xbb,0xb0,0xa0,0x80,0xa0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xb7,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,
+ 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xaf,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x80,0x00,0x00,0x00,
+ 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x9f,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,
+ 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x7f,0x7f,0x7f,0x7e,0x7f,0x7c,0x7c,0x78,0x7e,0x7c,0x78,0x70,
+ 0x78,0x70,0x70,0x60,0x7c,0x78,0x70,0x60,0x70,0x60,0x60,0x40,
+ 0x70,0x60,0x40,0x40,0x40,0x40,0x00,0x00,0x7b,0x70,0x70,0x40,
+ 0x60,0x40,0x40,0x00,0x60,0x40,0x40,0x00,0x00,0x00,0x00,0x00,
+ 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x77,0x60,0x40,0x40,0x40,0x00,0x00,0x00,
+ 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6f,0x40,0x40,0x00,
+ 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x1c,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00
+};
+
+static const UINT8 waveform60_8580[4096+4096] =
+{
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2f,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x3f,
+ 0x00,0x3f,0x3f,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5b,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x5d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5e,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x5f,0x00,0x5f,0x5f,0x5f,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6b,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6e,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6f,0x00,0x00,0x00,0x6f,
+ 0x40,0x6f,0x6f,0x6f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x74,0x00,0x00,0x00,0x00,
+ 0x00,0x40,0x40,0x77,0x00,0x40,0x40,0x77,0x40,0x77,0x77,0x77,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,
+ 0x00,0x00,0x00,0x40,0x00,0x40,0x40,0x79,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x40,0x40,0x78,
+ 0x00,0x00,0x00,0x40,0x00,0x40,0x40,0x78,0x40,0x60,0x60,0x78,
+ 0x60,0x7b,0x7b,0x7b,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x60,
+ 0x00,0x40,0x40,0x60,0x40,0x60,0x60,0x7c,0x40,0x40,0x40,0x60,
+ 0x40,0x70,0x70,0x7c,0x60,0x70,0x70,0x7c,0x70,0x7c,0x7d,0x7d,
+ 0x40,0x60,0x60,0x70,0x60,0x70,0x78,0x7e,0x70,0x78,0x78,0x7e,
+ 0x78,0x7e,0x7e,0x7e,0x78,0x7c,0x7c,0x7f,0x7c,0x7f,0x7f,0x7f,
+ 0x7c,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x8f,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x87,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x8b,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x9d,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x9e,
+ 0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x9f,0x80,0x80,0x80,0x9f,
+ 0x80,0x9f,0x9f,0x9f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x87,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,
+ 0x00,0x80,0x80,0x83,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x85,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x80,0x00,0x80,
+ 0x80,0x80,0x80,0xae,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0xaf,
+ 0x80,0x80,0x80,0xaf,0x80,0xaf,0xaf,0xaf,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0xa3,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x80,
+ 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0xa1,0x00,0x00,0x80,0x80,
+ 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xb0,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xb0,0x80,0x80,0x80,0xb5,
+ 0x80,0xb7,0xb7,0xb7,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,
+ 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xb1,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0xb8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xb8,
+ 0x80,0x80,0x80,0xb8,0x80,0xb8,0xbb,0xbb,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xbc,
+ 0x80,0x80,0x80,0xa0,0x80,0xa0,0xa0,0xbc,0x80,0xa0,0xa0,0xbc,
+ 0xb0,0xbc,0xbc,0xbd,0x80,0x80,0x80,0xb0,0x80,0xb0,0xb0,0xbc,
+ 0xa0,0xb0,0xb0,0xbc,0xb8,0xbc,0xbe,0xbe,0xb0,0xb8,0xb8,0xbe,
+ 0xbc,0xbe,0xbf,0xbf,0xbc,0xbf,0xbf,0xbf,0xbf,0xbf,0xbf,0xbf,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,
+ 0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x80,
+ 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0xc7,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,
+ 0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
+ 0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x80,0x00,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc3,
+ 0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x80,0x00,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc5,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0xc0,0x80,0x80,0x80,0xc0,0x80,0xc0,0xc0,0xce,
+ 0x80,0x80,0x80,0xc0,0x80,0xc0,0xc0,0xcf,0x80,0xc0,0xc0,0xcf,
+ 0xc0,0xcf,0xcf,0xcf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
+ 0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0xc0,0xc0,0xc3,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc0,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc0,0x80,0x80,0x80,0xc0,
+ 0x80,0xc0,0xc0,0xc1,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc0,
+ 0x80,0x80,0x80,0xc0,0x80,0xc0,0xc0,0xc0,0x80,0x80,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xd0,0xc0,0xd5,0xd7,0xd7,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0xc0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc0,
+ 0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xd1,0x80,0x80,0x80,0xc0,
+ 0x80,0xc0,0xc0,0xc0,0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xd0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xd0,0xc0,0xc0,0xc0,0xd8,
+ 0xc0,0xd8,0xd9,0xdb,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xd8,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xdc,0xc0,0xc0,0xc0,0xdc,0xd0,0xdc,0xdc,0xdd,
+ 0xc0,0xc0,0xc0,0xd0,0xc0,0xd0,0xd0,0xdc,0xc0,0xd0,0xd0,0xdc,
+ 0xd0,0xdc,0xde,0xde,0xd0,0xd8,0xd8,0xde,0xd8,0xde,0xde,0xdf,
+ 0xdc,0xdf,0xdf,0xdf,0xdf,0xdf,0xdf,0xdf,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0xc0,0xc0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc0,
+ 0x80,0x80,0x80,0xc0,0x80,0xc0,0xc0,0xc0,0x80,0x80,0x80,0xc0,
+ 0x80,0xc0,0xc0,0xc0,0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xe3,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc0,0x80,0x80,0x80,0xc0,
+ 0x80,0xc0,0xc0,0xc0,0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xe1,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xe0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xe0,0xc0,0xe0,0xe0,0xe0,
+ 0xe0,0xe1,0xe7,0xe7,0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xe0,0xe0,0xe1,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xe0,0xc0,0xc0,0xc0,0xe0,
+ 0xc0,0xe0,0xe0,0xe0,0xc0,0xc0,0xc0,0xe0,0xc0,0xe0,0xe0,0xe0,
+ 0xe0,0xe0,0xe0,0xe0,0xe0,0xe8,0xe8,0xeb,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xe0,0xc0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe8,
+ 0xc0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe8,0xe0,0xe0,0xe0,0xec,
+ 0xe0,0xec,0xec,0xed,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xec,
+ 0xe0,0xe0,0xe0,0xec,0xe0,0xec,0xec,0xee,0xe0,0xe8,0xe8,0xee,
+ 0xe8,0xee,0xee,0xef,0xec,0xef,0xef,0xef,0xef,0xef,0xef,0xef,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xe0,0xc0,0xc0,0xc0,0xc0,0xc0,0xe0,0xe0,0xe0,
+ 0xc0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xf0,0xc0,0xc0,0xc0,0xe0,
+ 0xc0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xf0,
+ 0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xf0,0xe0,0xe0,0xf0,0xf0,
+ 0xf0,0xf0,0xf0,0xf3,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xf0,
+ 0xe0,0xe0,0xe0,0xf0,0xe0,0xf0,0xf0,0xf0,0xe0,0xe0,0xe0,0xf0,
+ 0xe0,0xf0,0xf0,0xf0,0xe0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf4,0xf5,
+ 0xe0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf4,0xf0,0xf0,0xf0,0xf4,
+ 0xf0,0xf4,0xf4,0xf6,0xf0,0xf0,0xf0,0xf4,0xf0,0xf4,0xf6,0xf7,
+ 0xf4,0xf6,0xf7,0xf7,0xf7,0xf7,0xf7,0xf7,0xe0,0xe0,0xe0,0xf0,
+ 0xe0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf8,
+ 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf8,0xf0,0xf0,0xf8,0xf8,
+ 0xf8,0xf8,0xf8,0xf9,0xf0,0xf0,0xf0,0xf8,0xf0,0xf8,0xf8,0xf8,
+ 0xf0,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xfa,0xf8,0xf8,0xf8,0xf8,
+ 0xf8,0xf8,0xfa,0xfb,0xf8,0xfa,0xfa,0xfb,0xfb,0xfb,0xfb,0xfb,
+ 0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xfc,0xf8,0xf8,0xf8,0xfc,
+ 0xfc,0xfc,0xfc,0xfc,0xf8,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfd,
+ 0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfd,0xfc,0xfc,0xfc,0xfe,
+ 0xfc,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,
+ 0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff
+};
+
+static const UINT8 waveform70_8580[4096+4096] =
+{
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,
+ 0x20,0x70,0x70,0x7c,0x7c,0x7e,0x7f,0x7f,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x3f,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x9f,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xcf,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0xc0,0xc0,0xc0,0xc0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xc0,0x80,0x80,0x80,0x80,
+ 0x80,0x80,0x80,0x80,0x80,0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0x80,0x80,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xe0,0xe0,0xe3,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,
+ 0xc0,0xe0,0xe0,0xe0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xe0,
+ 0xc0,0xc0,0xc0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,
+ 0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xf0,0xf0,0xf0,
+ 0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xe0,0xf0,0xe0,0xe0,0xe0,0xf0,
+ 0xf0,0xf0,0xf0,0xf0,0xe0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
+ 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf8,0xf8,0xf0,0xf0,0xf0,0xf8,
+ 0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xfc,0xfc,
+ 0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfe,0xfe,0xfe,0xfe,0xff,
+ 0xff,0xff,0xff,0xff
+};
+
+
+#endif /* __SIDW8580_H__ */
diff --git a/src/devices/sound/sn76477.c b/src/devices/sound/sn76477.c
new file mode 100644
index 00000000000..ea90e03b185
--- /dev/null
+++ b/src/devices/sound/sn76477.c
@@ -0,0 +1,2308 @@
+// license:BSD-3-Clause
+// copyright-holders:Zsolt Vasvari
+/*****************************************************************************
+
+ Texas Instruments SN76477 emulator
+
+ authors: Derrick Renaud - info
+ Zsolt Vasvari - software
+
+ (see sn76477.h for details)
+
+ Notes:
+ * All formulas were derived by taking measurements of a real device,
+ then running the data sets through the numerical analysis
+ application at http://zunzun.com to come up with the functions.
+
+ Known issues/to-do's:
+ * VCO
+ * confirm value of VCO_MAX_EXT_VOLTAGE, VCO_TO_SLF_VOLTAGE_DIFF
+ VCO_CAP_VOLTAGE_MIN and VCO_CAP_VOLTAGE_MAX
+ * confirm value of VCO_MIN_DUTY_CYCLE
+ * get real formulas for VCO cap charging and discharging
+ * get real formula for VCO duty cycle
+ * what happens if no vco_res
+ * what happens if no vco_cap
+
+ * Attack/Decay
+ * get real formulas for a/d cap charging and discharging
+
+ *****************************************************************************/
+
+#include "emu.h"
+#include "wavwrite.h"
+#include "sn76477.h"
+
+
+/*****************************************************************************
+ *
+ * Debugging
+ *
+ *****************************************************************************/
+
+#define VERBOSE 1
+
+/* if 1, create a .wav file of the sound produced */
+#define LOG_WAV 0
+
+/* if 1 and LOG_WAV is 1, only logs to .wav file in chip is enabled */
+#define LOG_WAV_ENABLED_ONLY 0
+
+/* determines what value will be logged in the left channel of the .wav file */
+#define LOG_WAV_VALUE_L 0 /* 0 = OUT voltage */
+ /* 1 = enable line */
+ /* 2 = one-shot cap voltage */
+ /* 3 = a/d cap voltage */
+ /* 4 = slf cap voltage */
+ /* 5 = vco cap voltage */
+ /* 6 = noise filter cap voltage */
+
+/* determines what value will be logged in the right channel of the .wav file
+ same values as for the left channel above */
+#define LOG_WAV_VALUE_R 3
+
+#define LOG_WAV_GAIN_FACTOR 1000
+
+#define LOG_WAV_FILE_NAME "sn76477_%s.wav"
+
+
+#define LOG(n,x) do { if (VERBOSE >= (n)) logerror x; } while (0)
+
+#define CHECK_CHIP_NUM assert(this != NULL)
+#define CHECK_CHIP_NUM_AND_BOOLEAN CHECK_CHIP_NUM; assert((state & 0x01) == state)
+#define CHECK_CHIP_NUM_AND_POSITIVE CHECK_CHIP_NUM; assert(data >= 0.0)
+#define CHECK_CHIP_NUM_AND_VOLTAGE CHECK_CHIP_NUM; assert((data >= 0.0) && (data <= 5.0))
+#define CHECK_CHIP_NUM_AND_CAP_VOLTAGE CHECK_CHIP_NUM; assert(((data >= 0.0) && (data <= 5.0)) || (data == SN76477_EXTERNAL_VOLTAGE_DISCONNECT))
+
+
+
+/*****************************************************************************
+ *
+ * Test Mode
+ *
+ * in test mode, calls by the driver to
+ * the input setter functions are ignored.
+ * Interface values can be set in device_start
+ * to any desired test value.
+ * Use the space bar to enable/disable the chip.
+ *
+ *****************************************************************************/
+
+#define TEST_MODE 0
+
+/*****************************************************************************
+ *
+ * Constants
+ *
+ *****************************************************************************/
+
+#define ONE_SHOT_CAP_VOLTAGE_MIN (0) /* the voltage at which the one-shot starts from (measured) */
+#define ONE_SHOT_CAP_VOLTAGE_MAX (2.5) /* the voltage at which the one-shot finishes (measured) */
+#define ONE_SHOT_CAP_VOLTAGE_RANGE (ONE_SHOT_CAP_VOLTAGE_MAX - ONE_SHOT_CAP_VOLTAGE_MIN)
+
+#define SLF_CAP_VOLTAGE_MIN (0.33) /* the voltage at the bottom peak of the SLF triangle wave (measured) */
+#define SLF_CAP_VOLTAGE_MAX (2.37) /* the voltage at the top peak of the SLF triangle wave (measured) */
+#define SLF_CAP_VOLTAGE_RANGE (SLF_CAP_VOLTAGE_MAX - SLF_CAP_VOLTAGE_MIN)
+
+#define VCO_MAX_EXT_VOLTAGE (2.35) /* the external voltage at which the VCO saturates and produces no output,
+ also used as the voltage threshold for the SLF */
+#define VCO_TO_SLF_VOLTAGE_DIFF (0.35)
+#define VCO_CAP_VOLTAGE_MIN (SLF_CAP_VOLTAGE_MIN) /* the voltage at the bottom peak of the VCO triangle wave */
+#define VCO_CAP_VOLTAGE_MAX (SLF_CAP_VOLTAGE_MAX + VCO_TO_SLF_VOLTAGE_DIFF) /* the voltage at the bottom peak of the VCO triangle wave */
+#define VCO_CAP_VOLTAGE_RANGE (VCO_CAP_VOLTAGE_MAX - VCO_CAP_VOLTAGE_MIN)
+#define VCO_DUTY_CYCLE_50 (5.0) /* the high voltage that produces a 50% duty cycle */
+#define VCO_MIN_DUTY_CYCLE (18) /* the smallest possible duty cycle, in % */
+
+#define NOISE_MIN_CLOCK_RES RES_K(10) /* the maximum resistor value that still produces a noise (measured) */
+#define NOISE_MAX_CLOCK_RES RES_M(3.3) /* the minimum resistor value that still produces a noise (measured) */
+#define NOISE_CAP_VOLTAGE_MIN (0) /* the minimum voltage that the noise filter cap can hold (measured) */
+#define NOISE_CAP_VOLTAGE_MAX (5.0) /* the maximum voltage that the noise filter cap can hold (measured) */
+#define NOISE_CAP_VOLTAGE_RANGE (NOISE_CAP_VOLTAGE_MAX - NOISE_CAP_VOLTAGE_MIN)
+#define NOISE_CAP_HIGH_THRESHOLD (3.35) /* the voltage at which the filtered noise bit goes to 0 (measured) */
+#define NOISE_CAP_LOW_THRESHOLD (0.74) /* the voltage at which the filtered noise bit goes to 1 (measured) */
+
+#define AD_CAP_VOLTAGE_MIN (0) /* the minimum voltage the attack/decay cap can hold (measured) */
+#define AD_CAP_VOLTAGE_MAX (4.44) /* the minimum voltage the attack/decay cap can hold (measured) */
+#define AD_CAP_VOLTAGE_RANGE (AD_CAP_VOLTAGE_MAX - AD_CAP_VOLTAGE_MIN)
+
+#define OUT_CENTER_LEVEL_VOLTAGE (2.57) /* the voltage that gets outputted when the volumne is 0 (measured) */
+#define OUT_HIGH_CLIP_THRESHOLD (3.51) /* the maximum voltage that can be put out (measured) */
+#define OUT_LOW_CLIP_THRESHOLD (0.715) /* the minimum voltage that can be put out (measured) */
+
+/* gain factors for OUT voltage in 0.1V increments (measured) */
+static const double out_pos_gain[] =
+{
+ 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.01, /* 0.0 - 0.9V */
+ 0.03, 0.11, 0.15, 0.19, 0.21, 0.23, 0.26, 0.29, 0.31, 0.33, /* 1.0 - 1.9V */
+ 0.36, 0.38, 0.41, 0.43, 0.46, 0.49, 0.52, 0.54, 0.57, 0.60, /* 2.0 - 2.9V */
+ 0.62, 0.65, 0.68, 0.70, 0.73, 0.76, 0.80, 0.82, 0.84, 0.87, /* 3.0 - 3.9V */
+ 0.90, 0.93, 0.96, 0.98, 1.00 /* 4.0 - 4.4V */
+};
+
+static const double out_neg_gain[] =
+{
+ 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, -0.01, /* 0.0 - 0.9V */
+ -0.02, -0.09, -0.13, -0.15, -0.17, -0.19, -0.22, -0.24, -0.26, -0.28, /* 1.0 - 1.9V */
+ -0.30, -0.32, -0.34, -0.37, -0.39, -0.41, -0.44, -0.46, -0.48, -0.51, /* 2.0 - 2.9V */
+ -0.53, -0.56, -0.58, -0.60, -0.62, -0.65, -0.67, -0.69, -0.72, -0.74, /* 3.0 - 3.9V */
+ -0.76, -0.78, -0.81, -0.84, -0.85 /* 4.0 - 4.4V */
+};
+
+
+const device_type SN76477 = &device_creator<sn76477_device>;
+
+sn76477_device::sn76477_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, SN76477, "SN76477", tag, owner, clock, "sn76477", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_enable(0),
+ m_envelope_mode(0),
+ m_vco_mode(0),
+ m_mixer_mode(0),
+ m_one_shot_res(0),
+ m_one_shot_cap(0),
+ m_one_shot_cap_voltage_ext(0),
+ m_slf_res(0),
+ m_slf_cap(0),
+ m_slf_cap_voltage_ext(0),
+ m_vco_voltage(0),
+ m_vco_res(0),
+ m_vco_cap(0),
+ m_vco_cap_voltage_ext(0),
+ m_noise_clock_res(0),
+ m_noise_clock_ext(0),
+ m_noise_clock(0),
+ m_noise_filter_res(0),
+ m_noise_filter_cap(0),
+ m_noise_filter_cap_voltage_ext(0),
+ m_attack_res(0),
+ m_decay_res(0),
+ m_attack_decay_cap(0),
+ m_attack_decay_cap_voltage_ext(0),
+ m_amplitude_res(0),
+ m_feedback_res(0),
+ m_pitch_voltage(0),
+ m_one_shot_cap_voltage(0),
+ m_one_shot_running_ff(0),
+ m_slf_cap_voltage(0),
+ m_slf_out_ff(0),
+ m_vco_cap_voltage(0),
+ m_vco_out_ff(0),
+ m_vco_alt_pos_edge_ff(0),
+ m_noise_filter_cap_voltage(0),
+ m_real_noise_bit_ff(0),
+ m_filtered_noise_bit_ff(0),
+ m_noise_gen_count(0),
+ m_attack_decay_cap_voltage(0),
+ m_rng(0),
+ m_mixer_a(0),
+ m_mixer_b(0),
+ m_mixer_c(0),
+ m_envelope_1(0),
+ m_envelope_2(0),
+ m_channel(NULL),
+ m_our_sample_rate(0),
+ m_file(NULL)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void sn76477_device::device_start()
+{
+ m_channel = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate());
+
+ if (clock() > 0)
+ {
+ m_our_sample_rate = clock();
+ }
+ else
+ {
+ m_our_sample_rate = machine().sample_rate();
+ }
+
+ intialize_noise();
+
+ // set up mixer and envelope modes, based on interface values
+ _SN76477_mixer_a_w(m_mixer_a);
+ _SN76477_mixer_b_w(m_mixer_b);
+ _SN76477_mixer_c_w(m_mixer_c);
+ _SN76477_envelope_1_w(m_envelope_1);
+ _SN76477_envelope_2_w(m_envelope_2);
+
+ m_one_shot_cap_voltage = ONE_SHOT_CAP_VOLTAGE_MIN;
+ m_slf_cap_voltage = SLF_CAP_VOLTAGE_MIN;
+ m_vco_cap_voltage = VCO_CAP_VOLTAGE_MIN;
+ m_noise_filter_cap_voltage = NOISE_CAP_VOLTAGE_MIN;
+ m_attack_decay_cap_voltage = AD_CAP_VOLTAGE_MIN;
+
+ state_save_register();
+
+ log_complete_state();
+
+ if (LOG_WAV)
+ open_wav_file();
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void sn76477_device::device_stop()
+{
+ if (LOG_WAV)
+ close_wav_file();
+}
+
+
+/*****************************************************************************
+ *
+ * Max/min
+ *
+ *****************************************************************************/
+
+#undef max
+#undef min
+
+INLINE double max(double a, double b)
+{
+ return (a > b) ? a : b;
+}
+
+
+INLINE double min(double a, double b)
+{
+ return (a < b) ? a : b;
+}
+
+
+
+/*****************************************************************************
+ *
+ * Functions for computing frequencies, voltages and similar values based
+ * on the hardware itself. Do NOT put anything emulation specific here,
+ * such as calculations based on sample_rate.
+ *
+ *****************************************************************************/
+
+double sn76477_device::compute_one_shot_cap_charging_rate() /* in V/sec */
+{
+ /* this formula was derived using the data points below
+
+ Res (kohms) Cap (uF) Time (millisec)
+ 47 0.33 11.84
+ 47 1.0 36.2
+ 47 1.5 52.1
+ 47 2.0 76.4
+ 100 0.33 24.4
+ 100 1.0 75.2
+ 100 1.5 108.5
+ 100 2.0 158.4
+ */
+
+ double ret = 0;
+
+ if ((m_one_shot_res > 0) && (m_one_shot_cap > 0))
+ {
+ ret = ONE_SHOT_CAP_VOLTAGE_RANGE / (0.8024 * m_one_shot_res * m_one_shot_cap + 0.002079);
+ }
+ else if (m_one_shot_cap > 0)
+ {
+ /* if no resistor, there is no current to charge the cap,
+ effectively making the one-shot time effectively infinite */
+ ret = +1e-30;
+ }
+ else if (m_one_shot_res > 0)
+ {
+ /* if no cap, the voltage changes extremely fast,
+ effectively making the one-shot time 0 */
+ ret = +1e+30;
+ }
+
+ return ret;
+}
+
+
+double sn76477_device::compute_one_shot_cap_discharging_rate() /* in V/sec */
+{
+ /* this formula was derived using the data points below
+
+ Cap (uF) Time (microsec)
+ 0.33 300
+ 1.0 850
+ 1.5 1300
+ 2.0 1900
+ */
+
+ double ret = 0;
+
+ if ((m_one_shot_res > 0) && (m_one_shot_cap > 0))
+ {
+ ret = ONE_SHOT_CAP_VOLTAGE_RANGE / (854.7 * m_one_shot_cap + 0.00001795);
+ }
+ else if (m_one_shot_res > 0)
+ {
+ /* if no cap, the voltage changes extremely fast,
+ effectively making the one-shot time 0 */
+ ret = +1e+30;
+ }
+
+ return ret;
+}
+
+
+double sn76477_device::compute_slf_cap_charging_rate() /* in V/sec */
+{
+ /* this formula was derived using the data points below
+
+ Res (kohms) Cap (uF) Time (millisec)
+ 47 0.47 14.3
+ 120 0.47 35.6
+ 200 0.47 59.2
+ 47 1.00 28.6
+ 120 1.00 71.6
+ 200 1.00 119.0
+ */
+ double ret = 0;
+
+ if ((m_slf_res > 0) && (m_slf_cap > 0))
+ {
+ ret = SLF_CAP_VOLTAGE_RANGE / (0.5885 * m_slf_res * m_slf_cap + 0.001300);
+ }
+
+ return ret;
+}
+
+
+double sn76477_device::compute_slf_cap_discharging_rate() /* in V/sec */
+{
+ /* this formula was derived using the data points below
+
+ Res (kohms) Cap (uF) Time (millisec)
+ 47 0.47 13.32
+ 120 0.47 32.92
+ 200 0.47 54.4
+ 47 1.00 26.68
+ 120 1.00 66.2
+ 200 1.00 109.6
+ */
+ double ret = 0;
+
+ if ((m_slf_res > 0) && (m_slf_cap > 0))
+ {
+ ret = SLF_CAP_VOLTAGE_RANGE / (0.5413 * m_slf_res * m_slf_cap + 0.001343);
+ }
+
+ return ret;
+}
+
+
+double sn76477_device::compute_vco_cap_charging_discharging_rate() /* in V/sec */
+{
+ double ret = 0;
+
+ if ((m_vco_res > 0) && (m_vco_cap > 0))
+ {
+ ret = 0.64 * 2 * VCO_CAP_VOLTAGE_RANGE / (m_vco_res * m_vco_cap);
+ }
+
+ return ret;
+}
+
+
+double sn76477_device::compute_vco_duty_cycle() /* no measure, just a number */
+{
+ double ret = 0.5; /* 50% */
+
+ if ((m_vco_voltage > 0) && (m_pitch_voltage != VCO_DUTY_CYCLE_50))
+ {
+ ret = max(0.5 * (m_pitch_voltage / m_vco_voltage), (VCO_MIN_DUTY_CYCLE / 100.0));
+
+ ret = min(ret, 1);
+ }
+
+ return ret;
+}
+
+
+UINT32 sn76477_device::compute_noise_gen_freq() /* in Hz */
+{
+ /* this formula was derived using the data points below
+
+ Res (ohms) Freq (Hz)
+ 10k 97493
+ 12k 83333
+ 15k 68493
+ 22k 49164
+ 27k 41166
+ 33k 34449
+ 36k 31969
+ 47k 25126
+ 56k 21322
+ 68k 17721.5
+ 82k 15089.2
+ 100k 12712.0
+ 150k 8746.4
+ 220k 6122.4
+ 270k 5101.5
+ 330k 4217.2
+ 390k 3614.5
+ 470k 3081.7
+ 680k 2132.7
+ 820k 1801.8
+ 1M 1459.9
+ 2.2M 705.13
+ 3.3M 487.59
+ */
+
+ UINT32 ret = 0;
+
+ if ((m_noise_clock_res >= NOISE_MIN_CLOCK_RES) &&
+ (m_noise_clock_res <= NOISE_MAX_CLOCK_RES))
+ {
+ ret = 339100000 * pow(m_noise_clock_res, -0.8849);
+ }
+
+ return ret;
+}
+
+
+double sn76477_device::compute_noise_filter_cap_charging_rate() /* in V/sec */
+{
+ /* this formula was derived using the data points below
+
+ R*C Time (sec)
+ .000068 .0000184
+ .0001496 .0000378
+ .0002244 .0000548
+ .0003196 .000077
+ .0015 .000248
+ .0033 .000540
+ .00495 .000792
+ .00705 .001096
+ */
+
+ double ret = 0;
+
+ if ((m_noise_filter_res > 0) && (m_noise_filter_cap > 0))
+ {
+ ret = NOISE_CAP_VOLTAGE_RANGE / (0.1571 * m_noise_filter_res * m_noise_filter_cap + 0.00001430);
+ }
+ else if (m_noise_filter_cap > 0)
+ {
+ /* if no resistor, there is no current to charge the cap,
+ effectively making the filter's output constants */
+ ret = +1e-30;
+ }
+ else if (m_noise_filter_res > 0)
+ {
+ /* if no cap, the voltage changes extremely fast,
+ effectively disabling the filter */
+ ret = +1e+30;
+ }
+
+ return ret;
+}
+
+
+double sn76477_device::compute_noise_filter_cap_discharging_rate() /* in V/sec */
+{
+ /* this formula was derived using the data points below
+
+ R*C Time (sec)
+ .000068 .000016
+ .0001496 .0000322
+ .0002244 .0000472
+ .0003196 .0000654
+ .0015 .000219
+ .0033 .000468
+ .00495 .000676
+ .00705 .000948
+ */
+
+ double ret = 0;
+
+ if ((m_noise_filter_res > 0) && (m_noise_filter_cap > 0))
+ {
+ ret = NOISE_CAP_VOLTAGE_RANGE / (0.1331 * m_noise_filter_res * m_noise_filter_cap + 0.00001734);
+ }
+ else if (m_noise_filter_cap > 0)
+ {
+ /* if no resistor, there is no current to charge the cap,
+ effectively making the filter's output constants */
+ ret = +1e-30;
+ }
+ else if (m_noise_filter_res > 0)
+ {
+ /* if no cap, the voltage changes extremely fast,
+ effectively disabling the filter */
+ ret = +1e+30;
+ }
+
+ return ret;
+}
+
+
+double sn76477_device::compute_attack_decay_cap_charging_rate() /* in V/sec */
+{
+ double ret = 0;
+
+ if ((m_attack_res > 0) && (m_attack_decay_cap > 0))
+ {
+ ret = AD_CAP_VOLTAGE_RANGE / (m_attack_res * m_attack_decay_cap);
+ }
+ else if (m_attack_decay_cap > 0)
+ {
+ /* if no resistor, there is no current to charge the cap,
+ effectively making the attack time infinite */
+ ret = +1e-30;
+ }
+ else if (m_attack_res > 0)
+ {
+ /* if no cap, the voltage changes extremely fast,
+ effectively making the attack time 0 */
+ ret = +1e+30;
+ }
+
+ return ret;
+}
+
+
+double sn76477_device::compute_attack_decay_cap_discharging_rate() /* in V/sec */
+{
+ double ret = 0;
+
+ if ((m_decay_res > 0) && (m_attack_decay_cap > 0))
+ {
+ ret = AD_CAP_VOLTAGE_RANGE / (m_decay_res * m_attack_decay_cap);
+ }
+ else if (m_attack_decay_cap > 0)
+ {
+ /* if no resistor, there is no current to charge the cap,
+ effectively making the decay time infinite */
+ ret = +1e-30;
+ }
+ else if (m_attack_res > 0)
+ {
+ /* if no cap, the voltage changes extremely fast,
+ effectively making the decay time 0 */
+ ret = +1e+30;
+ }
+
+ return ret;
+}
+
+
+double sn76477_device::compute_center_to_peak_voltage_out()
+{
+ /* this formula was derived using the data points below
+
+ Ra (kohms) Rf (kohms) Voltage
+ 150 47 1.28
+ 200 47 0.96
+ 47 22 1.8
+ 100 22 0.87
+ 150 22 0.6
+ 200 22 0.45
+ 47 10 0.81
+ 100 10 0.4
+ 150 10 0.27
+ */
+
+ double ret = 0;
+
+ if (m_amplitude_res > 0)
+ {
+ ret = 3.818 * (m_feedback_res / m_amplitude_res) + 0.03;
+ }
+
+ return ret;
+}
+
+
+
+/*****************************************************************************
+ *
+ * Logging functions
+ *
+ *****************************************************************************/
+
+void sn76477_device::log_enable_line()
+{
+ static const char *const desc[] =
+ {
+ "Enabled", "Inhibited"
+ };
+
+ LOG(1, ("SN76477 '%s': Enable line (9): %d [%s]\n", tag(), m_enable, desc[m_enable]));
+}
+
+
+void sn76477_device::log_mixer_mode()
+{
+ static const char *const desc[] =
+ {
+ "VCO", "SLF", "Noise", "VCO/Noise",
+ "SLF/Noise", "SLF/VCO/Noise", "SLF/VCO", "Inhibit"
+ };
+
+ LOG(1, ("SN76477 '%s': Mixer mode (25-27): %d [%s]\n", tag(), m_mixer_mode, desc[m_mixer_mode]));
+}
+
+
+void sn76477_device::log_envelope_mode()
+{
+ static const char *const desc[] =
+ {
+ "VCO", "One-Shot", "Mixer Only", "VCO with Alternating Polarity"
+ };
+
+ LOG(1, ("SN76477 '%s': Envelope mode (1,28): %d [%s]\n", tag(), m_envelope_mode, desc[m_envelope_mode]));
+}
+
+
+void sn76477_device::log_vco_mode()
+{
+ static const char *const desc[] =
+ {
+ "External (Pin 16)", "Internal (SLF)"
+ };
+
+ LOG(1, ("SN76477 '%s': VCO mode (22): %d [%s]\n", tag(), m_vco_mode, desc[m_vco_mode]));
+}
+
+
+void sn76477_device::log_one_shot_time()
+{
+ if (!m_one_shot_cap_voltage_ext)
+ {
+ if (compute_one_shot_cap_charging_rate() > 0)
+ {
+ LOG(1, ("SN76477 '%s': One-shot time (23,24): %.4f sec\n", tag(), ONE_SHOT_CAP_VOLTAGE_RANGE * (1 / compute_one_shot_cap_charging_rate())));
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': One-shot time (23,24): N/A\n", tag()));
+ }
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': One-shot time (23,24): External (cap = %.2fV)\n", tag(), m_one_shot_cap_voltage));
+ }
+}
+
+
+void sn76477_device::log_slf_freq()
+{
+ if (!m_slf_cap_voltage_ext)
+ {
+ if (compute_slf_cap_charging_rate() > 0)
+ {
+ double charging_time = (1 / compute_slf_cap_charging_rate()) * SLF_CAP_VOLTAGE_RANGE;
+ double discharging_time = (1 / compute_slf_cap_discharging_rate()) * SLF_CAP_VOLTAGE_RANGE;
+
+ LOG(1, ("SN76477 '%s': SLF frequency (20,21): %.2f Hz\n", tag(), 1 / (charging_time + discharging_time)));
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': SLF frequency (20,21): N/A\n", tag()));
+ }
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': SLF frequency (20,21): External (cap = %.2fV)\n", tag(), m_slf_cap_voltage));
+ }
+}
+
+
+void sn76477_device::log_vco_pitch_voltage()
+{
+ LOG(1, ("SN76477 '%s': VCO pitch voltage (19): %.2fV\n", tag(), m_pitch_voltage));
+}
+
+
+void sn76477_device::log_vco_duty_cycle()
+{
+ LOG(1, ("SN76477 '%s': VCO duty cycle (16,19): %.0f%%\n", tag(), compute_vco_duty_cycle() * 100.0));
+}
+
+
+void sn76477_device::log_vco_freq()
+{
+ if (!m_vco_cap_voltage_ext)
+ {
+ if (compute_vco_cap_charging_discharging_rate() > 0)
+ {
+ double min_freq = compute_vco_cap_charging_discharging_rate() / (2 * VCO_CAP_VOLTAGE_RANGE);
+ double max_freq = compute_vco_cap_charging_discharging_rate() / (2 * VCO_TO_SLF_VOLTAGE_DIFF);
+
+ LOG(1, ("SN76477 '%s': VCO frequency (17,18): %.2f Hz - %.1f Hz\n", tag(), min_freq, max_freq));
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': VCO frequency (17,18): N/A\n", tag()));
+ }
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': VCO frequency (17,18): External (cap = %.2fV)\n", tag(), m_vco_cap_voltage));
+ }
+}
+
+
+void sn76477_device::log_vco_ext_voltage()
+{
+ if (m_vco_voltage <= VCO_MAX_EXT_VOLTAGE)
+ {
+ double min_freq = compute_vco_cap_charging_discharging_rate() / (2 * VCO_CAP_VOLTAGE_RANGE);
+ double max_freq = compute_vco_cap_charging_discharging_rate() / (2 * VCO_TO_SLF_VOLTAGE_DIFF);
+
+ LOG(1, ("SN76477 '%s': VCO ext. voltage (16): %.2fV (%.2f Hz)\n", tag(),
+ m_vco_voltage,
+ min_freq + ((max_freq - min_freq) * m_vco_voltage / VCO_MAX_EXT_VOLTAGE)));
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': VCO ext. voltage (16): %.2fV (saturated, no output)\n", tag(), m_vco_voltage));
+ }
+}
+
+
+void sn76477_device::log_noise_gen_freq()
+{
+ if (m_noise_clock_ext)
+ {
+ LOG(1, ("SN76477 '%s': Noise gen frequency (4): External\n", tag()));
+ }
+ else
+ {
+ if (compute_noise_gen_freq() > 0)
+ {
+ LOG(1, ("SN76477 '%s': Noise gen frequency (4): %d Hz\n", tag(), compute_noise_gen_freq()));
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': Noise gen frequency (4): N/A\n", tag()));
+ }
+ }
+}
+
+
+void sn76477_device::log_noise_filter_freq()
+{
+ if (!m_noise_filter_cap_voltage_ext)
+ {
+ double charging_rate = compute_noise_filter_cap_charging_rate();
+
+ if (charging_rate > 0)
+ {
+ if (charging_rate < 1000000.0)
+ {
+ double charging_time = (1 / charging_rate) * NOISE_CAP_VOLTAGE_RANGE;
+ double discharging_time = (1 / charging_rate) * NOISE_CAP_VOLTAGE_RANGE;
+
+ LOG(1, ("SN76477 '%s': Noise filter frequency (5,6): %.0f Hz\n", tag(), 1 / (charging_time + discharging_time)));
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': Noise filter frequency (5,6): Very Large (Filtering Disabled)\n", tag()));
+ }
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': Noise filter frequency (5,6): N/A\n", tag()));
+ }
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': Noise filter frequency (5,6): External (cap = %.2fV)\n", tag(), m_noise_filter_cap));
+ }
+}
+
+
+void sn76477_device::log_attack_time()
+{
+ if (!m_attack_decay_cap_voltage_ext)
+ {
+ if (compute_attack_decay_cap_charging_rate() > 0)
+ {
+ LOG(1, ("SN76477 '%s': Attack time (8,10): %.4f sec\n", tag(), AD_CAP_VOLTAGE_RANGE * (1 / compute_attack_decay_cap_charging_rate())));
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': Attack time (8,10): N/A\n", tag()));
+ }
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': Attack time (8,10): External (cap = %.2fV)\n", tag(), m_attack_decay_cap_voltage));
+ }
+}
+
+
+void sn76477_device::log_decay_time()
+{
+ if (!m_attack_decay_cap_voltage_ext)
+ {
+ if (compute_attack_decay_cap_discharging_rate() > 0)
+ {
+ LOG(1, ("SN76477 '%s': Decay time (7,8): %.4f sec\n", tag(), AD_CAP_VOLTAGE_RANGE * (1 / compute_attack_decay_cap_discharging_rate())));
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': Decay time (8,10): N/A\n", tag()));
+ }
+ }
+ else
+ {
+ LOG(1, ("SN76477 '%s': Decay time (7, 8): External (cap = %.2fV)\n", tag(), m_attack_decay_cap_voltage));
+ }
+}
+
+
+void sn76477_device::log_voltage_out()
+{
+ LOG(1, ("SN76477 '%s': Voltage OUT range (11,12): %.2fV - %.2fV (clips above %.2fV)\n",
+ tag(),
+ OUT_CENTER_LEVEL_VOLTAGE + compute_center_to_peak_voltage_out() * out_neg_gain[(int)(AD_CAP_VOLTAGE_MAX * 10)],
+ OUT_CENTER_LEVEL_VOLTAGE + compute_center_to_peak_voltage_out() * out_pos_gain[(int)(AD_CAP_VOLTAGE_MAX * 10)],
+ OUT_HIGH_CLIP_THRESHOLD));
+}
+
+
+void sn76477_device::log_complete_state()
+{
+ log_enable_line();
+ log_mixer_mode();
+ log_envelope_mode();
+ log_vco_mode();
+ log_one_shot_time();
+ log_slf_freq();
+ log_vco_freq();
+ log_vco_ext_voltage();
+ log_vco_pitch_voltage();
+ log_vco_duty_cycle();
+ log_noise_filter_freq();
+ log_noise_gen_freq();
+ log_attack_time();
+ log_decay_time();
+ log_voltage_out();
+}
+
+
+
+/*****************************************************************************
+ *
+ * .WAV file functions
+ *
+ *****************************************************************************/
+
+
+void sn76477_device::open_wav_file()
+{
+ char wav_file_name[30];
+
+ sprintf(wav_file_name, LOG_WAV_FILE_NAME, tag());
+ m_file = wav_open(wav_file_name, m_our_sample_rate, 2);
+
+ LOG(1, ("SN76477 '%s': Logging output: %s\n", tag(), wav_file_name));
+}
+
+
+void sn76477_device::close_wav_file()
+{
+ wav_close(m_file);
+}
+
+
+void sn76477_device::add_wav_data(INT16 data_l, INT16 data_r)
+{
+ wav_add_data_16lr(m_file, &data_l, &data_r, 1);
+}
+
+
+
+/*****************************************************************************
+ *
+ * Noise generator
+ *
+ *****************************************************************************/
+
+void sn76477_device::intialize_noise()
+{
+ m_rng = 0;
+}
+
+
+inline UINT32 sn76477_device::generate_next_real_noise_bit()
+{
+ UINT32 out = ((m_rng >> 28) & 1) ^ ((m_rng >> 0) & 1);
+
+ /* if bits 0-4 and 28 are all zero then force the output to 1 */
+ if ((m_rng & 0x1000001f) == 0)
+ {
+ out = 1;
+ }
+
+ m_rng = (m_rng >> 1) | (out << 30);
+
+ return out;
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set enable input
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_enable_w(UINT32 data)
+{
+ m_enable = data;
+
+ /* if falling edge */
+ if (!m_enable)
+ {
+ /* start the attack phase */
+ m_attack_decay_cap_voltage = AD_CAP_VOLTAGE_MIN;
+
+ /* one-shot runs regardless of envelope mode */
+ m_one_shot_running_ff = 1;
+ }
+}
+
+
+void sn76477_device::SN76477_test_enable_w(UINT32 data)
+{
+ if (data != m_enable)
+ {
+ m_channel->update();
+
+ _SN76477_enable_w(data);
+
+ log_enable_line();
+ }
+}
+
+
+WRITE_LINE_MEMBER( sn76477_device::enable_w )
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_BOOLEAN;
+
+ SN76477_test_enable_w(state);
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set mixer select inputs
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_mixer_a_w(UINT32 data)
+{
+ m_mixer_mode = (m_mixer_mode & ~0x01) | (data << 0);
+}
+
+
+WRITE_LINE_MEMBER( sn76477_device::mixer_a_w )
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_BOOLEAN;
+
+ if (state != ((m_mixer_mode >> 0) & 0x01))
+ {
+ m_channel->update();
+
+ _SN76477_mixer_a_w(state);
+
+ log_mixer_mode();
+ }
+#endif
+}
+
+
+void sn76477_device::_SN76477_mixer_b_w(UINT32 data)
+{
+ m_mixer_mode = (m_mixer_mode & ~0x02) | (data << 1);
+}
+
+
+WRITE_LINE_MEMBER( sn76477_device::mixer_b_w )
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_BOOLEAN;
+
+ if (state != ((m_mixer_mode >> 1) & 0x01))
+ {
+ m_channel->update();
+
+ _SN76477_mixer_b_w(state);
+
+ log_mixer_mode();
+ }
+#endif
+}
+
+
+void sn76477_device::_SN76477_mixer_c_w(UINT32 data)
+{
+ m_mixer_mode = (m_mixer_mode & ~0x04) | (data << 2);
+}
+
+
+WRITE_LINE_MEMBER( sn76477_device::mixer_c_w )
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_BOOLEAN;
+
+ if (state != ((m_mixer_mode >> 2) & 0x01))
+ {
+ m_channel->update();
+
+ _SN76477_mixer_c_w(state);
+
+ log_mixer_mode();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set envelope select inputs
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_envelope_1_w(UINT32 data)
+{
+ m_envelope_mode = (m_envelope_mode & ~0x01) | (data << 0);
+}
+
+
+WRITE_LINE_MEMBER( sn76477_device::envelope_1_w )
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_BOOLEAN;
+
+ if (state != ((m_envelope_mode >> 0) & 0x01))
+ {
+ m_channel->update();
+
+ _SN76477_envelope_1_w(state);
+
+ log_envelope_mode();
+ }
+#endif
+}
+
+
+void sn76477_device::_SN76477_envelope_2_w(UINT32 data)
+{
+ m_envelope_mode = (m_envelope_mode & ~0x02) | (data << 1);
+}
+
+
+WRITE_LINE_MEMBER( sn76477_device::envelope_2_w )
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_BOOLEAN;
+
+ if (state != ((m_envelope_mode >> 1) & 0x01))
+ {
+ m_channel->update();
+
+ _SN76477_envelope_2_w(state);
+
+ log_envelope_mode();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set VCO select input
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_vco_w(UINT32 data)
+{
+ m_vco_mode = data;
+}
+
+
+WRITE_LINE_MEMBER( sn76477_device::vco_w )
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_BOOLEAN;
+
+ if (state != m_vco_mode)
+ {
+ m_channel->update();
+
+ _SN76477_vco_w(state);
+
+ log_vco_mode();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set one-shot resistor
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_one_shot_res_w(double data)
+{
+ m_one_shot_res = data;
+}
+
+
+void sn76477_device::one_shot_res_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_POSITIVE;
+
+ if (data != m_one_shot_res)
+ {
+ m_channel->update();
+
+ _SN76477_one_shot_res_w(data);
+
+ log_one_shot_time();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set one-shot capacitor
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_one_shot_cap_w(double data)
+{
+ m_one_shot_cap = data;
+}
+
+
+void sn76477_device::one_shot_cap_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_POSITIVE;
+
+ if (data != m_one_shot_cap)
+ {
+ m_channel->update();
+
+ _SN76477_one_shot_cap_w(data);
+
+ log_one_shot_time();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set the voltage on the one-shot capacitor
+ *
+ *****************************************************************************/
+
+void sn76477_device::one_shot_cap_voltage_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_CAP_VOLTAGE;
+
+ if (data == SN76477_EXTERNAL_VOLTAGE_DISCONNECT)
+ {
+ /* switch to internal, if not already */
+ if (m_one_shot_cap_voltage_ext)
+ {
+ m_channel->update();
+
+ m_one_shot_cap_voltage_ext = 0;
+
+ log_one_shot_time();
+ }
+ }
+ else
+ {
+ /* set the voltage on the cap */
+ if (!m_one_shot_cap_voltage_ext || (data != m_one_shot_cap_voltage))
+ {
+ m_channel->update();
+
+ m_one_shot_cap_voltage_ext = 1;
+ m_one_shot_cap_voltage = data;
+
+ log_one_shot_time();
+ }
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set SLF resistor
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_slf_res_w(double data)
+{
+ m_slf_res = data;
+}
+
+
+void sn76477_device::slf_res_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_POSITIVE;
+
+ if (data != m_slf_res)
+ {
+ m_channel->update();
+
+ _SN76477_slf_res_w(data);
+
+ log_slf_freq();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set SLF capacitor
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_slf_cap_w(double data)
+{
+ m_slf_cap = data;
+}
+
+
+void sn76477_device::slf_cap_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_POSITIVE;
+
+ if (data != m_slf_cap)
+ {
+ m_channel->update();
+
+ _SN76477_slf_cap_w(data);
+
+ log_slf_freq();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set the voltage on the SLF capacitor
+ *
+ * This is an alternate way of controlling the VCO as described in the book
+ *
+ *****************************************************************************/
+
+void sn76477_device::slf_cap_voltage_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_CAP_VOLTAGE;
+
+ if (data == SN76477_EXTERNAL_VOLTAGE_DISCONNECT)
+ {
+ /* switch to internal, if not already */
+ if (m_slf_cap_voltage_ext)
+ {
+ m_channel->update();
+
+ m_slf_cap_voltage_ext = 0;
+
+ log_slf_freq();
+ }
+ }
+ else
+ {
+ /* set the voltage on the cap */
+ if (!m_slf_cap_voltage_ext || (data != m_slf_cap_voltage))
+ {
+ m_channel->update();
+
+ m_slf_cap_voltage_ext = 1;
+ m_slf_cap_voltage = data;
+
+ log_slf_freq();
+ }
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set VCO resistor
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_vco_res_w(double data)
+{
+ m_vco_res = data;
+}
+
+
+void sn76477_device::vco_res_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_POSITIVE;
+
+ if (data != m_vco_res)
+ {
+ m_channel->update();
+
+ _SN76477_vco_res_w(data);
+
+ log_vco_freq();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set VCO capacitor
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_vco_cap_w(double data)
+{
+ m_vco_cap = data;
+}
+
+
+void sn76477_device::vco_cap_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_POSITIVE;
+
+ if (data != m_vco_cap)
+ {
+ m_channel->update();
+
+ _SN76477_vco_cap_w(data);
+
+ log_vco_freq();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set the voltage on the VCO capacitor
+ *
+ *****************************************************************************/
+
+void sn76477_device::vco_cap_voltage_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_CAP_VOLTAGE;
+
+ if (data == SN76477_EXTERNAL_VOLTAGE_DISCONNECT)
+ {
+ /* switch to internal, if not already */
+ if (m_vco_cap_voltage_ext)
+ {
+ m_channel->update();
+
+ m_vco_cap_voltage_ext = 0;
+
+ log_vco_freq();
+ }
+ }
+ else
+ {
+ /* set the voltage on the cap */
+ if (!m_vco_cap_voltage_ext || (data != m_vco_cap_voltage))
+ {
+ m_channel->update();
+
+ m_vco_cap_voltage_ext = 1;
+ m_vco_cap_voltage = data;
+
+ log_vco_freq();
+ }
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set VCO voltage
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_vco_voltage_w(double data)
+{
+ m_vco_voltage = data;
+}
+
+
+void sn76477_device::vco_voltage_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_VOLTAGE;
+
+ if (data != m_vco_voltage)
+ {
+ m_channel->update();
+
+ _SN76477_vco_voltage_w(data);
+
+ log_vco_ext_voltage();
+ log_vco_duty_cycle();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set pitch voltage
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_pitch_voltage_w(double data)
+{
+ m_pitch_voltage = data;
+}
+
+
+void sn76477_device::pitch_voltage_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_VOLTAGE;
+
+ if (data != m_pitch_voltage)
+ {
+ m_channel->update();
+
+ _SN76477_pitch_voltage_w(data);
+
+ log_vco_pitch_voltage();
+ log_vco_duty_cycle();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set noise external clock
+ *
+ *****************************************************************************/
+
+WRITE_LINE_MEMBER( sn76477_device::noise_clock_w )
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_BOOLEAN;
+
+ if (state != m_noise_clock)
+ {
+ m_noise_clock = state;
+
+ /* on the rising edge shift generate next value,
+ if external control is enabled */
+ if (m_noise_clock && m_noise_clock_ext)
+ {
+ m_channel->update();
+
+ m_real_noise_bit_ff = generate_next_real_noise_bit();
+ }
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set noise clock resistor
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_noise_clock_res_w(double data)
+{
+ if (data == 0)
+ {
+ m_noise_clock_ext = 1;
+ }
+ else
+ {
+ m_noise_clock_ext = 0;
+
+ m_noise_clock_res = data;
+ }
+}
+
+
+void sn76477_device::noise_clock_res_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_POSITIVE;
+
+ if (((data == 0) && !m_noise_clock_ext) ||
+ ((data != 0) && (data != m_noise_clock_res)))
+ {
+ m_channel->update();
+
+ _SN76477_noise_clock_res_w(data);
+
+ log_noise_gen_freq();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set noise filter resistor
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_noise_filter_res_w(double data)
+{
+ m_noise_filter_res = data;
+}
+
+
+void sn76477_device::noise_filter_res_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_POSITIVE;
+
+ if (data != m_noise_filter_res)
+ {
+ m_channel->update();
+
+ _SN76477_noise_filter_res_w(data);
+
+ log_noise_filter_freq();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set noise filter capacitor
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_noise_filter_cap_w(double data)
+{
+ m_noise_filter_cap = data;
+}
+
+
+void sn76477_device::noise_filter_cap_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_POSITIVE;
+
+ if (data != m_noise_filter_cap)
+ {
+ m_channel->update();
+
+ _SN76477_noise_filter_cap_w(data);
+
+ log_noise_filter_freq();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set the voltage on the noise filter capacitor
+ *
+ *****************************************************************************/
+
+void sn76477_device::noise_filter_cap_voltage_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_CAP_VOLTAGE;
+
+ if (data == SN76477_EXTERNAL_VOLTAGE_DISCONNECT)
+ {
+ /* switch to internal, if not already */
+ if (m_noise_filter_cap_voltage_ext)
+ {
+ m_channel->update();
+
+ m_noise_filter_cap_voltage_ext = 0;
+
+ log_noise_filter_freq();
+ }
+ }
+ else
+ {
+ /* set the voltage on the cap */
+ if (!m_noise_filter_cap_voltage_ext || (data != m_noise_filter_cap_voltage))
+ {
+ m_channel->update();
+
+ m_noise_filter_cap_voltage_ext = 1;
+ m_noise_filter_cap_voltage = data;
+
+ log_noise_filter_freq();
+ }
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set attack resistor
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_attack_res_w(double data)
+{
+ m_attack_res = data;
+}
+
+
+void sn76477_device::attack_res_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_POSITIVE;
+
+ if (data != m_attack_res)
+ {
+ m_channel->update();
+
+ _SN76477_attack_res_w(data);
+
+ log_attack_time();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set decay resistor
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_decay_res_w(double data)
+{
+ m_decay_res = data;
+}
+
+
+void sn76477_device::decay_res_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_POSITIVE;
+
+ if (data != m_decay_res)
+ {
+ m_channel->update();
+
+ _SN76477_decay_res_w(data);
+
+ log_decay_time();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set attack/decay capacitor
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_attack_decay_cap_w(double data)
+{
+ m_attack_decay_cap = data;
+}
+
+
+void sn76477_device::attack_decay_cap_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_POSITIVE;
+
+ if (data != m_attack_decay_cap)
+ {
+ m_channel->update();
+
+ _SN76477_attack_decay_cap_w(data);
+
+ log_attack_time();
+ log_decay_time();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set the voltage on the attack/decay capacitor
+ *
+ *****************************************************************************/
+
+void sn76477_device::attack_decay_cap_voltage_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_CAP_VOLTAGE;
+
+ if (data == SN76477_EXTERNAL_VOLTAGE_DISCONNECT)
+ {
+ /* switch to internal, if not already */
+ if (m_attack_decay_cap_voltage_ext)
+ {
+ m_channel->update();
+
+ m_attack_decay_cap_voltage_ext = 0;
+
+ log_attack_time();
+ log_decay_time();
+ }
+ }
+ else
+ {
+ /* set the voltage on the cap */
+ if (!m_attack_decay_cap_voltage_ext || (data != m_attack_decay_cap_voltage))
+ {
+ m_channel->update();
+
+ m_attack_decay_cap_voltage_ext = 1;
+ m_attack_decay_cap_voltage = data;
+
+ log_attack_time();
+ log_decay_time();
+ }
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set amplitude resistor
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_amplitude_res_w(double data)
+{
+ m_amplitude_res = data;
+}
+
+
+void sn76477_device::amplitude_res_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_POSITIVE;
+
+ if (data != m_amplitude_res)
+ {
+ m_channel->update();
+
+ _SN76477_amplitude_res_w(data);
+
+ log_voltage_out();
+ }
+#endif
+}
+
+
+
+/*****************************************************************************
+ *
+ * Set feedback resistor
+ *
+ *****************************************************************************/
+
+void sn76477_device::_SN76477_feedback_res_w(double data)
+{
+ m_feedback_res = data;
+}
+
+
+void sn76477_device::feedback_res_w(double data)
+{
+#if TEST_MODE == 0
+ CHECK_CHIP_NUM_AND_POSITIVE;
+
+ if (data != m_feedback_res)
+ {
+ m_channel->update();
+
+ _SN76477_feedback_res_w(data);
+
+ log_voltage_out();
+ }
+#endif
+}
+
+
+/*****************************************************************************
+ *
+ * State saving
+ *
+ *****************************************************************************/
+
+void sn76477_device::state_save_register()
+{
+ save_item(NAME(m_enable));
+ save_item(NAME(m_envelope_mode));
+ save_item(NAME(m_vco_mode));
+ save_item(NAME(m_mixer_mode));
+
+ save_item(NAME(m_one_shot_res));
+ save_item(NAME(m_one_shot_cap));
+ save_item(NAME(m_one_shot_cap_voltage_ext));
+
+ save_item(NAME(m_slf_res));
+ save_item(NAME(m_slf_cap));
+ save_item(NAME(m_slf_cap_voltage_ext));
+
+ save_item(NAME(m_vco_voltage));
+ save_item(NAME(m_vco_res));
+ save_item(NAME(m_vco_cap));
+ save_item(NAME(m_vco_cap_voltage_ext));
+
+ save_item(NAME(m_noise_clock_res));
+ save_item(NAME(m_noise_clock_ext));
+ save_item(NAME(m_noise_clock));
+ save_item(NAME(m_noise_filter_res));
+ save_item(NAME(m_noise_filter_cap));
+ save_item(NAME(m_noise_filter_cap_voltage_ext));
+
+ save_item(NAME(m_attack_res));
+ save_item(NAME(m_decay_res));
+ save_item(NAME(m_attack_decay_cap));
+ save_item(NAME(m_attack_decay_cap_voltage_ext));
+
+ save_item(NAME(m_amplitude_res));
+ save_item(NAME(m_feedback_res));
+ save_item(NAME(m_pitch_voltage));
+
+ save_item(NAME(m_one_shot_cap_voltage));
+ save_item(NAME(m_one_shot_running_ff));
+
+ save_item(NAME(m_slf_cap_voltage));
+ save_item(NAME(m_slf_out_ff));
+
+ save_item(NAME(m_vco_cap_voltage));
+ save_item(NAME(m_vco_out_ff));
+ save_item(NAME(m_vco_alt_pos_edge_ff));
+
+ save_item(NAME(m_noise_filter_cap_voltage));
+ save_item(NAME(m_real_noise_bit_ff));
+ save_item(NAME(m_filtered_noise_bit_ff));
+ save_item(NAME(m_noise_gen_count));
+
+ save_item(NAME(m_attack_decay_cap_voltage));
+
+ save_item(NAME(m_rng));
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void sn76477_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ double one_shot_cap_charging_step;
+ double one_shot_cap_discharging_step;
+ double slf_cap_charging_step;
+ double slf_cap_discharging_step;
+ double vco_duty_cycle_multiplier;
+ double vco_cap_charging_step;
+ double vco_cap_discharging_step;
+ double vco_cap_voltage_max;
+ UINT32 noise_gen_freq;
+ double noise_filter_cap_charging_step;
+ double noise_filter_cap_discharging_step;
+ double attack_decay_cap_charging_step;
+ double attack_decay_cap_discharging_step;
+ int attack_decay_cap_charging;
+ double voltage_out;
+ double center_to_peak_voltage_out;
+
+ stream_sample_t *buffer = outputs[0];
+
+
+#if TEST_MODE
+ static int recursing = 0; /* we need to prevent recursion since enable_w calls machine().input().code_pressed_once(KEYCODE_SPACE->update */
+
+ if () && !recursing)
+ {
+ recursing = 1;
+
+ machine().sound().system_enable();
+ SN76477_test_enable_w(!m_enable);
+ }
+
+ recursing = 0;
+#endif
+
+ /* compute charging values, doing it here ensures that we always use the latest values */
+ one_shot_cap_charging_step = compute_one_shot_cap_charging_rate() / m_our_sample_rate;
+ one_shot_cap_discharging_step = compute_one_shot_cap_discharging_rate() / m_our_sample_rate;
+
+ slf_cap_charging_step = compute_slf_cap_charging_rate() / m_our_sample_rate;
+ slf_cap_discharging_step = compute_slf_cap_discharging_rate() / m_our_sample_rate;
+
+ vco_duty_cycle_multiplier = (1 - compute_vco_duty_cycle()) * 2;
+ vco_cap_charging_step = compute_vco_cap_charging_discharging_rate() / vco_duty_cycle_multiplier / m_our_sample_rate;
+ vco_cap_discharging_step = compute_vco_cap_charging_discharging_rate() * vco_duty_cycle_multiplier / m_our_sample_rate;
+
+ noise_filter_cap_charging_step = compute_noise_filter_cap_charging_rate() / m_our_sample_rate;
+ noise_filter_cap_discharging_step = compute_noise_filter_cap_discharging_rate() / m_our_sample_rate;
+ noise_gen_freq = compute_noise_gen_freq();
+
+ attack_decay_cap_charging_step = compute_attack_decay_cap_charging_rate() / m_our_sample_rate;
+ attack_decay_cap_discharging_step = compute_attack_decay_cap_discharging_rate() / m_our_sample_rate;
+
+ center_to_peak_voltage_out = compute_center_to_peak_voltage_out();
+
+
+ /* process 'samples' number of samples */
+ while (samples--)
+ {
+ /* update the one-shot cap voltage */
+ if (!m_one_shot_cap_voltage_ext)
+ {
+ if (m_one_shot_running_ff)
+ {
+ /* charging */
+ m_one_shot_cap_voltage = min(m_one_shot_cap_voltage + one_shot_cap_charging_step, ONE_SHOT_CAP_VOLTAGE_MAX);
+ }
+ else
+ {
+ /* discharging */
+ m_one_shot_cap_voltage = max(m_one_shot_cap_voltage - one_shot_cap_discharging_step, ONE_SHOT_CAP_VOLTAGE_MIN);
+ }
+ }
+
+ if (m_one_shot_cap_voltage >= ONE_SHOT_CAP_VOLTAGE_MAX)
+ {
+ m_one_shot_running_ff = 0;
+ }
+
+
+ /* update the SLF (super low frequency oscillator) */
+ if (!m_slf_cap_voltage_ext)
+ {
+ /* internal */
+ if (!m_slf_out_ff)
+ {
+ /* charging */
+ m_slf_cap_voltage = min(m_slf_cap_voltage + slf_cap_charging_step, SLF_CAP_VOLTAGE_MAX);
+ }
+ else
+ {
+ /* discharging */
+ m_slf_cap_voltage = max(m_slf_cap_voltage - slf_cap_discharging_step, SLF_CAP_VOLTAGE_MIN);
+ }
+ }
+
+ if (m_slf_cap_voltage >= SLF_CAP_VOLTAGE_MAX)
+ {
+ m_slf_out_ff = 1;
+ }
+ else if (m_slf_cap_voltage <= SLF_CAP_VOLTAGE_MIN)
+ {
+ m_slf_out_ff = 0;
+ }
+
+
+ /* update the VCO (voltage controlled oscillator) */
+ if (m_vco_mode)
+ {
+ /* VCO is controlled by SLF */
+ vco_cap_voltage_max = m_slf_cap_voltage + VCO_TO_SLF_VOLTAGE_DIFF;
+ }
+ else
+ {
+ /* VCO is controlled by external voltage */
+ vco_cap_voltage_max = m_vco_voltage + VCO_TO_SLF_VOLTAGE_DIFF;
+ }
+
+ if (!m_vco_cap_voltage_ext)
+ {
+ if (!m_vco_out_ff)
+ {
+ /* charging */
+ m_vco_cap_voltage = min(m_vco_cap_voltage + vco_cap_charging_step, vco_cap_voltage_max);
+ }
+ else
+ {
+ /* discharging */
+ m_vco_cap_voltage = max(m_vco_cap_voltage - vco_cap_discharging_step, VCO_CAP_VOLTAGE_MIN);
+ }
+ }
+
+ if (m_vco_cap_voltage >= vco_cap_voltage_max)
+ {
+ if (!m_vco_out_ff)
+ {
+ /* positive edge */
+ m_vco_alt_pos_edge_ff = !m_vco_alt_pos_edge_ff;
+ }
+
+ m_vco_out_ff = 1;
+ }
+ else if (m_vco_cap_voltage <= VCO_CAP_VOLTAGE_MIN)
+ {
+ m_vco_out_ff = 0;
+ }
+
+
+ /* update the noise generator */
+ while (!m_noise_clock_ext && (m_noise_gen_count <= noise_gen_freq))
+ {
+ m_noise_gen_count = m_noise_gen_count + m_our_sample_rate;
+
+ m_real_noise_bit_ff = generate_next_real_noise_bit();
+ }
+
+ m_noise_gen_count = m_noise_gen_count - noise_gen_freq;
+
+
+ /* update the noise filter */
+ if (!m_noise_filter_cap_voltage_ext)
+ {
+ /* internal */
+ if (m_real_noise_bit_ff)
+ {
+ /* charging */
+ m_noise_filter_cap_voltage = min(m_noise_filter_cap_voltage + noise_filter_cap_charging_step, NOISE_CAP_VOLTAGE_MAX);
+ }
+ else
+ {
+ /* discharging */
+ m_noise_filter_cap_voltage = max(m_noise_filter_cap_voltage - noise_filter_cap_discharging_step, NOISE_CAP_VOLTAGE_MIN);
+ }
+ }
+
+ /* check the thresholds */
+ if (m_noise_filter_cap_voltage >= NOISE_CAP_HIGH_THRESHOLD)
+ {
+ m_filtered_noise_bit_ff = 0;
+ }
+ else if (m_noise_filter_cap_voltage <= NOISE_CAP_LOW_THRESHOLD)
+ {
+ m_filtered_noise_bit_ff = 1;
+ }
+
+
+ /* based on the envelope mode figure out the attack/decay phase we are in */
+ switch (m_envelope_mode)
+ {
+ case 0: /* VCO */
+ attack_decay_cap_charging = m_vco_out_ff;
+ break;
+
+ case 1: /* one-shot */
+ attack_decay_cap_charging = m_one_shot_running_ff;
+ break;
+
+ case 2:
+ default: /* mixer only */
+ attack_decay_cap_charging = 1; /* never a decay phase */
+ break;
+
+ case 3: /* VCO with alternating polarity */
+ attack_decay_cap_charging = m_vco_out_ff && m_vco_alt_pos_edge_ff;
+ break;
+ }
+
+
+ /* update a/d cap voltage */
+ if (!m_attack_decay_cap_voltage_ext)
+ {
+ if (attack_decay_cap_charging)
+ {
+ if (attack_decay_cap_charging_step > 0)
+ {
+ m_attack_decay_cap_voltage = min(m_attack_decay_cap_voltage + attack_decay_cap_charging_step, AD_CAP_VOLTAGE_MAX);
+ }
+ else
+ {
+ /* no attack, voltage to max instantly */
+ m_attack_decay_cap_voltage = AD_CAP_VOLTAGE_MAX;
+ }
+ }
+ else
+ {
+ /* discharging */
+ if (attack_decay_cap_discharging_step > 0)
+ {
+ m_attack_decay_cap_voltage = max(m_attack_decay_cap_voltage - attack_decay_cap_discharging_step, AD_CAP_VOLTAGE_MIN);
+ }
+ else
+ {
+ /* no decay, voltage to min instantly */
+ m_attack_decay_cap_voltage = AD_CAP_VOLTAGE_MIN;
+ }
+ }
+ }
+
+
+ /* mix the output, if enabled, or not saturated by the VCO */
+ if (!m_enable && (m_vco_cap_voltage <= VCO_CAP_VOLTAGE_MAX))
+ {
+ UINT32 out;
+
+ /* enabled */
+ switch (m_mixer_mode)
+ {
+ case 0: /* VCO */
+ out = m_vco_out_ff;
+ break;
+
+ case 1: /* SLF */
+ out = m_slf_out_ff;
+ break;
+
+ case 2: /* noise */
+ out = m_filtered_noise_bit_ff;
+ break;
+
+ case 3: /* VCO and noise */
+ out = m_vco_out_ff & m_filtered_noise_bit_ff;
+ break;
+
+ case 4: /* SLF and noise */
+ out = m_slf_out_ff & m_filtered_noise_bit_ff;
+ break;
+
+ case 5: /* VCO, SLF and noise */
+ out = m_vco_out_ff & m_slf_out_ff & m_filtered_noise_bit_ff;
+ break;
+
+ case 6: /* VCO and SLF */
+ out = m_vco_out_ff & m_slf_out_ff;
+ break;
+
+ case 7: /* inhibit */
+ default:
+ out = 0;
+ break;
+ }
+
+ /* determine the OUT voltage from the attack/delay cap voltage and clip it */
+ if (out)
+ {
+ voltage_out = OUT_CENTER_LEVEL_VOLTAGE + center_to_peak_voltage_out * out_pos_gain[(int)(m_attack_decay_cap_voltage * 10)],
+ voltage_out = min(voltage_out, OUT_HIGH_CLIP_THRESHOLD);
+ }
+ else
+ {
+ voltage_out = OUT_CENTER_LEVEL_VOLTAGE + center_to_peak_voltage_out * out_neg_gain[(int)(m_attack_decay_cap_voltage * 10)],
+ voltage_out = max(voltage_out, OUT_LOW_CLIP_THRESHOLD);
+ }
+ }
+ else
+ {
+ /* disabled */
+ voltage_out = OUT_CENTER_LEVEL_VOLTAGE;
+ }
+
+
+ /* convert it to a signed 16-bit sample,
+ -32767 = OUT_LOW_CLIP_THRESHOLD
+ 0 = OUT_CENTER_LEVEL_VOLTAGE
+ 32767 = 2 * OUT_CENTER_LEVEL_VOLTAGE + OUT_LOW_CLIP_THRESHOLD
+
+ / Vout - Vmin \
+ sample = | ----------- - 1 | * 32767
+ \ Vcen - Vmin /
+ */
+ *buffer++ = (((voltage_out - OUT_LOW_CLIP_THRESHOLD) / (OUT_CENTER_LEVEL_VOLTAGE - OUT_LOW_CLIP_THRESHOLD)) - 1) * 32767;
+
+ if (LOG_WAV && LOG_WAV_ENABLED_ONLY && !m_enable)
+ {
+ INT16 log_data_l;
+ INT16 log_data_r;
+
+ switch (LOG_WAV_VALUE_L)
+ {
+ case 0:
+ log_data_l = LOG_WAV_GAIN_FACTOR * voltage_out;
+ log_data_r = LOG_WAV_GAIN_FACTOR * voltage_out;
+ break;
+ case 1:
+ log_data_l = LOG_WAV_GAIN_FACTOR * m_enable;
+ log_data_r = LOG_WAV_GAIN_FACTOR * m_enable;
+ break;
+ case 2:
+ log_data_l = LOG_WAV_GAIN_FACTOR * m_one_shot_cap_voltage;
+ log_data_r = LOG_WAV_GAIN_FACTOR * m_one_shot_cap_voltage;
+ break;
+ case 3:
+ log_data_l = LOG_WAV_GAIN_FACTOR * m_attack_decay_cap_voltage;
+ log_data_r = LOG_WAV_GAIN_FACTOR * m_attack_decay_cap_voltage;
+ break;
+ case 4:
+ log_data_l = LOG_WAV_GAIN_FACTOR * m_slf_cap_voltage;
+ log_data_r = LOG_WAV_GAIN_FACTOR * m_slf_cap_voltage;
+ break;
+ case 5:
+ log_data_l = LOG_WAV_GAIN_FACTOR * m_vco_cap_voltage;
+ log_data_r = LOG_WAV_GAIN_FACTOR * m_vco_cap_voltage;
+ break;
+ case 6:
+ log_data_l = LOG_WAV_GAIN_FACTOR * m_noise_filter_cap_voltage;
+ log_data_r = LOG_WAV_GAIN_FACTOR * m_noise_filter_cap_voltage;
+ break;
+ }
+
+ add_wav_data(log_data_l, log_data_r);
+ }
+ }
+}
diff --git a/src/devices/sound/sn76477.h b/src/devices/sound/sn76477.h
new file mode 100644
index 00000000000..392beb13489
--- /dev/null
+++ b/src/devices/sound/sn76477.h
@@ -0,0 +1,337 @@
+// license:BSD-3-Clause
+// copyright-holders:Zsolt Vasvari
+/*****************************************************************************
+
+ Texas Instruments SN76477 emulator
+
+ SN76477 pin layout. There is a corresponding interface variable with the
+ same name. The only exception is noise_clock which must be programmatically
+ set. The other pins have programmatic equivalents as well.
+ The name of the function is SN76477_<pinname>_w.
+ All capacitor functions can also specify a fixed voltage on the cap.
+ The name of this function is SN76477_<pinname>_voltage_w
+
+ +-------------------+
+ envelope_1 | 1 | | 28| envelope_2
+ | 2 GND - 27| mixer_c
+ noise_clock | 3 26| mixer_a
+ noise_clock_res | 4 25| mixer_b
+ noise_filter_res | 5 24| one_shot_res
+ noise_filter_cap | 6 23| one_shot_cap
+ decay_res | 7 22| vco
+ attack_decay_cap | 8 21| slf_cap
+ enable o| 9 20| slf_res
+ attack_res |10 19| pitch_voltage
+ amplitude_res |11 18| vco_res
+ feedback_res |12 17| vco_cap
+ |13 OUTPUT 16| vco_voltage
+ |14 Vcc +5V OUT 15|
+ +-------------------+
+
+ All resistor values in Ohms
+ All capacitor values in Farads
+ Use RES_K, RES_M and CAP_U, CAP_N, CAP_P macros in rescap.h to convert
+ magnitudes, eg. 220k = RES_K(220), 47nF = CAP_N(47)
+
+ *****************************************************************************/
+
+#pragma once
+
+#ifndef __SN76477_H__
+#define __SN76477_H__
+
+#include "machine/rescap.h"
+
+
+/*****************************************************************************
+ *
+ * Interface definition
+ *
+ *****************************************************************************/
+
+
+#define MCFG_SN76477_NOISE_PARAMS(_clock_res, _filter_res, _filter_cap) \
+ sn76477_device::set_noise_params(*device, _clock_res, _filter_res, _filter_cap);
+
+#define MCFG_SN76477_DECAY_RES(_decay_res) \
+ sn76477_device::set_decay_res(*device, _decay_res);
+
+#define MCFG_SN76477_ATTACK_PARAMS(_decay_cap, _res) \
+ sn76477_device::set_attack_params(*device, _decay_cap, _res);
+
+#define MCFG_SN76477_AMP_RES(_amp_res) \
+ sn76477_device::set_amp_res(*device, _amp_res);
+
+#define MCFG_SN76477_FEEDBACK_RES(_feedback_res) \
+ sn76477_device::set_feedack_res(*device, _feedback_res);
+
+#define MCFG_SN76477_VCO_PARAMS(_volt, _cap, _res) \
+ sn76477_device::set_vco_params(*device, _volt, _cap, _res);
+
+#define MCFG_SN76477_PITCH_VOLTAGE(_volt) \
+ sn76477_device::set_pitch_voltage(*device, _volt);
+
+#define MCFG_SN76477_SLF_PARAMS(_cap, _res) \
+ sn76477_device::set_slf_params(*device, _cap, _res);
+
+#define MCFG_SN76477_ONESHOT_PARAMS(_cap, _res) \
+ sn76477_device::set_oneshot_params(*device, _cap, _res);
+
+#define MCFG_SN76477_VCO_MODE(_mode) \
+ sn76477_device::set_vco_mode(*device, _mode);
+
+#define MCFG_SN76477_MIXER_PARAMS(_a, _b, _c) \
+ sn76477_device::set_mixer_params(*device, _a, _b, _c);
+
+#define MCFG_SN76477_ENVELOPE_PARAMS(_env1, _env2) \
+ sn76477_device::set_envelope_params(*device, _env1, _env2);
+
+#define MCFG_SN76477_ENABLE(_enable) \
+ sn76477_device::set_enable(*device, _enable);
+
+class sn76477_device : public device_t,
+ public device_sound_interface
+{
+public:
+ sn76477_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~sn76477_device() {}
+
+ static void set_noise_params(device_t &device, double clock_res, double filter_res, double filter_cap)
+ {
+ sn76477_device &dev = downcast<sn76477_device &>(device);
+ dev.m_noise_clock_res = clock_res;
+ dev.m_noise_filter_res = filter_res;
+ dev.m_noise_filter_cap = filter_cap;
+ }
+ static void set_decay_res(device_t &device, double decay_res) { downcast<sn76477_device &>(device).m_decay_res = decay_res; }
+ static void set_attack_params(device_t &device, double decay_cap, double res)
+ {
+ sn76477_device &dev = downcast<sn76477_device &>(device);
+ dev.m_attack_decay_cap = decay_cap;
+ dev.m_attack_res = res;
+ }
+ static void set_amp_res(device_t &device, double amp_res) { downcast<sn76477_device &>(device).m_amplitude_res = amp_res; }
+ static void set_feedack_res(device_t &device, double feedback_res) { downcast<sn76477_device &>(device).m_feedback_res = feedback_res; }
+ static void set_vco_params(device_t &device, double volt, double cap, double res)
+ {
+ sn76477_device &dev = downcast<sn76477_device &>(device);
+ dev.m_vco_voltage = volt;
+ dev.m_vco_cap = cap;
+ dev.m_vco_res = res;
+ }
+ static void set_pitch_voltage(device_t &device, double volt) { downcast<sn76477_device &>(device).m_pitch_voltage = volt; }
+ static void set_slf_params(device_t &device, double cap, double res)
+ {
+ sn76477_device &dev = downcast<sn76477_device &>(device);
+ dev.m_slf_cap = cap;
+ dev.m_slf_res = res;
+ }
+ static void set_oneshot_params(device_t &device, double cap, double res)
+ {
+ sn76477_device &dev = downcast<sn76477_device &>(device);
+ dev.m_one_shot_cap = cap;
+ dev.m_one_shot_res = res;
+ }
+ static void set_vco_mode(device_t &device, UINT32 mode) { downcast<sn76477_device &>(device).m_vco_mode = mode; }
+ static void set_mixer_params(device_t &device, UINT32 a, UINT32 b, UINT32 c)
+ {
+ sn76477_device &dev = downcast<sn76477_device &>(device);
+ dev.m_mixer_a = a;
+ dev.m_mixer_b = b;
+ dev.m_mixer_c = c;
+ }
+ static void set_envelope_params(device_t &device, UINT32 env1, UINT32 env2)
+ {
+ sn76477_device &dev = downcast<sn76477_device &>(device);
+ dev.m_envelope_1 = env1;
+ dev.m_envelope_2 = env2;
+ }
+ static void set_enable(device_t &device, UINT32 enable) { downcast<sn76477_device &>(device).m_enable = enable; }
+
+
+ /* these functions take 0 or 1 as a logic input */
+ WRITE_LINE_MEMBER( enable_w ); /* active LO, 0 = enabled, 1 = disabled */
+ WRITE_LINE_MEMBER( mixer_a_w );
+ WRITE_LINE_MEMBER( mixer_b_w );
+ WRITE_LINE_MEMBER( mixer_c_w );
+ WRITE_LINE_MEMBER( envelope_1_w );
+ WRITE_LINE_MEMBER( envelope_2_w );
+ WRITE_LINE_MEMBER( vco_w ); /* 0 = external, 1 = controlled by SLF */
+ WRITE_LINE_MEMBER( noise_clock_w ); /* noise clock write, if noise_clock_res = 0 */
+
+ /* these functions take a resistor value in Ohms */
+ void one_shot_res_w(double data);
+ void slf_res_w(double data);
+ void vco_res_w(double data);
+ void noise_clock_res_w(double data); /* = 0 if the noise gen is clocked via noise_clock */
+ void noise_filter_res_w(double data);
+ void decay_res_w(double data);
+ void attack_res_w(double data);
+ void amplitude_res_w(double data);
+ void feedback_res_w(double data);
+
+ /* these functions take a capacitor value in Farads or the voltage on it in Volts */
+ #define SN76477_EXTERNAL_VOLTAGE_DISCONNECT (-1.0) /* indicates that the voltage is internally computed,
+ can be used in all the functions that take a
+ voltage on a capacitor */
+ void one_shot_cap_w(double data);
+ void one_shot_cap_voltage_w(double data);
+ void slf_cap_w(double data);
+ void slf_cap_voltage_w(double data);
+ void vco_cap_w(double data);
+ void vco_cap_voltage_w(double data);
+ void noise_filter_cap_w(double data);
+ void noise_filter_cap_voltage_w(double data);
+ void attack_decay_cap_w(double data);
+ void attack_decay_cap_voltage_w(double data);
+
+ /* these functions take a voltage value in Volts */
+ void vco_voltage_w(double data);
+ void pitch_voltage_w(double data);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_stop();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ /* chip's external interface */
+ UINT32 m_enable;
+ UINT32 m_envelope_mode;
+ UINT32 m_vco_mode;
+ UINT32 m_mixer_mode;
+
+ double m_one_shot_res;
+ double m_one_shot_cap;
+ UINT32 m_one_shot_cap_voltage_ext;
+
+ double m_slf_res;
+ double m_slf_cap;
+ UINT32 m_slf_cap_voltage_ext;
+
+ double m_vco_voltage;
+ double m_vco_res;
+ double m_vco_cap;
+ UINT32 m_vco_cap_voltage_ext;
+
+ double m_noise_clock_res;
+ UINT32 m_noise_clock_ext;
+ UINT32 m_noise_clock;
+ double m_noise_filter_res;
+ double m_noise_filter_cap;
+ UINT32 m_noise_filter_cap_voltage_ext;
+
+ double m_attack_res;
+ double m_decay_res;
+ double m_attack_decay_cap;
+ UINT32 m_attack_decay_cap_voltage_ext;
+
+ double m_amplitude_res;
+ double m_feedback_res;
+ double m_pitch_voltage;
+
+ // internal state
+ double m_one_shot_cap_voltage; /* voltage on the one-shot cap */
+ UINT32 m_one_shot_running_ff; /* 1 = one-shot running, 0 = stopped */
+
+ double m_slf_cap_voltage; /* voltage on the SLF cap */
+ UINT32 m_slf_out_ff; /* output of the SLF */
+
+ double m_vco_cap_voltage; /* voltage on the VCO cap */
+ UINT32 m_vco_out_ff; /* output of the VCO */
+ UINT32 m_vco_alt_pos_edge_ff; /* keeps track of the # of positive edges for VCO Alt envelope */
+
+ double m_noise_filter_cap_voltage; /* voltage on the noise filter cap */
+ UINT32 m_real_noise_bit_ff; /* the current noise bit before filtering */
+ UINT32 m_filtered_noise_bit_ff; /* the noise bit after filtering */
+ UINT32 m_noise_gen_count; /* noise freq emulation */
+
+ double m_attack_decay_cap_voltage; /* voltage on the attack/decay cap */
+
+ UINT32 m_rng; /* current value of the random number generator */
+
+ // configured by the drivers and used to setup m_mixer_mode & m_envelope_mode at start
+ UINT32 m_mixer_a;
+ UINT32 m_mixer_b;
+ UINT32 m_mixer_c;
+ UINT32 m_envelope_1;
+ UINT32 m_envelope_2;
+
+ /* others */
+ sound_stream *m_channel; /* returned by stream_create() */
+ int m_our_sample_rate; /* from machine.sample_rate() */
+
+ wav_file *m_file; /* handle of the wave file to produce */
+
+ double compute_one_shot_cap_charging_rate();
+ double compute_one_shot_cap_discharging_rate();
+ double compute_slf_cap_charging_rate();
+ double compute_slf_cap_discharging_rate();
+ double compute_vco_cap_charging_discharging_rate();
+ double compute_vco_duty_cycle();
+ UINT32 compute_noise_gen_freq();
+ double compute_noise_filter_cap_charging_rate();
+ double compute_noise_filter_cap_discharging_rate();
+ double compute_attack_decay_cap_charging_rate();
+ double compute_attack_decay_cap_discharging_rate();
+ double compute_center_to_peak_voltage_out();
+
+ void log_enable_line();
+ void log_mixer_mode();
+ void log_envelope_mode();
+ void log_vco_mode();
+ void log_one_shot_time();
+ void log_slf_freq();
+ void log_vco_pitch_voltage();
+ void log_vco_duty_cycle();
+ void log_vco_freq();
+ void log_vco_ext_voltage();
+ void log_noise_gen_freq();
+ void log_noise_filter_freq();
+ void log_attack_time();
+ void log_decay_time();
+ void log_voltage_out();
+ void log_complete_state();
+
+ void open_wav_file();
+ void close_wav_file();
+ void add_wav_data(INT16 data_l, INT16 data_r);
+
+ void intialize_noise();
+ inline UINT32 generate_next_real_noise_bit();
+
+ void state_save_register();
+
+ void _SN76477_enable_w(UINT32 data);
+ void _SN76477_vco_w(UINT32 data);
+ void _SN76477_mixer_a_w(UINT32 data);
+ void _SN76477_mixer_b_w(UINT32 data);
+ void _SN76477_mixer_c_w(UINT32 data);
+ void _SN76477_envelope_1_w(UINT32 data);
+ void _SN76477_envelope_2_w(UINT32 data);
+ void _SN76477_one_shot_res_w(double data);
+ void _SN76477_one_shot_cap_w(double data);
+ void _SN76477_slf_res_w(double data);
+ void _SN76477_slf_cap_w(double data);
+ void _SN76477_vco_res_w(double data);
+ void _SN76477_vco_cap_w(double data);
+ void _SN76477_vco_voltage_w(double data);
+ void _SN76477_noise_clock_res_w(double data);
+ void _SN76477_noise_filter_res_w(double data);
+ void _SN76477_noise_filter_cap_w(double data);
+ void _SN76477_decay_res_w(double data);
+ void _SN76477_attack_res_w(double data);
+ void _SN76477_attack_decay_cap_w(double data);
+ void _SN76477_amplitude_res_w(double data);
+ void _SN76477_feedback_res_w(double data);
+ void _SN76477_pitch_voltage_w(double data);
+ void SN76477_test_enable_w(UINT32 data);
+};
+
+extern const device_type SN76477;
+
+
+#endif/* __SN76477_H__ */
diff --git a/src/devices/sound/sn76496.c b/src/devices/sound/sn76496.c
new file mode 100644
index 00000000000..7d0ccf50319
--- /dev/null
+++ b/src/devices/sound/sn76496.c
@@ -0,0 +1,441 @@
+// 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.
+
+ TODO: * Implement the TMS9919 - any difference to sn94624?
+ * Implement the T6W28; has registers in a weird order, needs writes
+ to be 'sanitized' first. Also is stereo, similar to game gear.
+ * Test the NCR7496; Smspower says the whitenoise taps are A and E,
+ but this needs verification on real hardware.
+ * Factor out common code so that the SAA1099 can share some code.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "sn76496.h"
+
+#define MAX_OUTPUT 0x7fff
+
+
+sn76496_base_device::sn76496_base_device(const machine_config &mconfig, device_type type, const char *name,
+ const char *tag, int feedbackmask, int noisetap1, int noisetap2, bool negate, bool stereo, int clockdivider, int freq0,
+ device_t *owner, UINT32 clock, const char *shortname, const char *source)
+
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ 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_freq0_is_max(freq0)
+{
+}
+
+sn76496_device::sn76496_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : sn76496_base_device(mconfig, SN76496, "SN76496", tag, 0x10000, 0x04, 0x08, false, false, 8, true, owner, clock, "sn76496", __FILE__)
+ { }
+u8106_device::u8106_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : sn76496_base_device(mconfig, U8106, "U8106", tag, 0x4000, 0x01, 0x02, true, false, 8, true, owner, clock, "u8106", __FILE__)
+ { }
+y2404_device::y2404_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : sn76496_base_device(mconfig, Y2404, "Y2404", tag, 0x10000, 0x04, 0x08, false, false, 8, true, owner, clock, "y2404", __FILE__)
+ { }
+sn76489_device::sn76489_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : sn76496_base_device(mconfig, SN76489, "SN76489", tag, 0x4000, 0x01, 0x02, true, false, 8, true, owner, clock, "sn76489", __FILE__)
+ { }
+sn76489a_device::sn76489a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : sn76496_base_device(mconfig, SN76489A, "SN76489A", tag, 0x10000, 0x04, 0x08, false, false, 8, true, owner, clock, "sn76489a", __FILE__)
+ { }
+sn76494_device::sn76494_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : sn76496_base_device(mconfig, SN76494, "SN76494", tag, 0x10000, 0x04, 0x08, false, false, 1, true, owner, clock, "sn76494", __FILE__)
+ { }
+sn94624_device::sn94624_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : sn76496_base_device(mconfig, SN94624, "SN94624", tag, 0x4000, 0x01, 0x02, true, false, 1, true, owner, clock, "sn94624", __FILE__)
+ { }
+ncr7496_device::ncr7496_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : sn76496_base_device(mconfig, NCR7496, "NCR7496", tag, 0x8000, 0x02, 0x20, false, false, 8, true, owner, clock, "ncr7496", __FILE__)
+ { }
+gamegear_device::gamegear_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : sn76496_base_device(mconfig, GAMEGEAR, "Game Gear PSG", tag, 0x8000, 0x01, 0x08, true, true, 8, false, owner, clock, "gamegear_psg", __FILE__)
+ { }
+segapsg_device::segapsg_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : sn76496_base_device(mconfig, SEGAPSG, "SEGA VDP PSG", tag, 0x8000, 0x01, 0x08, true, false, 8, false, owner, clock, "segapsg", __FILE__)
+ { }
+
+
+void sn76496_base_device::device_start()
+{
+ int sample_rate = clock()/2;
+ int i;
+ double out;
+ int gain;
+
+ m_ready_handler.resolve_safe();
+
+ m_sound = machine().sound().stream_alloc(*this, 0, (m_stereo? 2:1), sample_rate);
+
+ for (i = 0; i < 4; i++) m_volume[i] = 0;
+
+ m_last_register = 0;
+ for (i = 0; i < 8; i+=2)
+ {
+ m_register[i] = 0;
+ m_register[i + 1] = 0x0f; // volume = 0
+ }
+
+ 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 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_freq0_is_max)) 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] : NULL;
+
+ INT16 out = 0;
+ INT16 out2 = 0;
+
+ while (samples > 0)
+ {
+ // clock chip once
+ if (m_current_clock > 0) // not ready for new divided clock
+ {
+ m_current_clock--;
+ }
+ else // ready for new divided clock, make a new sample
+ {
+ m_current_clock = m_clock_divider-1;
+ // decrement Cycles to READY by one
+ countdown_cycles();
+
+ // handle channels 0,1,2
+ for (i = 0; i < 3; i++)
+ {
+ m_count[i]--;
+ if (m_count[i] <= 0)
+ {
+ m_output[i] ^= 1;
+ m_count[i] = m_period[i];
+ }
+ }
+
+ // handle channel 3
+ m_count[3]--;
+ if (m_count[3] <= 0)
+ {
+ // if noisemode is 1, both taps are enabled
+ // if noisemode is 0, the lower tap, whitenoisetap2, is held at 0
+ // The != was a bit-XOR (^) before
+ if (((m_RNG & m_whitenoise_tap1)!=0) != (((m_RNG & m_whitenoise_tap2)!=0) && in_noise_mode()))
+ {
+ m_RNG >>= 1;
+ m_RNG |= m_feedback_mask;
+ }
+ else
+ {
+ m_RNG >>= 1;
+ }
+ m_output[3] = m_RNG & 1;
+
+ m_count[3] = m_period[3];
+ }
+ }
+
+ if (m_stereo)
+ {
+ out = ((((m_stereo_mask & 0x10)!=0) && (m_output[0]!=0))? m_volume[0] : 0)
+ + ((((m_stereo_mask & 0x20)!=0) && (m_output[1]!=0))? m_volume[1] : 0)
+ + ((((m_stereo_mask & 0x40)!=0) && (m_output[2]!=0))? m_volume[2] : 0)
+ + ((((m_stereo_mask & 0x80)!=0) && (m_output[3]!=0))? m_volume[3] : 0);
+
+ out2= ((((m_stereo_mask & 0x1)!=0) && (m_output[0]!=0))? m_volume[0] : 0)
+ + ((((m_stereo_mask & 0x2)!=0) && (m_output[1]!=0))? m_volume[1] : 0)
+ + ((((m_stereo_mask & 0x4)!=0) && (m_output[2]!=0))? m_volume[2] : 0)
+ + ((((m_stereo_mask & 0x8)!=0) && (m_output[3]!=0))? m_volume[3] : 0);
+ }
+ else
+ {
+ out= ((m_output[0]!=0)? m_volume[0]:0)
+ +((m_output[1]!=0)? m_volume[1]:0)
+ +((m_output[2]!=0)? m_volume[2]:0)
+ +((m_output[3]!=0)? m_volume[3]:0);
+ }
+
+ if (m_negate) { out = -out; out2 = -out2; }
+
+ *(lbuffer++) = out;
+ if (m_stereo) *(rbuffer++) = out2;
+ samples--;
+ }
+}
+
+void sn76496_base_device::register_for_save_states()
+{
+ save_item(NAME(m_vol_table));
+ save_item(NAME(m_register));
+ save_item(NAME(m_last_register));
+ save_item(NAME(m_volume));
+ save_item(NAME(m_RNG));
+// save_item(NAME(m_clock_divider));
+ save_item(NAME(m_current_clock));
+// save_item(NAME(m_feedback_mask));
+// save_item(NAME(m_whitenoise_tap1));
+// save_item(NAME(m_whitenoise_tap2));
+// save_item(NAME(m_negate));
+// save_item(NAME(m_stereo));
+ save_item(NAME(m_stereo_mask));
+ save_item(NAME(m_period));
+ save_item(NAME(m_count));
+ save_item(NAME(m_output));
+ save_item(NAME(m_cycles_to_ready));
+// save_item(NAME(m_freq0_is_max));
+}
+
+const device_type SN76496 = &device_creator<sn76496_device>;
+const device_type U8106 = &device_creator<u8106_device>;
+const device_type Y2404 = &device_creator<y2404_device>;
+const device_type SN76489 = &device_creator<sn76489_device>;
+const device_type SN76489A = &device_creator<sn76489a_device>;
+const device_type SN76494 = &device_creator<sn76494_device>;
+const device_type SN94624 = &device_creator<sn94624_device>;
+const device_type NCR7496 = &device_creator<ncr7496_device>;
+const device_type GAMEGEAR = &device_creator<gamegear_device>;
+const device_type SEGAPSG = &device_creator<segapsg_device>;
diff --git a/src/devices/sound/sn76496.h b/src/devices/sound/sn76496.h
new file mode 100644
index 00000000000..bedee2de532
--- /dev/null
+++ b/src/devices/sound/sn76496.h
@@ -0,0 +1,144 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria
+#pragma once
+
+#ifndef __SN76496_H__
+#define __SN76496_H__
+
+
+extern const device_type SN76496;
+extern const device_type U8106;
+extern const device_type Y2404;
+extern const device_type SN76489;
+extern const device_type SN76489A;
+extern const device_type SN76494;
+extern const device_type SN94624;
+extern const device_type NCR7496;
+extern const device_type GAMEGEAR;
+extern const device_type SEGAPSG;
+
+#define MCFG_SN76496_READY_HANDLER(_devcb) \
+ devcb = &sn76496_base_device::set_ready_handler(*device, DEVCB_##_devcb);
+
+class sn76496_base_device : public device_t, public device_sound_interface
+{
+public:
+ sn76496_base_device(const machine_config &mconfig, device_type type, const char *name, const char *tag,
+ int feedbackmask, int noisetap1, int noisetap2, bool negate, bool stereo, int clockdivider, int freq0,
+ device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_ready_handler(device_t &device, _Object object) { return downcast<sn76496_base_device &>(device).m_ready_handler.set_callback(object); }
+
+ DECLARE_WRITE8_MEMBER( stereo_w );
+ void write(UINT8 data);
+ DECLARE_WRITE8_MEMBER( write );
+ DECLARE_READ_LINE_MEMBER( ready_r ) { return m_ready_state ? 1 : 0; }
+
+protected:
+ 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 m_feedback_mask; // mask for feedback
+ const INT32 m_whitenoise_tap1; // mask for white noise tap 1 (higher one, usually bit 14)
+ const INT32 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 m_clock_divider; // clock divider
+ const bool m_freq0_is_max; // flag for if frequency zero acts as if it is one more than max (0x3ff+1) or if it acts like 0
+
+ INT32 m_vol_table[16]; // volume table (for 4-bit to db conversion)
+ INT32 m_register[8]; // registers
+ INT32 m_last_register; // last register written
+ INT32 m_volume[4]; // db volume of voice 0-2 and noise
+ UINT32 m_RNG; // noise generator LFSR
+ INT32 m_current_clock;
+ INT32 m_stereo_mask; // the stereo output mask
+ INT32 m_period[4]; // Length of 1/2 of waveform
+ INT32 m_count[4]; // Position within the waveform
+ INT32 m_output[4]; // 1-bit output of each channel, pre-volume
+ INT32 m_cycles_to_ready; // number of cycles until the READY line goes active
+};
+
+// SN76496: Whitenoise verified, phase verified, periodic verified (by Michael Zapf)
+class sn76496_device : public sn76496_base_device
+{
+public:
+ sn76496_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 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 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 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 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 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 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 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 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 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 clock);
+};
+
+#endif /* __SN76496_H__ */
diff --git a/src/devices/sound/snkwave.c b/src/devices/sound/snkwave.c
new file mode 100644
index 00000000000..bc9cca6b59c
--- /dev/null
+++ b/src/devices/sound/snkwave.c
@@ -0,0 +1,153 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria
+/***************************************************************************
+
+ SNK Wave sound driver.
+
+ This is a very simple single-voice generator with a programmable waveform.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "snkwave.h"
+
+
+#define CLOCK_SHIFT 8
+
+
+const device_type SNKWAVE = &device_creator<snkwave_device>;
+
+//-------------------------------------------------
+// snkwave_device - constructor
+//-------------------------------------------------
+
+snkwave_device::snkwave_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, SNKWAVE, "SNK Wave", tag, owner, clock, "snkwave", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_external_clock(0),
+ m_sample_rate(0),
+ m_frequency(0),
+ m_counter(0),
+ m_waveform_position(0)
+{
+ memset(m_waveform, 0, sizeof(m_waveform));
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void snkwave_device::device_start()
+{
+ /* adjust internal clock */
+ m_external_clock = clock();
+
+ /* adjust output clock */
+ m_sample_rate = m_external_clock >> CLOCK_SHIFT;
+
+ /* get stream channels */
+ m_stream = stream_alloc(0, 1, m_sample_rate);
+
+ /* reset all the voices */
+ m_frequency = 0;
+ m_counter = 0;
+ m_waveform_position = 0;
+
+ /* register with the save state system */
+ save_item(NAME(m_frequency));
+ save_item(NAME(m_counter));
+ save_item(NAME(m_waveform_position));
+ save_pointer(NAME(m_waveform), SNKWAVE_WAVEFORM_LENGTH);
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests
+// for our sound stream
+//-------------------------------------------------
+
+void snkwave_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *buffer = outputs[0];
+
+ /* zap the contents of the buffer */
+ memset(buffer, 0, samples * sizeof(*buffer));
+
+ assert(m_counter < 0x1000);
+ assert(m_frequency < 0x1000);
+
+ /* if no sound, we're done */
+ if (m_frequency == 0xfff)
+ return;
+
+ /* generate sound into buffer while updating the counter */
+ while (samples-- > 0)
+ {
+ int loops;
+ INT16 out = 0;
+
+ loops = 1 << CLOCK_SHIFT;
+ while (loops > 0)
+ {
+ int steps = 0x1000 - m_counter;
+
+ if (steps <= loops)
+ {
+ out += m_waveform[m_waveform_position] * steps;
+ m_counter = m_frequency;
+ m_waveform_position = (m_waveform_position + 1) & (SNKWAVE_WAVEFORM_LENGTH-1);
+ loops -= steps;
+ }
+ else
+ {
+ out += m_waveform[m_waveform_position] * loops;
+ m_counter += loops;
+ loops = 0;
+ }
+ }
+
+ *buffer++ = out;
+ }
+}
+
+
+/* SNK wave register map
+ all registers are 6-bit
+ 0-1 frequency (12-bit)
+ 2-5 waveform (8 3-bit nibbles)
+*/
+
+WRITE8_MEMBER( snkwave_device::snkwave_w )
+{
+ m_stream->update();
+
+ // all registers are 6-bit
+ data &= 0x3f;
+
+ if (offset == 0)
+ m_frequency = (m_frequency & 0x03f) | (data << 6);
+ else if (offset == 1)
+ m_frequency = (m_frequency & 0xfc0) | data;
+ else if (offset <= 5)
+ update_waveform(offset - 2, data);
+}
+
+
+/* update the decoded waveform data */
+/* The programmable waveform consists of 8 3-bit nibbles.
+ The waveform goes to a 4-bit DAC and is played alternatingly forwards and
+ backwards.
+ When going forwards, bit 3 is 1. When going backwards, it's 0.
+ So the sequence 01234567 will play as
+ 89ABCDEF76543210
+*/
+void snkwave_device::update_waveform(unsigned int offset, UINT8 data)
+{
+ assert(offset < SNKWAVE_WAVEFORM_LENGTH/4);
+
+ m_waveform[offset * 2] = ((data & 0x38) >> 3) << (12-CLOCK_SHIFT);
+ m_waveform[offset * 2 + 1] = ((data & 0x07) >> 0) << (12-CLOCK_SHIFT);
+ m_waveform[SNKWAVE_WAVEFORM_LENGTH-2 - offset * 2] = ~m_waveform[offset * 2 + 1];
+ m_waveform[SNKWAVE_WAVEFORM_LENGTH-1 - offset * 2] = ~m_waveform[offset * 2];
+}
diff --git a/src/devices/sound/snkwave.h b/src/devices/sound/snkwave.h
new file mode 100644
index 00000000000..f613ca3ff5b
--- /dev/null
+++ b/src/devices/sound/snkwave.h
@@ -0,0 +1,64 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria
+#pragma once
+
+#ifndef __SNKWAVE_H__
+#define __SNKWAVE_H__
+
+#define SNKWAVE_WAVEFORM_LENGTH 16
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_SNKWAVE_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, SNKWAVE, _clock)
+#define MCFG_SNKWAVE_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, SNKWAVE, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+
+// ======================> snkwave_device
+
+class snkwave_device : public device_t,
+ public device_sound_interface
+{
+public:
+ snkwave_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~snkwave_device() { }
+
+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);
+
+public:
+ DECLARE_WRITE8_MEMBER( snkwave_w );
+
+private:
+ void update_waveform(unsigned int offset, UINT8 data);
+
+private:
+ sound_stream *m_stream;
+ int m_external_clock;
+ int m_sample_rate;
+
+ // data about the sound system
+ UINT32 m_frequency;
+ UINT32 m_counter;
+ int m_waveform_position;
+
+ // decoded waveform table
+ INT16 m_waveform[SNKWAVE_WAVEFORM_LENGTH];
+};
+
+extern const device_type SNKWAVE;
+
+
+#endif /* __SNKWAVE_H__ */
diff --git a/src/devices/sound/sp0250.c b/src/devices/sound/sp0250.c
new file mode 100644
index 00000000000..5bf37c4e924
--- /dev/null
+++ b/src/devices/sound/sp0250.c
@@ -0,0 +1,247 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/*
+ GI SP0250 digital LPC sound synthesizer
+
+ By O. Galibert.
+
+ Unknown:
+ - Exact clock divider
+ - Exact noise algorithm
+ - Exact noise pitch (probably ok)
+ - 7 bits output mapping
+ - Whether the pitch starts counting from 0 or 1
+
+ Unimplemented:
+ - Direct Data test mode (pin 7)
+
+ Sound quite reasonably already though.
+*/
+
+#include "emu.h"
+#include "sp0250.h"
+
+/*
+standard external clock is 3.12MHz
+the chip provides a 445.7kHz output clock, which is = 3.12MHz / 7
+therefore I expect the clock divider to be a multiple of 7
+Also there are 6 cascading filter stages so I expect the divider to be a multiple of 6.
+
+The SP0250 manual states that the original speech is sampled at 10kHz, so the divider
+should be 312, but 312 = 39*8 so it doesn't look right because a divider by 39 is unlikely.
+
+7*6*8 = 336 gives a 9.286kHz sample rate and matches the samples from the Sega boards.
+*/
+#define CLOCK_DIVIDER (7*6*8)
+
+const device_type SP0250 = &device_creator<sp0250_device>;
+
+sp0250_device::sp0250_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, SP0250, "SP0250", tag, owner, clock, "sp0250", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_amp(0),
+ m_pitch(0),
+ m_repeat(0),
+ m_pcount(0),
+ m_rcount(0),
+ m_playing(0),
+ m_RNG(0),
+ m_stream(NULL),
+ m_voiced(0),
+ m_fifo_pos(0),
+ m_drq(*this)
+{
+ for (int i = 0; i < 15; i++)
+ {
+ m_fifo[i] = 0;
+ }
+
+ for (int i = 0; i < 6; i++)
+ {
+ m_filter[i].F = 0;
+ m_filter[i].B = 0;
+ m_filter[i].z1 = 0;
+ m_filter[i].z2 = 0;
+ }
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void sp0250_device::device_start()
+{
+ m_RNG = 1;
+ m_drq.resolve_safe();
+ if (!m_drq.isnull())
+ {
+ m_drq( ASSERT_LINE);
+ machine().scheduler().timer_pulse(attotime::from_hz(clock()) * CLOCK_DIVIDER, timer_expired_delegate(FUNC(sp0250_device::timer_tick), this));
+ }
+
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / CLOCK_DIVIDER);
+
+ save_item(NAME(m_amp));
+ save_item(NAME(m_pitch));
+ save_item(NAME(m_repeat));
+ save_item(NAME(m_pcount));
+ save_item(NAME(m_rcount));
+ save_item(NAME(m_playing));
+ save_item(NAME(m_RNG));
+ save_item(NAME(m_voiced));
+ save_item(NAME(m_fifo));
+ save_item(NAME(m_fifo_pos));
+}
+
+static UINT16 sp0250_ga(UINT8 v)
+{
+ return (v & 0x1f) << (v>>5);
+}
+
+static INT16 sp0250_gc(UINT8 v)
+{
+ // Internal ROM to the chip, cf. manual
+ static const UINT16 coefs[128] =
+ {
+ 0, 9, 17, 25, 33, 41, 49, 57, 65, 73, 81, 89, 97, 105, 113, 121,
+ 129, 137, 145, 153, 161, 169, 177, 185, 193, 201, 203, 217, 225, 233, 241, 249,
+ 257, 265, 273, 281, 289, 297, 301, 305, 309, 313, 317, 321, 325, 329, 333, 337,
+ 341, 345, 349, 353, 357, 361, 365, 369, 373, 377, 381, 385, 389, 393, 397, 401,
+ 405, 409, 413, 417, 421, 425, 427, 429, 431, 433, 435, 437, 439, 441, 443, 445,
+ 447, 449, 451, 453, 455, 457, 459, 461, 463, 465, 467, 469, 471, 473, 475, 477,
+ 479, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495,
+ 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511
+ };
+ INT16 res = coefs[v & 0x7f];
+
+ if (!(v & 0x80))
+ res = -res;
+ return res;
+}
+
+void sp0250_device::load_values()
+{
+ int f;
+
+
+ m_filter[0].B = sp0250_gc(m_fifo[ 0]);
+ m_filter[0].F = sp0250_gc(m_fifo[ 1]);
+ m_amp = sp0250_ga(m_fifo[ 2]);
+ m_filter[1].B = sp0250_gc(m_fifo[ 3]);
+ m_filter[1].F = sp0250_gc(m_fifo[ 4]);
+ m_pitch = m_fifo[ 5];
+ m_filter[2].B = sp0250_gc(m_fifo[ 6]);
+ m_filter[2].F = sp0250_gc(m_fifo[ 7]);
+ m_repeat = m_fifo[ 8] & 0x3f;
+ m_voiced = m_fifo[ 8] & 0x40;
+ m_filter[3].B = sp0250_gc(m_fifo[ 9]);
+ m_filter[3].F = sp0250_gc(m_fifo[10]);
+ m_filter[4].B = sp0250_gc(m_fifo[11]);
+ m_filter[4].F = sp0250_gc(m_fifo[12]);
+ m_filter[5].B = sp0250_gc(m_fifo[13]);
+ m_filter[5].F = sp0250_gc(m_fifo[14]);
+ m_fifo_pos = 0;
+ m_drq(ASSERT_LINE);
+
+ m_pcount = 0;
+ m_rcount = 0;
+
+ for (f = 0; f < 6; f++)
+ m_filter[f].z1 = m_filter[f].z2 = 0;
+
+ m_playing = 1;
+}
+
+TIMER_CALLBACK_MEMBER( sp0250_device::timer_tick )
+{
+ m_stream->update();
+}
+
+WRITE8_MEMBER( sp0250_device::write )
+{
+ m_stream->update();
+ if (m_fifo_pos != 15)
+ {
+ m_fifo[m_fifo_pos++] = data;
+ if (m_fifo_pos == 15)
+ m_drq(CLEAR_LINE);
+ }
+ else
+ logerror("%s: overflow SP0250 FIFO\n", machine().describe_context());
+}
+
+
+UINT8 sp0250_device::drq_r()
+{
+ m_stream->update();
+ return (m_fifo_pos == 15) ? CLEAR_LINE : ASSERT_LINE;
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void sp0250_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *output = outputs[0];
+ int i;
+ for (i = 0; i < samples; i++)
+ {
+ if (m_playing)
+ {
+ INT16 z0;
+ int f;
+
+ if (m_voiced)
+ {
+ if(!m_pcount)
+ z0 = m_amp;
+ else
+ z0 = 0;
+ }
+ else
+ {
+ // Borrowing the ay noise generation LFSR
+ if(m_RNG & 1)
+ {
+ z0 = m_amp;
+ m_RNG ^= 0x24000;
+ }
+ else
+ z0 = -m_amp;
+
+ m_RNG >>= 1;
+ }
+
+ for (f = 0; f < 6; f++)
+ {
+ z0 += ((m_filter[f].z1 * m_filter[f].F) >> 8)
+ + ((m_filter[f].z2 * m_filter[f].B) >> 9);
+ m_filter[f].z2 = m_filter[f].z1;
+ m_filter[f].z1 = z0;
+ }
+
+ // Physical resolution is only 7 bits, but heh
+
+ // max amplitude is 0x0f80 so we have margin to push up the output
+ output[i] = z0 << 3;
+
+ m_pcount++;
+ if (m_pcount >= m_pitch)
+ {
+ m_pcount = 0;
+ m_rcount++;
+ if (m_rcount >= m_repeat)
+ m_playing = 0;
+ }
+ }
+ else
+ output[i] = 0;
+
+ if (!m_playing)
+ {
+ if(m_fifo_pos == 15)
+ load_values();
+ }
+ }
+}
diff --git a/src/devices/sound/sp0250.h b/src/devices/sound/sp0250.h
new file mode 100644
index 00000000000..846a193cfa2
--- /dev/null
+++ b/src/devices/sound/sp0250.h
@@ -0,0 +1,58 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+#pragma once
+
+#ifndef __SP0250_H__
+#define __SP0250_H__
+
+class sp0250_device : public device_t,
+ public device_sound_interface
+{
+public:
+ sp0250_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~sp0250_device() {}
+
+ template<class _Object> static devcb_base &set_drq_callback(device_t &device, _Object object) { return downcast<sp0250_device &>(device).m_drq.set_callback(object); }
+
+ DECLARE_WRITE8_MEMBER( write );
+ UINT8 drq_r();
+
+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:
+ // internal state
+ INT16 m_amp;
+ UINT8 m_pitch;
+ UINT8 m_repeat;
+ int m_pcount, m_rcount;
+ int m_playing;
+ UINT32 m_RNG;
+ sound_stream * m_stream;
+ int m_voiced;
+ UINT8 m_fifo[15];
+ int m_fifo_pos;
+ devcb_write_line m_drq;
+
+ struct
+ {
+ INT16 F, B;
+ INT16 z1, z2;
+ } m_filter[6];
+
+ void load_values();
+ TIMER_CALLBACK_MEMBER( timer_tick );
+};
+
+extern const device_type SP0250;
+
+#define MCFG_SP0250_DRQ_CALLBACK(_write) \
+ devcb = &sp0250_device::set_drq_callback(*device, DEVCB_##_write);
+
+
+
+#endif /* __SP0250_H__ */
diff --git a/src/devices/sound/sp0256.c b/src/devices/sound/sp0256.c
new file mode 100644
index 00000000000..31071bb247e
--- /dev/null
+++ b/src/devices/sound/sp0256.c
@@ -0,0 +1,1367 @@
+// license:GPL-2.0+
+// copyright-holders:Joseph Zbiciak,Tim Lindner
+/*
+ GI SP0256 Narrator Speech Processor
+ GI SPB640 Speech Buffer
+
+ By Joe Zbiciak. Ported to MESS by tim lindner.
+
+ Unimplemented:
+ - Microsequencer repeat count of zero
+ - Support for non bit-flipped ROMs
+ - SPB-640 perpherial/RAM bus
+
+ Copyright Joseph Zbiciak, all rights reserved.
+ Copyright tim lindner, all rights reserved.
+
+ - This source code is released as freeware for non-commercial purposes.
+ - You are free to use and redistribute this code in modified or
+ unmodified form, provided you list us in the credits.
+ - If you modify this source code, you must add a notice to each
+ modified source file that it has been changed. If you're a nice
+ person, you will clearly mark each change too. :)
+ - If you wish to use this for commercial purposes, please contact us at
+ intvnut@gmail.com (Joseph Zbiciak), tlindner@macmess.org (tim lindner)
+ - This entire notice must remain in the source code.
+
+ Note: Bit flipping.
+ This emulation flips the bits on every byte of the memory map during
+ the sp0256_start() call.
+
+ If the memory map contents is modified during execution (accross of ROM
+ bank switching) the bitrevbuff() call must be called after the section
+ of ROM is modified.
+*/
+
+#include "emu.h"
+#include "sp0256.h"
+
+#define CLOCK_DIVIDER (7*6*8)
+#define HIGH_QUALITY
+
+#define SCBUF_SIZE (4096) /* Must be power of 2 */
+#define SCBUF_MASK (SCBUF_SIZE - 1)
+#define PER_PAUSE (64) /* Equiv timing period for pauses. */
+#define PER_NOISE (64) /* Equiv timing period for noise. */
+
+#define FIFO_ADDR (0x1800 << 3) /* SP0256 address of SPB260 speech FIFO. */
+
+#define VERBOSE 0
+#define DEBUG_FIFO 0
+
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+#define LOG_FIFO(x) do { if (DEBUG_FIFO) logerror x; } while (0)
+
+#define SET_SBY(line_state) { \
+ if (m_sby_line != line_state) \
+ { \
+ m_sby_line = line_state; \
+ m_sby_cb(m_sby_line); \
+ } \
+}
+
+/* ======================================================================== */
+/* qtbl -- Coefficient Quantization Table. This comes from a */
+/* SP0250 data sheet, and should be correct for SP0256. */
+/* ======================================================================== */
+static const INT16 qtbl[128] =
+{
+ 0, 9, 17, 25, 33, 41, 49, 57,
+ 65, 73, 81, 89, 97, 105, 113, 121,
+ 129, 137, 145, 153, 161, 169, 177, 185,
+ 193, 201, 209, 217, 225, 233, 241, 249,
+ 257, 265, 273, 281, 289, 297, 301, 305,
+ 309, 313, 317, 321, 325, 329, 333, 337,
+ 341, 345, 349, 353, 357, 361, 365, 369,
+ 373, 377, 381, 385, 389, 393, 397, 401,
+ 405, 409, 413, 417, 421, 425, 427, 429,
+ 431, 433, 435, 437, 439, 441, 443, 445,
+ 447, 449, 451, 453, 455, 457, 459, 461,
+ 463, 465, 467, 469, 471, 473, 475, 477,
+ 479, 481, 482, 483, 484, 485, 486, 487,
+ 488, 489, 490, 491, 492, 493, 494, 495,
+ 496, 497, 498, 499, 500, 501, 502, 503,
+ 504, 505, 506, 507, 508, 509, 510, 511
+};
+
+
+
+// device type definition
+const device_type SP0256 = &device_creator<sp0256_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+sp0256_device::sp0256_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, SP0256, "SP0256", tag, owner, clock, "sp0256", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_rom(*this, DEVICE_SELF),
+ m_drq_cb(*this),
+ m_sby_cb(*this)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void sp0256_device::device_start()
+{
+ m_drq_cb.resolve_safe();
+ m_sby_cb.resolve_safe();
+ m_drq_cb(1);
+ m_sby_cb(1);
+
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / CLOCK_DIVIDER);
+
+ /* -------------------------------------------------------------------- */
+ /* Configure our internal variables. */
+ /* -------------------------------------------------------------------- */
+ m_filt.rng = 1;
+
+ /* -------------------------------------------------------------------- */
+ /* Allocate a scratch buffer for generating ~10kHz samples. */
+ /* -------------------------------------------------------------------- */
+ m_scratch = auto_alloc_array(machine(), INT16, SCBUF_SIZE);
+ save_pointer(NAME(m_scratch), SCBUF_SIZE);
+
+ m_sc_head = m_sc_tail = 0;
+
+ /* -------------------------------------------------------------------- */
+ /* Set up the microsequencer's initial state. */
+ /* -------------------------------------------------------------------- */
+ m_halted = 1;
+ m_filt.rpt = -1;
+ m_lrq = 0x8000;
+ m_page = 0x1000 << 3;
+ m_silent = 1;
+
+ /* -------------------------------------------------------------------- */
+ /* Setup the ROM. */
+ /* -------------------------------------------------------------------- */
+ // the rom is not supposed to be reversed first; according to Joe Zbiciak.
+ // see http://forums.bannister.org/ubbthreads.php?ubb=showflat&Number=72385#Post72385
+ // TODO: because of this, check if the bitrev functions are even used anywhere else
+ // bitrevbuff(m_rom, 0, 0xffff);
+
+ m_lrq_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(sp0256_device::set_lrq_timer_proc),this));
+
+ // save device variables
+ save_item(NAME(m_sby_line));
+ save_item(NAME(m_cur_len));
+ save_item(NAME(m_silent));
+ save_item(NAME(m_sc_head));
+ save_item(NAME(m_sc_tail));
+ save_item(NAME(m_lrq));
+ save_item(NAME(m_ald));
+ save_item(NAME(m_pc));
+ save_item(NAME(m_stack));
+ save_item(NAME(m_fifo_sel));
+ save_item(NAME(m_halted));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_page));
+ save_item(NAME(m_fifo_head));
+ save_item(NAME(m_fifo_tail));
+ save_item(NAME(m_fifo_bitp));
+ save_item(NAME(m_fifo));
+ // save filter variables
+ save_item(NAME(m_filt.rpt));
+ save_item(NAME(m_filt.cnt));
+ save_item(NAME(m_filt.per));
+ save_item(NAME(m_filt.rng));
+ save_item(NAME(m_filt.amp));
+ save_item(NAME(m_filt.f_coef));
+ save_item(NAME(m_filt.b_coef));
+ save_item(NAME(m_filt.z_data));
+ save_item(NAME(m_filt.r));
+ save_item(NAME(m_filt.interp));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void sp0256_device::device_reset()
+{
+ // reset FIFO and SP0256
+ m_fifo_head = m_fifo_tail = m_fifo_bitp = 0;
+
+ memset(&m_filt, 0, sizeof(m_filt));
+ m_halted = 1;
+ m_filt.rpt = -1;
+ m_filt.rng = 1;
+ m_lrq = 0x8000;
+ m_ald = 0x0000;
+ m_pc = 0x0000;
+ m_stack = 0x0000;
+ m_fifo_sel = 0;
+ m_mode = 0;
+ m_page = 0x1000 << 3;
+ m_silent = 1;
+ m_sby_line = 0;
+ m_drq_cb(1);
+ SET_SBY(1)
+
+ m_lrq = 0;
+ m_lrq_timer->adjust(attotime::from_ticks(50, m_clock));
+}
+
+
+
+/* ======================================================================== */
+/* LIMIT -- Limiter function for digital sample output. */
+/* ======================================================================== */
+INLINE INT16 limit(INT16 s)
+{
+#ifdef HIGH_QUALITY /* Higher quality than the original, but who cares? */
+ if (s > 8191) return 8191;
+ if (s < -8192) return -8192;
+#else
+ if (s > 127) return 127;
+ if (s < -128) return -128;
+#endif
+ return s;
+}
+
+/* ======================================================================== */
+/* LPC12_UPDATE -- Update the 12-pole filter, outputting samples. */
+/* ======================================================================== */
+INLINE int lpc12_update(struct lpc12_t *f, int num_samp, INT16 *out, UINT32 *optr)
+{
+ int i, j;
+ INT16 samp;
+ int do_int;
+ int oidx = *optr;
+
+ /* -------------------------------------------------------------------- */
+ /* Iterate up to the desired number of samples. We actually may */
+ /* break out early if our repeat count expires. */
+ /* -------------------------------------------------------------------- */
+ for (i = 0; i < num_samp; i++)
+ {
+ /* ---------------------------------------------------------------- */
+ /* Generate a series of periodic impulses, or random noise. */
+ /* ---------------------------------------------------------------- */
+ do_int = 0;
+ samp = 0;
+ if (f->per)
+ {
+ if (f->cnt <= 0)
+ {
+ f->cnt += f->per;
+ samp = f->amp;
+ f->rpt--;
+ do_int = f->interp;
+
+ for (j = 0; j < 6; j++)
+ f->z_data[j][1] = f->z_data[j][0] = 0;
+
+ } else
+ {
+ samp = 0;
+ f->cnt--;
+ }
+
+ } else
+ {
+ int bit;
+
+ if (--f->cnt <= 0)
+ {
+ do_int = f->interp;
+ f->cnt = PER_NOISE;
+ f->rpt--;
+ for (j = 0; j < 6; j++)
+ f->z_data[j][0] = f->z_data[j][1] = 0;
+ }
+
+ bit = f->rng & 1;
+ f->rng = (f->rng >> 1) ^ (bit ? 0x4001 : 0);
+
+ if (bit) { samp = f->amp; }
+ else { samp = -f->amp; }
+ }
+
+ /* ---------------------------------------------------------------- */
+ /* If we need to, process the interpolation registers. */
+ /* ---------------------------------------------------------------- */
+ if (do_int)
+ {
+ f->r[0] += f->r[14];
+ f->r[1] += f->r[15];
+
+ f->amp = (f->r[0] & 0x1F) << (((f->r[0] & 0xE0) >> 5) + 0);
+ f->per = f->r[1];
+
+ do_int = 0;
+ }
+
+ /* ---------------------------------------------------------------- */
+ /* Stop if we expire our repeat counter and return the actual */
+ /* number of samples we did. */
+ /* ---------------------------------------------------------------- */
+ if (f->rpt <= 0) break;
+
+ /* ---------------------------------------------------------------- */
+ /* Each 2nd order stage looks like one of these. The App. Manual */
+ /* gives the first form, the patent gives the second form. */
+ /* They're equivalent except for time delay. I implement the */
+ /* first form. (Note: 1/Z == 1 unit of time delay.) */
+ /* */
+ /* ---->(+)-------->(+)----------+-------> */
+ /* ^ ^ | */
+ /* | | | */
+ /* | | | */
+ /* [B] [2*F] | */
+ /* ^ ^ | */
+ /* | | | */
+ /* | | | */
+ /* +---[1/Z]<--+---[1/Z]<--+ */
+ /* */
+ /* */
+ /* +---[2*F]<---+ */
+ /* | | */
+ /* | | */
+ /* v | */
+ /* ---->(+)-->[1/Z]-->+-->[1/Z]---+------> */
+ /* ^ | */
+ /* | | */
+ /* | | */
+ /* +-----------[B]<---------+ */
+ /* */
+ /* ---------------------------------------------------------------- */
+ for (j = 0; j < 6; j++)
+ {
+ samp += (((int)f->b_coef[j] * (int)f->z_data[j][1]) >> 9);
+ samp += (((int)f->f_coef[j] * (int)f->z_data[j][0]) >> 8);
+
+ f->z_data[j][1] = f->z_data[j][0];
+ f->z_data[j][0] = samp;
+ }
+
+#ifdef HIGH_QUALITY /* Higher quality than the original, but who cares? */
+ out[oidx++ & SCBUF_MASK] = limit(samp) << 2;
+#else
+ out[oidx++ & SCBUF_MASK] = (limit(samp >> 4) << 8);
+#endif
+ }
+
+ *optr = oidx;
+
+ return i;
+}
+
+static const int stage_map[6] = { 0, 1, 2, 3, 4, 5 };
+
+/* ======================================================================== */
+/* LPC12_REGDEC -- Decode the register set in the filter bank. */
+/* ======================================================================== */
+INLINE void lpc12_regdec(struct lpc12_t *f)
+{
+ int i;
+
+ /* -------------------------------------------------------------------- */
+ /* Decode the Amplitude and Period registers. Force the 'cnt' to 0 */
+ /* to get an initial impulse. We compensate elsewhere by setting */
+ /* the repeat count to "repeat + 1". */
+ /* -------------------------------------------------------------------- */
+ f->amp = (f->r[0] & 0x1F) << (((f->r[0] & 0xE0) >> 5) + 0);
+ f->cnt = 0;
+ f->per = f->r[1];
+
+ /* -------------------------------------------------------------------- */
+ /* Decode the filter coefficients from the quant table. */
+ /* -------------------------------------------------------------------- */
+ for (i = 0; i < 6; i++)
+ {
+ #define IQ(x) (((x) & 0x80) ? qtbl[0x7F & -(x)] : -qtbl[(x)])
+
+ f->b_coef[stage_map[i]] = IQ(f->r[2 + 2*i]);
+ f->f_coef[stage_map[i]] = IQ(f->r[3 + 2*i]);
+ }
+
+ /* -------------------------------------------------------------------- */
+ /* Set the Interp flag based on whether we have interpolation parms */
+ /* -------------------------------------------------------------------- */
+ f->interp = f->r[14] || f->r[15];
+
+ return;
+}
+
+/* ======================================================================== */
+/* SP0256_DATAFMT -- Data format table for the SP0256's microsequencer */
+/* */
+/* len 4 bits Length of field to extract */
+/* lshift 4 bits Left-shift amount on field */
+/* param 4 bits Parameter number being updated */
+/* delta 1 bit This is a delta-update. (Implies sign-extend) */
+/* field 1 bit This is a field replace. */
+/* clr5 1 bit Clear F5, B5. */
+/* clrall 1 bit Clear all before doing this update */
+/* ======================================================================== */
+
+#define CR(l,s,p,d,f,c5,ca) \
+ ( \
+ (((l) & 15) << 0) | \
+ (((s) & 15) << 4) | \
+ (((p) & 15) << 8) | \
+ (((d) & 1) << 12) | \
+ (((f) & 1) << 13) | \
+ (((c5) & 1) << 14) | \
+ (((ca) & 1) << 15) \
+ )
+
+#define CR_DELTA CR(0,0,0,1,0,0,0)
+#define CR_FIELD CR(0,0,0,0,1,0,0)
+#define CR_CLR5 CR(0,0,0,0,0,1,0)
+#define CR_CLRA CR(0,0,0,0,0,0,1)
+#define CR_LEN(x) ((x) & 15)
+#define CR_SHF(x) (((x) >> 4) & 15)
+#define CR_PRM(x) (((x) >> 8) & 15)
+
+enum { AM = 0, PR, B0, F0, B1, F1, B2, F2, B3, F3, B4, F4, B5, F5, IA, IP };
+
+static const UINT16 sp0256_datafmt[] =
+{
+ /* -------------------------------------------------------------------- */
+ /* OPCODE 1111: PAUSE */
+ /* -------------------------------------------------------------------- */
+ /* 0 */ CR( 0, 0, 0, 0, 0, 0, 1), /* Clear all */
+
+ /* -------------------------------------------------------------------- */
+ /* Opcode 0001: LOADALL */
+ /* -------------------------------------------------------------------- */
+ /* All modes */
+ /* 1 */ CR( 8, 0, AM, 0, 0, 0, 1), /* Amplitude */
+ /* 2 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */
+ /* 3 */ CR( 8, 0, B0, 0, 0, 0, 0), /* B0 */
+ /* 4 */ CR( 8, 0, F0, 0, 0, 0, 0), /* F0 */
+ /* 5 */ CR( 8, 0, B1, 0, 0, 0, 0), /* B1 */
+ /* 6 */ CR( 8, 0, F1, 0, 0, 0, 0), /* F1 */
+ /* 7 */ CR( 8, 0, B2, 0, 0, 0, 0), /* B2 */
+ /* 8 */ CR( 8, 0, F2, 0, 0, 0, 0), /* F2 */
+ /* 9 */ CR( 8, 0, B3, 0, 0, 0, 0), /* B3 */
+ /* 10 */ CR( 8, 0, F3, 0, 0, 0, 0), /* F3 */
+ /* 11 */ CR( 8, 0, B4, 0, 0, 0, 0), /* B4 */
+ /* 12 */ CR( 8, 0, F4, 0, 0, 0, 0), /* F4 */
+ /* 13 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */
+ /* 14 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */
+ /* Mode 01 and 11 only */
+ /* 15 */ CR( 8, 0, IA, 0, 0, 0, 0), /* Amp Interp */
+ /* 16 */ CR( 8, 0, IP, 0, 0, 0, 0), /* Pit Interp */
+
+ /* -------------------------------------------------------------------- */
+ /* Opcode 0100: LOAD_4 */
+ /* -------------------------------------------------------------------- */
+ /* Mode 00 and 01 */
+ /* 17 */ CR( 6, 2, AM, 0, 0, 0, 1), /* Amplitude */
+ /* 18 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */
+ /* 19 */ CR( 4, 3, B3, 0, 0, 0, 0), /* B3 (S=0) */
+ /* 20 */ CR( 6, 2, F3, 0, 0, 0, 0), /* F3 */
+ /* 21 */ CR( 7, 1, B4, 0, 0, 0, 0), /* B4 */
+ /* 22 */ CR( 6, 2, F4, 0, 0, 0, 0), /* F4 */
+ /* Mode 01 only */
+ /* 23 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */
+ /* 24 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */
+
+ /* Mode 10 and 11 */
+ /* 25 */ CR( 6, 2, AM, 0, 0, 0, 1), /* Amplitude */
+ /* 26 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */
+ /* 27 */ CR( 6, 1, B3, 0, 0, 0, 0), /* B3 (S=0) */
+ /* 28 */ CR( 7, 1, F3, 0, 0, 0, 0), /* F3 */
+ /* 29 */ CR( 8, 0, B4, 0, 0, 0, 0), /* B4 */
+ /* 30 */ CR( 8, 0, F4, 0, 0, 0, 0), /* F4 */
+ /* Mode 11 only */
+ /* 31 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */
+ /* 32 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */
+
+ /* -------------------------------------------------------------------- */
+ /* Opcode 0110: SETMSB_6 */
+ /* -------------------------------------------------------------------- */
+ /* Mode 00 only */
+ /* 33 */ CR( 0, 0, 0, 0, 0, 1, 0), /* Clear 5 */
+ /* Mode 00 and 01 */
+ /* 34 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */
+ /* 35 */ CR( 6, 2, F3, 0, 1, 0, 0), /* F3 (5 MSBs) */
+ /* 36 */ CR( 6, 2, F4, 0, 1, 0, 0), /* F4 (5 MSBs) */
+ /* Mode 01 only */
+ /* 37 */ CR( 8, 0, F5, 0, 1, 0, 0), /* F5 (5 MSBs) */
+
+ /* Mode 10 only */
+ /* 38 */ CR( 0, 0, 0, 0, 0, 1, 0), /* Clear 5 */
+ /* Mode 10 and 11 */
+ /* 39 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */
+ /* 40 */ CR( 7, 1, F3, 0, 1, 0, 0), /* F3 (6 MSBs) */
+ /* 41 */ CR( 8, 0, F4, 0, 1, 0, 0), /* F4 (6 MSBs) */
+ /* Mode 11 only */
+ /* 42 */ CR( 8, 0, F5, 0, 1, 0, 0), /* F5 (6 MSBs) */
+
+ /* 43 */ 0, /* unused */
+ /* 44 */ 0, /* unused */
+
+ /* -------------------------------------------------------------------- */
+ /* Opcode 1001: DELTA_9 */
+ /* -------------------------------------------------------------------- */
+ /* Mode 00 and 01 */
+ /* 45 */ CR( 4, 2, AM, 1, 0, 0, 0), /* Amplitude */
+ /* 46 */ CR( 5, 0, PR, 1, 0, 0, 0), /* Period */
+ /* 47 */ CR( 3, 4, B0, 1, 0, 0, 0), /* B0 4 MSBs */
+ /* 48 */ CR( 3, 3, F0, 1, 0, 0, 0), /* F0 5 MSBs */
+ /* 49 */ CR( 3, 4, B1, 1, 0, 0, 0), /* B1 4 MSBs */
+ /* 50 */ CR( 3, 3, F1, 1, 0, 0, 0), /* F1 5 MSBs */
+ /* 51 */ CR( 3, 4, B2, 1, 0, 0, 0), /* B2 4 MSBs */
+ /* 52 */ CR( 3, 3, F2, 1, 0, 0, 0), /* F2 5 MSBs */
+ /* 53 */ CR( 3, 3, B3, 1, 0, 0, 0), /* B3 5 MSBs */
+ /* 54 */ CR( 4, 2, F3, 1, 0, 0, 0), /* F3 6 MSBs */
+ /* 55 */ CR( 4, 1, B4, 1, 0, 0, 0), /* B4 7 MSBs */
+ /* 56 */ CR( 4, 2, F4, 1, 0, 0, 0), /* F4 6 MSBs */
+ /* Mode 01 only */
+ /* 57 */ CR( 5, 0, B5, 1, 0, 0, 0), /* B5 8 MSBs */
+ /* 58 */ CR( 5, 0, F5, 1, 0, 0, 0), /* F5 8 MSBs */
+
+ /* Mode 10 and 11 */
+ /* 59 */ CR( 4, 2, AM, 1, 0, 0, 0), /* Amplitude */
+ /* 60 */ CR( 5, 0, PR, 1, 0, 0, 0), /* Period */
+ /* 61 */ CR( 4, 1, B0, 1, 0, 0, 0), /* B0 7 MSBs */
+ /* 62 */ CR( 4, 2, F0, 1, 0, 0, 0), /* F0 6 MSBs */
+ /* 63 */ CR( 4, 1, B1, 1, 0, 0, 0), /* B1 7 MSBs */
+ /* 64 */ CR( 4, 2, F1, 1, 0, 0, 0), /* F1 6 MSBs */
+ /* 65 */ CR( 4, 1, B2, 1, 0, 0, 0), /* B2 7 MSBs */
+ /* 66 */ CR( 4, 2, F2, 1, 0, 0, 0), /* F2 6 MSBs */
+ /* 67 */ CR( 4, 1, B3, 1, 0, 0, 0), /* B3 7 MSBs */
+ /* 68 */ CR( 5, 1, F3, 1, 0, 0, 0), /* F3 7 MSBs */
+ /* 69 */ CR( 5, 0, B4, 1, 0, 0, 0), /* B4 8 MSBs */
+ /* 70 */ CR( 5, 0, F4, 1, 0, 0, 0), /* F4 8 MSBs */
+ /* Mode 11 only */
+ /* 71 */ CR( 5, 0, B5, 1, 0, 0, 0), /* B5 8 MSBs */
+ /* 72 */ CR( 5, 0, F5, 1, 0, 0, 0), /* F5 8 MSBs */
+
+ /* -------------------------------------------------------------------- */
+ /* Opcode 1010: SETMSB_A */
+ /* -------------------------------------------------------------------- */
+ /* Mode 00 only */
+ /* 73 */ CR( 0, 0, 0, 0, 0, 1, 0), /* Clear 5 */
+ /* Mode 00 and 01 */
+ /* 74 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */
+ /* 75 */ CR( 5, 3, F0, 0, 1, 0, 0), /* F0 (5 MSBs) */
+ /* 76 */ CR( 5, 3, F1, 0, 1, 0, 0), /* F1 (5 MSBs) */
+ /* 77 */ CR( 5, 3, F2, 0, 1, 0, 0), /* F2 (5 MSBs) */
+
+ /* Mode 10 only */
+ /* 78 */ CR( 0, 0, 0, 0, 0, 1, 0), /* Clear 5 */
+ /* Mode 10 and 11 */
+ /* 79 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */
+ /* 80 */ CR( 6, 2, F0, 0, 1, 0, 0), /* F0 (6 MSBs) */
+ /* 81 */ CR( 6, 2, F1, 0, 1, 0, 0), /* F1 (6 MSBs) */
+ /* 82 */ CR( 6, 2, F2, 0, 1, 0, 0), /* F2 (6 MSBs) */
+
+ /* -------------------------------------------------------------------- */
+ /* Opcode 0010: LOAD_2 Mode 00 and 10 */
+ /* Opcode 1100: LOAD_C Mode 00 and 10 */
+ /* -------------------------------------------------------------------- */
+ /* LOAD_2, LOAD_C Mode 00 */
+ /* 83 */ CR( 6, 2, AM, 0, 0, 0, 1), /* Amplitude */
+ /* 84 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */
+ /* 85 */ CR( 3, 4, B0, 0, 0, 0, 0), /* B0 (S=0) */
+ /* 86 */ CR( 5, 3, F0, 0, 0, 0, 0), /* F0 */
+ /* 87 */ CR( 3, 4, B1, 0, 0, 0, 0), /* B1 (S=0) */
+ /* 88 */ CR( 5, 3, F1, 0, 0, 0, 0), /* F1 */
+ /* 89 */ CR( 3, 4, B2, 0, 0, 0, 0), /* B2 (S=0) */
+ /* 90 */ CR( 5, 3, F2, 0, 0, 0, 0), /* F2 */
+ /* 91 */ CR( 4, 3, B3, 0, 0, 0, 0), /* B3 (S=0) */
+ /* 92 */ CR( 6, 2, F3, 0, 0, 0, 0), /* F3 */
+ /* 93 */ CR( 7, 1, B4, 0, 0, 0, 0), /* B4 */
+ /* 94 */ CR( 6, 2, F4, 0, 0, 0, 0), /* F4 */
+ /* LOAD_2 only */
+ /* 95 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */
+ /* 96 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */
+
+ /* LOAD_2, LOAD_C Mode 10 */
+ /* 97 */ CR( 6, 2, AM, 0, 0, 0, 1), /* Amplitude */
+ /* 98 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */
+ /* 99 */ CR( 6, 1, B0, 0, 0, 0, 0), /* B0 (S=0) */
+ /* 100 */ CR( 6, 2, F0, 0, 0, 0, 0), /* F0 */
+ /* 101 */ CR( 6, 1, B1, 0, 0, 0, 0), /* B1 (S=0) */
+ /* 102 */ CR( 6, 2, F1, 0, 0, 0, 0), /* F1 */
+ /* 103 */ CR( 6, 1, B2, 0, 0, 0, 0), /* B2 (S=0) */
+ /* 104 */ CR( 6, 2, F2, 0, 0, 0, 0), /* F2 */
+ /* 105 */ CR( 6, 1, B3, 0, 0, 0, 0), /* B3 (S=0) */
+ /* 106 */ CR( 7, 1, F3, 0, 0, 0, 0), /* F3 */
+ /* 107 */ CR( 8, 0, B4, 0, 0, 0, 0), /* B4 */
+ /* 108 */ CR( 8, 0, F4, 0, 0, 0, 0), /* F4 */
+ /* LOAD_2 only */
+ /* 109 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */
+ /* 110 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */
+
+ /* -------------------------------------------------------------------- */
+ /* OPCODE 1101: DELTA_D */
+ /* -------------------------------------------------------------------- */
+ /* Mode 00 and 01 */
+ /* 111 */ CR( 4, 2, AM, 1, 0, 0, 0), /* Amplitude */
+ /* 112 */ CR( 5, 0, PR, 1, 0, 0, 0), /* Period */
+ /* 113 */ CR( 3, 3, B3, 1, 0, 0, 0), /* B3 5 MSBs */
+ /* 114 */ CR( 4, 2, F3, 1, 0, 0, 0), /* F3 6 MSBs */
+ /* 115 */ CR( 4, 1, B4, 1, 0, 0, 0), /* B4 7 MSBs */
+ /* 116 */ CR( 4, 2, F4, 1, 0, 0, 0), /* F4 6 MSBs */
+ /* Mode 01 only */
+ /* 117 */ CR( 5, 0, B5, 1, 0, 0, 0), /* B5 8 MSBs */
+ /* 118 */ CR( 5, 0, F5, 1, 0, 0, 0), /* F5 8 MSBs */
+
+ /* Mode 10 and 11 */
+ /* 119 */ CR( 4, 2, AM, 1, 0, 0, 0), /* Amplitude */
+ /* 120 */ CR( 5, 0, PR, 1, 0, 0, 0), /* Period */
+ /* 121 */ CR( 4, 1, B3, 1, 0, 0, 0), /* B3 7 MSBs */
+ /* 122 */ CR( 5, 1, F3, 1, 0, 0, 0), /* F3 7 MSBs */
+ /* 123 */ CR( 5, 0, B4, 1, 0, 0, 0), /* B4 8 MSBs */
+ /* 124 */ CR( 5, 0, F4, 1, 0, 0, 0), /* F4 8 MSBs */
+ /* Mode 11 only */
+ /* 125 */ CR( 5, 0, B5, 1, 0, 0, 0), /* B5 8 MSBs */
+ /* 126 */ CR( 5, 0, F5, 1, 0, 0, 0), /* F5 8 MSBs */
+
+ /* -------------------------------------------------------------------- */
+ /* OPCODE 1110: LOAD_E */
+ /* -------------------------------------------------------------------- */
+ /* 127 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */
+ /* 128 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */
+
+ /* -------------------------------------------------------------------- */
+ /* Opcode 0010: LOAD_2 Mode 01 and 11 */
+ /* Opcode 1100: LOAD_C Mode 01 and 11 */
+ /* -------------------------------------------------------------------- */
+ /* LOAD_2, LOAD_C Mode 01 */
+ /* 129 */ CR( 6, 2, AM, 0, 0, 0, 1), /* Amplitude */
+ /* 130 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */
+ /* 131 */ CR( 3, 4, B0, 0, 0, 0, 0), /* B0 (S=0) */
+ /* 132 */ CR( 5, 3, F0, 0, 0, 0, 0), /* F0 */
+ /* 133 */ CR( 3, 4, B1, 0, 0, 0, 0), /* B1 (S=0) */
+ /* 134 */ CR( 5, 3, F1, 0, 0, 0, 0), /* F1 */
+ /* 135 */ CR( 3, 4, B2, 0, 0, 0, 0), /* B2 (S=0) */
+ /* 136 */ CR( 5, 3, F2, 0, 0, 0, 0), /* F2 */
+ /* 137 */ CR( 4, 3, B3, 0, 0, 0, 0), /* B3 (S=0) */
+ /* 138 */ CR( 6, 2, F3, 0, 0, 0, 0), /* F3 */
+ /* 139 */ CR( 7, 1, B4, 0, 0, 0, 0), /* B4 */
+ /* 140 */ CR( 6, 2, F4, 0, 0, 0, 0), /* F4 */
+ /* 141 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */
+ /* 142 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */
+ /* LOAD_2 only */
+ /* 143 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */
+ /* 144 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */
+
+ /* LOAD_2, LOAD_C Mode 11 */
+ /* 145 */ CR( 6, 2, AM, 0, 0, 0, 1), /* Amplitude */
+ /* 146 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */
+ /* 147 */ CR( 6, 1, B0, 0, 0, 0, 0), /* B0 (S=0) */
+ /* 148 */ CR( 6, 2, F0, 0, 0, 0, 0), /* F0 */
+ /* 149 */ CR( 6, 1, B1, 0, 0, 0, 0), /* B1 (S=0) */
+ /* 150 */ CR( 6, 2, F1, 0, 0, 0, 0), /* F1 */
+ /* 151 */ CR( 6, 1, B2, 0, 0, 0, 0), /* B2 (S=0) */
+ /* 152 */ CR( 6, 2, F2, 0, 0, 0, 0), /* F2 */
+ /* 153 */ CR( 6, 1, B3, 0, 0, 0, 0), /* B3 (S=0) */
+ /* 154 */ CR( 7, 1, F3, 0, 0, 0, 0), /* F3 */
+ /* 155 */ CR( 8, 0, B4, 0, 0, 0, 0), /* B4 */
+ /* 156 */ CR( 8, 0, F4, 0, 0, 0, 0), /* F4 */
+ /* 157 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */
+ /* 158 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */
+ /* LOAD_2 only */
+ /* 159 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */
+ /* 160 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */
+
+ /* -------------------------------------------------------------------- */
+ /* Opcode 0011: SETMSB_3 */
+ /* Opcode 0101: SETMSB_5 */
+ /* -------------------------------------------------------------------- */
+ /* Mode 00 only */
+ /* 161 */ CR( 0, 0, 0, 0, 0, 1, 0), /* Clear 5 */
+ /* Mode 00 and 01 */
+ /* 162 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */
+ /* 163 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */
+ /* 164 */ CR( 5, 3, F0, 0, 1, 0, 0), /* F0 (5 MSBs) */
+ /* 165 */ CR( 5, 3, F1, 0, 1, 0, 0), /* F1 (5 MSBs) */
+ /* 166 */ CR( 5, 3, F2, 0, 1, 0, 0), /* F2 (5 MSBs) */
+ /* SETMSB_3 only */
+ /* 167 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */
+ /* 168 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */
+
+ /* Mode 10 only */
+ /* 169 */ CR( 0, 0, 0, 0, 0, 1, 0), /* Clear 5 */
+ /* Mode 10 and 11 */
+ /* 170 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */
+ /* 171 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */
+ /* 172 */ CR( 6, 2, F0, 0, 1, 0, 0), /* F0 (6 MSBs) */
+ /* 173 */ CR( 6, 2, F1, 0, 1, 0, 0), /* F1 (6 MSBs) */
+ /* 174 */ CR( 6, 2, F2, 0, 1, 0, 0), /* F2 (6 MSBs) */
+ /* SETMSB_3 only */
+ /* 175 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */
+ /* 176 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */
+};
+
+static const INT16 sp0256_df_idx[16 * 8] =
+{
+ /* OPCODE 0000 */ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* OPCODE 1000 */ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* OPCODE 0100 */ 17, 22, 17, 24, 25, 30, 25, 32,
+ /* OPCODE 1100 */ 83, 94, 129,142, 97, 108, 145,158,
+ /* OPCODE 0010 */ 83, 96, 129,144, 97, 110, 145,160,
+ /* OPCODE 1010 */ 73, 77, 74, 77, 78, 82, 79, 82,
+ /* OPCODE 0110 */ 33, 36, 34, 37, 38, 41, 39, 42,
+ /* OPCODE 1110 */ 127,128, 127,128, 127,128, 127,128,
+ /* OPCODE 0001 */ 1, 14, 1, 16, 1, 14, 1, 16,
+ /* OPCODE 1001 */ 45, 56, 45, 58, 59, 70, 59, 72,
+ /* OPCODE 0101 */ 161,166, 162,166, 169,174, 170,174,
+ /* OPCODE 1101 */ 111,116, 111,118, 119,124, 119,126,
+ /* OPCODE 0011 */ 161,168, 162,168, 169,176, 170,176,
+ /* OPCODE 1011 */ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* OPCODE 0111 */ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* OPCODE 1111 */ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* ======================================================================== */
+/* BITREV32 -- Bit-reverse a 32-bit number. */
+/* ======================================================================== */
+INLINE UINT32 bitrev32(UINT32 val)
+{
+ val = ((val & 0xFFFF0000) >> 16) | ((val & 0x0000FFFF) << 16);
+ val = ((val & 0xFF00FF00) >> 8) | ((val & 0x00FF00FF) << 8);
+ val = ((val & 0xF0F0F0F0) >> 4) | ((val & 0x0F0F0F0F) << 4);
+ val = ((val & 0xCCCCCCCC) >> 2) | ((val & 0x33333333) << 2);
+ val = ((val & 0xAAAAAAAA) >> 1) | ((val & 0x55555555) << 1);
+
+ return val;
+}
+
+/* ======================================================================== */
+/* BITREV8 -- Bit-reverse a 8-bit number. */
+/* ======================================================================== */
+INLINE UINT8 bitrev8(UINT8 val)
+{
+ val = ((val & 0xF0) >> 4) | ((val & 0x0F) << 4);
+ val = ((val & 0xCC) >> 2) | ((val & 0x33) << 2);
+ val = ((val & 0xAA) >> 1) | ((val & 0x55) << 1);
+
+ return val;
+}
+
+/* ======================================================================== */
+/* BITREVBUFF -- Bit-reverse a buffer. */
+/* ======================================================================== */
+void sp0256_device::bitrevbuff(UINT8 *buffer, unsigned int start, unsigned int length)
+{
+ for (unsigned int i = start; i < length; i++ )
+ buffer[i] = bitrev8(buffer[i]);
+}
+
+/* ======================================================================== */
+/* SP0256_GETB -- Get up to 8 bits at the current PC. */
+/* ======================================================================== */
+UINT32 sp0256_device::getb( int len )
+{
+ UINT32 data = 0;
+ UINT32 d0, d1;
+
+ /* -------------------------------------------------------------------- */
+ /* Fetch data from the FIFO or from the MASK */
+ /* -------------------------------------------------------------------- */
+ if (m_fifo_sel)
+ {
+ d0 = m_fifo[(m_fifo_tail ) & 63];
+ d1 = m_fifo[(m_fifo_tail + 1) & 63];
+
+ data = ((d1 << 10) | d0) >> m_fifo_bitp;
+
+ LOG_FIFO(("sp0256: RD_FIFO %.3X %d.%d %d\n", data & ((1 << len) - 1),
+ m_fifo_tail, m_fifo_bitp, m_fifo_head));
+
+ /* ---------------------------------------------------------------- */
+ /* Note the PC doesn't advance when we execute from FIFO. */
+ /* Just the FIFO's bit-pointer advances. (That's not REALLY */
+ /* what happens, but that's roughly how it behaves.) */
+ /* ---------------------------------------------------------------- */
+ m_fifo_bitp += len;
+ if (m_fifo_bitp >= 10)
+ {
+ m_fifo_tail++;
+ m_fifo_bitp -= 10;
+ }
+ } else
+ {
+ /* ---------------------------------------------------------------- */
+ /* Figure out which ROMs are being fetched into, and grab two */
+ /* adjacent bytes. The byte we're interested in is extracted */
+ /* from the appropriate bit-boundary between them. */
+ /* ---------------------------------------------------------------- */
+ int idx0 = (m_pc ) >> 3, d0;
+ int idx1 = (m_pc + 8) >> 3, d1;
+
+ d0 = m_rom[idx0 & 0xffff];
+ d1 = m_rom[idx1 & 0xffff];
+
+ data = ((d1 << 8) | d0) >> (m_pc & 7);
+
+ m_pc += len;
+ }
+
+ /* -------------------------------------------------------------------- */
+ /* Mask data to the requested length. */
+ /* -------------------------------------------------------------------- */
+ data &= ((1 << len) - 1);
+
+ return data;
+}
+
+/* ======================================================================== */
+/* SP0256_MICRO -- Emulate the microsequencer in the SP0256. Executes */
+/* instructions either until the repeat count != 0 or */
+/* the sequencer gets halted by a RTS to 0. */
+/* ======================================================================== */
+void sp0256_device::micro()
+{
+ UINT8 immed4;
+ UINT8 opcode;
+ UINT16 cr;
+ int ctrl_xfer = 0;
+ int repeat = 0;
+ int i, idx0, idx1;
+
+ /* -------------------------------------------------------------------- */
+ /* Only execute instructions while the filter is not busy. */
+ /* -------------------------------------------------------------------- */
+ while (m_filt.rpt <= 0)
+ {
+ /* ---------------------------------------------------------------- */
+ /* If the CPU is halted, see if we have a new command pending */
+ /* in the Address LoaD buffer. */
+ /* ---------------------------------------------------------------- */
+ if (m_halted && !m_lrq)
+ {
+ m_pc = m_ald | (0x1000 << 3);
+ m_fifo_sel = 0;
+ m_halted = 0;
+ m_lrq = 0x8000;
+ m_ald = 0;
+ for (i = 0; i < 16; i++)
+ m_filt.r[i] = 0;
+ m_drq_cb(1);
+ }
+
+ /* ---------------------------------------------------------------- */
+ /* If we're still halted, do nothing. */
+ /* ---------------------------------------------------------------- */
+ if (m_halted)
+ {
+ m_filt.rpt = 1;
+ m_lrq = 0x8000;
+ m_ald = 0;
+ for (i = 0; i < 16; i++)
+ m_filt.r[i] = 0;
+
+ SET_SBY(1)
+
+ return;
+ }
+
+ /* ---------------------------------------------------------------- */
+ /* Fetch the first 8 bits of the opcode, which are always in the */
+ /* same approximate format -- immed4 followed by opcode. */
+ /* ---------------------------------------------------------------- */
+ immed4 = getb(4);
+ opcode = getb(4);
+ repeat = 0;
+ ctrl_xfer = 0;
+
+ LOG(("$%.4X.%.1X: OPCODE %d%d%d%d.%d%d\n",
+ (m_pc >> 3) - 1, m_pc & 7,
+ !!(opcode & 1), !!(opcode & 2),
+ !!(opcode & 4), !!(opcode & 8),
+ !!(m_mode&4), !!(m_mode&2)));
+
+ /* ---------------------------------------------------------------- */
+ /* Handle the special cases for specific opcodes. */
+ /* ---------------------------------------------------------------- */
+ switch (opcode)
+ {
+ /* ------------------------------------------------------------ */
+ /* OPCODE 0000: RTS / SETPAGE */
+ /* ------------------------------------------------------------ */
+ case 0x0:
+ {
+ /* -------------------------------------------------------- */
+ /* If immed4 != 0, then this is a SETPAGE instruction. */
+ /* -------------------------------------------------------- */
+ if (immed4) /* SETPAGE */
+ {
+ m_page = bitrev32(immed4) >> 13;
+ } else
+ /* -------------------------------------------------------- */
+ /* Otherwise, this is an RTS / HLT. */
+ /* -------------------------------------------------------- */
+ {
+ UINT32 btrg;
+
+ /* ---------------------------------------------------- */
+ /* Figure out our branch target. */
+ /* ---------------------------------------------------- */
+ btrg = m_stack;
+
+ m_stack = 0;
+
+ /* ---------------------------------------------------- */
+ /* If the branch target is zero, this is a HLT. */
+ /* Otherwise, it's an RTS, so set the PC. */
+ /* ---------------------------------------------------- */
+ if (!btrg)
+ {
+ m_halted = 1;
+ m_pc = 0;
+ ctrl_xfer = 1;
+ } else
+ {
+ m_pc = btrg;
+ ctrl_xfer = 1;
+ }
+ }
+
+ break;
+ }
+
+ /* ------------------------------------------------------------ */
+ /* OPCODE 0111: JMP Jump to 12-bit/16-bit Abs Addr */
+ /* OPCODE 1011: JSR Jump to Subroutine */
+ /* ------------------------------------------------------------ */
+ case 0xE:
+ case 0xD:
+ {
+ int btrg;
+
+ /* -------------------------------------------------------- */
+ /* Figure out our branch target. */
+ /* -------------------------------------------------------- */
+ btrg = m_page |
+ (bitrev32(immed4) >> 17) |
+ (bitrev32(getb(8)) >> 21);
+ ctrl_xfer = 1;
+
+ /* -------------------------------------------------------- */
+ /* If this is a JSR, push our return address on the */
+ /* stack. Make sure it's byte aligned. */
+ /* -------------------------------------------------------- */
+ if (opcode == 0xD)
+ m_stack = (m_pc + 7) & ~7;
+
+ /* -------------------------------------------------------- */
+ /* Jump to the new location! */
+ /* -------------------------------------------------------- */
+ m_pc = btrg;
+ break;
+ }
+
+ /* ------------------------------------------------------------ */
+ /* OPCODE 1000: SETMODE Set the Mode and Repeat MSBs */
+ /* ------------------------------------------------------------ */
+ case 0x1:
+ {
+ m_mode = ((immed4 & 8) >> 2) | (immed4 & 4) | ((immed4 & 3) << 4);
+ break;
+ }
+
+ /* ------------------------------------------------------------ */
+ /* OPCODE 0001: LOADALL Load All Parameters */
+ /* OPCODE 0010: LOAD_2 Load Per, Ampl, Coefs, Interp. */
+ /* OPCODE 0011: SETMSB_3 Load Pitch, Ampl, MSBs, & Intrp */
+ /* OPCODE 0100: LOAD_4 Load Pitch, Ampl, Coeffs */
+ /* OPCODE 0101: SETMSB_5 Load Pitch, Ampl, and Coeff MSBs */
+ /* OPCODE 0110: SETMSB_6 Load Ampl, and Coeff MSBs. */
+ /* OPCODE 1001: DELTA_9 Delta update Ampl, Pitch, Coeffs */
+ /* OPCODE 1010: SETMSB_A Load Ampl and MSBs of 3 Coeffs */
+ /* OPCODE 1100: LOAD_C Load Pitch, Ampl, Coeffs */
+ /* OPCODE 1101: DELTA_D Delta update Ampl, Pitch, Coeffs */
+ /* OPCODE 1110: LOAD_E Load Pitch, Amplitude */
+ /* OPCODE 1111: PAUSE Silent pause */
+ /* ------------------------------------------------------------ */
+ default:
+ {
+ repeat = immed4 | (m_mode & 0x30);
+ break;
+ }
+ }
+ if (opcode != 1) m_mode &= 0xF;
+
+ /* ---------------------------------------------------------------- */
+ /* If this was a control transfer, handle setting "fifo_sel" */
+ /* and all that ugliness. */
+ /* ---------------------------------------------------------------- */
+ if (ctrl_xfer)
+ {
+ LOG(("jumping to $%.4X.%.1X: ", m_pc >> 3, m_pc & 7));
+
+ /* ------------------------------------------------------------ */
+ /* Set our "FIFO Selected" flag based on whether we're going */
+ /* to the FIFO's address. */
+ /* ------------------------------------------------------------ */
+ m_fifo_sel = m_pc == FIFO_ADDR;
+
+ LOG(("%s ", m_fifo_sel ? "FIFO" : "ROM"));
+
+ /* ------------------------------------------------------------ */
+ /* Control transfers to the FIFO cause it to discard the */
+ /* partial decle that's at the front of the FIFO. */
+ /* ------------------------------------------------------------ */
+ if (m_fifo_sel && m_fifo_bitp)
+ {
+ LOG(("bitp = %d -> Flush", m_fifo_bitp));
+
+ /* Discard partially-read decle. */
+ if (m_fifo_tail < m_fifo_head) m_fifo_tail++;
+ m_fifo_bitp = 0;
+ }
+
+ LOG(("\n"));
+
+ continue;
+ }
+
+ /* ---------------------------------------------------------------- */
+ /* Otherwise, if we have a repeat count, then go grab the data */
+ /* block and feed it to the filter. */
+ /* ---------------------------------------------------------------- */
+ if (!repeat) continue;
+
+ m_filt.rpt = repeat + 1;
+ LOG(("repeat = %d\n", repeat));
+
+ i = (opcode << 3) | (m_mode & 6);
+ idx0 = sp0256_df_idx[i++];
+ idx1 = sp0256_df_idx[i ];
+
+ assert(idx0 >= 0 && idx1 >= 0 && idx1 >= idx0);
+
+ /* ---------------------------------------------------------------- */
+ /* Step through control words in the description for data block. */
+ /* ---------------------------------------------------------------- */
+ for (i = idx0; i <= idx1; i++)
+ {
+ int len, shf, delta, field, prm, clra, clr5;
+ INT8 value;
+
+ /* ------------------------------------------------------------ */
+ /* Get the control word and pull out some important fields. */
+ /* ------------------------------------------------------------ */
+ cr = sp0256_datafmt[i];
+
+ len = CR_LEN(cr);
+ shf = CR_SHF(cr);
+ prm = CR_PRM(cr);
+ clra = cr & CR_CLRA;
+ clr5 = cr & CR_CLR5;
+ delta = cr & CR_DELTA;
+ field = cr & CR_FIELD;
+ value = 0;
+
+ LOG(("$%.4X.%.1X: len=%2d shf=%2d prm=%2d d=%d f=%d ",
+ m_pc >> 3, m_pc & 7, len, shf, prm, !!delta, !!field));
+ /* ------------------------------------------------------------ */
+ /* Clear any registers that were requested to be cleared. */
+ /* ------------------------------------------------------------ */
+ if (clra)
+ {
+ for (int j = 0; j < 16; j++)
+ m_filt.r[j] = 0;
+
+ m_silent = 1;
+ }
+
+ if (clr5)
+ m_filt.r[B5] = m_filt.r[F5] = 0;
+
+ /* ------------------------------------------------------------ */
+ /* If this entry has a bitfield with it, grab the bitfield. */
+ /* ------------------------------------------------------------ */
+ if (len)
+ {
+ value = getb(len);
+ }
+ else
+ {
+ LOG((" (no update)\n"));
+ continue;
+ }
+
+ /* ------------------------------------------------------------ */
+ /* Sign extend if this is a delta update. */
+ /* ------------------------------------------------------------ */
+ if (delta) /* Sign extend */
+ {
+ if (value & (1 << (len - 1))) value |= -1 << len;
+ }
+
+ /* ------------------------------------------------------------ */
+ /* Shift the value to the appropriate precision. */
+ /* ------------------------------------------------------------ */
+ if (shf)
+ value <<= shf;
+
+ LOG(("v=%.2X (%c%.2X) ", value & 0xFF,
+ value & 0x80 ? '-' : '+',
+ 0xFF & (value & 0x80 ? -value : value)));
+
+ m_silent = 0;
+
+ /* ------------------------------------------------------------ */
+ /* If this is a field-replace, insert the field. */
+ /* ------------------------------------------------------------ */
+ if (field)
+ {
+ LOG(("--field-> r[%2d] = %.2X -> ", prm, m_filt.r[prm]));
+
+ m_filt.r[prm] &= ~(~0 << shf); /* Clear the old bits. */
+ m_filt.r[prm] |= value; /* Merge in the new bits. */
+
+ LOG(("%.2X\n", m_filt.r[prm]));
+
+ continue;
+ }
+
+ /* ------------------------------------------------------------ */
+ /* If this is a delta update, add to the appropriate field. */
+ /* ------------------------------------------------------------ */
+ if (delta)
+ {
+ LOG(("--delta-> r[%2d] = %.2X -> ", prm, m_filt.r[prm]));
+
+ m_filt.r[prm] += value;
+
+ LOG(("%.2X\n", m_filt.r[prm]));
+
+ continue;
+ }
+
+ /* ------------------------------------------------------------ */
+ /* Otherwise, just write the new value. */
+ /* ------------------------------------------------------------ */
+ m_filt.r[prm] = value;
+ LOG(("--value-> r[%2d] = %.2X\n", prm, m_filt.r[prm]));
+ }
+
+ /* ---------------------------------------------------------------- */
+ /* Special case: Set PAUSE's equivalent period. */
+ /* ---------------------------------------------------------------- */
+ if (opcode == 0xF)
+ {
+ m_silent = 1;
+ m_filt.r[1] = PER_PAUSE;
+ }
+
+ /* ---------------------------------------------------------------- */
+ /* Now that we've updated the registers, go decode them. */
+ /* ---------------------------------------------------------------- */
+ lpc12_regdec(&m_filt);
+
+ /* ---------------------------------------------------------------- */
+ /* Break out since we now have a repeat count. */
+ /* ---------------------------------------------------------------- */
+ break;
+ }
+}
+
+
+
+WRITE8_MEMBER( sp0256_device::ald_w )
+{
+ /* ---------------------------------------------------------------- */
+ /* Drop writes to the ALD register if we're busy. */
+ /* ---------------------------------------------------------------- */
+ if (!m_lrq)
+ {
+ LOG(("sp0256: Droped ALD write\n"));
+ return;
+ }
+
+ /* ---------------------------------------------------------------- */
+ /* Set LRQ to "busy" and load the 8 LSBs of the data into the ALD */
+ /* reg. We take the command address, and multiply by 2 bytes to */
+ /* get the new PC address. */
+ /* ---------------------------------------------------------------- */
+ m_lrq = 0;
+ m_ald = (0xff & data) << 4;
+ m_drq_cb(0);
+ SET_SBY(0)
+
+ return;
+}
+
+READ_LINE_MEMBER( sp0256_device::lrq_r )
+{
+ // force stream update
+ m_stream->update();
+
+ return m_lrq == 0x8000;
+}
+
+READ_LINE_MEMBER( sp0256_device::sby_r )
+{
+ // TODO: force stream update??
+
+ return m_sby_line;
+}
+
+READ16_MEMBER( sp0256_device::spb640_r )
+{
+ /* -------------------------------------------------------------------- */
+ /* Offset 0 returns the SP0256 LRQ status on bit 15. */
+ /* -------------------------------------------------------------------- */
+ if (offset == 0)
+ {
+ return m_lrq;
+ }
+
+ /* -------------------------------------------------------------------- */
+ /* Offset 1 returns the SPB640 FIFO full status on bit 15. */
+ /* -------------------------------------------------------------------- */
+ if (offset == 1)
+ {
+ return (m_fifo_head - m_fifo_tail) >= 64 ? 0x8000 : 0;
+ }
+
+ /* -------------------------------------------------------------------- */
+ /* Just return 255 for all other addresses in our range. */
+ /* -------------------------------------------------------------------- */
+ return 0x00ff;
+}
+
+WRITE16_MEMBER( sp0256_device::spb640_w )
+{
+ if (offset == 0)
+ {
+ ald_w(space, 0, data & 0xff);
+ return;
+ }
+
+ if (offset == 1)
+ {
+ /* ---------------------------------------------------------------- */
+ /* If Bit 10 is set, reset the FIFO, and SP0256. */
+ /* ---------------------------------------------------------------- */
+
+ if (data & 0x400)
+ {
+ m_fifo_head = m_fifo_tail = m_fifo_bitp = 0;
+ device_reset();
+ return;
+ }
+
+ /* ---------------------------------------------------------------- */
+ /* If the FIFO is full, drop the data. */
+ /* ---------------------------------------------------------------- */
+ if ((m_fifo_head - m_fifo_tail) >= 64)
+ {
+ LOG(("spb640: Dropped FIFO write\n"));
+ return;
+ }
+
+ /* ---------------------------------------------------------------- */
+ /* FIFO up the lower 10 bits of the data. */
+ /* ---------------------------------------------------------------- */
+
+ LOG(("spb640: WR_FIFO %.3X %d.%d %d\n", data & 0x3ff,
+ m_fifo_tail, m_fifo_bitp, m_fifo_head));
+
+ m_fifo[m_fifo_head++ & 63] = data & 0x3ff;
+
+ return;
+ }
+}
+
+void sp0256_device::set_clock(int clock)
+{
+ set_unscaled_clock(clock);
+ m_stream->set_sample_rate(clock / CLOCK_DIVIDER);
+}
+
+
+TIMER_CALLBACK_MEMBER(sp0256_device::set_lrq_timer_proc)
+{
+ m_lrq = 0x8000;
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void sp0256_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *output = outputs[0];
+ int output_index = 0;
+ int length, did_samp/*, old_idx*/;
+
+ while (output_index < samples)
+ {
+ /* ---------------------------------------------------------------- */
+ /* First, drain as much of our scratch buffer as we can into the */
+ /* sound buffer. */
+ /* ---------------------------------------------------------------- */
+
+ while (m_sc_tail != m_sc_head)
+ {
+ output[output_index++] = m_scratch[m_sc_tail++ & SCBUF_MASK];
+ m_sc_tail &= SCBUF_MASK;
+
+ if (output_index > samples)
+ break;
+ }
+
+ /* ---------------------------------------------------------------- */
+ /* If output outputs is full, then we're done. */
+ /* ---------------------------------------------------------------- */
+ if (output_index > samples)
+ break;
+
+ length = samples - output_index;
+
+ /* ---------------------------------------------------------------- */
+ /* Process the current set of filter coefficients as long as the */
+ /* repeat count holds up and we have room in our scratch buffer. */
+ /* ---------------------------------------------------------------- */
+ did_samp = 0;
+ //old_idx = m_sc_head;
+ if (length > 0) do
+ {
+ int do_samp;
+
+ /* ------------------------------------------------------------ */
+ /* If our repeat count expired, emulate the microsequencer. */
+ /* ------------------------------------------------------------ */
+ if (m_filt.rpt <= 0)
+ micro();
+
+ /* ------------------------------------------------------------ */
+ /* Do as many samples as we can. */
+ /* ------------------------------------------------------------ */
+ do_samp = length - did_samp;
+ if (m_sc_head + do_samp - m_sc_tail > SCBUF_SIZE)
+ do_samp = m_sc_tail + SCBUF_SIZE - m_sc_head;
+
+ if (do_samp == 0) break;
+
+ if (m_silent && m_filt.rpt <= 0)
+ {
+ int y = m_sc_head;
+
+ for (int x = 0; x < do_samp; x++)
+ m_scratch[y++ & SCBUF_MASK] = 0;
+ m_sc_head += do_samp;
+ did_samp += do_samp;
+ }
+ else
+ {
+ did_samp += lpc12_update(&m_filt, do_samp,
+ m_scratch, &m_sc_head);
+ }
+
+ m_sc_head &= SCBUF_MASK;
+
+ } while (m_filt.rpt >= 0 && length > did_samp);
+ }
+}
diff --git a/src/devices/sound/sp0256.h b/src/devices/sound/sp0256.h
new file mode 100644
index 00000000000..f4ceb61bb68
--- /dev/null
+++ b/src/devices/sound/sp0256.h
@@ -0,0 +1,136 @@
+// license:GPL-2.0+
+// copyright-holders:Joseph Zbiciak,Tim Lindner
+/**********************************************************************
+
+ SP0256 Narrator Speech Processor emulation
+
+**********************************************************************
+ _____ _____
+ Vss 1 |* \_/ | 28 OSC 2
+ _RESET 2 | | 27 OSC 1
+ ROM DISABLE 3 | | 26 ROM CLOCK
+ C1 4 | | 25 _SBY RESET
+ C2 5 | | 24 DIGITAL OUT
+ C3 6 | | 23 Vdi
+ Vdd 7 | SP0256 | 22 TEST
+ SBY 8 | | 21 SER IN
+ _LRQ 9 | | 20 _ALD
+ A8 10 | | 19 SE
+ A7 11 | | 18 A1
+ SER OUT 12 | | 17 A2
+ A6 13 | | 16 A3
+ A5 14 |_____________| 15 A4
+
+**********************************************************************/
+
+/*
+ GI SP0256 Narrator Speech Processor
+
+ By Joe Zbiciak. Ported to MESS by tim lindner.
+
+ Copyright Joseph Zbiciak, all rights reserved.
+ Copyright tim lindner, all rights reserved.
+
+ - This source code is released as freeware for non-commercial purposes.
+ - You are free to use and redistribute this code in modified or
+ unmodified form, provided you list us in the credits.
+ - If you modify this source code, you must add a notice to each
+ modified source file that it has been changed. If you're a nice
+ person, you will clearly mark each change too. :)
+ - If you wish to use this for commercial purposes, please contact us at
+ intvnut@gmail.com (Joe Zbiciak), tlindner@macmess.org (tim lindner)
+ - This entire notice must remain in the source code.
+
+*/
+
+#pragma once
+
+#ifndef __SP0256_H__
+#define __SP0256_H__
+
+#define MCFG_SP0256_DATA_REQUEST_CB(_devcb) \
+ devcb = &sp0256_device::set_data_request_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_SP0256_STANDBY_CB(_devcb) \
+ devcb = &sp0256_device::set_standby_callback(*device, DEVCB_##_devcb);
+
+
+struct lpc12_t
+{
+ int rpt, cnt; /* Repeat counter, Period down-counter. */
+ UINT32 per, rng; /* Period, Amplitude, Random Number Generator */
+ int amp;
+ INT16 f_coef[6]; /* F0 through F5. */
+ INT16 b_coef[6]; /* B0 through B5. */
+ INT16 z_data[6][2]; /* Time-delay data for the filter stages. */
+ UINT8 r[16]; /* The encoded register set. */
+ int interp;
+};
+
+class sp0256_device : public device_t,
+ public device_sound_interface
+{
+public:
+ sp0256_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~sp0256_device() { }
+
+ template<class _Object> static devcb_base &set_data_request_callback(device_t &device, _Object object) { return downcast<sp0256_device &>(device).m_drq_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_standby_callback(device_t &device, _Object object) { return downcast<sp0256_device &>(device).m_sby_cb.set_callback(object); }
+
+ DECLARE_WRITE8_MEMBER(ald_w);
+ DECLARE_READ_LINE_MEMBER(lrq_r);
+ DECLARE_READ_LINE_MEMBER(sby_r);
+ DECLARE_READ16_MEMBER(spb640_r);
+ DECLARE_WRITE16_MEMBER(spb640_w);
+
+ TIMER_CALLBACK_MEMBER(set_lrq_timer_proc);
+ void set_clock(int clock);
+ void bitrevbuff(UINT8 *buffer, unsigned int start, unsigned int length);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ UINT32 getb(int len);
+ void micro();
+ required_region_ptr<UINT8> m_rom; /* 64K ROM. */
+ sound_stream *m_stream; /* MAME core sound stream */
+ devcb_write_line m_drq_cb; /* Data request callback */
+ devcb_write_line m_sby_cb; /* Standby callback */
+
+ int m_sby_line; /* Standby line state */
+ int m_cur_len; /* Fullness of current sound buffer. */
+
+ int m_silent; /* Flag: SP0256 is silent. */
+
+ INT16 *m_scratch; /* Scratch buffer for audio. */
+ UINT32 m_sc_head; /* Head pointer into scratch circular buf */
+ UINT32 m_sc_tail; /* Tail pointer into scratch circular buf */
+
+ struct lpc12_t m_filt; /* 12-pole filter */
+ int m_lrq; /* Load ReQuest. == 0 if we can accept a load */
+ int m_ald; /* Address LoaD. < 0 if no command pending. */
+ int m_pc; /* Microcontroller's PC value. */
+ int m_stack; /* Microcontroller's PC stack. */
+ int m_fifo_sel; /* True when executing from FIFO. */
+ int m_halted; /* True when CPU is halted. */
+ UINT32 m_mode; /* Mode register. */
+ UINT32 m_page; /* Page set by SETPAGE */
+
+ UINT32 m_fifo_head; /* FIFO head pointer (where new data goes). */
+ UINT32 m_fifo_tail; /* FIFO tail pointer (where data comes from). */
+ UINT32 m_fifo_bitp; /* FIFO bit-pointer (for partial decles). */
+ UINT16 m_fifo[64]; /* The 64-decle FIFO. */
+
+ emu_timer *m_lrq_timer;
+};
+
+extern const device_type SP0256;
+
+
+#endif /* __SP0256_H__ */
diff --git a/src/devices/sound/speaker.c b/src/devices/sound/speaker.c
new file mode 100644
index 00000000000..7872351a0e3
--- /dev/null
+++ b/src/devices/sound/speaker.c
@@ -0,0 +1,409 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria
+/***************************************************************************
+
+ speaker.c
+
+ Sound driver to emulate a simple speaker,
+ driven by one or more output bits
+
+ Original author: (unsigned)
+ Filtering: Anders Hallstr?m
+****************************************************************************/
+
+/* Discussion of oversampling and anti-alias filtering: (Anders Hallstr?m)
+ *
+ * This driver is for machines that directly control
+ * one or more simple digital-to-analog converters (DAC)
+ * connected to one or more audio outputs (such as analog amp + speaker).
+ * Currently only 1-bit DAC is supported via the interface to this module.
+ *
+ * Frequently such machines would oversample the DAC
+ * in order to overcome the limited DAC resolution.
+ * For faithful reproduction of the sound, this must be carefully handled
+ * with anti-alias filtering when converting a high-rate low-resolution signal
+ * to a moderate-rate high-resolution signal suitable for the DAC in the emulator's sound card.
+ * (Originally, removal of any redundant high frequency content occurred on the analog side
+ * with no aliasing effects.)
+ *
+ * The most straightforward, naive way to handle this is to use two streams;
+ * stream 1 modeling the native audio, with a sampling rate that allows for
+ * accurate representation of over-sampling, i.e. the sampling rate should match
+ * the clock frequency of the audio generating device (such as the CPU).
+ * Stream 1 is connected to stream 2, which is concerned with feeding the sound card.
+ * The stream system has features to handle rate conversion from stream 1 to 2.
+ *
+ * I tried it out of curiosity; it works fine conceptually, but
+ * - it puts an unnecessary burden on system resources
+ * - sound quality is still not satisfactory, though better than without anti-alias
+ * - "stream 1" properties are machine specific and so should be configured
+ * individually in each machine driver using this approach.
+ * This can also be seen as an advantage for flexibility, though.
+ *
+ * Instead, dedicated filtering is implemented in this module,
+ * in a machine-neutral way (based on machine time and external -samplerate only).
+ *
+ * The basic average filter has the advantage that it can be used without
+ * explicitly generating all samples in "stream 1". However,
+ * it is poor for anti-alias filtering.
+ * Therefore, average filtering is combined with windowed sinc.
+ *
+ * Virtual stream 1: Samples in true machine time.
+ * Any sampling rate up to attotime resolution is implicitly supported.
+ * -> average filtering over each stream 2 sample ->
+ * Virtual stream 2: Intermediate representation.
+ * Sample rate = RATE_MULTIPLIER * stream 3 sample rate.
+ * If effective rate of stream 1 exceeds rate of stream 2,
+ * some aliasing distorsion is introduced in this step because the average filtering is a compromise.
+ * The distorsion is however mostly in the higher frequencies.
+ * -> low-pass anti-alias filtering with kernel ampl[] ->
+ * -> down-sampling ->
+ * Actual stream 3: channel output generated by speaker_sound_update().
+ * Sample rate = device sample rate = configured "-samplerate".
+ *
+ * In the speaker_state data structure,
+ * "intermediate samples" refers to "stream 2"
+ * "channel samples" refers to "stream 3"
+ */
+
+/* IMPROVEMENTS POSSIBLE:
+ * - Make filter length a run-time configurable parameter. min=1 max=1000 or something
+ * - Optimize cutoff freq automatically after filter length, or configurable too
+ * - Generalise this approach to other DAC-based sound types if susceptible to aliasing
+ */
+
+#include "emu.h"
+#include "sound/speaker.h"
+
+static const INT16 default_levels[2] = {0, 32767};
+
+// Internal oversampling factor (interm. samples vs stream samples)
+static const int RATE_MULTIPLIER = 4;
+
+
+const device_type SPEAKER_SOUND = &device_creator<speaker_sound_device>;
+
+speaker_sound_device::speaker_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, SPEAKER_SOUND, "Filtered 1-bit DAC", tag, owner, clock, "speaker_sound", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_num_levels(2),
+ m_levels(default_levels)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void speaker_sound_device::device_start()
+{
+ int i;
+ double x;
+
+ m_channel = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate());
+
+ m_level = 0;
+ for (i = 0; i < FILTER_LENGTH; i++)
+ m_composed_volume[i] = 0;
+
+ m_composed_sample_index = 0;
+ m_last_update_time = machine().time();
+ m_channel_sample_period = HZ_TO_ATTOSECONDS(machine().sample_rate());
+ m_channel_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(m_channel_sample_period);
+ m_interm_sample_period = m_channel_sample_period / RATE_MULTIPLIER;
+ m_interm_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(m_interm_sample_period);
+ m_channel_last_sample_time = m_channel->sample_time();
+ m_channel_next_sample_time = m_channel_last_sample_time + attotime(0, m_channel_sample_period);
+ m_next_interm_sample_time = m_channel_last_sample_time + attotime(0, m_interm_sample_period);
+ m_interm_sample_index = 0;
+ m_prevx = m_prevy = 0.0;
+
+ /* Note: To avoid time drift due to floating point inaccuracies,
+ * it is good if the speaker time synchronizes itself with the stream timing regularly.
+ */
+
+ /* Compute filter kernel; */
+ /* (Done for each device though the data is shared...
+ * No problem really, but should be done as part of system init if I knew how)
+ */
+#if 1
+ /* This is an approximated sinc (a perfect sinc makes an ideal low-pass filter).
+ * FILTER_STEP determines the cutoff frequency,
+ * which should be below the Nyquist freq, i.e. half the sample rate.
+ * Smaller step => kernel extends in time domain => lower cutoff freq
+ * In this case, with sinc, filter step PI corresponds to the Nyq. freq.
+ * Since we do not get a perfect filter => must lower the cutoff freq some more.
+ * For example, step PI/(2*RATE_MULTIPLIER) corresponds to cutoff freq = sample rate / 4;
+ * With -samplerate 48000, cutoff freq is ca 12kHz while the Nyq. freq is 24kHz.
+ * With -samplerate 96000, cutoff freq is ca 24kHz while the Nyq. freq is 48kHz.
+ * For a steeper, more efficient filter, increase FILTER_LENGTH at the expense of CPU usage.
+ */
+#define FILTER_STEP (M_PI / 2 / RATE_MULTIPLIER)
+ /* Distribute symmetrically on x axis; center has x=0 if length is odd */
+ for (i = 0, x = (0.5 - FILTER_LENGTH / 2.) * FILTER_STEP;
+ i < FILTER_LENGTH;
+ i++, x += FILTER_STEP)
+ {
+ if (x == 0)
+ m_ampl[i] = 1;
+ else
+ m_ampl[i] = sin(x) / x;
+ }
+#else
+ /* Trivial average filter with poor frequency cutoff properties;
+ * First zero (frequency where amplification=0) = sample rate / filter length
+ * Cutoff frequency approx <= first zero / 2
+ */
+ for (i = 0, i < FILTER_LENGTH; i++)
+ m_ampl[i] = 1;
+#endif
+
+ save_item(NAME(m_level));
+ save_item(NAME(m_composed_volume));
+ save_item(NAME(m_composed_sample_index));
+ save_item(NAME(m_channel_last_sample_time));
+ save_item(NAME(m_interm_sample_index));
+ save_item(NAME(m_last_update_time));
+ save_item(NAME(m_prevx));
+ save_item(NAME(m_prevy));
+
+ machine().save().register_postload(save_prepost_delegate(FUNC(speaker_sound_device::speaker_postload), this));
+}
+
+void speaker_sound_device::device_reset()
+{
+ int i;
+
+ m_level = 0;
+ for (i = 0; i < FILTER_LENGTH; i++)
+ m_composed_volume[i] = 0;
+
+ m_composed_sample_index = 0;
+ m_last_update_time = machine().time();
+ m_channel_sample_period = HZ_TO_ATTOSECONDS(machine().sample_rate());
+ m_channel_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(m_channel_sample_period);
+ m_interm_sample_period = m_channel_sample_period / RATE_MULTIPLIER;
+ m_interm_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(m_interm_sample_period);
+ m_channel_last_sample_time = m_channel->sample_time();
+ m_channel_next_sample_time = m_channel_last_sample_time + attotime(0, m_channel_sample_period);
+ m_next_interm_sample_time = m_channel_last_sample_time + attotime(0, m_interm_sample_period);
+ m_interm_sample_index = 0;
+ m_prevx = m_prevy = 0.0;
+}
+
+void speaker_sound_device::speaker_postload()
+{
+ m_channel_next_sample_time = m_channel_last_sample_time + attotime(0, m_channel_sample_period);
+ m_next_interm_sample_time = m_channel_last_sample_time + attotime(0, m_interm_sample_period);
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+// This can be triggered by the core (based on emulated time) or via level_w().
+void speaker_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *buffer = outputs[0];
+ int volume = m_levels[m_level];
+ double filtered_volume;
+ attotime sampled_time = attotime::zero;
+
+ if (samples > 0)
+ {
+ /* Prepare to update time state */
+ sampled_time = attotime(0, m_channel_sample_period);
+ if (samples > 1)
+ sampled_time *= samples;
+
+ /* Note: since the stream is in the process of being updated,
+ * stream->sample_time() will return the time before the update! (MAME 0.130)
+ * Avoid using it here in order to avoid a subtle dependence on the stream implementation.
+ */
+ }
+
+ if (samples-- > 0)
+ {
+ /* Note that first interm. sample may be composed... */
+ filtered_volume = update_interm_samples_get_filtered_volume(volume);
+
+ /* Composite volume is now quantized to the stream resolution */
+ *buffer++ = (stream_sample_t)filtered_volume;
+
+ /* Any additional samples will be homogeneous, however may need filtering across samples: */
+ while (samples-- > 0)
+ {
+ filtered_volume = update_interm_samples_get_filtered_volume(volume);
+ *buffer++ = (stream_sample_t)filtered_volume;
+ }
+
+ /* Update the time state */
+ m_channel_last_sample_time += sampled_time;
+ m_channel_next_sample_time = m_channel_last_sample_time + attotime(0, m_channel_sample_period);
+ m_next_interm_sample_time = m_channel_last_sample_time + attotime(0, m_interm_sample_period);
+ m_last_update_time = m_channel_last_sample_time;
+ }
+}
+
+
+
+void speaker_sound_device::level_w(int new_level)
+{
+ int volume;
+ attotime time;
+
+ if (new_level == m_level)
+ return;
+
+ if (new_level < 0)
+ new_level = 0;
+ else
+ if (new_level >= m_num_levels)
+ new_level = m_num_levels - 1;
+
+ volume = m_levels[m_level];
+ time = machine().time();
+
+ if (time < m_channel_next_sample_time)
+ {
+ /* Stream sample is yet unfinished, but we may have one or more interm. samples */
+ update_interm_samples(time, volume);
+
+ /* Do not forget to update speaker state before returning! */
+ m_level = new_level;
+ return;
+ }
+ /* Reaching here means such time has passed since last stream update
+ * that we can add at least one complete sample to the stream.
+ * The details have to be handled by speaker_sound_update()
+ */
+
+ /* Force streams.c to update sound until this point in time now */
+ m_channel->update();
+
+ /* This is redundant because time update has to be done within speaker_sound_update() anyway,
+ * however this ensures synchronization between the speaker and stream timing:
+ */
+ m_channel_last_sample_time = m_channel->sample_time();
+ m_channel_next_sample_time = m_channel_last_sample_time + attotime(0, m_channel_sample_period);
+ m_next_interm_sample_time = m_channel_last_sample_time + attotime(0, m_interm_sample_period);
+ m_last_update_time = m_channel_last_sample_time;
+
+ /* Assertion: time - last_update_time < channel_sample_period, i.e. time < channel_next_sample_time */
+
+ /* The overshooting fraction of time will make zero, one or more interm. samples: */
+ update_interm_samples(time, volume);
+
+ /* Finally update speaker state before returning */
+ m_level = new_level;
+
+}
+
+
+void speaker_sound_device::update_interm_samples(const attotime &time, int volume)
+{
+ double fraction;
+
+ /* We may have completed zero, one or more interm. samples: */
+ while (time >= m_next_interm_sample_time)
+ {
+ /* First interm. sample may be composed, subsequent samples will be homogeneous. */
+ /* Treat all the same general way. */
+ finalize_interm_sample(volume);
+ init_next_interm_sample();
+ }
+ /* Depending on status above:
+ * a) Add latest fraction to unfinished composed sample
+ * b) The overshooting fraction of time will start a new composed sample
+ */
+ fraction = make_fraction(time, m_last_update_time, m_interm_sample_period_secfrac);
+ m_composed_volume[m_composed_sample_index] += volume * fraction;
+ m_last_update_time = time;
+}
+
+
+double speaker_sound_device::update_interm_samples_get_filtered_volume(int volume)
+{
+ double filtered_volume, tempx;
+
+ /* We may have one or more interm. samples to go */
+ if (m_interm_sample_index < RATE_MULTIPLIER)
+ {
+ /* First interm. sample may be composed. */
+ finalize_interm_sample(volume);
+
+ /* Subsequent interm. samples will be homogeneous. */
+ while (m_interm_sample_index + 1 < RATE_MULTIPLIER)
+ {
+ init_next_interm_sample();
+ m_composed_volume[m_composed_sample_index] = volume;
+ }
+ }
+ /* Important: next interm. sample not initialised yet, so that no data is destroyed before filtering... */
+ filtered_volume = get_filtered_volume();
+ init_next_interm_sample();
+ /* Reset counter to next stream sample: */
+ m_interm_sample_index = 0;
+
+ /* simple DC blocker filter */
+ tempx = filtered_volume;
+ filtered_volume = tempx - m_prevx + 0.995 * m_prevy;
+ m_prevx = tempx;
+ m_prevy = filtered_volume;
+
+ return filtered_volume;
+}
+
+
+void speaker_sound_device::finalize_interm_sample(int volume)
+{
+ double fraction;
+
+ /* Fill the composed sample up if it was incomplete */
+ fraction = make_fraction(m_next_interm_sample_time, m_last_update_time, m_interm_sample_period_secfrac);
+ m_composed_volume[m_composed_sample_index] += volume * fraction;
+ /* Update time state */
+ m_last_update_time = m_next_interm_sample_time;
+ m_next_interm_sample_time += attotime(0, m_interm_sample_period);
+
+ /* For compatibility with filtering, do not incr. index and initialise next sample yet. */
+}
+
+
+void speaker_sound_device::init_next_interm_sample()
+{
+ /* Move the index and initialize next composed sample */
+ m_composed_sample_index++;
+ if (m_composed_sample_index >= FILTER_LENGTH)
+ m_composed_sample_index = 0;
+ m_composed_volume[m_composed_sample_index] = 0;
+
+ m_interm_sample_index++;
+ /* No limit check on interm_sample_index here - to be handled by caller */
+}
+
+
+inline double speaker_sound_device::make_fraction(const attotime &a, const attotime &b, double timediv)
+{
+ /* fraction = (a - b) / timediv */
+ return (a - b).as_double() / timediv;
+}
+
+
+double speaker_sound_device::get_filtered_volume()
+{
+ double filtered_volume = 0;
+ double ampsum = 0;
+ int i, c;
+
+ /* Filter over composed samples (each composed sample is already average filtered) */
+ for (i = m_composed_sample_index + 1, c = 0; c < FILTER_LENGTH; i++, c++)
+ {
+ if (i >= FILTER_LENGTH) i = 0;
+ filtered_volume += m_composed_volume[i] * m_ampl[c];
+ ampsum += m_ampl[c];
+ }
+ filtered_volume /= ampsum;
+
+ return filtered_volume;
+}
diff --git a/src/devices/sound/speaker.h b/src/devices/sound/speaker.h
new file mode 100644
index 00000000000..67819769926
--- /dev/null
+++ b/src/devices/sound/speaker.h
@@ -0,0 +1,93 @@
+// license:BSD-3-Clause
+// copyright-holders:Nicola Salmoria
+/**********************************************************************
+
+ speaker.h
+ Sound driver to emulate a simple speaker,
+ driven by one or more output bits
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __SOUND_SPEAKER_H__
+#define __SOUND_SPEAKER_H__
+
+// Length of anti-aliasing filter kernel, measured in number of intermediate samples
+enum
+{
+ FILTER_LENGTH = 64
+};
+
+#define MCFG_SPEAKER_LEVELS(_num, _levels) \
+ speaker_sound_device::static_set_levels(*device, _num, _levels);
+
+class speaker_sound_device : public device_t,
+ public device_sound_interface
+{
+public:
+ speaker_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~speaker_sound_device() {}
+
+ // static configuration
+ static void static_set_levels(device_t &device, int num_levels, const INT16 *levels) { downcast<speaker_sound_device &>(device).m_num_levels = num_levels; downcast<speaker_sound_device &>(device).m_levels = levels;}
+
+ void level_w(int new_level);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ // internal state
+
+ // Updates the composed volume array according to time
+ void update_interm_samples(const attotime &time, int volume);
+
+ // Updates the composed volume array and returns final filtered volume of next stream sample
+ double update_interm_samples_get_filtered_volume(int volume);
+
+ void finalize_interm_sample(int volume);
+ void init_next_interm_sample();
+ inline double make_fraction(const attotime &a, const attotime &b, double timediv);
+ double get_filtered_volume();
+
+ // Kernel (pulse response) for filtering across samples (while we avoid fancy filtering within samples)
+ double m_ampl[FILTER_LENGTH];
+
+ sound_stream *m_channel;
+ int m_level;
+
+ /* The volume of a composed sample grows incrementally each time the speaker is over-sampled.
+ * That is in effect a basic average filter.
+ * Another filter can and will be applied to the array of composed samples.
+ */
+ double m_composed_volume[FILTER_LENGTH]; /* integrator(s) */
+ int m_composed_sample_index; /* array index for composed_volume */
+ attoseconds_t m_channel_sample_period; /* in as */
+ double m_channel_sample_period_secfrac; /* in fraction of second */
+ attotime m_channel_last_sample_time;
+ attotime m_channel_next_sample_time;
+ attoseconds_t m_interm_sample_period;
+ double m_interm_sample_period_secfrac;
+ attotime m_next_interm_sample_time;
+ int m_interm_sample_index; /* counts interm. samples between stream samples */
+ attotime m_last_update_time; /* internal timestamp */
+
+ void speaker_postload();
+
+ // DC blocker state
+ double m_prevx, m_prevy;
+
+ int m_num_levels; /* optional: number of levels (if not two) */
+ const INT16 *m_levels; /* optional: pointer to level lookup table */
+};
+
+extern const device_type SPEAKER_SOUND;
+
+
+#endif /* __SPEAKER_H__ */
diff --git a/src/devices/sound/spu.c b/src/devices/sound/spu.c
new file mode 100644
index 00000000000..ef5a81e0e82
--- /dev/null
+++ b/src/devices/sound/spu.c
@@ -0,0 +1,3081 @@
+// license:BSD-3-Clause
+// copyright-holders:pSXAuthor, R. Belmont
+/*
+
+ Sony PlayStation SPU (CXD2922BQ/CXD2925Q) emulator
+ by pSXAuthor
+ MAME adaptation by R. Belmont
+
+*/
+
+#include "emu.h"
+#include "spu.h"
+#include "spureverb.h"
+
+//
+//
+//
+
+//#define warn_if_sweep_used
+#define assert_if_sweep_used
+//#define debug_spu_registers
+//#define debug_spu
+//#define log_loop_cache
+//#define show_xa_debug
+
+//#ifndef _FINAL
+// #define show_cache_update
+//#endif
+
+#ifdef show_xa_debug
+ #define debug_xa printf
+#else
+ #define debug_xa if (0)
+#endif
+
+// device type definition
+const device_type SPU = &device_creator<spu_device>;
+
+//
+//
+//
+INLINE unsigned int min(unsigned int a, unsigned int b)
+{
+ return (a > b) ? b : a;
+}
+
+INLINE unsigned int max(unsigned int a, unsigned int b)
+{
+ return (a > b) ? a : b;
+}
+
+INLINE double mindb(double a, double b)
+{
+ return (a > b) ? b : a;
+}
+
+INLINE double maxdb(double a, double b)
+{
+ return (a > b) ? a : b;
+}
+
+enum spu_registers
+{
+ spureg_voice=0,
+ spureg_voice_last=0x17f,
+ spureg_mvol_l=0x180,
+ spureg_mvol_r=0x182,
+ spureg_rvol_l=0x184,
+ spureg_rvol_r=0x186,
+ spureg_keyon=0x188,
+ spureg_keyoff=0x18c,
+ spureg_fm=0x190,
+ spureg_noise=0x194,
+ spureg_reverb=0x198,
+ spureg_chon=0x19c,
+ spureg_reverb_addr=0x1a2,
+ spureg_irq_addr=0x1a4,
+ spureg_trans_addr=0x1a6,
+ spureg_data=0x1a8,
+ spureg_ctrl=0x1aa,
+ spureg_status=0x1ac,
+ spureg_cdvol_l=0x1b0,
+ spureg_cdvol_r=0x1b2,
+ spureg_exvol_l=0x1b4,
+ spureg_exvol_r=0x1b6,
+ spureg_reverb_config=0x1c0,
+ spureg_last=0x1ff
+};
+
+enum spu_ctrl
+{
+ spuctrl_irq_enable=0x40,
+ spuctrl_noise_shift=8,
+ spuctrl_noise_mask=0x3f<<spuctrl_noise_shift
+};
+
+enum
+{
+ adpcmflag_end=1,
+ adpcmflag_loop=2,
+ adpcmflag_loop_start=4
+};
+
+struct adpcm_packet
+{
+ unsigned char info,
+ flags,
+ data[14];
+};
+
+enum adsl_flags
+{
+ adsl_am=0x8000,
+ adsl_ar_shift=8,
+ adsl_ar_mask=0x7f<<adsl_ar_shift,
+ adsl_dr_shift=4,
+ adsl_dr_mask=0xf<<adsl_dr_shift,
+ adsl_sl_mask=0xf
+};
+
+enum srrr_flags
+{
+ srrr_sm=0x8000,
+ srrr_sd=0x4000,
+ srrr_sr_shift=6,
+ srrr_sr_mask=0x7f<<srrr_sr_shift,
+ srrr_rm=0x20,
+ srrr_rr_mask=0x1f
+};
+
+static const unsigned int /*sound_buffer_size=65536*4,*/
+ xa_sector_size=(18*28*8)<<1,
+ xa_buffer_sectors=16,
+ cdda_sector_size=2352,
+ cdda_buffer_sectors=16,
+ num_loop_cache_packets=4,
+ num_loop_cache_samples=num_loop_cache_packets*28,
+ spu_ram_size=512*1024,
+ spu_infinity=0xffffffff,
+
+ output_buffer_size=65536/8/*,
+
+ sample_loop_cache_pool_size=64,
+ sample_loop_cache_extend_size=64,
+ sample_cache_pool_size=64,
+ sample_cache_extend_size=64,
+
+ stream_marker_pool_size=64,
+ stream_marker_extend_size=64*/;
+
+//
+//
+//
+
+static const int filter_coef[5][2]=
+{
+ { 0,0 },
+ { 60,0 },
+ { 115,-52 },
+ { 98,-55 },
+ { 122,-60 },
+};
+
+//
+//
+//
+
+#ifdef debug_spu_registers
+ #define _voice_registers(_voice) \
+ "voice"#_voice".voll", \
+ "voice"#_voice".volr", \
+ "voice"#_voice".pitch", \
+ "voice"#_voice".addr", \
+ "voice"#_voice".adsl", \
+ "voice"#_voice".srrr", \
+ "voice"#_voice".curvol", \
+ "voice"#_voice".repaddr"
+
+ #define _voice_mask_register(_name) \
+ _name##"0-15", \
+ _name##"16-23"
+
+ static const char *spu_register_names[256]=
+ {
+ _voice_registers(0),
+ _voice_registers(1),
+ _voice_registers(2),
+ _voice_registers(3),
+ _voice_registers(4),
+ _voice_registers(5),
+ _voice_registers(6),
+ _voice_registers(7),
+ _voice_registers(8),
+ _voice_registers(9),
+ _voice_registers(10),
+ _voice_registers(11),
+ _voice_registers(12),
+ _voice_registers(13),
+ _voice_registers(14),
+ _voice_registers(15),
+ _voice_registers(16),
+ _voice_registers(17),
+ _voice_registers(18),
+ _voice_registers(19),
+ _voice_registers(20),
+ _voice_registers(21),
+ _voice_registers(22),
+ _voice_registers(23),
+ "mvoll",
+ "mvolr",
+ "rvoll",
+ "rvolr",
+ "keyon0-15", "keyon16-23",
+ "keyoff0-15", "keyoff16-23",
+ "fm0-15", "fm16-23",
+ "noise0-15", "noise16-23",
+ "reverb0-15", "reverb16-23",
+ "chon0-15", "chon16-23",
+ "unknown",
+ "reverbaddr",
+ "irqaddr",
+ "transaddr",
+ "data",
+ "ctrl",
+ "statusl",
+ "statush",
+ "cdvoll",
+ "cdvolr",
+ "exvoll",
+ "exvolr"
+ };
+
+ const char *get_register_name(const unsigned int addr)
+ {
+ return spu_register_names[(addr&0x1ff)>>1];
+ }
+#endif
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+reverb_params *spu_reverb_cfg=NULL;
+
+float spu_device::freq_multiplier=1.0f;
+
+//**************************************************************************
+// DEVICE CONFIGURATION
+//**************************************************************************
+
+class adpcm_decoder
+{
+ int l0,l1;
+
+public:
+ adpcm_decoder()
+ {
+ reset();
+ }
+
+ adpcm_decoder(const adpcm_decoder &other)
+ {
+ operator =(other);
+ }
+
+ adpcm_decoder &operator =(const adpcm_decoder &other)
+ {
+ l0=other.l0;
+ l1=other.l1;
+ return *this;
+ }
+
+ void reset()
+ {
+ l0=l1=0;
+ }
+
+ signed short *decode_packet(adpcm_packet *ap, signed short *dp);
+};
+
+//
+//
+//
+
+struct spu_device::sample_cache
+{
+public:
+ unsigned int start,
+ end,
+ invalid_start,
+ invalid_end,
+ loopaddr,
+ last_update_end;
+ signed short *data,*loop,*dend;
+ adpcm_decoder decoder, update_decoder;
+ mutable int ref_count;
+ bool valid,
+ is_loop;
+ sample_loop_cache *loop_cache;
+
+ static unsigned int cache_size;
+
+ sample_cache()
+ : invalid_start(0xffffffff),
+ invalid_end(0),
+ last_update_end(0xffffffff),
+ data(NULL),
+ ref_count(0),
+ valid(false),
+ is_loop(false),
+ loop_cache(NULL)
+ {
+ }
+
+ ~sample_cache();
+
+ void add_ref() const { ref_count++; }
+ void remove_ref() const
+ {
+ ref_count--;
+ if (ref_count==0)
+ {
+ cache_size-=(dend-data)<<1;
+ global_free(this);
+ }
+ }
+
+ signed short *get_sample_pointer(const unsigned int addr);
+ bool get_sample_pointer(const unsigned int addr, cache_pointer *cp);
+ bool get_loop_pointer(cache_pointer *cp);
+ unsigned int get_sample_address(const signed short *ptr) const;
+ sample_loop_cache *find_loop_cache(const unsigned int lpend, const unsigned int lpstart);
+ void add_loop_cache(sample_loop_cache *lc);
+
+ bool is_valid_pointer(signed short *ptr) const;
+
+ bool try_update(spu_device *spu);
+};
+
+unsigned int spu_device::sample_cache::cache_size;
+
+//
+//
+//
+
+struct spu_device::sample_loop_cache
+{
+public:
+ unsigned int loopend,
+ loopstart,
+ len;
+ signed short data[num_loop_cache_samples];
+ sample_loop_cache *next;
+
+ sample_loop_cache()
+ : next(NULL)
+ {
+ sample_cache::cache_size+=num_loop_cache_samples<<1;
+ }
+
+ ~sample_loop_cache()
+ {
+ sample_cache::cache_size-=num_loop_cache_samples<<1;
+
+ #ifdef log_loop_cache
+ log(log_spu,"spu: destroy loop cache %08x\n",this);
+ #endif
+ }
+};
+
+//
+//
+//
+
+struct spu_device::cache_pointer
+{
+ signed short *ptr;
+ sample_cache *cache;
+
+ cache_pointer()
+ : ptr(NULL),
+ cache(NULL)
+ {
+ }
+
+ cache_pointer(const cache_pointer &other)
+ : ptr(NULL),
+ cache(NULL)
+ {
+ operator =(other);
+ }
+
+ cache_pointer(signed short *_ptr, sample_cache *_cache)
+ : ptr(_ptr),
+ cache(_cache)
+ {
+ if (cache) cache->add_ref();
+ }
+
+ ~cache_pointer()
+ {
+ reset();
+ }
+
+ void reset();
+ cache_pointer &operator =(const cache_pointer &other);
+ bool update(spu_device *spu);
+
+ unsigned int get_address() const
+ {
+ if (cache)
+ {
+ return cache->get_sample_address(ptr);
+ } else
+ {
+ return -1;
+ }
+ }
+
+ operator bool() const { return cache!=NULL; }
+
+ bool is_valid() const { return ((cache) && (cache->is_valid_pointer(ptr))); }
+};
+
+//
+//
+//
+
+struct spu_device::voiceinfo
+{
+ cache_pointer play,loop;
+ sample_loop_cache *loop_cache;
+ unsigned int dptr,
+ lcptr;
+
+ int env_state;
+ float env_ar,
+ env_dr,
+ env_sr,
+ env_rr,
+ env_sl,
+ env_level,
+ env_delta,
+
+ //>>
+ sweep_vol[2],
+ sweep_rate[2];
+ int vol[2];
+ //<<
+
+ unsigned int pitch,
+ samplestoend,
+ samplestoirq,
+ envsamples;
+ bool hitirq,
+ inloopcache,
+ forceloop,
+ _pad;
+ INT64 keyontime;
+};
+
+//
+//
+//
+
+class stream_buffer
+{
+public:
+ struct stream_marker
+ {
+ public:
+ unsigned int sector,
+ offset;
+ stream_marker *next,
+ *prev;
+ };
+
+ dynamic_buffer buffer;
+ unsigned int head,
+ tail,
+ in,
+ sector_size,
+ num_sectors,
+ buffer_size;
+ stream_marker *marker_head,
+ *marker_tail;
+
+ stream_buffer(const unsigned int _sector_size,
+ const unsigned int _num_sectors)
+ : head(0),
+ tail(0),
+ in(0),
+ sector_size(_sector_size),
+ num_sectors(_num_sectors),
+ marker_head(NULL),
+ marker_tail(NULL)
+ {
+ buffer_size=sector_size*num_sectors;
+ buffer.resize(buffer_size);
+ memset(&buffer[0], 0, buffer_size);
+ }
+
+ ~stream_buffer()
+ {
+ flush_all();
+ }
+
+ unsigned char *add_sector(const unsigned int sector)
+ {
+ stream_marker *xam=new stream_marker;
+ xam->sector=sector;
+ xam->offset=head;
+ xam->next=NULL;
+ xam->prev=marker_tail;
+ if (marker_tail)
+ {
+ marker_tail->next=xam;
+ } else
+ {
+ marker_head=xam;
+ }
+ marker_tail=xam;
+
+ unsigned char *ret=&buffer[head];
+ head=(head+sector_size)%buffer_size;
+ in+=sector_size;
+ return ret;
+ }
+
+ void flush(const unsigned int sector)
+ {
+ // Remove markers from the end of the buffer if they are after
+ // the specified sector
+
+ while ((marker_tail) && (marker_tail->sector>=sector))
+ {
+// debug_xa("flushing: %d\n", marker_tail->sector);
+
+ stream_marker *xam=marker_tail;
+ head=xam->offset;
+ marker_tail=xam->prev;
+ if (marker_tail) marker_tail->next=NULL;
+ global_free(xam);
+ }
+
+ // Set marker head to NULL if the list is now empty
+
+ if (! marker_tail) marker_head=NULL;
+
+ // Adjust buffer size counter
+
+ int sz=(head-tail);
+ if (sz<0) sz+=buffer_size;
+ assert(sz<=(int)in);
+ in=sz;
+ }
+
+ void flush_all()
+ {
+ // NOTE: ??what happens to the markers??
+
+ while (marker_head)
+ {
+ stream_marker *m=marker_head;
+ marker_head=marker_head->next;
+ global_free(m);
+ }
+
+ marker_head=marker_tail=NULL;
+ head=tail=in=0;
+ }
+
+ void delete_markers(const unsigned int oldtail)
+ {
+ while (marker_head)
+ {
+ int olddist=marker_head->offset-oldtail,
+ dist=marker_head->offset-tail;
+ if (olddist<0) olddist+=buffer_size;
+ if (dist<0) dist+=buffer_size;
+ bool passed=(((olddist==0) && (dist!=0)) || (dist>olddist));
+ if (! passed) break;
+
+// debug_xa("passed: %d\n",marker_head->sector);
+
+ stream_marker *xam=marker_head;
+ marker_head=xam->next;
+ global_free(xam);
+ if (marker_head) marker_head->prev=NULL;
+ }
+
+ if (! marker_head) marker_head=marker_tail=NULL;
+ }
+
+ unsigned int get_bytes_in() const { return in; }
+ unsigned int get_bytes_free() const { return buffer_size-in; }
+
+ unsigned char *get_tail_ptr() { return &buffer[tail]; }
+ unsigned char *get_tail_ptr(const unsigned int offset)
+ {
+ return &buffer[((tail+offset)%buffer_size)];
+ }
+ unsigned int get_tail_offset() const { return tail; }
+ void increment_tail(const unsigned int offset)
+ {
+ tail=(tail+offset)%buffer_size;
+ in-=offset;
+ }
+};
+
+//
+//
+//
+
+static inline int clamp(const int v)
+{
+ if (v<-32768) return -32768;
+ if (v>32767) return 32767;
+ return v;
+}
+
+//
+//
+//
+
+spu_device::sample_cache::~sample_cache()
+{
+ global_free_array(data);
+ while (loop_cache)
+ {
+ sample_loop_cache *lc=loop_cache;
+ loop_cache=lc->next;
+ global_free(lc);
+ }
+}
+
+//
+//
+//
+
+signed short *spu_device::sample_cache::get_sample_pointer(const unsigned int addr)
+{
+ if ((addr>=start) && (addr<end))
+ {
+ return data+(((addr-start)>>4)*28);
+ } else
+ {
+ return NULL;
+ }
+}
+
+//
+//
+//
+
+bool spu_device::sample_cache::get_sample_pointer(const unsigned int addr, cache_pointer *cp)
+{
+ cp->reset();
+ if ((cp->ptr=get_sample_pointer(addr)))
+ {
+ cp->cache=this;
+ add_ref();
+ return true;
+ }
+ return false;
+}
+
+//
+//
+//
+
+bool spu_device::sample_cache::get_loop_pointer(cache_pointer *cp)
+{
+ cp->reset();
+ if ((cp->ptr=loop))
+ {
+ cp->cache=this;
+ add_ref();
+ return true;
+ }
+ return false;
+}
+
+//
+//
+//
+
+unsigned int spu_device::sample_cache::get_sample_address(const signed short *ptr) const
+{
+ if ((ptr>=data) && (ptr<=dend))
+ {
+ return start+(((ptr-data)/28)<<4);
+ } else
+ {
+ return -1;
+ }
+}
+
+//
+//
+//
+
+spu_device::sample_loop_cache *spu_device::sample_cache::find_loop_cache(const unsigned int lpend, const unsigned int lpstart)
+{
+ sample_loop_cache *lc;
+ for (lc=loop_cache; lc; lc=lc->next)
+ if ((lc->loopend==lpend) && (lc->loopstart==lpstart)) break;
+ return lc;
+}
+
+//
+//
+//
+
+void spu_device::sample_cache::add_loop_cache(sample_loop_cache *lc)
+{
+ lc->next=loop_cache;
+ loop_cache=lc;
+}
+
+//
+//
+//
+
+bool spu_device::sample_cache::is_valid_pointer(signed short *ptr) const
+{
+ if ((ptr>=data) && (data<=dend)) return true;
+ for (sample_loop_cache *slc=loop_cache; slc; slc=slc->next)
+ if ((ptr>=slc->data) && (ptr<(slc->data+num_loop_cache_samples)))
+ return true;
+ return false;
+}
+
+//
+//
+//
+
+bool spu_device::sample_cache::try_update(spu_device *spu)
+{
+ if ((invalid_start>=start) && (invalid_end<=end))
+ {
+ adpcm_packet *ap=(adpcm_packet *)(spu->spu_ram+start);
+ unsigned int a;
+ unsigned int loop=0;
+
+ for (a=start; a<=end; a+=16, ap++)
+ {
+ if ((ap->flags&adpcmflag_loop_start) && (ap->flags&adpcmflag_loop)) loop=a;
+ if (ap->flags&adpcmflag_end) break;
+ }
+
+ if ((a==(end-16)) && (loop==loopaddr))
+ {
+ #ifdef show_cache_update
+ printf("updating %p: ",this);
+ #endif
+
+ if (invalid_start==start)
+ {
+ #ifdef show_cache_update
+ printf("using end values");
+ #endif
+
+ update_decoder=decoder;
+ } else
+ if (invalid_start!=last_update_end)
+ {
+ #ifdef show_cache_update
+ printf("resetting decoder (istrt=%08x lupd=%08x)",invalid_start,last_update_end);
+ #endif
+
+ update_decoder.reset();
+ }
+ #ifdef show_cache_update
+ printf("\n");
+ #endif
+
+ signed short *dp=data+(((invalid_start-start)>>4)*28);
+ ap=(adpcm_packet *)(spu->spu_ram+invalid_start);
+ for (a=invalid_start; a<invalid_end; a+=16, ap++)
+ dp=update_decoder.decode_packet(ap,dp);
+
+ if (invalid_end==end)
+ {
+ #ifdef show_cache_update
+ printf("updating end values\n");
+ #endif
+ decoder=update_decoder;
+ }
+ last_update_end=invalid_end;
+
+ for (sample_loop_cache *lc=loop_cache; lc; lc=lc->next)
+ {
+ if (invalid_start==lc->loopstart)
+ {
+ adpcm_decoder tmp=decoder;
+ dp=lc->data;
+ signed short *dpend=dp+lc->len;
+ unsigned int adr=lc->loopstart;
+ for (unsigned int i=0; ((i<num_loop_cache_packets) && (dp<dpend)); i++, adr+=16)
+ dp=tmp.decode_packet((adpcm_packet *)(spu->spu_ram+adr),dp);
+ }
+ }
+
+ invalid_end=0;
+ invalid_start=0xffffffff;
+ valid=true;
+
+ for (a=start; a<end; a+=16, ap++)
+ {
+ spu->cache[a>>4]=this;
+ }
+
+ add_ref();
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//
+//
+//
+
+void spu_device::cache_pointer::reset()
+{
+ if (cache)
+ {
+ ptr=NULL;
+ cache->remove_ref();
+ cache=NULL;
+ }
+}
+
+//
+//
+//
+
+spu_device::cache_pointer &spu_device::cache_pointer::operator =(const cache_pointer &other)
+{
+ if (cache) cache->remove_ref();
+ ptr=other.ptr;
+ cache=other.cache;
+ if (cache) cache->add_ref();
+ return *this;
+}
+
+//
+//
+//
+
+bool spu_device::cache_pointer::update(spu_device *spu)
+{
+ if ((cache) && (! cache->valid))
+ {
+/* log(log_spu,"cache_pointer::update: block %08x-%08x invalidated %08x-%08x\n",
+ cache->start,
+ cache->end,
+ cache->invalid_start,
+ cache->invalid_end);*/
+
+ if (! cache->try_update(spu))
+ {
+ // Cache is invalid, calculate play address offset from start of
+ // old cache block
+
+ unsigned int off=ptr-cache->data,
+ addr=cache->start;
+
+ // Release cache block and get updated one
+
+ spu->translate_sample_addr(addr,this);
+
+ // Calculate play address in new cache block
+
+ ptr=cache->data+off;
+
+ if (ptr>=cache->dend)
+ {
+ // Play address is out of bounds in new cache block, release it and get a
+ // new one starting at the current play address
+
+ spu->translate_sample_addr(addr+((off/28)<<4),this);
+ }
+ }
+ }
+
+ // Return false if we do not have a cache block or the play address is invalid
+
+ if ((cache) && ((ptr>=cache->data) && (ptr<cache->dend)))
+ {
+ return true;
+ } else
+ {
+ reset();
+ return false;
+ }
+}
+
+//
+//
+//
+
+signed short *adpcm_decoder::decode_packet(adpcm_packet *ap, signed short *dp)
+{
+ int shift=ap->info&0xf,
+ filter=ap->info>>4,
+ f0=filter_coef[filter][0],
+ f1=filter_coef[filter][1];
+
+ for (int i=0; i<14; i++)
+ {
+ unsigned char b=ap->data[i];
+ short bl=(b&0xf)<<12,
+ bh=(b>>4)<<12;
+
+ bl=(bl>>shift)+(((l0*f0)+(l1*f1)+32)>>6);
+ *dp++=bl;
+ l1=l0;
+ l0=bl;
+
+ bh=(bh>>shift)+(((l0*f0)+(l1*f1)+32)>>6);
+ *dp++=bh;
+ l1=l0;
+ l0=bh;
+ }
+
+ return dp;
+}
+
+//
+//
+//
+
+static int shift_register15(int &shift)
+{
+ int bit0, bit1, bit14;
+
+ bit0 = shift & 1;
+ bit1 = (shift & 2) >> 1;
+ bit14 = (bit0 ^ bit1) ^ 1;
+ shift >>= 1;
+ shift |= (bit14 << 14);
+ return bit0;
+}
+
+//
+//
+//
+
+//-------------------------------------------------
+// spu_device - constructor
+//-------------------------------------------------
+
+spu_device::spu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, SPU, "SPU", tag, owner, clock, "spu", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_irq_handler(*this),
+ dirty_flags(-1),
+ status_enabled(false),
+ xa_voll(0x8000),
+ xa_volr(0x8000),
+ changed_xa_vol(0)
+{
+}
+
+//-------------------------------------------------
+// static_set_irqf - configuration helper to set
+// the IRQ callback
+//-------------------------------------------------
+
+void spu_device::device_start()
+{
+ m_irq_handler.resolve_safe();
+
+ voice=new voiceinfo [24];
+ spu_ram=new unsigned char [spu_ram_size];
+
+ xa_buffer=new stream_buffer(xa_sector_size,xa_buffer_sectors);
+ cdda_buffer=new stream_buffer(cdda_sector_size,cdda_buffer_sectors);
+
+ init_stream();
+
+ cache=new sample_cache *[spu_ram_size>>4];
+ memset(cache,0,(spu_ram_size>>4)*sizeof(sample_cache *));
+
+ // register save state stuff
+ save_item(NAME(reg)); // this covers all spureg.* plus the reverb parameter block
+ save_item(NAME(xa_cnt));
+ save_item(NAME(cdda_cnt));
+ save_item(NAME(xa_freq));
+ save_item(NAME(cdda_freq));
+ save_item(NAME(xa_channels));
+ save_item(NAME(xa_spf));
+ save_item(NAME(cur_frame_sample));
+ save_item(NAME(cur_generate_sample));
+ save_pointer(NAME(spu_ram), spu_ram_size);
+
+ save_item(NAME(xa_buffer->head));
+ save_item(NAME(xa_buffer->tail));
+ save_item(NAME(xa_buffer->in));
+ save_item(NAME(xa_buffer->sector_size));
+ save_item(NAME(xa_buffer->num_sectors));
+ save_item(NAME(xa_buffer->buffer_size));
+ save_item(NAME(xa_buffer->buffer));
+
+ save_item(NAME(cdda_buffer->head));
+ save_item(NAME(cdda_buffer->tail));
+ save_item(NAME(cdda_buffer->in));
+ save_item(NAME(cdda_buffer->sector_size));
+ save_item(NAME(cdda_buffer->num_sectors));
+ save_item(NAME(cdda_buffer->buffer_size));
+ save_item(NAME(cdda_buffer->buffer));
+}
+
+void spu_device::device_reset()
+{
+ cur_reverb_preset = NULL;
+ cur_frame_sample = 0;
+ cur_generate_sample = 0;
+
+ sample_cache::cache_size = 0;
+
+ status_enabled = false;
+ xa_voll = xa_volr = 0x8000;
+ dirty_flags = -1;
+ changed_xa_vol = 0;
+
+ xa_cnt=0;
+ xa_freq=0;
+ xa_channels=2;
+ xa_spf=0;
+ xa_out_ptr=0;
+ xa_playing=false;
+ memset(xa_last,0,sizeof(xa_last));
+
+ cdda_cnt=0;
+ cdda_playing=false;
+ m_cd_out_ptr = 0;
+
+ memset(spu_ram,0,spu_ram_size);
+ memset(reg,0,0x200);
+ memset(voice,0,sizeof(voiceinfo)*24);
+
+ spureg.status|=(1<<7)|(1<<10);
+
+ memset(cache,0,(spu_ram_size>>4)*sizeof(sample_cache *));
+
+ for (unsigned int i=0; i<4; i++)
+ output_buf[i]=new unsigned char [output_buffer_size];
+ output_head=output_tail=output_size=0;
+
+ noise_t=0;
+ noise_seed=12345;
+ noise_cur=shift_register15(noise_seed)?0x7fff:0x8000;
+}
+
+void spu_device::device_post_load()
+{
+ // invalidate the SPURAM cache
+ invalidate_cache(0, spu_ram_size);
+ flush_output_buffer();
+
+ // mark everything dirty
+ dirty_flags = -1;
+
+ // kill and reallocate reverb to avoid artifacts
+ global_free(rev);
+ rev = new reverb(44100);
+
+ // and do some update processing
+ update_reverb();
+ update_key();
+ update_voice_state();
+ update_irq_event();
+}
+
+//
+//
+//
+void spu_device::device_stop()
+{
+ for (unsigned int i=0; i<4; i++)
+ global_free_array(output_buf[i]);
+
+ kill_stream();
+
+ global_free_array(spu_ram);
+ invalidate_cache(0,spu_ram_size);
+ global_free_array(cache);
+ global_free(xa_buffer);
+ global_free(cdda_buffer);
+ global_free_array(voice);
+}
+//
+//
+//
+
+void spu_device::init_stream()
+{
+ const unsigned int hz=44100;
+
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, hz);
+
+ rev=new reverb(hz);
+
+ cdda_freq=(unsigned int)((44100.0f/(float)hz)*4096.0f);
+ freq_multiplier=(float)spu_base_frequency_hz/(float)hz;
+}
+
+//
+//
+//
+
+void spu_device::kill_stream()
+{
+ global_free(rev);
+ rev=NULL;
+}
+
+//
+//
+//
+
+void spu_device::reinit_sound()
+{
+ kill_stream();
+ init_stream();
+ flush_output_buffer();
+ dirty_flags|=dirtyflag_voice_mask;
+}
+
+//
+//
+//
+
+void spu_device::kill_sound()
+{
+ kill_stream();
+}
+
+//
+//
+//
+
+READ16_MEMBER( spu_device::read )
+{
+ unsigned short ret=0, *rp=(unsigned short *)(reg+((offset*2)&0x1ff));
+
+ m_stream->update();
+
+ ret=*rp;
+
+ #ifdef debug_spu_registers
+ printf("spu: read word %08x = %04x [%s]\n",
+ offset*2,
+ ret,
+ get_register_name(offset*2));
+ #endif
+
+ return ret;
+}
+
+//
+//
+//
+
+WRITE16_MEMBER( spu_device::write )
+{
+ #ifdef debug_spu_registers
+ printf("spu: write %08x = %04x [%s]\n",
+ offset*2,
+ data,
+ get_register_name(offset*2));
+ #endif
+
+ m_stream->update();
+
+ const unsigned int a=(offset*2)&0x1ff;
+ switch (a)
+ {
+ case spureg_trans_addr:
+ spureg.trans_addr=data;
+ taddr=data<<3;
+ break;
+
+ case spureg_data:
+ dirty_flags|=dirtyflag_ram;
+ write_data(data);
+ break;
+
+ default:
+ {
+ unsigned short *rp=(unsigned short *)(reg+a);
+
+ if ((a==spureg_irq_addr) ||
+ ((a==spureg_ctrl) && ((rp[0]^data)&spuctrl_irq_enable)))
+ dirty_flags|=dirtyflag_irq;
+
+ *rp=data;
+ break;
+ }
+ }
+
+ if ((a>spureg_reverb_config) && (a<=spureg_last))
+ dirty_flags|=dirtyflag_reverb;
+
+ if (a<=spureg_voice_last)
+ {
+ unsigned int v=(a>>4),r=(a&0xf);
+ if (r==0xe)
+ {
+ voice[v].forceloop=true;
+ }
+
+ dirty_flags|=(1<<v);
+ }
+
+ update_key();
+ update_vol(a);
+ update_voice_state();
+ update_irq_event();
+}
+
+//
+//
+//
+
+void spu_device::update_vol(const unsigned int addr)
+{
+ if (addr<0x180)
+ {
+ unsigned int ch=(addr&0xf)>>1;
+ if (ch<2)
+ {
+ unsigned int v=addr>>4;
+ unsigned short newval=*(unsigned short *)(reg+addr);
+
+ if (newval&0x8000)
+ {
+ #if 0
+ printf("cur=%04x on=%d",voice[v].vol[ch],(spureg.chon>>ch)&1);
+ switch ((newval>>13)&3)
+ {
+ case 0: printf("linear inc: phase=%d val=%02x\n",(newval>>12)&1,newval&0x7f); break;
+ case 1: printf("linear dec: phase=%d val=%02x\n",(newval>>12)&1,newval&0x7f); break;
+ case 2: printf("exp inc: phase=%d val=%02x\n",(newval>>12)&1,newval&0x7f); break;
+ case 3: printf("exp dec: phase=%d val=%02x\n",(newval>>12)&1,newval&0x7f); break;
+ }
+ #endif
+ }
+ else
+ {
+ voice[v].vol[ch]=((int)newval<<17)>>17;
+ }
+ }
+ }
+}
+
+//
+//
+//
+
+void spu_device::write_data(const unsigned short data)
+{
+ #ifdef debug_spu_registers
+ printf("spu: write data %04x @ %04x\n",data,taddr);
+ #endif
+
+ assert(taddr<spu_ram_size);
+ if (cache[taddr>>4]) flush_cache(cache[taddr>>4],taddr,taddr+2);
+ *((unsigned short *)(spu_ram+taddr))=data;
+ taddr+=2;
+}
+
+//
+//
+//
+
+void spu_device::update_key()
+{
+ dirty_flags|=((spureg.keyon|spureg.keyoff)&dirtyflag_voice_mask);
+
+ if (spureg.keyoff)
+ {
+ unsigned int d=spureg.keyoff;
+ for (int i=0; i<24; i++, d>>=1)
+ if (d&1) key_off(i);
+ }
+
+ if (spureg.keyon)
+ {
+ unsigned int d=spureg.keyon;
+ for (int i=0; i<24; i++, d>>=1)
+ if (d&1) key_on(i);
+ spureg.chon|=spureg.keyon;
+ }
+
+ spureg.keyon=spureg.keyoff=0;
+}
+
+//
+//
+//
+
+void spu_device::flush_cache(sample_cache *sc,
+ const unsigned int istart,
+ const unsigned int iend)
+{
+ for (unsigned int a=sc->start; a<sc->end; a+=16)
+ cache[a>>4]=NULL;
+
+/* log_static(log_spu,"cache_invalidate: %08x->%08x\n",
+ sc->start,
+ sc->end);*/
+
+ sc->invalid_start=min(sc->invalid_start,istart);
+ sc->invalid_end=max(sc->invalid_end,iend);
+ sc->valid=false;
+ sc->remove_ref();
+}
+
+//
+//
+//
+
+void spu_device::invalidate_cache(const unsigned int st, const unsigned int en)
+{
+ for (unsigned int a=st; a<en; a+=16)
+ if (cache[a>>4]) flush_cache(cache[a>>4],st,en);
+}
+
+//
+//
+//
+
+spu_device::sample_cache *spu_device::get_sample_cache(const unsigned int addr)
+{
+// log_static(log_spu,"get_sample_cache: %08x\n",addr);
+
+ assert(addr<spu_ram_size);
+ sample_cache *sc=cache[addr>>4];
+ if (sc) return sc;
+
+ unsigned int loop=0;
+
+ sc=new sample_cache;
+ sc->valid=true;
+ sc->start=addr;
+ sc->loop=NULL;
+
+ adpcm_packet *ap=(adpcm_packet *)(spu_ram+sc->start);
+ unsigned int a;
+ for (a=addr; a<(512*1024); a+=16, ap++)
+ {
+ if (cache[a>>4]) flush_cache(cache[a>>4],a,a+16);
+ cache[a>>4]=sc;
+
+ if ((ap->flags&adpcmflag_loop_start) && (ap->flags&adpcmflag_loop)) loop=a;
+ if (ap->flags&adpcmflag_end) break;
+ }
+
+ if ((a < 0x80000) && (ap->flags&adpcmflag_loop)) sc->is_loop=true;
+
+ sc->end=min(spu_ram_size,a+16);
+
+ unsigned int sz=((sc->end-sc->start)>>4)*28;
+ sc->data=new signed short [sz];
+ sample_cache::cache_size+=sz<<1;
+ sc->loopaddr=loop;
+ if (loop) sc->loop=sc->data+(((loop-sc->start)>>4)*28);
+
+ signed short *dp=sc->data;
+ ap=(adpcm_packet *)(spu_ram+sc->start);
+
+ for (a=sc->start; a<sc->end; a+=16, ap++)
+ dp=sc->decoder.decode_packet(ap,dp);
+
+ sc->dend=dp;
+ sc->add_ref();
+
+/* log_static(log_spu,"cache_add: %08x->%08x\n",
+ sc->start,
+ sc->end);*/
+
+ return sc;
+}
+
+//
+//
+//
+
+bool spu_device::translate_sample_addr(const unsigned int addr, cache_pointer *cp)
+{
+ assert((addr&0xf)==0);
+ cp->reset();
+ if ((cp->cache=get_sample_cache(addr)))
+ {
+ cp->ptr=cp->cache->data+(((addr-cp->cache->start)>>4)*28);
+ cp->cache->add_ref();
+ return true;
+ }
+ return false;
+}
+
+//
+// Get distance in input samples to next IRQ for voice
+//
+
+unsigned int spu_device::get_irq_distance(const voiceinfo *vi)
+{
+ if (spureg.ctrl&spuctrl_irq_enable)
+ {
+ unsigned int irq_addr=spureg.irq_addr<<3;
+ signed short *irq_ptr;
+
+ if ((irq_ptr=vi->play.cache->get_sample_pointer(irq_addr)))
+ {
+ // IRQ address is inside this voices current cache block. Return distance
+ // if current play address is lower, or equal (and irq has not already
+ // triggered)
+
+ if ((vi->play.ptr<irq_ptr) ||
+ ((! vi->hitirq) && (vi->play.ptr==irq_ptr)))
+ {
+ return irq_ptr-vi->play.ptr;
+ }
+ }
+
+ if ((vi->loop) &&
+ (irq_ptr=vi->loop.cache->get_sample_pointer(irq_addr)) &&
+ (irq_ptr>=vi->loop.ptr))
+ {
+ // IRQ address is inside this voices loop cache, return distance
+
+ return (vi->play.cache->dend-vi->play.ptr)+
+ (irq_ptr-vi->loop.ptr);
+ }
+ }
+
+ // IRQs not enabled, or IRQ address not reachable by voice, distance is spu_infinity
+
+ return spu_infinity;
+}
+
+//
+//
+//
+
+void spu_device::update_voice_events(voiceinfo *vi)
+{
+ if (vi->pitch)
+ {
+ // Calculate time until end of sample in output samples
+
+ vi->samplestoend=(unsigned int)((((INT64)(vi->play.cache->dend-vi->play.ptr)<<12)-vi->dptr)+(vi->pitch-1))/vi->pitch;
+ if (vi->inloopcache)
+ {
+ // Voice is inside loop cache, return time until end of that if lower
+
+ assert(vi->lcptr<vi->loop_cache->len);
+ vi->samplestoend=min(vi->samplestoend,
+ (unsigned int)((((INT64)(vi->loop_cache->len-vi->lcptr)<<12)-vi->dptr)+(vi->pitch-1))/vi->pitch);
+ }
+
+ // Calculate time until next IRQ in output samples
+
+ unsigned int irqdist=get_irq_distance(vi);
+ if (irqdist!=spu_infinity)
+ {
+ // Convert IRQ input sample distance to output samples
+
+ vi->samplestoirq=(unsigned int)(((((INT64)irqdist)<<12)-vi->dptr)+(vi->pitch-1))/vi->pitch;
+ } else
+ {
+ vi->samplestoirq=spu_infinity;
+ }
+ } else
+ {
+ // Voice pitch is 0, distance to sample end and IRQ is spu_infinity
+
+ vi->samplestoend=vi->samplestoirq=spu_infinity;
+ }
+}
+
+//
+//
+//
+
+bool spu_device::update_voice_state(const unsigned int v)
+{
+ voicereg *vr=&spureg.voice[v];
+ voiceinfo *vi=&voice[v];
+
+ // Update sample cache if necessary
+
+ if (! vi->play.update(this))
+ return false;
+ assert(vi->play.ptr<vi->play.cache->dend);
+
+ // Get pitch from voice register and apply frequency multiplier if
+ // there is one in effect
+
+ vi->pitch=vr->pitch;
+ vi->pitch=(unsigned int)(vi->pitch*freq_multiplier);
+
+ // Update event times
+
+ update_voice_events(vi);
+
+ return true;
+}
+
+//
+//
+//
+
+spu_device::sample_loop_cache *spu_device::get_loop_cache(sample_cache *cache, const unsigned int lpen, sample_cache *lpcache, const unsigned int lpst)
+{
+ // Check for existing loop cache
+
+ sample_loop_cache *ret=lpcache->find_loop_cache(lpen,lpst);
+ if (! ret)
+ {
+ // No loop cache exists for this address pair, create a new one
+
+ sample_loop_cache *lc=new sample_loop_cache;
+ lc->loopend=lpen;
+ lc->loopstart=lpst;
+ lpcache->add_loop_cache(lc);
+ ret=lc;
+
+ // Decode samples from start address using decoder state at end address
+
+ unsigned int adr=lpst;
+ adpcm_decoder tmp=cache->decoder;
+ signed short *dp=lc->data;
+ for (unsigned int i=0; ((i<num_loop_cache_packets) &&
+ (adr<lpcache->end)); i++, adr+=16)
+ dp=tmp.decode_packet((adpcm_packet *)(spu_ram+adr),dp);
+
+ #ifdef log_loop_cache
+ log(log_spu,"spu: add loop cache %08x %08x->%08x (end at %08x)\n",lc,lpen,lpst,adr);
+ #endif
+
+ lc->len=dp-lc->data;
+ }
+
+ return ret;
+}
+
+//
+//
+//
+
+void spu_device::update_voice_loop(const unsigned int v)
+{
+// voicereg *vr=&spureg.voice[v];
+ voiceinfo *vi=&voice[v];
+ unsigned int ra = 0;
+
+ // Check for looping using the voices repeat address register and get
+ // a pointer to the loop position if enabled
+
+ vi->loop.reset();
+
+ // If loop address is not forced get the loop pointer from the cache
+ // block (if present)
+
+ if ((! voice[v].forceloop) &&
+ (vi->play.cache->get_loop_pointer(&vi->loop)))
+ {
+ ra=vi->play.cache->loopaddr;
+ }
+
+ // Otherwise use the address set in repaddr (if set)
+
+ if ((! vi->loop) && (vi->play.cache->is_loop))
+ {
+ ra=spureg.voice[v].repaddr<<3;
+ ra=(ra+0xf)&~0xf;
+ const adpcm_packet *ap=ra?(adpcm_packet *)(spu_ram+ra):NULL;
+
+ if (ap)
+ {
+ if (ap->flags&adpcmflag_loop)
+ {
+ // Repeat address points to a block with loop flag set
+
+ if (! vi->play.cache->get_sample_pointer(ra,&vi->loop))
+ {
+ // Repeat address is in a different block
+
+ translate_sample_addr(ra,&vi->loop);
+ }
+ }
+ }
+ }
+
+ // Update loop cache
+
+ if (vi->loop)
+ {
+ vi->loop_cache=get_loop_cache(vi->play.cache,vi->play.cache->end,vi->loop.cache,ra);
+ }
+}
+
+//
+//
+//
+
+void spu_device::update_voice_state()
+{
+ // If RAM or irq state is dirty make all voices dirty
+
+ if (dirty_flags&(dirtyflag_ram|dirtyflag_irq))
+ {
+ dirty_flags|=dirtyflag_voice_mask;
+ dirty_flags&=~(dirtyflag_ram|dirtyflag_irq);
+ }
+
+ // Update state for dirty voices
+
+ if (dirty_flags&dirtyflag_voice_mask)
+ {
+ unsigned int voicemask=1;
+ for (unsigned int i=0; i<24; i++, voicemask<<=1)
+ if (dirty_flags&voicemask)
+ {
+ update_voice_state(i);
+ dirty_flags&=~voicemask;
+ }
+ }
+}
+
+//
+// Process voice state and build output segments
+//
+// Input: const unsigned int v Voice number
+// const unsigned int sz Amount of time to process (in output samples)
+// unsigned int *tleft Returned number of output samples remaining
+//
+// Output: bool true if voice is still playing
+//
+
+bool spu_device::process_voice(const unsigned int v,
+ const unsigned int sz,
+ void *ptr,
+ void *fmnoise_ptr,
+ void *outxptr,
+ unsigned int *tleft)
+{
+ bool ret=true;
+ unsigned int voice_mask=1<<v,
+ num=sz,
+ off=0;
+ bool noise=((spureg.noise&voice_mask)!=0),
+ fm=((spureg.fm&voice_mask)!=0);
+ voiceinfo *vi=&voice[v];
+
+ // Early exit if we don't have a sample cache block
+
+ if (! vi->play)
+ {
+ *tleft=sz;
+ return false;
+ }
+
+ // Generate samples
+
+ while (num)
+ {
+ // Play up to end of sample, envelope event, or IRQ, whichever comes first
+
+ unsigned int ntoplay=fm?1:num,
+ nextevent=min(vi->samplestoend,
+ min(vi->samplestoirq,vi->envsamples));
+ ntoplay=min(ntoplay,nextevent);
+
+ if (ntoplay)
+ {
+ signed short *noisep=NULL;
+
+ if (fm)
+ {
+ int fmv=((signed short *)fmnoise_ptr)[off<<1];
+ vi->pitch=spureg.voice[v].pitch;
+ vi->pitch=(unsigned int)(vi->pitch*freq_multiplier);
+ vi->pitch=(vi->pitch*(fmv+32768))>>15;
+ } else
+ if (noise)
+ {
+ noisep=(signed short *)fmnoise_ptr;
+ noisep+=(off<<1);
+ }
+
+ signed short *dp=(signed short *)ptr,
+ *outxp=(signed short *)outxptr;
+ dp+=off<<1;
+ if (outxp) outxp+=off<<1;
+
+ generate_voice(v, dp, noisep, outxp, ntoplay);
+
+ num-=ntoplay;
+ off+=ntoplay;
+
+ vi->samplestoend-=ntoplay;
+ if (vi->samplestoirq!=spu_infinity) vi->samplestoirq-=ntoplay;
+ if (vi->envsamples!=spu_infinity) vi->envsamples-=ntoplay;
+ vi->hitirq=false;
+ }
+
+ // Determine which event(s) we hit
+
+ bool hitend=fm?(vi->play.ptr>=vi->play.cache->dend)
+ :(vi->samplestoend==0),
+ hitirq=(vi->samplestoirq==0),
+ hitenv=(vi->envsamples==0);
+
+ // Update loop cache pointer if we are playing a loop cache
+
+ if ((vi->inloopcache) && (vi->lcptr>=vi->loop_cache->len))
+ {
+ vi->inloopcache=false;
+ hitend=(vi->play.ptr>=vi->play.cache->dend);
+
+ #ifdef log_loop_cache
+ log(log_spu,"spu: %d leave loop cache %08x, lcptr=%d, hitend=%d\n",
+ v,
+ vi->loop_cache,
+ vi->lcptr,
+ hitend);
+ #endif
+ }
+
+ bool stopped=false;
+
+ if (hitend)
+ {
+ // End of sample reached, calculate how far we overshot
+
+ unsigned int poff=vi->play.ptr-vi->play.cache->dend;
+
+ // Make sure loop info is up to date and end the current output segment
+
+ update_voice_loop(v);
+ if (vi->loop)
+ {
+ // We are looping, set play address to loop address and account for
+ // overshoot
+
+ vi->play=vi->loop;
+ vi->play.ptr+=poff;
+ vi->lcptr=poff;
+ vi->inloopcache=(poff<vi->loop_cache->len);
+
+ #ifdef log_loop_cache
+ if (vi->inloopcache)
+ log(log_spu,"spu: %d enter loop cache %08x, lcptr=%d\n",
+ v,
+ vi->loop_cache,
+ vi->lcptr);
+ #endif
+
+ // Check for IRQ at/just after repeat address
+
+ if (spureg.ctrl&spuctrl_irq_enable)
+ {
+ if (spureg.voice[v].repaddr==spureg.irq_addr)
+ hitirq=true;
+
+ signed short *irq_ptr;
+ unsigned int irq_addr=spureg.irq_addr<<3;
+
+ if ((irq_ptr=vi->loop.cache->get_sample_pointer(irq_addr)))
+ {
+ if ((irq_ptr>=vi->loop.ptr) &&
+ (vi->play.ptr>=irq_ptr))
+ hitirq=true;
+ }
+ }
+ } else
+ {
+ // Not looping, stop voice
+
+ spureg.reverb&=~(1<<v);
+ stopped=true;
+
+ // Check for IRQ at repeat address
+
+ if (spureg.ctrl&spuctrl_irq_enable)
+ {
+ if (spureg.voice[v].repaddr==spureg.irq_addr)
+ hitirq=true;
+ }
+ }
+
+ assert((stopped) || (vi->play.ptr<vi->play.cache->dend));
+ } else
+ {
+ assert(vi->play.ptr<vi->play.cache->dend);
+ }
+
+ if (hitirq)
+ {
+ // Went past IRQ address, trigger IRQ
+ m_irq_handler(1);
+
+ vi->samplestoirq=spu_infinity;
+ vi->hitirq=true;
+ }
+
+ if (hitenv)
+ {
+ // Envelope event, update the envelope (stop if necessary), and start
+ // a new output segment
+
+ stopped=((stopped) || (! update_envelope(v)));
+ }
+
+ if (stopped)
+ {
+ // Voice is now stopped
+
+ ret=false;
+ break;
+ }
+
+ // Update voice event times
+
+ update_voice_events(vi);
+ }
+
+ // Set current volume register
+
+ spureg.voice[v].curvol=(unsigned short)(vi->env_level*32767.0f);
+
+ // Return how much time is left and whether or not the voice is still playing
+
+ *tleft=num;
+ return ret;
+}
+
+//
+// Generate voice output samples
+//
+// Inputs: const unsigned int v Voice number
+// void *ptr Output buffer (if no reverb)
+// const unsigned int sz Number of samples to output
+//
+
+void spu_device::generate_voice(const unsigned int v,
+ void *ptr,
+ void *noiseptr,
+ void *outxptr,
+ const unsigned int sz)
+{
+ voiceinfo *vi=&voice[v];
+ signed short *fp,*sp;
+ unsigned int n=sz;
+
+ // Get input pointer
+
+ if (vi->inloopcache)
+ {
+ sp=vi->loop_cache->data+vi->lcptr;
+ } else
+ {
+ sp=vi->play.ptr;
+ }
+ fp=sp;
+
+ unsigned int dptr=vi->dptr;
+
+ // Get output pointer (and advance output offset)
+
+ signed short *dp=(signed short *)ptr;
+ signed short *outxp=(signed short *)outxptr;
+
+ // Calculate fixed point envelope levels/deltas premultiplied by channel volume
+
+ int vol_l=outxptr?0x3fff:vi->vol[0],
+ vol_r=outxptr?0x3fff:vi->vol[1],
+ env_l=(int)(vi->env_level*2.0f*vol_l),
+ env_r=(int)(vi->env_level*2.0f*vol_r),
+ envdelta_l=(int)(vi->env_delta*2.0f*vol_l),
+ envdelta_r=(int)(vi->env_delta*2.0f*vol_r);
+
+ // Update the segments envelope level
+
+ vi->env_level+=(n*vi->env_delta);
+
+ if (noiseptr)
+ {
+ INT64 dptr=((INT64)n*vi->pitch)+vi->dptr;
+ unsigned int d=(unsigned int)(dptr>>12);
+ vi->dptr=(unsigned int)(dptr&0xfff);
+ vi->play.ptr+=d;
+ if (vi->inloopcache) vi->lcptr+=d;
+
+ sp=(signed short *)noiseptr;
+
+ if (outxp)
+ {
+ while (n--)
+ {
+ int vl=*sp++,
+ vr=*sp++,
+ l=(vl*env_l)>>15,
+ r=(vr*env_r)>>15;
+ env_l+=envdelta_l;
+ env_r+=envdelta_r;
+
+ outxp[0]=l;
+ outxp[1]=r;
+ outxp+=2;
+
+ l=(l*vi->vol[0])>>15;
+ r=(r*vi->vol[1])>>15;
+
+ dp[0]=clamp(l+dp[0]);
+ dp[1]=clamp(r+dp[1]);
+ dp+=2;
+ }
+ } else
+ {
+ while (n--)
+ {
+ int vl=*sp++,
+ vr=*sp++,
+ l=(vl*env_l)>>15,
+ r=(vr*env_r)>>15;
+ env_l+=envdelta_l;
+ env_r+=envdelta_r;
+
+ dp[0]=clamp(l+dp[0]);
+ dp[1]=clamp(r+dp[1]);
+ dp+=2;
+ }
+ }
+ } else
+ {
+ if (1) //settings.sound_interpolate)
+ {
+ unsigned int num_stitch=0;
+ signed short *ep;
+
+ // Linear interpolation enabled, calculate how many samples we will
+ // read from input data
+
+ INT64 fracend=(((INT64)(n-1))*vi->pitch)+dptr;
+ unsigned int end=(unsigned int)(fracend>>12);
+
+ // Get pointer to last sample of input data
+
+ if (vi->inloopcache)
+ {
+ ep=vi->loop_cache->data+vi->loop_cache->len-1;
+ } else
+ {
+ ep=vi->play.cache->dend-1;
+ }
+
+ // If we read the last sample "stitching" will be necessary (inerpolation
+ // from last sample of segment to first sample of next segment)
+
+ if (((sp+end)>=ep) && (vi->pitch))
+ {
+ num_stitch=min(n,max(0x1fff/vi->pitch,1));
+ n-=num_stitch;
+ }
+
+ // Generate samples
+
+ if (outxp)
+ {
+ while (n--)
+ {
+ int v=sp[0];
+
+ v+=((sp[1]-v)*(int)dptr)>>12;
+
+ int l=(v*env_l)>>15,
+ r=(v*env_r)>>15;
+ env_l+=envdelta_l;
+ env_r+=envdelta_r;
+
+ outxp[0]=l;
+ outxp[1]=r;
+ outxp+=2;
+
+ l=(l*vi->vol[0])>>15;
+ r=(r*vi->vol[1])>>15;
+
+ dp[0]=clamp(l+dp[0]);
+ dp[1]=clamp(r+dp[1]);
+ dp+=2;
+
+ dptr+=vi->pitch;
+ sp+=(dptr>>12);
+ dptr&=0xfff;
+ }
+ }
+ else
+ {
+ while (n--)
+ {
+ int v=sp[0];
+
+ v+=((sp[1]-v)*(int)dptr)>>12;
+
+ int l=(v*env_l)>>15,
+ r=(v*env_r)>>15;
+ env_l+=envdelta_l;
+ env_r+=envdelta_r;
+
+ dp[0]=clamp(l+dp[0]);
+ dp[1]=clamp(r+dp[1]);
+ dp+=2;
+
+ dptr+=vi->pitch;
+ sp+=(dptr>>12);
+ dptr&=0xfff;
+ }
+ }
+
+ if (num_stitch)
+ {
+ // Stitch samples, get the first sample of the next segment
+
+ signed short *nsp=NULL;
+
+ if (vi->inloopcache)
+ {
+ nsp=vi->play.ptr+(vi->loop_cache->len-vi->lcptr);
+ if (nsp>=vi->play.cache->dend)
+ nsp=NULL;
+ }
+
+ if (! nsp)
+ {
+ update_voice_loop(v);
+ if (vi->loop) nsp=vi->loop_cache->data;
+ }
+
+ int ns=nsp?nsp[0]:0;
+
+ // Generate stitch samples
+
+ if (outxp)
+ {
+ while (num_stitch--)
+ {
+ int v=sp[0],
+ nv=(sp>=ep)?ns:sp[1];
+
+ v+=((nv-v)*(int)dptr)>>12;
+
+ int l=(v*env_l)>>15,
+ r=(v*env_r)>>15;
+ env_l+=envdelta_l;
+ env_r+=envdelta_r;
+
+ outxp[0]=l;
+ outxp[1]=r;
+ outxp+=2;
+
+ l=(l*vi->vol[0])>>15;
+ r=(r*vi->vol[1])>>15;
+
+ dp[0]=clamp(l+dp[0]);
+ dp[1]=clamp(r+dp[1]);
+ dp+=2;
+
+ dptr+=vi->pitch;
+ sp+=(dptr>>12);
+ dptr&=0xfff;
+ }
+ } else
+ {
+ while (num_stitch--)
+ {
+ int v=sp[0],
+ nv=(sp>=ep)?ns:sp[1];
+
+ v+=((nv-v)*(int)dptr)>>12;
+
+ int l=(v*env_l)>>15,
+ r=(v*env_r)>>15;
+ env_l+=envdelta_l;
+ env_r+=envdelta_r;
+
+ dp[0]=clamp(l+dp[0]);
+ dp[1]=clamp(r+dp[1]);
+ dp+=2;
+
+ dptr+=vi->pitch;
+ sp+=(dptr>>12);
+ dptr&=0xfff;
+ }
+ }
+ }
+ } else
+ {
+ // Generate samples with no interpolation
+
+ if (outxp)
+ {
+ while (n--)
+ {
+ int l=(sp[0]*env_l)>>15,
+ r=(sp[0]*env_r)>>15;
+ env_l+=envdelta_l;
+ env_r+=envdelta_r;
+
+ outxp[0]=l;
+ outxp[1]=r;
+ outxp+=2;
+
+ l=(l*vi->vol[0])>>15;
+ r=(r*vi->vol[1])>>15;
+
+ dp[0]=clamp(l+dp[0]);
+ dp[1]=clamp(r+dp[1]);
+ dp+=2;
+
+ dptr+=vi->pitch;
+ sp+=(dptr>>12);
+ dptr&=0xfff;
+ }
+ } else
+ {
+ while (n--)
+ {
+ int l=(sp[0]*env_l)>>15,
+ r=(sp[0]*env_r)>>15;
+ env_l+=envdelta_l;
+ env_r+=envdelta_r;
+
+ dp[0]=clamp(l+dp[0]);
+ dp[1]=clamp(r+dp[1]);
+ dp+=2;
+
+ dptr+=vi->pitch;
+ sp+=(dptr>>12);
+ dptr&=0xfff;
+ }
+ }
+ }
+
+ // Update segment pointer
+
+ vi->play.ptr+=sp-fp;
+ vi->dptr=dptr;
+ if (vi->inloopcache)
+ vi->lcptr=sp-vi->loop_cache->data;
+ }
+}
+
+//
+//
+//
+
+bool spu_device::update_envelope(const int v)
+{
+ while (voice[v].envsamples==0)
+ {
+ voice[v].env_state++;
+
+ switch (voice[v].env_state)
+ {
+ case 1: // decay
+ voice[v].env_level=1.0f;
+ voice[v].env_delta=voice[v].env_dr;
+ if (voice[v].env_dr!=0.0f)
+ {
+ voice[v].envsamples=(unsigned int)((voice[v].env_sl-1.0f)/voice[v].env_dr);
+ } else
+ {
+ voice[v].envsamples=spu_infinity;
+ }
+ break;
+
+ case 2: // sustain
+ voice[v].env_level=voice[v].env_sl;
+ voice[v].env_delta=voice[v].env_sr;
+
+ if (voice[v].env_sr>0.0f)
+ {
+ voice[v].envsamples=(unsigned int)((1.0f-voice[v].env_level)/voice[v].env_sr);
+ } else
+ if (voice[v].env_sr<0.0f)
+ {
+ voice[v].envsamples=(unsigned int)(voice[v].env_level/-voice[v].env_sr);
+ } else
+ {
+ voice[v].envsamples=spu_infinity;
+ }
+ break;
+
+ case 3: // sustain end
+ voice[v].envsamples=spu_infinity;
+ voice[v].env_delta=0.0f;
+ if (voice[v].env_sr<=0.0f)
+ {
+ voice[v].env_level=0.0f;
+ return false;
+ } else
+ {
+ voice[v].env_level=1.0f;
+ }
+ break;
+
+ case 4: // release
+ voice[v].env_level=mindb(1.0f,maxdb(0.0f,voice[v].env_level));
+ voice[v].env_delta=voice[v].env_rr;
+ if (voice[v].env_rr == -0.0f) // 0.0 release means infinite time
+ {
+ voice[v].envsamples=spu_infinity;
+ }
+ else
+ {
+ voice[v].envsamples=(unsigned int)(voice[v].env_level/-voice[v].env_rr);
+ }
+ break;
+
+ case 5: // release end
+ voice[v].env_level=0.0f;
+ voice[v].env_delta=0.0f;
+ voice[v].envsamples=spu_infinity;
+ return false;
+ }
+ }
+ return true;
+}
+
+//
+//
+//
+
+void spu_device::key_on(const int v)
+{
+ voice[v].loop.reset();
+
+// printf("key_on: %d @ %x (pitch %x)\n", v, spureg.voice[v].addr<<3, spureg.voice[v].pitch);
+
+ translate_sample_addr(spureg.voice[v].addr<<3,&voice[v].play);
+ assert(voice[v].play.ptr<voice[v].play.cache->dend);
+
+ voice[v].keyontime=0; //get_system_time();
+
+ voice[v].dptr=0;
+ voice[v].inloopcache=false;
+ voice[v].lcptr=-1;
+ voice[v].env_level=0.0f;
+ voice[v].env_state=0;
+ voice[v].forceloop=false;
+
+ // Note: ChronoCross has note hang problems if this is 0 immediately
+ // after key on
+ spureg.voice[v].curvol=1;
+
+ for (unsigned int ch=0; ch<2; ch++)
+ {
+ {
+ voice[v].sweep_vol[ch]=1.0f;
+ }
+ }
+
+ #ifdef warn_if_sweep_used
+ static bool sweepused;
+ if ((spureg.voice[v].vol_l|spureg.voice[v].vol_r)&0x8000)
+ {
+ if (! sweepused)
+ {
+ printf("sweep\n");
+ sweepused=true;
+ }
+ }
+ #endif
+
+ #ifdef assert_if_sweep_used
+ assert(((spureg.voice[v].vol_l|spureg.voice[v].vol_r)&0x8000)==0);
+ #endif
+
+ if (spureg.voice[v].adsl&adsl_am)
+ {
+ voice[v].env_ar=get_pos_exp_rate((spureg.voice[v].adsl&adsl_ar_mask)>>adsl_ar_shift);
+ } else
+ {
+ voice[v].env_ar=get_linear_rate((spureg.voice[v].adsl&adsl_ar_mask)>>adsl_ar_shift);
+ }
+
+ voice[v].env_dr=-get_decay_rate((spureg.voice[v].adsl&adsl_dr_mask)>>adsl_dr_shift);
+ voice[v].env_sl=get_sustain_level(spureg.voice[v].adsl&adsl_sl_mask);
+
+ if (spureg.voice[v].srrr&srrr_sm)
+ {
+ if (spureg.voice[v].srrr&srrr_sd)
+ {
+ voice[v].env_sr=get_neg_exp_rate((spureg.voice[v].srrr&srrr_sr_mask)>>srrr_sr_shift);
+ } else
+ {
+ voice[v].env_sr=get_pos_exp_rate((spureg.voice[v].srrr&srrr_sr_mask)>>srrr_sr_shift);
+ }
+ } else
+ {
+ voice[v].env_sr=get_linear_rate((spureg.voice[v].srrr&srrr_sr_mask)>>srrr_sr_shift);
+ if (spureg.voice[v].srrr&srrr_sd)
+ voice[v].env_sr=-voice[v].env_sr;
+ }
+
+ if (spureg.voice[v].srrr&srrr_rm)
+ {
+ voice[v].env_rr=-get_exp_release_rate(spureg.voice[v].srrr&srrr_rr_mask);
+ } else
+ {
+ voice[v].env_rr=-get_linear_release_rate(spureg.voice[v].srrr&srrr_rr_mask);
+ }
+
+ voice[v].envsamples=(unsigned int)(1.0f/voice[v].env_ar);
+ voice[v].env_delta=voice[v].env_ar;
+}
+
+//
+//
+//
+
+void spu_device::set_xa_format(const float _freq, const int channels)
+{
+ // Adjust frequency to compensate for slightly slower/faster frame rate
+// float freq=44100.0; //(_freq*get_adjusted_frame_rate())/ps1hw.rcnt->get_vertical_refresh();
+
+ xa_freq=(unsigned int)((_freq/44100.0f)*4096.0f);
+ xa_channels=channels;
+ xa_spf=(unsigned int)(_freq/60.0f)*channels;
+}
+
+//
+//
+//
+
+void spu_device::generate_xa(void *ptr, const unsigned int sz)
+{
+ if (xa_buffer->get_bytes_in())
+ {
+ // Don't start playing until 8 frames worth of data are in
+
+ if ((! xa_playing) && (xa_buffer->get_bytes_in()<(xa_spf<<3)))
+ {
+// debug_xa("waiting...\n");
+ return;
+ }
+
+ xa_playing=true;
+
+ // Init buffer pointers/counters
+
+ int n=sz>>2;
+ signed short *sp=(signed short *)xa_buffer->get_tail_ptr(),
+ *dp=(signed short *)ptr;
+ unsigned int noff=(1<<xa_channels),
+ oldtail=xa_buffer->get_tail_offset();
+
+ assert((xa_channels==1) || (xa_channels==2));
+
+ // Calculate volume
+
+ int voll=spureg.cdvol_l,
+ volr=spureg.cdvol_r;
+ voll=(voll*xa_voll)>>14;
+ volr=(volr*xa_volr)>>14;
+
+ // Generate requested number of XA samples
+
+ while ((xa_buffer->get_bytes_in()) && (n--))
+ {
+ // Get left/right input samples
+
+ int vl=sp[0],
+ vr=sp[xa_channels-1];
+
+ // Linear interpolation
+
+ if (1) //settings.sound_interpolate)
+ {
+ signed short *nsp=(signed short *)xa_buffer->get_tail_ptr(noff);
+ int vdl=nsp[0]-vl,
+ vdr=nsp[xa_channels-1]-vr;
+
+ vl+=(vdl*(int)xa_cnt)>>12;
+ vr+=(vdr*(int)xa_cnt)>>12;
+ }
+
+ // Multiply by
+
+ vl=(vl*voll)>>15;
+ vr=(vr*volr)>>15;
+
+ // Write to SPU XA buffer (for emulation purposes - some games read this
+ // back to do analysers, etc...)
+
+ *(signed short *)(spu_ram+xa_out_ptr)=vl;
+ *(signed short *)(spu_ram+xa_out_ptr+0x800)=vr;
+ xa_out_ptr=(xa_out_ptr+2)&0x7ff;
+
+ // Mix samples into output buffer
+
+ dp[0]=clamp(dp[0]+vl);
+ dp[1]=clamp(dp[1]+vr);
+ dp+=2;
+
+ // Advance input counter/pointer
+
+ xa_cnt+=xa_freq;
+ int ss=(xa_cnt>>12);
+ xa_cnt&=0xfff;
+
+ if (ss)
+ {
+ ss<<=xa_channels;
+ ss=min(ss,(int)xa_buffer->get_bytes_in());
+
+ xa_buffer->increment_tail(ss);
+ sp=(signed short *)xa_buffer->get_tail_ptr();
+ }
+ }
+
+ // Delete buffer markers we have passed
+
+ xa_buffer->delete_markers(oldtail);
+ }
+
+ // If we run out of input set status to stopped and clear the SPU XA buffer
+
+ if (! xa_buffer->get_bytes_in())
+ {
+ xa_playing=false;
+
+ memset(spu_ram,0,0x1000);
+ xa_out_ptr=0;
+ }
+}
+
+//
+//
+//
+
+void spu_device::generate_cdda(void *ptr, const unsigned int sz)
+{
+ if (cdda_buffer->get_bytes_in())
+ {
+ unsigned int cdda_spf=(44100*4)/60.0,
+ freq=(unsigned int)((cdda_freq*60.0)/60.0);
+
+ if ((! cdda_playing) && (cdda_buffer->get_bytes_in()<(cdda_spf<<3)))
+ return;
+
+ cdda_playing=true;
+
+ int n=sz>>2;
+ signed short *sp=(signed short *)cdda_buffer->get_tail_ptr(),
+ *dp=(signed short *)ptr;
+ unsigned int oldtail=cdda_buffer->get_tail_offset();
+
+ int voll=spureg.cdvol_l,
+ volr=spureg.cdvol_r;
+
+ while ((cdda_buffer->get_bytes_in()) && (n--))
+ {
+ INT16 vl = ((sp[0]*voll)>>15);
+ INT16 vr = ((sp[1]*volr)>>15);
+
+ // if the volume adjusted samples are stored here, vibribbon does nothing
+ *(signed short *)(spu_ram+m_cd_out_ptr)=sp[0];
+ *(signed short *)(spu_ram+m_cd_out_ptr+0x400)=sp[1];
+ m_cd_out_ptr=(m_cd_out_ptr+2)&0x3ff;
+
+ //if((m_cd_out_ptr == ((spureg.irq_addr << 3) & ~0x400)) && (spureg.ctrl & spuctrl_irq_enable))
+ // m_irq_handler(1);
+
+ dp[0]=clamp(dp[0]+vl);
+ dp[1]=clamp(dp[1]+vr);
+ dp+=2;
+
+ cdda_cnt+=freq;
+ int ss=(cdda_cnt>>12);
+ cdda_cnt&=0xfff;
+
+ if (ss)
+ {
+ ss<<=2;
+
+ cdda_buffer->increment_tail(ss);
+ sp=(signed short *)cdda_buffer->get_tail_ptr();
+ }
+ }
+
+ cdda_buffer->delete_markers(oldtail);
+
+ if (! cdda_buffer->get_bytes_in())
+ cdda_playing=false;
+
+// if (n>0) printf("cdda buffer underflow (n=%d cdda_in=%d spf=%d)\n",n,cdda_buffer->get_bytes_in(),cdda_spf);
+ }
+ else if(((spureg.irq_addr << 3) < 0x800) && (spureg.ctrl & spuctrl_irq_enable))
+ {
+ UINT16 irq_addr = (spureg.irq_addr << 3) & ~0x400;
+ UINT32 end = m_cd_out_ptr + (sz >> 1);
+ if(((m_cd_out_ptr < irq_addr) && (end > irq_addr)) || ((m_cd_out_ptr > (end & 0x3ff)) && ((end & 0x3ff) > irq_addr)))
+ m_irq_handler(1);
+ m_cd_out_ptr = end & 0x3fe;
+ }
+}
+
+//
+//
+//
+
+void spu_device::key_off(const int v)
+{
+// printf("key_off: %d\n", v);
+
+ if (voice[v].env_state<=3)
+ {
+ voice[v].env_state=3;
+ voice[v].envsamples=0;
+ }
+}
+
+//
+//
+//
+
+void spu_device::update_reverb()
+{
+ if (dirty_flags&dirtyflag_reverb)
+ {
+ cur_reverb_preset=find_reverb_preset((unsigned short *)&reg[0x1c0]);
+
+ if (cur_reverb_preset==NULL)
+ {
+// printf("spu: reverb=unknown (reg 1c0 = %x)\n", reg[0x1c0]);
+ } else
+ {
+// printf("spu: reverb=%s\n",cur_reverb_preset->name);
+ spu_reverb_cfg=&cur_reverb_preset->cfg;
+
+ if ((core_stricmp("reverb off",cur_reverb_preset->name)) && (spu_reverb_cfg->band_gain<=0.0f))
+ {
+// printf("spu: no reverb config for %s\n",cur_reverb_preset->name);
+ }
+ }
+
+ dirty_flags&=~dirtyflag_reverb;
+ }
+}
+
+//
+//
+//
+
+void spu_device::flush_output_buffer()
+{
+ output_head=output_tail=output_size=0;
+}
+
+//
+//
+//
+
+void spu_device::generate(void *ptr, const unsigned int sz)
+{
+ cur_generate_sample+=sz>>2;
+ process_until(cur_generate_sample);
+
+ update_reverb();
+
+ unsigned int left=sz;
+ unsigned char *dp=(unsigned char *)ptr;
+
+ while ((left) && (output_size))
+ {
+ unsigned int n=min(min(left,output_buffer_size-output_head),output_size);
+ memcpy(dp,output_buf[0]+output_head,n);
+
+ rev->process((signed short *)dp,
+ (signed short *)(output_buf[1]+output_head),
+ spu_reverb_cfg,
+ (signed short)spureg.rvol_l,
+ (signed short)spureg.rvol_r,
+ n);
+
+ output_size-=n;
+ output_head+=n;
+ output_head&=(output_buffer_size-1);
+ dp+=n;
+ left-=n;
+ }
+
+ if (left)
+ {
+ memset(dp,0,left);
+ }
+
+ generate_xa(ptr,sz);
+ generate_cdda(ptr,sz);
+}
+
+//
+//
+//
+
+void spu_device::update_irq_event()
+{
+ if (spureg.ctrl&spuctrl_irq_enable)
+ {
+ unsigned int samplestoirq=spu_infinity;
+ for (int i=0; i<24; i++)
+ if (voice[i].samplestoirq!=spu_infinity)
+ {
+ if (voice[i].samplestoirq==0)
+ {
+ m_irq_handler(1);
+
+ voice[i].samplestoirq=spu_infinity;
+ voice[i].hitirq=true;
+ } else
+ {
+ samplestoirq=min(samplestoirq,voice[i].samplestoirq);
+ }
+ }
+ }
+}
+
+
+//
+//
+//
+
+void spu_device::generate_noise(void *ptr, const unsigned int num)
+{
+ unsigned int np=(unsigned int)(65536.0f/(0x40-((spureg.ctrl&spuctrl_noise_mask)>>spuctrl_noise_shift)));
+ np=((np<<1)+np)>>1;
+
+ signed short *dp=(signed short *)ptr;
+
+ for (unsigned int i=0; i<num; i++)
+ {
+ signed short v=noise_cur;
+ *dp++=v;
+ *dp++=v;
+ noise_t+=np;
+
+ if (noise_t>0xffff)
+ {
+ noise_t-=0xffff;
+ shift_register15(noise_seed);
+ noise_cur=noise_seed<<1;
+ }
+ }
+}
+
+//
+//
+//
+
+void spu_device::process_until(const unsigned int tsample)
+{
+ while (tsample>cur_frame_sample)
+ {
+ unsigned int process_samples=(unsigned int)(tsample-cur_frame_sample);
+
+ // Drop samples from the head of the queue if its full
+
+ process_samples=min(process_samples,output_buffer_size>>2);
+ unsigned int nsz=output_size+(process_samples<<2);
+ if (nsz>output_buffer_size)
+ {
+ nsz-=output_buffer_size;
+
+ output_head+=nsz;
+ output_size-=nsz;
+ output_head&=(output_buffer_size-1);
+ }
+
+ // Decide how many samples to process taking into account buffer
+ // wrap in output queue. Get pointers to the queues.
+
+ process_samples=min(process_samples,
+ (output_buffer_size-output_tail)>>2);
+
+ unsigned char *outptr=output_buf[0]+output_tail,
+ *reverbptr=output_buf[1]+output_tail,
+ *fmptr=output_buf[2]+output_tail,
+ *noiseptr=output_buf[3]+output_tail;
+
+ output_tail+=process_samples<<2;
+ output_tail&=(output_buffer_size-1);
+ output_size+=process_samples<<2;
+ assert(output_size<=output_buffer_size);
+
+ // Intialise the output samples to 0 (process_voice always adds samples)
+
+ memset(outptr,0,process_samples<<2);
+ memset(reverbptr,0,process_samples<<2);
+
+ // If noise is enabled for any channels generate noise samples
+
+ if (spureg.noise&0xffffff)
+ generate_noise(noiseptr,process_samples);
+
+ unsigned int mask=1;
+ for (int i=0; i<24; i++, mask<<=1)
+ {
+ unsigned int tleft=process_samples;
+ bool isfmin=((i<23) && (spureg.fm&(1<<(i+1)))),
+ isfm=(spureg.fm&(1<<i))!=0,
+ isnoise=(spureg.noise&(1<<i))!=0,
+ isreverb=(spureg.reverb&(1<<i))!=0;
+
+ // This channel is an FM input for the next channel - clear the
+ // FM input buffer
+
+ if (isfmin)
+ memset(fmptr,0,process_samples<<2);
+
+ if (spureg.chon&mask)
+ {
+ // Generate samples
+
+ if (! process_voice(i,
+ process_samples,
+ isreverb?reverbptr:outptr,
+ isnoise?noiseptr
+ :(isfm?fmptr:NULL),
+ isfmin?fmptr:NULL,
+ &tleft))
+ {
+ spureg.chon&=~mask;
+ //spureg.reverb&=~mask;
+
+ voice[i].play.reset();
+ voice[i].loop.reset();
+ }
+ } else
+ {
+ spureg.voice[i].curvol=0;
+ }
+ }
+
+ cur_frame_sample+=process_samples;
+ }
+}
+
+//
+//
+//
+
+void spu_device::update_timing()
+{
+ samples_per_frame=44100.0/60.0; //get_adjusted_frame_rate();
+ samples_per_cycle=samples_per_frame/60*(44100*768); //ps1hw.rcnt->get_vertical_cycles();
+
+}
+
+//
+//
+//
+
+void spu_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *outL, *outR;
+ INT16 temp[44100], *src;
+
+ outL = outputs[0];
+ outR = outputs[1];
+
+ generate(temp, samples*4); // second parameter is bytes, * 2 (size of INT16) * 2 (stereo)
+
+ src = &temp[0];
+ for (int i = 0; i < samples; i++)
+ {
+ *outL++ = *src++;
+ *outR++ = *src++;
+ }
+}
+
+//
+//
+//
+
+void spu_device::start_dma(UINT8 *mainram, bool to_spu, UINT32 size)
+{
+ UINT32 st=spureg.trans_addr<<3, en=st+size;
+
+ if (en>(512*1024))
+ {
+ en=512*1024;
+ size=en-st;
+ }
+
+ if (to_spu)
+ {
+ invalidate_cache(st,en);
+
+ memcpy(spu_ram+(spureg.trans_addr<<3), mainram, size);
+
+ dirty_flags|=dirtyflag_ram;
+ }
+ else
+ {
+ memcpy(mainram, spu_ram+(spureg.trans_addr<<3), size);
+ }
+}
+
+//
+//
+//
+
+void spu_device::decode_xa_mono(const unsigned char *xa,
+ unsigned char *ptr)
+{
+ signed short *dp=(signed short *)ptr;
+
+ int l0=xa_last[0],
+ l1=xa_last[1];
+
+ for (int b=0; b<18; b++)
+ {
+ for (int s=0; s<4; s++)
+ {
+ unsigned char flags=xa[4+(s<<1)],
+ shift=flags&0xf,
+ filter=flags>>4;
+ int f0=filter_coef[filter][0],
+ f1=filter_coef[filter][1];
+ int i;
+
+ for (i=0; i<28; i++)
+ {
+ short d=(xa[16+(i<<2)+s]&0xf)<<12;
+ d=clamp((d>>shift)+(((l0*f0)+(l1*f1)+32)>>6));
+ *dp++=d;
+ l1=l0;
+ l0=d;
+ }
+
+ flags=xa[5+(s<<1)];
+ shift=flags&0xf;
+ filter=flags>>4;
+ f0=filter_coef[filter][0];
+ f1=filter_coef[filter][1];
+
+ for (i=0; i<28; i++)
+ {
+ short d=(xa[16+(i<<2)+s]>>4)<<12;
+ d=clamp((d>>shift)+(((l0*f0)+(l1*f1)+32)>>6));
+ *dp++=d;
+ l1=l0;
+ l0=d;
+ }
+ }
+
+ xa+=128;
+ }
+
+ xa_last[0]=l0;
+ xa_last[1]=l1;
+}
+
+//
+//
+//
+
+void spu_device::decode_xa_stereo(const unsigned char *xa,
+ unsigned char *ptr)
+{
+ signed short *dp=(signed short *)ptr;
+
+ int l0=xa_last[0],
+ l1=xa_last[1],
+ l2=xa_last[2],
+ l3=xa_last[3];
+
+ for (int b=0; b<18; b++)
+ {
+ for (int s=0; s<4; s++)
+ {
+ unsigned char flags0=xa[4+(s<<1)],
+ shift0=flags0&0xf,
+ filter0=flags0>>4,
+ flags1=xa[5+(s<<1)],
+ shift1=flags1&0xf,
+ filter1=flags1>>4;
+
+ int f0=filter_coef[filter0][0],
+ f1=filter_coef[filter0][1],
+ f2=filter_coef[filter1][0],
+ f3=filter_coef[filter1][1];
+
+ for (int i=0; i<28; i++)
+ {
+ short d=xa[16+(i<<2)+s],
+ d0=(d&0xf)<<12,
+ d1=(d>>4)<<12;
+ d0=clamp((int)(d0>>shift0)+(((l0*f0)+(l1*f1)+32)>>6));
+ *dp++=d0;
+ l1=l0;
+ l0=d0;
+
+ d1=clamp((int)(d1>>shift1)+(((l2*f2)+(l3*f3)+32)>>6));
+ *dp++=d1;
+ l3=l2;
+ l2=d1;
+ }
+ }
+
+ xa+=128;
+ }
+
+ xa_last[0]=l0;
+ xa_last[1]=l1;
+ xa_last[2]=l2;
+ xa_last[3]=l3;
+}
+
+//
+//
+//
+
+/*
+enum
+{
+ xaencoding_stereo_mask=3,
+ xaencoding_freq_shift=2,
+ xaencoding_freq_mask=3<<xaencoding_freq_shift,
+ xaencoding_bps_shift=4,
+ xaencoding_bps_mask=3<<xaencoding_bps_shift,
+ xaencoding_emphasis=(1<<6)
+};
+*/
+
+bool spu_device::play_xa(const unsigned int sector, const unsigned char *xa)
+{
+ // Don't process the sector if the buffer is full
+
+ if (xa_buffer->get_bytes_free()<xa_sector_size) return false;
+
+// debug_xa("play_xa: %d\n",sector);
+
+ // Get XA format from sector header
+
+ const unsigned char *hdr=xa+4;
+ float freq;
+ int channels;
+
+ switch (hdr[3]&0x3f) // ignore emphasis and reserved bits
+ {
+ case 0:
+ channels=1;
+ freq=37800.0f; //18900.0f;
+ break;
+
+ case 1:
+ channels=2;
+ freq=37800.0f;
+ break;
+
+ case 4:
+ channels=1;
+ freq=18900.0f; ///2.0f;
+ break;
+
+ case 5:
+ channels=2;
+ freq=18900.0f; //37800.0f/2.0f;
+ break;
+
+ default:
+ printf("play_xa: unhandled xa mode %08x\n",hdr[3]);
+ return true;
+ }
+
+ set_xa_format(freq,channels);
+
+ // Store XA marker
+
+ unsigned char *ptr=xa_buffer->add_sector(sector);
+
+ // Decode the sector
+
+ if (channels==2)
+ {
+ decode_xa_stereo(xa+8,ptr);
+ } else
+ {
+ decode_xa_mono(xa+8,ptr);
+ }
+
+ // Return that we processed the sector
+ return true;
+}
+
+//
+// Flush everything after a given sector in the XA buffer
+//
+
+void spu_device::flush_xa(const unsigned int sector)
+{
+// debug_xa("flush_xa: %d\n",sector);
+
+ if (xa_playing)
+ {
+ xa_buffer->flush(sector);
+ } else
+ {
+ // Not playing, flush the entire buffer
+
+ xa_buffer->flush_all();
+ xa_cnt=0;
+ }
+}
+
+//
+//
+//
+
+bool spu_device::play_cdda(const unsigned int sector, const unsigned char *cdda)
+{
+ if (cdda_buffer->get_bytes_free()<cdda_sector_size) return false;
+
+ signed short *dp=(signed short *)cdda_buffer->add_sector(sector);
+ memcpy(dp,cdda,cdda_sector_size);
+
+ // data coming in in MAME is big endian as stored on the CD
+ unsigned char *flip = (unsigned char *)dp;
+ for (int i = 0; i < cdda_sector_size; i+= 2)
+ {
+ unsigned char temp = flip[i];
+ flip[i] = flip[i+1];
+ flip[i+1] = temp;
+ }
+ // this should be done in generate but sound_stream_update may not be called frequently enough
+ if(((spureg.irq_addr << 3) < 0x800) && (spureg.ctrl & spuctrl_irq_enable))
+ m_irq_handler(1);
+
+ return true;
+}
+
+void spu_device::flush_cdda(const unsigned int sector)
+{
+// debug_xa("flush_cdda: %d\n",sector);
+
+ if (cdda_playing)
+ {
+ cdda_buffer->flush(sector);
+ } else
+ {
+ cdda_buffer->flush_all();
+ cdda_cnt=0;
+ }
+}
+
+void spu_device::dma_read( UINT32 *p_n_ram, UINT32 n_address, INT32 n_size )
+{
+ UINT8 *psxram = (UINT8 *)p_n_ram;
+
+ start_dma(psxram + n_address, false, n_size*4);
+}
+
+void spu_device::dma_write( UINT32 *p_n_ram, UINT32 n_address, INT32 n_size )
+{
+ UINT8 *psxram = (UINT8 *)p_n_ram;
+
+// printf("SPU DMA write from %x, size %x\n", n_address, n_size);
+
+ start_dma(psxram + n_address, true, n_size*4);
+}
diff --git a/src/devices/sound/spu.h b/src/devices/sound/spu.h
new file mode 100644
index 00000000000..8a6727e4833
--- /dev/null
+++ b/src/devices/sound/spu.h
@@ -0,0 +1,255 @@
+// license:BSD-3-Clause
+// copyright-holders:pSXAuthor, R. Belmont
+#pragma once
+
+#ifndef __SPU_H__
+#define __SPU_H__
+
+#include "spureverb.h"
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_SPU_IRQ_HANDLER(_devcb) \
+ devcb = &spu_device::set_irq_handler(*device, DEVCB_##_devcb);
+
+#define MCFG_SPU_ADD(_tag, _clock) \
+ MCFG_DEVICE_MODIFY( "maincpu" ) \
+ MCFG_PSX_SPU_READ_HANDLER(DEVREAD16(_tag, spu_device, read)) \
+ MCFG_PSX_SPU_WRITE_HANDLER(DEVWRITE16(_tag, spu_device, write)) \
+ MCFG_DEVICE_ADD(_tag, SPU, _clock) \
+ MCFG_SPU_IRQ_HANDLER(DEVWRITELINE("maincpu:irq", psxirq_device, intin9)) \
+ MCFG_PSX_DMA_CHANNEL_READ( "maincpu", 4, psx_dma_read_delegate( FUNC( spu_device::dma_read ), (spu_device *) device ) ) \
+ MCFG_PSX_DMA_CHANNEL_WRITE( "maincpu", 4, psx_dma_write_delegate( FUNC( spu_device::dma_write ), (spu_device *) device ) )
+
+// ======================> spu_device
+
+const unsigned int spu_base_frequency_hz=44100;
+
+class stream_buffer;
+
+class spu_device : public device_t, public device_sound_interface
+{
+ struct sample_cache;
+ struct sample_loop_cache;
+ struct cache_pointer;
+ struct voiceinfo;
+
+ enum
+ {
+ dirtyflag_voice_mask=0x00ffffff,
+ dirtyflag_reverb=0x01000000,
+ dirtyflag_ram=0x02000000,
+ dirtyflag_irq=0x04000000
+ };
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_post_load();
+ virtual void device_stop();
+
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ // internal state
+ devcb_write_line m_irq_handler;
+
+ unsigned char *spu_ram;
+ reverb *rev;
+ unsigned int taddr, sample_t;
+
+ stream_buffer *xa_buffer, *cdda_buffer;
+ unsigned int xa_cnt,cdda_cnt,
+ xa_freq,cdda_freq,
+ xa_channels,
+ xa_spf,
+ xa_out_ptr,
+ cur_frame_sample,
+ cur_generate_sample,
+ dirty_flags;
+
+ UINT16 m_cd_out_ptr;
+
+ signed short xa_last[4];
+ bool status_enabled,
+ xa_playing,
+ cdda_playing;
+ int xa_voll,xa_volr,
+ changed_xa_vol;
+ voiceinfo *voice;
+ sample_cache **cache;
+ float samples_per_frame,
+ samples_per_cycle;
+
+ static float freq_multiplier;
+
+ unsigned char *output_buf[4];
+ unsigned int output_head,
+ output_tail,
+ output_size,
+ cur_qsz;
+
+ unsigned int noise_t;
+ signed short noise_cur;
+ int noise_seed;
+
+ #pragma pack(push,spureg,1)
+
+ struct voicereg
+ {
+ unsigned short vol_l, // 0
+ vol_r, // 2
+ pitch, // 4
+ addr, // 6
+ adsl, // 8
+ srrr, // a
+ curvol, // c
+ repaddr; // e
+ };
+
+ union
+ {
+ unsigned char reg[0x200];
+ struct
+ {
+ voicereg voice[24];
+ unsigned short mvol_l,mvol_r,
+ rvol_l,rvol_r;
+ unsigned int keyon,
+ keyoff,
+ fm,
+ noise,
+ reverb,
+ chon;
+ unsigned short _unknown,
+ reverb_addr,
+ irq_addr,
+ trans_addr,
+ data,
+ ctrl;
+ unsigned int status;
+ signed short cdvol_l,cdvol_r,
+ exvol_l,exvol_r;
+ } spureg;
+ };
+
+ #pragma pack(pop,spureg)
+
+ struct reverb_preset
+ {
+ const char *name;
+ unsigned short param[32];
+ reverb_params cfg;
+ };
+
+ reverb_preset *cur_reverb_preset;
+
+ static reverb_preset reverb_presets[];
+
+ void key_on(const int v);
+ void key_off(const int v);
+ bool update_envelope(const int v);
+ void write_data(const unsigned short data);
+ void generate(void *ptr, const unsigned int sz);
+ void generate_voice(const unsigned int v,
+ void *ptr,
+ void *noiseptr,
+ void *outxptr,
+ const unsigned int sz);
+ void generate_noise(void *ptr, const unsigned int num);
+ bool process_voice(const unsigned int v,
+ const unsigned int sz,
+ void *ptr,
+ void *fmnoise_ptr,
+ void *outxptr,
+ unsigned int *tleft);
+ void process();
+ void process_until(const unsigned int tsample);
+ void update_voice_loop(const unsigned int v);
+ bool update_voice_state(const unsigned int v);
+ void update_voice_state();
+ void update_voice_events(voiceinfo *vi);
+ void update_irq_event();
+ unsigned int get_irq_distance(const voiceinfo *vi);
+ void generate_xa(void *ptr, const unsigned int sz);
+ void generate_cdda(void *ptr, const unsigned int sz);
+ void decode_xa_mono(const unsigned char *xa, unsigned char *ptr);
+ void decode_xa_stereo(const unsigned char *xa, unsigned char *ptr);
+ void update_key();
+ void update_reverb();
+ void update_timing();
+
+ bool translate_sample_addr(const unsigned int addr, cache_pointer *cp);
+ sample_cache *get_sample_cache(const unsigned int addr);
+ void flush_cache(sample_cache *sc,
+ const unsigned int istart,
+ const unsigned int iend);
+ void invalidate_cache(const unsigned int st, const unsigned int en);
+
+ void set_xa_format(const float freq, const int channels);
+
+ void init_stream();
+ void kill_stream();
+
+ void update_vol(const unsigned int addr);
+
+ void flush_output_buffer();
+
+ sample_loop_cache *get_loop_cache(sample_cache *cache,
+ const unsigned int lpen,
+ sample_cache *lpcache,
+ const unsigned int lpst);
+#if 0
+ void write_cache_pointer(outfile *fout,
+ cache_pointer *cp,
+ sample_loop_cache *lc=NULL);
+ void read_cache_pointer(infile *fin,
+ cache_pointer *cp,
+ sample_loop_cache **lc=NULL);
+#endif
+ static float get_linear_rate(const int n);
+ static float get_linear_rate_neg_phase(const int n);
+ static float get_pos_exp_rate(const int n);
+ static float get_pos_exp_rate_neg_phase(const int n);
+ static float get_neg_exp_rate(const int n);
+ static float get_neg_exp_rate_neg_phase(const int n);
+ static float get_decay_rate(const int n);
+ static float get_sustain_level(const int n);
+ static float get_linear_release_rate(const int n);
+ static float get_exp_release_rate(const int n);
+ static reverb_preset *find_reverb_preset(const unsigned short *param);
+
+public:
+ spu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<spu_device &>(device).m_irq_handler.set_callback(object); }
+
+ void dma_read( UINT32 *ram, UINT32 n_address, INT32 n_size );
+ void dma_write( UINT32 *ram, UINT32 n_address, INT32 n_size );
+
+ void reinit_sound();
+ void kill_sound();
+
+ void update();
+
+ void start_dma(UINT8 *mainram, bool to_spu, UINT32 size);
+ bool play_xa(const unsigned int sector, const unsigned char *sec);
+ bool play_cdda(const unsigned int sector, const unsigned char *sec);
+ void flush_xa(const unsigned int sector=0);
+ void flush_cdda(const unsigned int sector=0);
+
+ sound_stream *m_stream;
+
+ DECLARE_READ16_MEMBER( read );
+ DECLARE_WRITE16_MEMBER( write );
+};
+
+extern reverb_params *spu_reverb_cfg;
+
+// device type definition
+extern const device_type SPU;
+
+#endif
diff --git a/src/devices/sound/spu_tables.c b/src/devices/sound/spu_tables.c
new file mode 100644
index 00000000000..faa5719c4c6
--- /dev/null
+++ b/src/devices/sound/spu_tables.c
@@ -0,0 +1,703 @@
+// license:BSD-3-Clause
+// copyright-holders:pSXAuthor, R. Belmont
+#include "emu.h"
+#include "spu.h"
+
+static inline float ms_to_rate(const float ms)
+{
+ return 1.0f/(ms*((float)spu_base_frequency_hz/1000.0f));
+}
+
+static inline float s_to_rate(const float s)
+{
+ return ms_to_rate(s*1000.0f);
+}
+
+static float linear_rate[]=
+{
+ ms_to_rate(0.05f),
+ ms_to_rate(0.06f),
+ ms_to_rate(0.07f),
+ ms_to_rate(0.09f),
+ ms_to_rate(0.10f),
+ ms_to_rate(0.12f),
+ ms_to_rate(0.15f),
+ ms_to_rate(0.18f),
+ ms_to_rate(0.21f),
+ ms_to_rate(0.24f),
+ ms_to_rate(0.29f),
+ ms_to_rate(0.36f),
+ ms_to_rate(0.41f),
+ ms_to_rate(0.48f),
+ ms_to_rate(0.58f),
+ ms_to_rate(0.73f),
+ ms_to_rate(0.83f),
+ ms_to_rate(0.97f),
+ ms_to_rate(1.2f),
+ ms_to_rate(1.5f),
+ ms_to_rate(1.7f),
+ ms_to_rate(1.9f),
+ ms_to_rate(2.3f),
+ ms_to_rate(2.9f),
+ ms_to_rate(3.3f),
+ ms_to_rate(3.9f),
+ ms_to_rate(4.6f),
+ ms_to_rate(5.8f),
+ ms_to_rate(6.6f),
+ ms_to_rate(7.7f),
+ ms_to_rate(9.3f),
+ ms_to_rate(12.0f),
+ ms_to_rate(13.0f),
+ ms_to_rate(15.0f),
+ ms_to_rate(19.0f),
+ ms_to_rate(23.0f),
+ ms_to_rate(27.0f),
+ ms_to_rate(31.0f),
+ ms_to_rate(37.0f),
+ ms_to_rate(46.0f),
+ ms_to_rate(53.0f),
+ ms_to_rate(62.0f),
+ ms_to_rate(74.0f),
+ ms_to_rate(93.0f),
+ s_to_rate(0.11f),
+ s_to_rate(0.12f),
+ s_to_rate(0.15f),
+ s_to_rate(0.19f),
+ s_to_rate(0.21f),
+ s_to_rate(0.25f),
+ s_to_rate(0.30f),
+ s_to_rate(0.37f),
+ s_to_rate(0.42f),
+ s_to_rate(0.50f),
+ s_to_rate(0.59f),
+ s_to_rate(0.74f),
+ s_to_rate(0.85f),
+ s_to_rate(0.99f),
+ s_to_rate(1.2f),
+ s_to_rate(1.5f),
+ s_to_rate(1.7f),
+ s_to_rate(2.0f),
+ s_to_rate(2.4f),
+ s_to_rate(3.0f),
+ s_to_rate(3.4f),
+ s_to_rate(4.0f),
+ s_to_rate(4.8f),
+ s_to_rate(5.9f),
+ s_to_rate(6.8f),
+ s_to_rate(7.9f),
+ s_to_rate(9.5f),
+ s_to_rate(12.0f),
+ s_to_rate(14.0f),
+ s_to_rate(16.0f),
+ s_to_rate(19.0f),
+ s_to_rate(24.0f),
+ s_to_rate(27.0f),
+ s_to_rate(32.0f),
+ s_to_rate(38.0f),
+ s_to_rate(48.0f),
+ s_to_rate(54.0f),
+ s_to_rate(63.0f),
+ s_to_rate(76.0f),
+ s_to_rate(95.0f),
+ s_to_rate(109.0f),
+ s_to_rate(127.0f),
+ s_to_rate(152.0f),
+ s_to_rate(190.0f),
+ s_to_rate(218.0f),
+ s_to_rate(254.0f),
+ s_to_rate(304.0f),
+ s_to_rate(380.0f),
+ s_to_rate(436.0f),
+ s_to_rate(508.0f),
+ s_to_rate(608.0f),
+ s_to_rate(760.0f),
+ s_to_rate(872.0f),
+ s_to_rate(1016.0f),
+ s_to_rate(1216.0f),
+ s_to_rate(1520.0f),
+ s_to_rate(1744.0f),
+ s_to_rate(2032.0f),
+ s_to_rate(2432.0f),
+ s_to_rate(3040.0f),
+ s_to_rate(3488.0f),
+ s_to_rate(4064.0f),
+ s_to_rate(4864.0f),
+ s_to_rate(6080.0f)
+};
+
+static const int num_linear_rates=ARRAY_LENGTH(linear_rate);
+
+static const float pos_exp_rate[]=
+{
+ ms_to_rate(0.09f),
+ ms_to_rate(0.11f),
+ ms_to_rate(0.13f),
+ ms_to_rate(0.16f),
+ ms_to_rate(0.18f),
+ ms_to_rate(0.21f),
+ ms_to_rate(0.25f),
+ ms_to_rate(0.32f),
+ ms_to_rate(0.36f),
+ ms_to_rate(0.42f),
+ ms_to_rate(0.51f),
+ ms_to_rate(0.64f),
+ ms_to_rate(0.73f),
+ ms_to_rate(0.85f),
+ ms_to_rate(1.0f),
+ ms_to_rate(1.3f),
+ ms_to_rate(1.5f),
+ ms_to_rate(1.7f),
+ ms_to_rate(2.0f),
+ ms_to_rate(2.5f),
+ ms_to_rate(2.9f),
+ ms_to_rate(3.4f),
+ ms_to_rate(4.1f),
+ ms_to_rate(5.1f),
+ ms_to_rate(5.8f),
+ ms_to_rate(6.8f),
+ ms_to_rate(8.1f),
+ ms_to_rate(10.0f),
+ ms_to_rate(12.0f),
+ ms_to_rate(14.0f),
+ ms_to_rate(16.0f),
+ ms_to_rate(20.0f),
+ ms_to_rate(23.0f),
+ ms_to_rate(27.0f),
+ ms_to_rate(33.0f),
+ ms_to_rate(41.0f),
+ ms_to_rate(46.0f),
+ ms_to_rate(54.0f),
+ ms_to_rate(65.0f),
+ ms_to_rate(81.0f),
+ ms_to_rate(93.0f),
+ s_to_rate(0.11f),
+ s_to_rate(0.13f),
+ s_to_rate(0.16f),
+ s_to_rate(0.19f),
+ s_to_rate(0.22f),
+ s_to_rate(0.26f),
+ s_to_rate(0.33f),
+ s_to_rate(0.37f),
+ s_to_rate(0.43f),
+ s_to_rate(0.52f),
+ s_to_rate(0.65f),
+ s_to_rate(0.74f),
+ s_to_rate(0.87f),
+ s_to_rate(1.0f),
+ s_to_rate(1.3f),
+ s_to_rate(1.5f),
+ s_to_rate(1.7f),
+ s_to_rate(2.1f),
+ s_to_rate(2.6f),
+ s_to_rate(3.0f),
+ s_to_rate(3.5f),
+ s_to_rate(4.2f),
+ s_to_rate(5.2f),
+ s_to_rate(5.9f),
+ s_to_rate(6.9f),
+ s_to_rate(8.3f),
+ s_to_rate(10.0f),
+ s_to_rate(12.0f),
+ s_to_rate(14.0f),
+ s_to_rate(17.0f),
+ s_to_rate(21.0f),
+ s_to_rate(24.0f),
+ s_to_rate(28.0f),
+ s_to_rate(33.0f),
+ s_to_rate(42.0f),
+ s_to_rate(48.0f),
+ s_to_rate(55.0f),
+ s_to_rate(67.0f),
+ s_to_rate(83.0f),
+ s_to_rate(95.0f),
+ s_to_rate(111.0f),
+ s_to_rate(133.0f),
+ s_to_rate(166.0f),
+ s_to_rate(190.0f),
+ s_to_rate(222.0f),
+ s_to_rate(266.0f),
+ s_to_rate(333.0f),
+ s_to_rate(380.0f),
+ s_to_rate(444.0f),
+ s_to_rate(532.0f),
+ s_to_rate(666.0f),
+ s_to_rate(760.0f),
+ s_to_rate(888.0f),
+ s_to_rate(1064.0f),
+ s_to_rate(1332.0f),
+ s_to_rate(1520.0f),
+ s_to_rate(1776.0f),
+ s_to_rate(2128.0f),
+ s_to_rate(2664.0f)
+};
+
+static const int num_pos_exp_rates=ARRAY_LENGTH(pos_exp_rate);
+
+static const float neg_exp_rate[]=
+{
+ ms_to_rate(0.07f),
+ ms_to_rate(0.09f),
+ ms_to_rate(0.11f),
+ ms_to_rate(0.14f),
+ ms_to_rate(0.18f),
+ ms_to_rate(0.21f),
+ ms_to_rate(0.25f),
+ ms_to_rate(0.31f),
+ ms_to_rate(0.39f),
+ ms_to_rate(0.45f),
+ ms_to_rate(0.53f),
+ ms_to_rate(0.64f),
+ ms_to_rate(0.81f),
+ ms_to_rate(0.93f),
+ ms_to_rate(1.1f),
+ ms_to_rate(1.3f),
+ ms_to_rate(1.6f),
+ ms_to_rate(1.9f),
+ ms_to_rate(2.2f),
+ ms_to_rate(2.6f),
+ ms_to_rate(3.3f),
+ ms_to_rate(3.8f),
+ ms_to_rate(4.4f),
+ ms_to_rate(5.3f),
+ ms_to_rate(6.7f),
+ ms_to_rate(7.6f),
+ ms_to_rate(8.9f),
+ ms_to_rate(11.0f),
+ ms_to_rate(13.0f),
+ ms_to_rate(15.0f),
+ ms_to_rate(18.0f),
+ ms_to_rate(21.0f),
+ ms_to_rate(27.0f),
+ ms_to_rate(31.0f),
+ ms_to_rate(36.0f),
+ ms_to_rate(43.0f),
+ ms_to_rate(53.0f),
+ ms_to_rate(61.0f),
+ ms_to_rate(71.0f),
+ ms_to_rate(86.0f),
+ s_to_rate(0.11f),
+ s_to_rate(0.12f),
+ s_to_rate(0.14f),
+ s_to_rate(0.17f),
+ s_to_rate(0.21f),
+ s_to_rate(0.24f),
+ s_to_rate(0.29f),
+ s_to_rate(0.34f),
+ s_to_rate(0.43f),
+ s_to_rate(0.49f),
+ s_to_rate(0.57f),
+ s_to_rate(0.68f),
+ s_to_rate(0.86f),
+ s_to_rate(0.98f),
+ s_to_rate(1.1f),
+ s_to_rate(1.4f),
+ s_to_rate(1.7f),
+ s_to_rate(2.0f),
+ s_to_rate(2.3f),
+ s_to_rate(2.7f),
+ s_to_rate(3.4f),
+ s_to_rate(3.9f),
+ s_to_rate(4.6f),
+ s_to_rate(5.5f),
+ s_to_rate(6.8f),
+ s_to_rate(7.8f),
+ s_to_rate(9.1f),
+ s_to_rate(11.0f),
+ s_to_rate(14.0f),
+ s_to_rate(16.0f),
+ s_to_rate(18.0f),
+ s_to_rate(22.0f),
+ s_to_rate(27.0f),
+ s_to_rate(31.0f),
+ s_to_rate(36.0f),
+ s_to_rate(44.0f),
+ s_to_rate(55.0f),
+ s_to_rate(63.0f),
+ s_to_rate(73.0f),
+ s_to_rate(88.0f),
+ s_to_rate(109.0f),
+ s_to_rate(125.0f),
+ s_to_rate(146.0f),
+ s_to_rate(175.0f),
+ s_to_rate(219.0f),
+ s_to_rate(250.0f),
+ s_to_rate(292.0f),
+ s_to_rate(350.0f),
+ s_to_rate(438.0f),
+ s_to_rate(500.0f),
+ s_to_rate(584.0f),
+ s_to_rate(700.0f),
+ s_to_rate(876.0f),
+ s_to_rate(1000.0f),
+ s_to_rate(1168.0f),
+ s_to_rate(1400.0f),
+ s_to_rate(1752.0f),
+ s_to_rate(2000.0f),
+ s_to_rate(2336.0f),
+ s_to_rate(2800.0f),
+ s_to_rate(3504.0f),
+ s_to_rate(4000.0f),
+ s_to_rate(4672.0f),
+ s_to_rate(5600.0f),
+ s_to_rate(7008.0f),
+ s_to_rate(8000.0f),
+ s_to_rate(9344.0f),
+ s_to_rate(11200.0f)
+};
+
+static const int num_neg_exp_rates=ARRAY_LENGTH(neg_exp_rate);
+
+static const float decay_rate[16]=
+{
+ ms_to_rate(0.07f),
+ ms_to_rate(0.18f),
+ ms_to_rate(0.39f),
+ ms_to_rate(0.81f),
+ ms_to_rate(1.6f),
+ ms_to_rate(3.3f),
+ ms_to_rate(6.7f),
+ ms_to_rate(13.0f),
+ ms_to_rate(27.0f),
+ ms_to_rate(53.0f),
+ s_to_rate(0.11f),
+ s_to_rate(0.21f),
+ s_to_rate(0.43f),
+ s_to_rate(0.86f),
+ s_to_rate(1.7f),
+ s_to_rate(3.4f),
+};
+
+static const float linear_release_rate[]=
+{
+ ms_to_rate(0.04f),
+ ms_to_rate(0.09f),
+ ms_to_rate(0.18f),
+ ms_to_rate(0.36f),
+ ms_to_rate(0.73f),
+ ms_to_rate(1.5f),
+ ms_to_rate(2.9f),
+ ms_to_rate(5.8f),
+ ms_to_rate(12.0f),
+ ms_to_rate(23.0f),
+ ms_to_rate(46.0f),
+ ms_to_rate(93.0f),
+ s_to_rate(0.19f),
+ s_to_rate(0.37f),
+ s_to_rate(1.74f),
+ s_to_rate(1.5f),
+ s_to_rate(3.0f),
+ s_to_rate(5.9f),
+ s_to_rate(12.0f),
+ s_to_rate(24.0f),
+ s_to_rate(48.0f),
+ s_to_rate(95.0f),
+ s_to_rate(190.0f),
+ s_to_rate(380.0f),
+ s_to_rate(760.0f),
+ s_to_rate(1520.0f),
+ s_to_rate(3040.0f)
+};
+
+static const int num_linear_release_rates=ARRAY_LENGTH(linear_release_rate);
+
+static const float exp_release_rate[]=
+{
+ ms_to_rate(0.07f),
+ ms_to_rate(0.18f),
+ ms_to_rate(0.39f),
+ ms_to_rate(0.81f),
+ ms_to_rate(1.6f),
+ ms_to_rate(3.3f),
+ ms_to_rate(6.7f),
+ ms_to_rate(13.0f),
+ ms_to_rate(27.0f),
+ ms_to_rate(53.0f),
+ s_to_rate(0.11f),
+ s_to_rate(0.21f),
+ s_to_rate(0.43f),
+ s_to_rate(0.86f),
+ s_to_rate(1.7f),
+ s_to_rate(3.4f),
+ s_to_rate(6.8f),
+ s_to_rate(14.0f),
+ s_to_rate(27.0f),
+ s_to_rate(55.0f),
+ s_to_rate(109.0f),
+ s_to_rate(219.0f),
+ s_to_rate(438.0f),
+ s_to_rate(876.0f),
+ s_to_rate(1752.0f),
+ s_to_rate(3504.0f),
+ s_to_rate(7008.0f)
+};
+
+static const int num_exp_release_rates=ARRAY_LENGTH(exp_release_rate);
+
+//
+//
+//
+
+spu_device::reverb_preset spu_device::reverb_presets[]=
+{
+ {
+ "Reverb off",
+ {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+ }
+ },
+
+ {
+ "Room",
+ {
+ 0x007D, 0x005B, 0x6D80, 0x54B8, 0xBED0, 0x0000, 0x0000, 0xBA80,
+ 0x5800, 0x5300, 0x04D6, 0x0333, 0x03F0, 0x0227, 0x0374, 0x01EF,
+ 0x0334, 0x01B5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x01B4, 0x0136, 0x00B8, 0x005C, 0x8000, 0x8000
+ },
+ { // made up from studio small [used by BOF3]
+ -0.3112f,
+ 0.7832f,
+
+ { { 20.7f, 31.3f,40.1711f,51.6803f },
+ { 21.7025f,30.2f,42.6655f,48.6691f } },
+ 0.8f,
+
+ 0.0f,
+ 0.0f
+ }
+ },
+
+ {
+ "Studio Small",
+ {
+ 0x0033, 0x0025, 0x70F0, 0x4FA8, 0xBCE0, 0x4410, 0xC0F0, 0x9C00,
+ 0x5280, 0x4EC0, 0x03E4, 0x031B, 0x03A4, 0x02AF, 0x0372, 0x0266,
+ 0x031C, 0x025D, 0x025C, 0x018E, 0x022F, 0x0135, 0x01D2, 0x00B7,
+ 0x018F, 0x00B5, 0x00B4, 0x0080, 0x004C, 0x0026, 0x8000, 0x8000
+ },
+ { // made up from studio large [used by Fighters Impact]
+ -0.3112f,
+ 0.7832f,
+
+ { { 24.7f,44.3f,49.1711f,59.6803f },
+ { 27.7025f,38.2f,51.6655f,59.6691f } },
+ 0.6462f,
+
+ 0.0f,
+ 0.0f
+ }
+ },
+
+ {
+ "Studio Medium",
+ {
+ 0x00B1, 0x007F, 0x70F0, 0x4FA8, 0xBCE0, 0x4510, 0xBEF0, 0xB4C0,
+ 0x5280, 0x4EC0, 0x0904, 0x076B, 0x0824, 0x065F, 0x07A2, 0x0616,
+ 0x076C, 0x05ED, 0x05EC, 0x042E, 0x050F, 0x0305, 0x0462, 0x02B7,
+ 0x042F, 0x0265, 0x0264, 0x01B2, 0x0100, 0x0080, 0x8000, 0x8000
+ },
+ { // made up from studio large
+ -0.3112f,
+ 0.7832f,
+
+ { { 37.7f,62.3f,82.1711f,71.6803f },
+ { 43.7025f,62.2f,85.6655f,78.6691f } },
+ 0.6462f,
+
+ 0.0f,
+ 0.0f
+ }
+ },
+
+ {
+ "Studio Large",
+ {
+ 0x00E3, 0x00A9, 0x6F60, 0x4FA8, 0xBCE0, 0x4510, 0xBEF0, 0xA680,
+ 0x5680, 0x52C0, 0x0DFB, 0x0B58, 0x0D09, 0x0A3C, 0x0BD9, 0x0973,
+ 0x0B59, 0x08DA, 0x08D9, 0x05E9, 0x07EC, 0x04B0, 0x06EF, 0x03D2,
+ 0x05EA, 0x031D, 0x031C, 0x0238, 0x0154, 0x00AA, 0x8000, 0x8000
+ },
+ { // tuned from xenogears ost
+ -0.3112f,
+ 0.7832f,
+
+ { { 37.7f,62.3f,82.1711f,71.6803f },
+ { 43.7025f,62.2f,85.6655f,78.6691f } },
+ 0.8462f,
+
+ 0.0f,
+ 0.0f
+ }
+ },
+
+ {
+ "Hall",
+ {
+ 0x01A5, 0x0139, 0x6000, 0x5000, 0x4C00, 0xB800, 0xBC00, 0xC000,
+ 0x6000, 0x5C00, 0x15BA, 0x11BB, 0x14C2, 0x10BD, 0x11BC, 0x0DC1,
+ 0x11C0, 0x0DC3, 0x0DC0, 0x09C1, 0x0BC4, 0x07C1, 0x0A00, 0x06CD,
+ 0x09C2, 0x05C1, 0x05C0, 0x041A, 0x0274, 0x013A, 0x8000, 0x8000
+ },
+ { // made up
+ -0.4222f,
+ 0.8889f,
+
+ { { 20.37f,79.63f,107.40f,94.44f },
+ { 31.47f,72.22f,116.66f,105.55f } },
+ 0.8889f,
+
+ 0.0f,
+ 0.0f
+ }
+ },
+
+ {
+ "Space Echo",
+ {
+ 0x033D, 0x0231, 0x7E00, 0x5000, 0xB400, 0xB000, 0x4C00, 0xB000,
+ 0x6000, 0x5400, 0x1ED6, 0x1A31, 0x1D14, 0x183B, 0x1BC2, 0x16B2,
+ 0x1A32, 0x15EF, 0x15EE, 0x1055, 0x1334, 0x0F2D, 0x11F6, 0x0C5D,
+ 0x1056, 0x0AE1, 0x0AE0, 0x07A2, 0x0464, 0x0232, 0x8000, 0x8000
+ },
+ { // made up
+ 0.3951f,
+ 0.7552f,
+
+ {
+ { 36.7138f, 134.6171f, 24.4758f, 194.0596f },
+ { 54.1932f, 90.9070f, 29.7174f, 200.0000f },
+ },
+
+ 0.9301f,
+
+ 109.2563f,
+ 0.4222f
+ }
+ },
+
+ {
+ "Echo",
+ {
+ 0x0001, 0x0001, 0x7FFF, 0x7FFF, 0x0000, 0x0000, 0x0000, 0x8100,
+ 0x0000, 0x0000, 0x1FFF, 0x0FFF, 0x1005, 0x0005, 0x0000, 0x0000,
+ 0x1005, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x1004, 0x1002, 0x0004, 0x0002, 0x8000, 0x8000
+ }
+ },
+
+ {
+ "Delay",
+ {
+ 0x0001, 0x0001, 0x7FFF, 0x7FFF, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x1FFF, 0x0FFF, 0x1005, 0x0005, 0x0000, 0x0000,
+ 0x1005, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x1004, 0x1002, 0x0004, 0x0002, 0x8000, 0x8000,
+ }
+ },
+
+ {
+ "Half Echo",
+ {
+ 0x0017, 0x0013, 0x70F0, 0x4FA8, 0xBCE0, 0x4510, 0xBEF0, 0x8500,
+ 0x5F80, 0x54C0, 0x0371, 0x02AF, 0x02E5, 0x01DF, 0x02B0, 0x01D7,
+ 0x0358, 0x026A, 0x01D6, 0x011E, 0x012D, 0x00B1, 0x011F, 0x0059,
+ 0x01A0, 0x00E3, 0x0058, 0x0040, 0x0028, 0x0014, 0x8000, 0x8000
+ },
+ { // made up from space echo
+ 0.3951f,
+ 0.7552f,
+
+ {
+ { 36.7138f, 134.6171f, 24.4758f, 194.0596f },
+ { 54.1932f, 90.9070f, 29.7174f, 200.0000f },
+ },
+
+ 0.7301f,
+
+ 109.2563f,
+ 0.7222f
+ }
+ },
+
+ { NULL }
+};
+
+//
+//
+//
+
+float spu_device::get_linear_rate(const int n)
+{
+ if (n>=num_linear_rates) return 0.0f;
+ return linear_rate[n]*freq_multiplier;
+}
+
+float spu_device::get_linear_rate_neg_phase(const int n)
+{
+ if (n==0) return ms_to_rate(0.04f);
+ return get_linear_rate(n-1);
+}
+
+float spu_device::get_pos_exp_rate(const int n)
+{
+ if (n>=num_pos_exp_rates) return 0.0f;
+ return pos_exp_rate[n]*freq_multiplier;
+}
+
+float spu_device::get_pos_exp_rate_neg_phase(const int n)
+{
+ if (n==0) return ms_to_rate(0.04f);
+ return get_pos_exp_rate(n-1);
+}
+
+float spu_device::get_neg_exp_rate(const int n)
+{
+ if (n>=num_neg_exp_rates) return 0.0f;
+ return -neg_exp_rate[n]*freq_multiplier;
+}
+
+float spu_device::get_neg_exp_rate_neg_phase(const int n)
+{
+ if (n==0) return -ms_to_rate(0.04f);
+ return get_neg_exp_rate(n-1);
+}
+
+float spu_device::get_decay_rate(const int n)
+{
+ return decay_rate[n]*freq_multiplier;
+}
+
+float spu_device::get_sustain_level(const int n)
+{
+ return ((float)(n+1))/16.0f;
+}
+
+float spu_device::get_linear_release_rate(const int n)
+{
+ if (n>=num_linear_release_rates) return 0.0f;
+ return linear_release_rate[n]*freq_multiplier;
+}
+
+float spu_device::get_exp_release_rate(const int n)
+{
+ if (n>=num_exp_release_rates) return 0.0f;
+ return exp_release_rate[n]*freq_multiplier;
+}
+
+spu_device::reverb_preset *spu_device::find_reverb_preset(const unsigned short *param)
+{
+ for (int i=0; reverb_presets[i].name; i++)
+ {
+ int j;
+
+ for (j=0; j<32; j++)
+ if (reverb_presets[i].param[j]!=param[j])
+ break;
+ if (j==32) return &reverb_presets[i];
+ }
+
+ return NULL;
+}
diff --git a/src/devices/sound/spureverb.c b/src/devices/sound/spureverb.c
new file mode 100644
index 00000000000..3c784acbbee
--- /dev/null
+++ b/src/devices/sound/spureverb.c
@@ -0,0 +1,343 @@
+// license:BSD-3-Clause
+// copyright-holders:pSXAuthor, R. Belmont
+#include "emu.h"
+#include "spureverb.h"
+
+//
+//
+//
+
+//#define use_intrinsics
+
+#ifdef use_intrinsics
+ #include <mmintrin.h>
+#endif
+
+//
+//
+//
+
+static inline int clamp(int v)
+{
+ if (v<-32768) return -32768;
+ if (v>32767) return 32767;
+ return v;
+}
+
+//
+//
+//
+
+reverb::reverb(const int hz, const int maxdelay)
+ : yp(0),
+ max_delay(maxdelay),
+ sound_hz(hz)
+{
+ for (int c=0; c<2; c++)
+ {
+ for (int f=0; f<4; f++) {
+ y[c][f]=new signed short [maxdelay];
+ memset(y[c][f], 0, sizeof(signed short) * maxdelay);
+ }
+ x[c]=new signed short [maxdelay];
+ memset(x[c], 0, sizeof(signed short) * maxdelay);
+ ax[c]=new signed short [maxdelay];
+ memset(ax[c], 0, sizeof(signed short) * maxdelay);
+ ay[c]=new signed short [maxdelay];
+ memset(ay[c], 0, sizeof(signed short) * maxdelay);
+ }
+ memset(bx1,0,sizeof(bx1));
+ memset(by1,0,sizeof(by1));
+}
+
+//
+//
+//
+
+reverb::~reverb()
+{
+ for (int c=0; c<2; c++)
+ {
+ for (int f=0; f<4; f++)
+ global_free_array(y[c][f]);
+ global_free_array(x[c]);
+ global_free_array(ax[c]);
+ global_free_array(ay[c]);
+ }
+}
+
+//
+//
+//
+
+void reverb::bandpass(signed short *sp,
+ const reverb_params *rp,
+ const unsigned int sz)
+{
+ int band_pole=(int)(rp->band_pole*32767),
+ band_gain=(int)(rp->band_gain*32767);
+
+ // Bandpass
+
+ int xp=yp;
+ for (unsigned int i=0; i<(sz>>2); i++, sp+=2)
+ {
+ for (int c=0; c<2; c++)
+ {
+ int x1=(xp-1)&(max_delay-1),
+ bv;
+
+ bv=sp[c]+bx1[c][1]+((band_pole*x[c][x1])>>15);
+ bv=(bv*band_gain)>>15;
+ x[c][xp]=clamp(bv);
+ bx1[c][1]=bx1[c][0];
+ bx1[c][0]=sp[c];
+ }
+
+ xp=(xp+1)&(max_delay-1);
+ }
+}
+
+void reverb::comb_allpass1(signed short *sp,
+ signed short *dp,
+ const comb_param &comb_delay,
+ const int comb_gain,
+ const int allpass_delay,
+ const int allpass_gain,
+ const int *rvol,
+ const unsigned int sz)
+{
+ for (unsigned int i=0; i<(sz>>2); i++, sp+=2, dp+=2)
+ {
+ for (int c=0; c<2; c++)
+ {
+ // Comb
+
+ int v=0;
+
+ for (int f=0; f<4; f++)
+ {
+ int yck=(yp-comb_delay[c][f])&(max_delay-1);
+ y[c][f][yp]=clamp(x[c][yck]+((comb_gain*y[c][f][yck])>>15));
+ v+=y[c][f][yp];
+ }
+
+ v>>=2;
+
+ // Allpass
+
+ if (allpass_delay)
+ {
+ ax[c][yp]=v;
+
+ int ypa=(yp-allpass_delay)&(max_delay-1);
+ v=((allpass_gain*(ay[c][ypa]-x[c][yp]))>>15)+ax[c][ypa];
+ v=clamp(v);
+ ay[c][yp]=v;
+ }
+
+ // Output
+
+ dp[c]=clamp(((v*rvol[c])>>15)+dp[c]+sp[c]);
+ }
+ yp=(yp+1)&(max_delay-1);
+ }
+}
+
+//
+//
+//
+
+void reverb::comb_allpass4(signed short *sp,
+ signed short *dp,
+ const comb_param &comb_delay,
+ const int comb_gain,
+ const int allpass_delay,
+ const int allpass_gain,
+ const int *rvol,
+ const unsigned int sz)
+{
+#ifdef use_intrinsics
+ __m64 cg=_mm_set1_pi16(comb_gain),
+ ag=_mm_set1_pi16(allpass_gain),
+ rv[2];
+ rv[0]=_mm_set1_pi16(rvol[0]);
+ rv[1]=_mm_set1_pi16(rvol[1]);
+
+ for (unsigned int i=0; i<(sz>>4); i++, sp+=2<<2, dp+=2<<2)
+ {
+ __m64 dv[2];
+
+ for (int c=0; c<2; c++)
+ {
+ // Comb
+
+ __m64 v=_mm_setzero_si64();
+
+ for (int f=0; f<4; f++)
+ {
+ int yck=(yp-comb_delay[c][f])&(max_delay-1);
+ __m64 xv=*(__m64 *)(&x[c][yck]),
+ yv=*(__m64 *)(&y[c][f][yck]);
+ yv=_mm_mulhi_pi16(yv,cg);
+ yv=_mm_adds_pi16(yv,yv);
+ yv=_mm_adds_pi16(xv,yv);
+ *((__m64 *)&y[c][f][yp])=yv;
+ yv=_mm_srai_pi16(yv,2);
+ v=_mm_adds_pi16(v,yv);
+ }
+
+ // Allpass
+
+ if (allpass_delay)
+ {
+ *((__m64 *)&ax[c][yp])=v;
+
+ int ypa=(yp-allpass_delay)&(max_delay-1);
+ __m64 ayv=*(__m64 *)&ay[c][ypa],
+ xv=*(__m64 *)&x[c][yp],
+ axv=*(__m64 *)&ax[c][ypa];
+
+ ayv=_mm_subs_pi16(ayv,xv);
+ ayv=_mm_mulhi_pi16(ayv,ag);
+ ayv=_mm_adds_pi16(ayv,ayv);
+ v=_mm_adds_pi16(ayv,axv);
+ *((__m64 *)&ay[c][yp])=v;
+ }
+
+ // Output
+
+ dv[c]=_mm_mulhi_pi16(v,rv[c]);
+ dv[c]=_mm_adds_pi16(dv[c],dv[c]);
+ }
+
+ __m64 dv1=_mm_unpacklo_pi16(dv[0],dv[1]),
+ dv2=_mm_unpackhi_pi16(dv[0],dv[1]),
+ d1=*(__m64 *)&dp[0],
+ d2=*(__m64 *)&dp[4],
+ s1=*(__m64 *)&sp[0],
+ s2=*(__m64 *)&sp[4];
+ d1=_mm_adds_pi16(d1,s1);
+ d2=_mm_adds_pi16(d2,s2);
+ d1=_mm_adds_pi16(d1,dv1);
+ d2=_mm_adds_pi16(d2,dv2);
+ *(__m64 *)&dp[0]=d1;
+ *(__m64 *)&dp[4]=d2;
+
+ yp=(yp+4)&(max_delay-1);
+ }
+
+ _mm_empty();
+#endif
+}
+
+//
+//
+//
+
+void reverb::comb_allpass(signed short *sp,
+ signed short *dp,
+ const reverb_params *rp,
+ const int wetvol_l,
+ const int wetvol_r,
+ const unsigned int _sz)
+{
+ unsigned int sz=_sz;
+ comb_param comb_delay;
+ int comb_gain=(int)(rp->comb_gain*32767),
+ allpass_delay=(int)(((rp->allpass_delay/1000.0f)*sound_hz))&~3,
+ allpass_gain=(int)(rp->allpass_gain*32767),
+ rvol[2]={ (signed short)wetvol_l,
+ (signed short)wetvol_r };
+
+ for (int i=0; i<4; i++)
+ for (int c=0; c<2; c++)
+ comb_delay[c][i]=(int)(((rp->comb_delay[c][i]/1000.0f)*sound_hz))&~3;
+
+ #ifdef use_intrinsics
+
+ if (yp&3)
+ {
+ unsigned int n=min(sz,(unsigned int)(4-(yp&3))<<2);
+ comb_allpass1(sp,dp,
+ (const comb_param &)comb_delay,
+ comb_gain,
+ allpass_delay,
+ allpass_gain,
+ rvol,
+ n);
+ sp+=(n>>1);
+ dp+=(n>>1);
+ sz-=n;
+ }
+
+ if (sz>=16)
+ {
+ unsigned int n=sz&~15;
+ comb_allpass4(sp,dp,
+ (const comb_param &)comb_delay,
+ comb_gain,
+ allpass_delay,
+ allpass_gain,
+ rvol,
+ n);
+ sp+=n>>1;
+ dp+=n>>1;
+ sz-=n;
+ }
+
+ if (sz)
+ {
+ comb_allpass1(sp,dp,
+ (const comb_param &)comb_delay,
+ comb_gain,
+ allpass_delay,
+ allpass_gain,
+ rvol,
+ sz);
+ }
+
+ #else
+ comb_allpass1(sp,dp,
+ (const comb_param &)comb_delay,
+ comb_gain,
+ allpass_delay,
+ allpass_gain,
+ rvol,
+ sz);
+ #endif
+}
+
+//
+//
+//
+
+void reverb::process(signed short *output,
+ signed short *reverb_input,
+ const reverb_params *rp,
+ const int wetvol_l,
+ const int wetvol_r,
+ const unsigned int sz)
+{
+ signed short *sp=(signed short *)reverb_input,
+ *dp=(signed short *)output;
+
+ if (rp->band_gain>0.0f)
+ {
+ // Do reverb processing
+
+ bandpass(sp,rp,sz);
+ comb_allpass(sp,dp,rp,wetvol_l,wetvol_r,sz);
+ } else
+ {
+ // Reverb disabled - just mix the input to the output
+
+ for (unsigned int i=0; i<(sz>>2); i++)
+ {
+ output[0]=clamp(output[0]+reverb_input[0]);
+ output[1]=clamp(output[1]+reverb_input[1]);
+ output+=2;
+ reverb_input+=2;
+ }
+ }
+}
diff --git a/src/devices/sound/spureverb.h b/src/devices/sound/spureverb.h
new file mode 100644
index 00000000000..8a8cb040ba8
--- /dev/null
+++ b/src/devices/sound/spureverb.h
@@ -0,0 +1,72 @@
+// license:BSD-3-Clause
+// copyright-holders:pSXAuthor, R. Belmont
+#ifndef _included_reverb_
+ #define _included_reverb_
+
+ struct reverb_params
+ {
+ float band_pole,
+ band_gain,
+
+ comb_delay[2][4],
+ comb_gain,
+
+ allpass_delay,
+ allpass_gain;
+ };
+
+ class reverb
+ {
+ signed short *y[2][4],
+ *x[2],
+ *ax[2],
+ *ay[2],
+ bx1[2][2],by1[2];
+ int yp,
+ max_delay,
+ sound_hz;
+ typedef int comb_param[2][4];
+
+
+
+ void comb_allpass(signed short *sp,
+ signed short *dp,
+ const reverb_params *rp,
+ const int wetvol_l,
+ const int wetvol_r,
+ const unsigned int _sz);
+ void comb_allpass4(signed short *sp,
+ signed short *dp,
+ const comb_param &comb_delay,
+ const int comb_gain,
+ const int allpass_delay,
+ const int allpass_gain,
+ const int *rvol,
+ const unsigned int sz);
+ void comb_allpass1(signed short *sp,
+ signed short *dp,
+ const comb_param &comb_delay,
+ const int comb_gain,
+ const int allpass_delay,
+ const int allpass_gain,
+ const int *rvol,
+ const unsigned int sz);
+ void bandpass(signed short *sp,
+ const reverb_params *rp,
+ const unsigned int sz);
+
+ public:
+ reverb(const int hz, const int maxdelay=65536);
+ ~reverb();
+
+ void process(signed short *output,
+ signed short *reverb_input,
+ const reverb_params *rp,
+ const int wetvol_l,
+ const int wetvol_r,
+ const unsigned int sz);
+
+ void reset();
+ };
+
+#endif
diff --git a/src/devices/sound/st0016.c b/src/devices/sound/st0016.c
new file mode 100644
index 00000000000..99328a93b4d
--- /dev/null
+++ b/src/devices/sound/st0016.c
@@ -0,0 +1,164 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont, Tomasz Slanina, David Haywood
+/************************************
+ Seta custom ST-0016 chip
+ sound emulation by R. Belmont, Tomasz Slanina, and David Haywood
+************************************/
+
+#include "emu.h"
+#include "st0016.h"
+
+#define VERBOSE (0)
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+// device type definition
+const device_type ST0016 = &device_creator<st0016_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// st0016_device - constructor
+//-------------------------------------------------
+
+st0016_device::st0016_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, ST0016, "ST0016 (Audio)", tag, owner, clock, "st0016_audio", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_ram_read_cb(*this)
+{
+ memset(m_vpos, 0, sizeof(int)*8);
+ memset(m_frac, 0, sizeof(int)*8);
+ memset(m_lponce, 0, sizeof(int)*8);
+ memset(m_regs, 0, sizeof(UINT8)*0x100);
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void st0016_device::device_start()
+{
+ m_stream = stream_alloc(0, 2, 44100);
+ m_ram_read_cb.resolve_safe(0);
+
+ save_item(NAME(m_vpos));
+ save_item(NAME(m_frac));
+ save_item(NAME(m_lponce));
+ save_item(NAME(m_regs));
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void st0016_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int v, i, snum;
+ unsigned char *slot;
+ INT32 mix[48000*2];
+ INT32 *mixp;
+ INT16 sample;
+ int sptr, eptr, freq, lsptr, leptr;
+
+ memset(mix, 0, sizeof(mix[0])*samples*2);
+
+ for (v = 0; v < 8; v++)
+ {
+ slot = (unsigned char *)&m_regs[v * 32];
+
+ if (slot[0x16] & 0x06)
+ {
+ mixp = &mix[0];
+
+ sptr = slot[0x02]<<16 | slot[0x01]<<8 | slot[0x00];
+ eptr = slot[0x0e]<<16 | slot[0x0d]<<8 | slot[0x0c];
+ freq = slot[0x11]<<8 | slot[0x10];
+ lsptr = slot[0x06]<<16 | slot[0x05]<<8 | slot[0x04];
+ leptr = slot[0x0a]<<16 | slot[0x09]<<8 | slot[0x08];
+
+ for (snum = 0; snum < samples; snum++)
+ {
+ sample = m_ram_read_cb((sptr + m_vpos[v]) & 0x1fffff) << 8;
+
+ *mixp++ += (sample * (char)slot[0x14]) >> 8;
+ *mixp++ += (sample * (char)slot[0x15]) >> 8;
+
+ m_frac[v] += freq;
+ m_vpos[v] += (m_frac[v]>>16);
+ m_frac[v] &= 0xffff;
+
+ // stop if we're at the end
+ if (m_lponce[v])
+ {
+ // we've looped once, check loop end rather than sample end
+ if ((m_vpos[v] + sptr) >= leptr)
+ {
+ m_vpos[v] = (lsptr - sptr);
+ }
+ }
+ else
+ {
+ // not looped yet, check sample end
+ if ((m_vpos[v] + sptr) >= eptr)
+ {
+ if (slot[0x16] & 0x01) // loop?
+ {
+ m_vpos[v] = (lsptr - sptr);
+ m_lponce[v] = 1;
+ }
+ else
+ {
+ slot[0x16] = 0;
+ m_vpos[v] = m_frac[v] = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ mixp = &mix[0];
+ for (i = 0; i < samples; i++)
+ {
+ outputs[0][i] = (*mixp++)>>4;
+ outputs[1][i] = (*mixp++)>>4;
+ }
+}
+
+
+READ8_MEMBER( st0016_device::st0016_snd_r )
+{
+ return m_regs[offset];
+}
+
+WRITE8_MEMBER( st0016_device::st0016_snd_w )
+{
+ int voice = offset/32;
+ int reg = offset & 0x1f;
+ int oldreg = m_regs[offset];
+ int vbase = offset & ~0x1f;
+
+ m_regs[offset] = data;
+
+ if ((voice < 8) && (data != oldreg))
+ {
+ if ((reg == 0x16) && (data != 0))
+ {
+ m_vpos[voice] = m_frac[voice] = m_lponce[voice] = 0;
+
+ LOG(("Key on V%02d: st %06x-%06x lp %06x-%06x frq %x flg %x\n", voice,
+ m_regs[vbase+2]<<16 | m_regs[vbase+1]<<8 | m_regs[vbase+2],
+ m_regs[vbase+0xe]<<16 | m_regs[vbase+0xd]<<8 | m_regs[vbase+0xc],
+ m_regs[vbase+6]<<16 | m_regs[vbase+5]<<8 | m_regs[vbase+4],
+ m_regs[vbase+0xa]<<16 | m_regs[vbase+0x9]<<8 | m_regs[vbase+0x8],
+ m_regs[vbase+0x11]<<8 | m_regs[vbase+0x10],
+ m_regs[vbase+0x16]));
+ }
+ }
+}
diff --git a/src/devices/sound/st0016.h b/src/devices/sound/st0016.h
new file mode 100644
index 00000000000..dd23b0c9b7a
--- /dev/null
+++ b/src/devices/sound/st0016.h
@@ -0,0 +1,54 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont, Tomasz Slanina, David Haywood
+#pragma once
+
+#ifndef __ST0016_H__
+#define __ST0016_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_ST0016_SOUNDRAM_READ_CB(_devcb) \
+ devcb = &st0016_device::set_soundram_callback(*device, DEVCB_##_devcb);
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> st0016_device
+
+class st0016_device : public device_t,
+ public device_sound_interface
+{
+public:
+ st0016_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~st0016_device() { }
+
+ template<class _Object> static devcb_base &set_soundram_callback(device_t &device, _Object object) { return downcast<st0016_device &>(device).m_ram_read_cb.set_callback(object); }
+
+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);
+
+public:
+ DECLARE_READ8_MEMBER( st0016_snd_r );
+ DECLARE_WRITE8_MEMBER( st0016_snd_w );
+
+private:
+ sound_stream *m_stream;
+ devcb_read8 m_ram_read_cb;
+ int m_vpos[8];
+ int m_frac[8];
+ int m_lponce[8];
+ UINT8 m_regs[0x100];
+};
+
+extern const device_type ST0016;
+
+
+#endif /* __ST0016_H__ */
diff --git a/src/devices/sound/t6721a.c b/src/devices/sound/t6721a.c
new file mode 100644
index 00000000000..2cc9828f407
--- /dev/null
+++ b/src/devices/sound/t6721a.c
@@ -0,0 +1,112 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ Toshiba T6721A C2MOS Voice Synthesizing LSI emulation
+
+**********************************************************************/
+
+#include "t6721a.h"
+
+
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+// device type definition
+const device_type T6721A = &device_creator<t6721a_device>;
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// t6721a_device - constructor
+//-------------------------------------------------
+
+t6721a_device::t6721a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, T6721A, "T6721A", tag, owner, clock, "t6721a", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_write_eos(*this),
+ m_write_phi2(*this),
+ m_write_dtrd(*this),
+ m_write_apd(*this),
+ m_stream(NULL)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void t6721a_device::device_start()
+{
+ // resolve callbacks
+ m_write_eos.resolve_safe();
+ m_write_phi2.resolve_safe();
+ m_write_dtrd.resolve_safe();
+ m_write_apd.resolve_safe();
+
+ // create sound stream
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate());
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void t6721a_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+}
+
+
+//-------------------------------------------------
+// read -
+//-------------------------------------------------
+
+READ8_MEMBER( t6721a_device::read )
+{
+ return 0;
+}
+
+
+//-------------------------------------------------
+// write -
+//-------------------------------------------------
+
+WRITE8_MEMBER( t6721a_device::write )
+{
+}
+
+
+//-------------------------------------------------
+// di_w - data input write
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( t6721a_device::di_w )
+{
+}
+
+
+//-------------------------------------------------
+// eos_r - eos read
+//-------------------------------------------------
+
+READ_LINE_MEMBER( t6721a_device::eos_r )
+{
+ return 1;
+}
diff --git a/src/devices/sound/t6721a.h b/src/devices/sound/t6721a.h
new file mode 100644
index 00000000000..ff969fa60be
--- /dev/null
+++ b/src/devices/sound/t6721a.h
@@ -0,0 +1,123 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/**********************************************************************
+
+ Toshiba T6721A C2MOS Voice Synthesizing LSI emulation
+
+**********************************************************************
+ _____ _____
+ SP3 1 |* \_/ | 42 Vdd
+ LOSS 2 | | 41 SP2
+ TS 3 | | 40 SP1
+ TSN 4 | | 39 SP0
+ W 5 | | 38 TEM
+ TDAI 6 | | 37 FR
+ TFIO 7 | | 36 BR
+ DAO 8 | | 35 OD
+ APD 9 | | 34 REP
+ phi2 10 | | 33 EXP
+ PD 11 | T6721A | 32 CK2
+ ROM ADR RST 12 | | 31 CK1
+ ROM RST 13 | | 30 M-START
+ ALD 14 | | 29 TPN
+ DI 15 | | 28 _ACL
+ DTRD 16 | | 27 CPUM
+ D3 17 | | 26 _EOS
+ D2 18 | | 25 _BSY
+ D1 19 | | 24 _CE
+ D0 20 | | 23 _RD
+ GND 21 |_____________| 22 _WR
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __T6721__
+#define __T6721__
+
+#include "emu.h"
+
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_T6721A_EOS_HANDLER(_eos) \
+ downcast<t6721a_device *>(device)->set_eos_callback(DEVCB_##_eos);
+
+#define MCFG_T6721A_PHI2_HANDLER(_phi2) \
+ downcast<t6721a_device *>(device)->set_phi2_callback(DEVCB_##_phi2);
+
+#define MCFG_T6721A_DTRD_HANDLER(_dtrd) \
+ downcast<t6721a_device *>(device)->set_dtrd_callback(DEVCB_##_dtrd);
+
+#define MCFG_T6721A_APD_HANDLER(_apd) \
+ downcast<t6721a_device *>(device)->set_apd_callback(DEVCB_##_apd);
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> t6721a_device
+
+class t6721a_device : public device_t,
+ public device_sound_interface
+{
+public:
+ t6721a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _eos> void set_eos_callback(_eos eos) { m_write_eos.set_callback(eos); }
+ template<class _phi2> void set_phi2_callback(_phi2 phi2) { m_write_phi2.set_callback(phi2); }
+ template<class _dtrd> void set_dtrd_callback(_dtrd dtrd) { m_write_dtrd.set_callback(dtrd); }
+ template<class _apd> void set_apd_callback(_apd apd) { m_write_apd.set_callback(apd); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ DECLARE_WRITE_LINE_MEMBER( di_w );
+
+ DECLARE_READ_LINE_MEMBER( eos_r );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ enum
+ {
+ CMD_NOP = 0,
+ CMD_STRT,
+ CMD_STOP,
+ CMD_ADLD,
+ CMD_AAGN,
+ CMD_SPLD,
+ CMD_CNDT1,
+ CMD_CNDT2,
+ CMD_RRDM,
+ CMD_SPDN,
+ CMD_APDN,
+ CMD_SAGN
+ };
+
+ devcb_write_line m_write_eos;
+ devcb_write_line m_write_phi2;
+ devcb_write_line m_write_dtrd;
+ devcb_write_line m_write_apd;
+
+ sound_stream *m_stream;
+};
+
+
+// device type definition
+extern const device_type T6721A;
+
+
+
+#endif
diff --git a/src/devices/sound/t6w28.c b/src/devices/sound/t6w28.c
new file mode 100644
index 00000000000..25303ec4f1b
--- /dev/null
+++ b/src/devices/sound/t6w28.c
@@ -0,0 +1,356 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/***************************************************************************
+
+ t6w28.c (based on sn74696.c)
+
+ The t6w28 sound core is used in the SNK NeoGeo Pocket. It is a stereo
+ sound chip based on 2 partial sn76489a cores.
+
+ The block diagram for this chip is as follows:
+
+Offset 0:
+ Tone 0 /----------> Att0 ---\
+ | |
+ Tone 1 | /-------> Att1 ---+
+ | | | Right
+ Tone 2 | | /----> Att2 ---+-------->
+ | | | | |
+ Noise -----+-------------> Att3 ---/
+ | | | |
+ | | | |
+ Offset 1: | | | |
+ Tone 0 --------+----------> Att0 ---\
+ | | | |
+ Tone 1 -----------+-------> Att1 ---+
+ | | | Left
+ Tone 2 --------------+----> Att2 ---+-------->
+ | |
+ Noise \-------------> Att3 ---/
+
+
+***************************************************************************/
+
+#include "emu.h"
+#include "t6w28.h"
+
+
+#define MAX_OUTPUT 0x7fff
+
+#define STEP 0x10000
+
+WRITE8_MEMBER( t6w28_device::write )
+{
+ int n, r, c;
+
+
+ /* update the output buffer before changing the registers */
+ m_channel->update();
+
+ offset &= 1;
+
+ if (data & 0x80)
+ {
+ r = (data & 0x70) >> 4;
+ m_last_register[offset] = r;
+ m_register[offset * 8 + r] = (m_register[offset * 8 + r] & 0x3f0) | (data & 0x0f);
+ }
+ else
+ {
+ r = m_last_register[offset];
+ }
+ c = r/2;
+ switch (r)
+ {
+ case 0: /* tone 0 : frequency */
+ case 2: /* tone 1 : frequency */
+ case 4: /* tone 2 : frequency */
+ if ((data & 0x80) == 0) m_register[offset * 8 + r] = (m_register[offset * 8 + r] & 0x0f) | ((data & 0x3f) << 4);
+ m_period[offset * 4 + c] = STEP * m_register[offset * 8 + r];
+ if (m_period[offset * 4 + c] == 0) m_period[offset * 4 + c] = STEP;
+ if (r == 4)
+ {
+ /* update noise shift frequency */
+ if ((m_register[offset * 8 + 6] & 0x03) == 0x03)
+ m_period[offset * 4 + 3] = 2 * m_period[offset * 4 + 2];
+ }
+ break;
+ case 1: /* tone 0 : volume */
+ case 3: /* tone 1 : volume */
+ case 5: /* tone 2 : volume */
+ case 7: /* noise : volume */
+ m_volume[offset * 4 + c] = m_vol_table[data & 0x0f];
+ if ((data & 0x80) == 0) m_register[offset * 8 + r] = (m_register[offset * 8 + r] & 0x3f0) | (data & 0x0f);
+ break;
+ case 6: /* noise : frequency, mode */
+ {
+ if ((data & 0x80) == 0) m_register[offset * 8 + r] = (m_register[offset * 8 + r] & 0x3f0) | (data & 0x0f);
+ n = m_register[offset * 8 + 6];
+ m_noise_mode[offset] = (n & 4) ? 1 : 0;
+ /* N/512,N/1024,N/2048,Tone #3 output */
+ m_period[offset * 4 + 3] = ((n&3) == 3) ? 2 * m_period[offset * 4 + 2] : (STEP << (5+(n&3)));
+ /* Reset noise shifter */
+ m_rng[offset] = m_feedback_mask; /* this is correct according to the smspower document */
+ //m_rng = 0xF35; /* this is not, but sounds better in do run run */
+ m_output[offset * 4 + 3] = m_rng[offset] & 1;
+ }
+ break;
+ }
+}
+
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void t6w28_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int i;
+ stream_sample_t *buffer0 = outputs[0];
+ stream_sample_t *buffer1 = outputs[1];
+
+
+ /* If the volume is 0, increase the counter */
+ for (i = 0;i < 8;i++)
+ {
+ if (m_volume[i] == 0)
+ {
+ /* note that I do count += samples, NOT count = samples + 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 (m_count[i] <= samples*STEP) m_count[i] += samples*STEP;
+ }
+ }
+
+ while (samples > 0)
+ {
+ int vol[8];
+ unsigned int out0, out1;
+ 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] = vol[4] = vol[5] = vol[6] = vol[7] = 0;
+
+ for (i = 2;i < 3;i++)
+ {
+ if (m_output[i]) vol[i] += m_count[i];
+ m_count[i] -= STEP;
+ /* m_period[i] is the half period of the square wave. Here, in each */
+ /* loop I add m_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 m_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, m_output[i] has to be inverted */
+ /* and vol[i] incremented only if the exit status of the square */
+ /* wave is 1. */
+ while (m_count[i] <= 0)
+ {
+ m_count[i] += m_period[i];
+ if (m_count[i] > 0)
+ {
+ m_output[i] ^= 1;
+ if (m_output[i]) vol[i] += m_period[i];
+ break;
+ }
+ m_count[i] += m_period[i];
+ vol[i] += m_period[i];
+ }
+ if (m_output[i]) vol[i] -= m_count[i];
+ }
+
+ for (i = 4;i < 7;i++)
+ {
+ if (m_output[i]) vol[i] += m_count[i];
+ m_count[i] -= STEP;
+ /* m_period[i] is the half period of the square wave. Here, in each */
+ /* loop I add m_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 m_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, m_output[i] has to be inverted */
+ /* and vol[i] incremented only if the exit status of the square */
+ /* wave is 1. */
+ while (m_count[i] <= 0)
+ {
+ m_count[i] += m_period[i];
+ if (m_count[i] > 0)
+ {
+ m_output[i] ^= 1;
+ if (m_output[i]) vol[i] += m_period[i];
+ break;
+ }
+ m_count[i] += m_period[i];
+ vol[i] += m_period[i];
+ }
+ if (m_output[i]) vol[i] -= m_count[i];
+ }
+
+ left = STEP;
+ do
+ {
+ int nextevent;
+
+
+ if (m_count[3] < left) nextevent = m_count[3];
+ else nextevent = left;
+
+ if (m_output[3]) vol[3] += m_count[3];
+ m_count[3] -= nextevent;
+ if (m_count[3] <= 0)
+ {
+ if (m_noise_mode[0] == 1) /* White Noise Mode */
+ {
+ if (((m_rng[0] & m_whitenoise_taps) != m_whitenoise_taps) && ((m_rng[0] & m_whitenoise_taps) != 0)) /* crappy xor! */
+ {
+ m_rng[0] >>= 1;
+ m_rng[0] |= m_feedback_mask;
+ }
+ else
+ {
+ m_rng[0] >>= 1;
+ }
+ m_output[3] = m_whitenoise_invert ? !(m_rng[0] & 1) : m_rng[0] & 1;
+ }
+ else /* Periodic noise mode */
+ {
+ if (m_rng[0] & 1)
+ {
+ m_rng[0] >>= 1;
+ m_rng[0] |= m_feedback_mask;
+ }
+ else
+ {
+ m_rng[0] >>= 1;
+ }
+ m_output[3] = m_rng[0] & 1;
+ }
+ m_count[3] += m_period[3];
+ if (m_output[3]) vol[3] += m_period[3];
+ }
+ if (m_output[3]) vol[3] -= m_count[3];
+
+ left -= nextevent;
+ } while (left > 0);
+
+ if (m_enabled)
+ {
+ out0 = vol[4] * m_volume[4] + vol[5] * m_volume[5] +
+ vol[6] * m_volume[6] + vol[3] * m_volume[7];
+
+ out1 = vol[4] * m_volume[0] + vol[5] * m_volume[1] +
+ vol[6] * m_volume[2] + vol[3] * m_volume[3];
+ }
+ else
+ {
+ out0 = 0;
+ out1 = 0;
+ }
+
+ if (out0 > MAX_OUTPUT * STEP) out0 = MAX_OUTPUT * STEP;
+ if (out1 > MAX_OUTPUT * STEP) out1 = MAX_OUTPUT * STEP;
+
+ *(buffer0++) = out0 / STEP;
+ *(buffer1++) = out1 / STEP;
+
+ samples--;
+ }
+}
+
+
+
+void t6w28_device::set_gain(int gain)
+{
+ 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) m_vol_table[i] = MAX_OUTPUT / 3;
+ else m_vol_table[i] = out;
+
+ out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */
+ }
+ m_vol_table[15] = 0;
+}
+
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void t6w28_device::device_start()
+{
+ int i;
+
+ m_sample_rate = clock() / 16;
+ m_channel = machine().sound().stream_alloc(*this, 0, 2, m_sample_rate);
+
+ for (i = 0;i < 8;i++) m_volume[i] = 0;
+
+ m_last_register[0] = 0;
+ m_last_register[1] = 0;
+ for (i = 0;i < 8;i+=2)
+ {
+ m_register[i] = 0;
+ m_register[i + 1] = 0x0f; /* volume = 0 */
+ }
+
+ for (i = 0;i < 8;i++)
+ {
+ m_output[i] = 0;
+ m_period[i] = m_count[i] = STEP;
+ }
+
+ /* Default is SN76489 non-A */
+ m_feedback_mask = 0x4000; /* mask for feedback */
+ m_whitenoise_taps = 0x03; /* mask for white noise taps */
+ m_whitenoise_invert = 1; /* white noise invert flag */
+
+ m_rng[0] = m_feedback_mask;
+ m_rng[1] = m_feedback_mask;
+ m_output[3] = m_rng[0] & 1;
+
+ set_gain(0);
+
+ /* values from sn76489a */
+ m_feedback_mask = 0x8000;
+ m_whitenoise_taps = 0x06;
+ m_whitenoise_invert = FALSE;
+
+ 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_noise_mode));
+ save_item(NAME(m_period));
+ save_item(NAME(m_count));
+ save_item(NAME(m_output));
+ save_item(NAME(m_enabled));
+}
+
+
+void t6w28_device::set_enable(bool enable)
+{
+ m_enabled = enable;
+}
+
+const device_type T6W28 = &device_creator<t6w28_device>;
+
+t6w28_device::t6w28_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, T6W28, "T6W28", tag, owner, clock, "t6w28", __FILE__),
+ device_sound_interface(mconfig, *this)
+{
+}
diff --git a/src/devices/sound/t6w28.h b/src/devices/sound/t6w28.h
new file mode 100644
index 00000000000..7bee03bbc6b
--- /dev/null
+++ b/src/devices/sound/t6w28.h
@@ -0,0 +1,47 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+#pragma once
+
+#ifndef __T6W28_H__
+#define __T6W28_H__
+
+class t6w28_device : public device_t,
+ public device_sound_interface
+{
+public:
+ t6w28_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_WRITE8_MEMBER( write );
+ void set_enable( bool enable );
+
+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);
+
+ void set_gain(int gain);
+
+private:
+ sound_stream *m_channel;
+ int m_sample_rate;
+ int m_vol_table[16]; /* volume table */
+ INT32 m_register[16]; /* registers */
+ INT32 m_last_register[2]; /* last register written */
+ INT32 m_volume[8]; /* volume of voice 0-2 and noise */
+ UINT32 m_rng[2]; /* noise generator */
+ INT32 m_noise_mode[2]; /* active noise mode */
+ INT32 m_feedback_mask; /* mask for feedback */
+ INT32 m_whitenoise_taps; /* mask for white noise taps */
+ INT32 m_whitenoise_invert; /* white noise invert flag */
+ INT32 m_period[8];
+ INT32 m_count[8];
+ INT32 m_output[8];
+ bool m_enabled;
+};
+
+extern const device_type T6W28;
+
+
+#endif /* __T6W28_H__ */
diff --git a/src/devices/sound/tc8830f.c b/src/devices/sound/tc8830f.c
new file mode 100644
index 00000000000..824b9d78958
--- /dev/null
+++ b/src/devices/sound/tc8830f.c
@@ -0,0 +1,256 @@
+// license:BSD-3-Clause
+// copyright-holders:hap
+/***************************************************************************
+
+ tc8830f.c - Toshiba TC8830F, CMOS voice recording/reproducing LSI
+ 1-bit ADM (Adaptive Delta Modulation), similar to TC8801 and T6668.
+
+ Very preliminary...
+
+ TODO:
+ - improve ADM decoder
+ - remaining commands
+ - manual control
+ - chip read
+ - RAM
+ - recording
+
+***************************************************************************/
+
+#include "emu.h"
+#include "tc8830f.h"
+
+
+// device type definition
+const device_type TC8830F = &device_creator<tc8830f_device>;
+
+tc8830f_device::tc8830f_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, TC8830F, "TC8830F", tag, owner, clock, "tc8830f", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_playing(false),
+ m_address(0),
+ m_stop_address(0),
+ m_bitcount(0),
+ m_bitrate(0),
+ m_prevbits(0),
+ m_delta(1),
+ m_output(0),
+ m_command(0),
+ m_cmd_rw(0),
+ m_phrase(0)
+{
+}
+
+
+void tc8830f_device::device_start()
+{
+ // create the stream
+ m_stream = stream_alloc(0, 1, clock() / 0x10);
+
+ m_mem_base = region()->base();
+ m_mem_mask = region()->bytes() - 1;
+
+ // register for savestates
+ save_item(NAME(m_playing));
+ save_item(NAME(m_address));
+ save_item(NAME(m_stop_address));
+ save_item(NAME(m_bitcount));
+ save_item(NAME(m_bitrate));
+ save_item(NAME(m_prevbits));
+ save_item(NAME(m_delta));
+ save_item(NAME(m_output));
+ save_item(NAME(m_command));
+ save_item(NAME(m_cmd_rw));
+ save_item(NAME(m_phrase));
+
+ reset();
+}
+
+
+void tc8830f_device::device_post_load()
+{
+ device_clock_changed();
+}
+
+
+void tc8830f_device::device_clock_changed()
+{
+ int divisor = 0x10 * (4 - (m_bitrate & 3));
+ m_stream->set_sample_rate(clock() / divisor);
+}
+
+
+
+void tc8830f_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ INT32 mix = 0;
+
+ for (int i = 0; i < samples; i++)
+ {
+ if (m_playing)
+ {
+ // get bit
+ int bit = m_mem_base[m_address] >> m_bitcount & 1;
+ m_bitcount = (m_bitcount + 1) & 7;
+ if (m_bitcount == 0)
+ {
+ m_address = (m_address + 1) & m_mem_mask;
+ if (m_address == m_stop_address)
+ m_playing = false;
+ }
+
+ // compute sample, ADM decoding
+ // if previous bits are 111 or 000, delta increases exponentially
+ // otherwise, delta decreases linearly
+ if ((m_prevbits & 7) == 7 || (m_prevbits & 7) == 0)
+ {
+ if (m_delta < 0x2000)
+ m_delta <<= 1;
+ }
+ else
+ m_delta -= 8;
+ if (m_delta <= 0)
+ m_delta = 1;
+
+ // determine direction
+ if (bit)
+ m_output += m_delta;
+ else
+ m_output -= m_delta;
+
+ if (m_output > 32767)
+ m_output = 32767;
+ else if (m_output < -32768)
+ m_output = -32768;
+
+ m_prevbits = m_prevbits << 1 | bit;
+ mix = m_output;
+ }
+
+ outputs[0][i] = mix;
+ }
+}
+
+
+void tc8830f_device::reset()
+{
+ m_stream->update();
+
+ m_playing = false;
+ m_address = 0x100;
+ m_bitcount = 0;
+ m_prevbits = 0;
+ m_delta = 1;
+ m_output = 0;
+ m_cmd_rw = 0;
+
+ // in cpu control, enter play mode and reset bitrate
+ write_p(1);
+ write_p(6);
+ write_p(0);
+}
+
+
+void tc8830f_device::write_p(UINT8 data)
+{
+ m_stream->update();
+ data &= 0xf;
+
+ if (m_cmd_rw == 0)
+ {
+ // select command
+ m_command = data;
+ switch (m_command)
+ {
+ // immediate commands
+ case 0x1:
+ break;
+
+ case 0x2:
+ m_playing = true;
+ break;
+
+ case 0x3:
+ m_playing = false;
+ break;
+
+ // multi-nibble commands
+ case 0x4: case 0x5: case 0x6: case 0x7:
+ m_cmd_rw = 1;
+ break;
+
+ case 0x8: case 0x9: case 0xa: case 0xb:
+ logerror("tc8830f: Unemulated command %X\n", m_command);
+ break;
+
+ default:
+ logerror("tc8830f: Invalid command %X\n", m_command);
+ break;
+ }
+ }
+
+ else
+ {
+ // write command
+ switch (m_command)
+ {
+ case 0x4:
+ // ADLD1: set address counter
+ m_address = (m_address & ~(0xf << (m_cmd_rw*4))) | (data << (m_cmd_rw*4));
+ if (m_cmd_rw == 5)
+ {
+ m_address &= m_mem_mask;
+ m_bitcount = 0;
+ m_cmd_rw = -1;
+ }
+ break;
+
+ case 0x5:
+ // ADLD2: set address stop
+ m_stop_address = (m_stop_address & ~(0xf << (m_cmd_rw*4))) | (data << (m_cmd_rw*4));
+ if (m_cmd_rw == 5)
+ {
+ m_stop_address &= m_mem_mask;
+ m_cmd_rw = -1;
+ }
+ break;
+
+ case 0x6:
+ // CNDT: d0-d1: bitrate, d2: enable overflow
+ m_bitrate = data & 3;
+ device_clock_changed();
+ m_cmd_rw = -1;
+ break;
+
+ case 0x7:
+ // LABEL: set phrase
+ if (m_cmd_rw == 1)
+ {
+ m_phrase = (m_phrase & 0x30) | data;
+ }
+ else
+ {
+ m_phrase = (m_phrase & 0x0f) | (data << 4 & 0x30);
+
+ // update addresses and start
+ UINT8 offs = m_phrase * 4;
+ m_address = (m_mem_base[offs] | m_mem_base[offs|1]<<8 | m_mem_base[offs|2]<<16) & m_mem_mask;
+ offs += 4;
+ m_stop_address = (m_mem_base[offs] | m_mem_base[offs|1]<<8 | m_mem_base[offs|2]<<16) & m_mem_mask;
+
+ m_bitcount = 0;
+ m_prevbits = 0;
+ m_delta = 1;
+ m_output = 0;
+ m_playing = true;
+ m_cmd_rw = -1;
+ }
+ break;
+
+ default:
+ m_cmd_rw = -1;
+ break;
+ }
+ m_cmd_rw++;
+ }
+}
diff --git a/src/devices/sound/tc8830f.h b/src/devices/sound/tc8830f.h
new file mode 100644
index 00000000000..a8b9a2c62f9
--- /dev/null
+++ b/src/devices/sound/tc8830f.h
@@ -0,0 +1,71 @@
+// license:BSD-3-Clause
+// copyright-holders:hap
+/***************************************************************************
+
+ tc8830f.h - Toshiba TC8830F, CMOS voice recording/reproducing LSI
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __TC8830F_H__
+#define __TC8830F_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_TC8830F_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, TC8830F, _clock)
+
+#define MCFG_TC8830F_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, TC8830F, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+class tc8830f_device : public device_t,
+ public device_sound_interface
+{
+public:
+ // construction/destruction
+ tc8830f_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ void reset();
+ void write_p(UINT8 data);
+
+ sound_stream *m_stream;
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_post_load();
+ virtual void device_clock_changed();
+
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ bool m_playing;
+ UINT32 m_address;
+ UINT32 m_stop_address;
+ UINT8 m_bitcount;
+ UINT8 m_bitrate;
+ UINT8 m_prevbits;
+ int m_delta;
+ int m_output;
+ UINT8 m_command;
+ int m_cmd_rw;
+ UINT8 m_phrase;
+
+ UINT8 *m_mem_base;
+ UINT32 m_mem_mask;
+};
+
+
+// device type definition
+extern const device_type TC8830F;
+
+#endif /* __TC8830F_H__ */
diff --git a/src/devices/sound/tiaintf.c b/src/devices/sound/tiaintf.c
new file mode 100644
index 00000000000..7cbfe6683e4
--- /dev/null
+++ b/src/devices/sound/tiaintf.c
@@ -0,0 +1,64 @@
+// license:GPL-2.0+
+// copyright-holders:Ron Fries,Dan Boris
+#include "emu.h"
+#include "tiaintf.h"
+#include "tiasound.h"
+
+// device type definition
+const device_type TIA = &device_creator<tia_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// tia_device - constructor
+//-------------------------------------------------
+
+tia_device::tia_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, TIA, "TIA", tag, owner, clock, "tia_sound", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_channel(NULL),
+ m_chip(NULL)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tia_device::device_start()
+{
+ m_channel = stream_alloc(0, 1, clock());
+ m_chip = tia_sound_init(this, clock(), clock(), 16);
+ assert_always(m_chip != NULL, "Error creating TIA chip");
+}
+
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void tia_device::device_stop()
+{
+ tia_sound_free(m_chip);
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void tia_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ tia_process(m_chip, outputs[0], samples);
+}
+
+
+WRITE8_MEMBER( tia_device::tia_sound_w )
+{
+ m_channel->update();
+ tia_write(m_chip, offset, data);
+}
diff --git a/src/devices/sound/tiaintf.h b/src/devices/sound/tiaintf.h
new file mode 100644
index 00000000000..0fd349b4037
--- /dev/null
+++ b/src/devices/sound/tiaintf.h
@@ -0,0 +1,50 @@
+// license:GPL-2.0+
+// copyright-holders:Ron Fries,Dan Boris
+#pragma once
+
+#ifndef __TIAINTF_H__
+#define __TIAINTF_H__
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_SOUND_TIA_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, TIA, _clock)
+#define MCFG_SOUND_TIA_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, TIA, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> tia_device
+
+class tia_device : public device_t,
+ public device_sound_interface
+{
+public:
+ tia_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~tia_device() { }
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_stop();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+public:
+ DECLARE_WRITE8_MEMBER( tia_sound_w );
+
+private:
+ sound_stream *m_channel;
+ void *m_chip;
+};
+
+extern const device_type TIA;
+
+
+#endif /* __TIAINTF_H__ */
diff --git a/src/devices/sound/tiasound.c b/src/devices/sound/tiasound.c
new file mode 100644
index 00000000000..960520b6b79
--- /dev/null
+++ b/src/devices/sound/tiasound.c
@@ -0,0 +1,612 @@
+// license:GPL-2.0+
+// copyright-holders:Ron Fries,Dan Boris
+/*****************************************************************************/
+/* */
+/* Module: TIA Chip Sound Simulator */
+/* Purpose: To emulate the sound generation hardware of the Atari TIA chip. */
+/* Author: Ron Fries */
+/* */
+/* Revision History: */
+/* 10-Sep-96 - V1.0 - Initial Release */
+/* 14-Jan-97 - V1.1 - Cleaned up sound output by eliminating counter */
+/* reset. */
+/* 30-Oct-98 - Modified for use in MESS by Dan Boris */
+/* 28-Jul-01 - Added support for sample rates > TIA clock rate, */
+/* through oversampling */
+/* 30-Jun-07 - Updated the poly generation. Improved handling of the */
+/* POLY5_DIV3 mode. (Wilbert Pol) */
+/* */
+/* */
+/* */
+/*****************************************************************************/
+/* */
+/* License Information and Copyright Notice */
+/* ======================================== */
+/* */
+/* TiaSound is Copyright Ron Fries */
+/* */
+/* This library is free software; you can redistribute it and/or modify it */
+/* under the terms of version 2 of the GNU Library General Public License */
+/* as published by the Free Software Foundation. */
+/* */
+/* This library is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library */
+/* General Public License for more details. */
+/* To obtain a copy of the GNU Library General Public License, write to the */
+/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+/* */
+/* Any permitted reproduction of these routines, in whole or in part, must */
+/* bear this legend. */
+/* */
+/*****************************************************************************/
+
+#include "emu.h"
+#include "tiaintf.h"
+#include "tiasound.h"
+
+/* number of bits to shift left AUDV0/AUDV1 registers for 16 bit volume */
+#define AUDV_SHIFT 10
+
+/* CONSTANT DEFINITIONS */
+
+/* definitions for AUDCx (15, 16) */
+#define SET_TO_1 0x00 /* 0000 */
+#define POLY4 0x01 /* 0001 */
+#define DIV31_POLY4 0x02 /* 0010 */
+#define POLY5_POLY4 0x03 /* 0011 */
+#define PURE 0x04 /* 0100 */
+#define PURE2 0x05 /* 0101 */
+#define DIV31_PURE 0x06 /* 0110 */
+#define POLY5_2 0x07 /* 0111 */
+#define POLY9 0x08 /* 1000 */
+#define POLY5 0x09 /* 1001 */
+#define DIV31_POLY5 0x0a /* 1010 */
+#define POLY5_POLY5 0x0b /* 1011 */
+#define DIV3_PURE 0x0c /* 1100 */
+#define DIV3_PURE2 0x0d /* 1101 */
+#define DIV93_PURE 0x0e /* 1110 */
+#define POLY5_DIV3 0x0f /* 1111 */
+
+#define DIV3_MASK 0x0c
+
+#define AUDC0 0x15
+#define AUDC1 0x16
+#define AUDF0 0x17
+#define AUDF1 0x18
+#define AUDV0 0x19
+#define AUDV1 0x1a
+
+/* the size (in entries) of the 4 polynomial tables */
+#define POLY4_SIZE 0x000f
+#define POLY5_SIZE 0x001f
+#define POLY9_SIZE 0x01ff
+
+/* channel definitions */
+#define CHAN1 0
+#define CHAN2 1
+
+/* LOCAL GLOBAL VARIABLE DEFINITIONS */
+
+struct tia
+{
+ /* structures to hold the 6 tia sound control bytes */
+ UINT8 AUDC[2]; /* AUDCx (15, 16) */
+ UINT8 AUDF[2]; /* AUDFx (17, 18) */
+ INT16 AUDV[2]; /* AUDVx (19, 1A) */
+
+ INT16 Outvol[2]; /* last output volume for each channel */
+
+ int tia_gain; /* initialized in tia_sound_init() */
+
+ /* Initialze the bit patterns for the polynomials. */
+
+ /* The 4bit and 5bit patterns are the identical ones used in the tia chip. */
+ /* Though the patterns could be packed with 8 bits per byte, using only a */
+ /* single bit per byte keeps the math simple, which is important for */
+ /* efficient processing. */
+
+ /* HJB: poly bits are initialized at runtime */
+
+ UINT8 Bit4[POLY4_SIZE];
+ UINT8 Bit5[POLY5_SIZE];
+ UINT8 Bit9[POLY9_SIZE];
+
+
+ UINT8 P4[2]; /* Position pointer for the 4-bit POLY array */
+ UINT8 P5[2]; /* Position pointer for the 5-bit POLY array */
+ UINT16 P9[2]; /* Position pointer for the 9-bit POLY array */
+
+ UINT8 Div_n_cnt[2]; /* Divide by n counter. one for each channel */
+ UINT8 Div_n_max[2]; /* Divide by n maximum, one for each channel */
+ UINT8 Div_3_cnt[2]; /* Div 3 counter, used for POLY5_DIV3 mode */
+
+
+ /* In my routines, I treat the sample output as another divide by N counter. */
+ /* For better accuracy, the Samp_n_cnt has a fixed binary decimal point */
+ /* which has 8 binary digits to the right of the decimal point. */
+
+ UINT16 Samp_n_max; /* Sample max, multiplied by 256 */
+ UINT16 Samp_n_cnt; /* Sample cnt. */
+
+ int oversampling; /* Added oversampling for sample_rate > clock_rate */
+};
+
+
+/* I've treated the 'Div by 31' counter as another polynomial because of */
+/* the way it operates. It does not have a 50% duty cycle, but instead */
+/* has a 13:18 ratio (of course, 13+18 = 31). This could also be */
+/* implemented by using counters. */
+
+static const UINT8 Div31[POLY5_SIZE] =
+ {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+
+/*****************************************************************************/
+/* Module: tia_sound_w() */
+/* Purpose: To process the latest control values stored in the AUDF, AUDC, */
+/* and AUDV registers. It pre-calculates as much information as */
+/* possible for better performance. This routine has not been */
+/* optimized. */
+/* */
+/* Author: Ron Fries */
+/* Date: January 14, 1997 */
+/* */
+/* Inputs: addr - the address of the parameter to be changed */
+/* val - the new value to be placed in the specified address */
+/* */
+/* Outputs: Adjusts local globals - no return value */
+/* */
+/*****************************************************************************/
+
+void tia_write(void *_chip, offs_t offset, UINT8 data)
+{
+ struct tia *chip = (struct tia *)_chip;
+ UINT16 new_val = 0;
+ UINT8 chan;
+
+ /* determine which address was changed */
+ switch (offset)
+ {
+ case AUDC0:
+ chip->AUDC[0] = data & 0x0f;
+ chan = 0;
+ break;
+
+ case AUDC1:
+ chip->AUDC[1] = data & 0x0f;
+ chan = 1;
+ break;
+
+ case AUDF0:
+ chip->AUDF[0] = data & 0x1f;
+ chan = 0;
+ break;
+
+ case AUDF1:
+ chip->AUDF[1] = data & 0x1f;
+ chan = 1;
+ break;
+
+ case AUDV0:
+ chip->AUDV[0] = ((data & 0x0f) << AUDV_SHIFT);
+ chan = 0;
+ break;
+
+ case AUDV1:
+ chip->AUDV[1] = ((data & 0x0f) << AUDV_SHIFT);
+ chan = 1;
+ break;
+
+ default:
+ chan = 255;
+ break;
+ }
+
+ /* if the output value changed */
+ if (chan != 255)
+ {
+ /* an AUDC value of 0 is a special case */
+ if (chip->AUDC[chan] == SET_TO_1 || chip->AUDC[chan] == POLY5_POLY5)
+ {
+ /* indicate the clock is zero so no processing will occur */
+ new_val = 0;
+
+ /* and set the output to the selected volume */
+ chip->Outvol[chan] = chip->AUDV[chan];
+ }
+ else
+ {
+ /* otherwise calculate the 'divide by N' value */
+ new_val = chip->AUDF[chan] + 1;
+
+ /* if bits 2 & 3 are set, then multiply the 'div by n' count by 3 */
+ if ((chip->AUDC[chan] & DIV3_MASK) == DIV3_MASK && chip->AUDC[chan] != POLY5_DIV3)
+ {
+ new_val *= 3;
+ }
+ }
+
+ /* only reset those channels that have changed */
+ if (new_val != chip->Div_n_max[chan])
+ {
+ /* reset the divide by n counters */
+ chip->Div_n_max[chan] = new_val;
+
+ /* if the channel is now volume only or was volume only */
+ if ((chip->Div_n_cnt[chan] == 0) || (new_val == 0))
+ {
+ /* reset the counter (otherwise let it complete the previous) */
+ chip->Div_n_cnt[chan] = new_val;
+ }
+ }
+ }
+}
+
+
+/*****************************************************************************/
+/* Module: tia_process() */
+/* Purpose: To fill the output buffer with the sound output based on the */
+/* tia chip parameters. This routine has been optimized. */
+/* */
+/* Author: Ron Fries */
+/* Date: September 10, 1996 */
+/* */
+/* Inputs: *buffer - pointer to the buffer where the audio output will */
+/* be placed */
+/* n - size of the playback buffer */
+/* */
+/* Outputs: the buffer will be filled with n bytes of audio - no return val */
+/* */
+/*****************************************************************************/
+
+void tia_process(void *_chip, stream_sample_t *buffer, int length)
+{
+ struct tia *chip = (struct tia *)_chip;
+ UINT8 audc0, audc1;
+ UINT8 div_n_cnt0, div_n_cnt1;
+ UINT8 p5_0, p5_1;
+ INT16 audv0, audv1, outvol_0, outvol_1;
+
+ audc0 = chip->AUDC[0];
+ audc1 = chip->AUDC[1];
+ audv0 = chip->AUDV[0];
+ audv1 = chip->AUDV[1];
+
+ /* make temporary local copy */
+ p5_0 = chip->P5[0];
+ p5_1 = chip->P5[1];
+ outvol_0 = chip->Outvol[0];
+ outvol_1 = chip->Outvol[1];
+ div_n_cnt0 = chip->Div_n_cnt[0];
+ div_n_cnt1 = chip->Div_n_cnt[1];
+
+ /* loop until the buffer is filled */
+ while (length > 0)
+ {
+ /* Process channel 0 */
+ if (div_n_cnt0 > 1)
+ {
+ div_n_cnt0--;
+ }
+ else if (div_n_cnt0 == 1)
+ {
+ int prev_bit5 = chip->Bit5[p5_0];
+
+ div_n_cnt0 = chip->Div_n_max[0];
+
+ /* the chip->P5 counter has multiple uses, so we inc it here */
+ p5_0++;
+ if (p5_0 == POLY5_SIZE)
+ p5_0 = 0;
+
+ /* check clock modifier for clock tick */
+ if ((audc0 & 0x02) == 0 ||
+ ((audc0 & 0x01) == 0 && Div31[p5_0]) ||
+ ((audc0 & 0x01) == 1 && chip->Bit5[p5_0]) ||
+ ((audc0 & 0x0f) == POLY5_DIV3 && chip->Bit5[p5_0] != prev_bit5))
+ {
+ if (audc0 & 0x04) /* pure modified clock selected */
+ {
+ if ((audc0 & 0x0f) == POLY5_DIV3) /* POLY5 -> DIV3 mode */
+ {
+ if ( chip->Bit5[p5_0] != prev_bit5 )
+ {
+ chip->Div_3_cnt[0]--;
+ if ( ! chip->Div_3_cnt[0] )
+ {
+ chip->Div_3_cnt[0] = 3;
+ if (outvol_0)
+ outvol_0 = 0;
+ else
+ outvol_0 = audv0;
+ }
+ }
+ }
+ else if (outvol_0) /* if the output was set */
+ outvol_0 = 0; /* turn it off */
+ else
+ outvol_0 = audv0; /* else turn it on */
+ }
+ else if (audc0 & 0x08) /* check for p5/p9 */
+ {
+ if (audc0 == POLY9) /* check for poly9 */
+ {
+ /* inc the poly9 counter */
+ chip->P9[0]++;
+ if (chip->P9[0] == POLY9_SIZE)
+ chip->P9[0] = 0;
+
+ if (chip->Bit9[chip->P9[0]])
+ outvol_0 = audv0;
+ else
+ outvol_0 = 0;
+ }
+ else if ( audc0 & 0x02 )
+ {
+ if (outvol_0 || audc0 & 0x01 )
+ outvol_0 = 0;
+ else
+ outvol_0 = audv0;
+ }
+ else
+ /* must be poly5 */
+ {
+ if (chip->Bit5[p5_0])
+ outvol_0 = audv0;
+ else
+ outvol_0 = 0;
+ }
+ }
+ else
+ /* poly4 is the only remaining option */
+ {
+ /* inc the poly4 counter */
+ chip->P4[0]++;
+ if (chip->P4[0] == POLY4_SIZE)
+ chip->P4[0] = 0;
+
+ if (chip->Bit4[chip->P4[0]])
+ outvol_0 = audv0;
+ else
+ outvol_0 = 0;
+ }
+ }
+ }
+
+
+ /* Process channel 1 */
+ if (div_n_cnt1 > 1)
+ {
+ div_n_cnt1--;
+ }
+ else if (div_n_cnt1 == 1)
+ {
+ int prev_bit5 = chip->Bit5[p5_1];
+
+ div_n_cnt1 = chip->Div_n_max[1];
+
+ /* the chip->P5 counter has multiple uses, so we inc it here */
+ p5_1++;
+ if (p5_1 == POLY5_SIZE)
+ p5_1 = 0;
+
+ /* check clock modifier for clock tick */
+ if ((audc1 & 0x02) == 0 ||
+ ((audc1 & 0x01) == 0 && Div31[p5_1]) ||
+ ((audc1 & 0x01) == 1 && chip->Bit5[p5_1]) ||
+ ((audc1 & 0x0f) == POLY5_DIV3 && chip->Bit5[p5_1] != prev_bit5))
+ {
+ if (audc1 & 0x04) /* pure modified clock selected */
+ {
+ if ((audc1 & 0x0f) == POLY5_DIV3) /* POLY5 -> DIV3 mode */
+ {
+ if ( chip->Bit5[p5_1] != prev_bit5 )
+ {
+ chip->Div_3_cnt[1]--;
+ if ( ! chip->Div_3_cnt[1] )
+ {
+ chip->Div_3_cnt[1] = 3;
+ if (outvol_1)
+ outvol_1 = 0;
+ else
+ outvol_1 = audv1;
+ }
+ }
+ }
+ else if (outvol_1) /* if the output was set */
+ outvol_1 = 0; /* turn it off */
+ else
+ outvol_1 = audv1; /* else turn it on */
+ }
+ else if (audc1 & 0x08) /* check for p5/p9 */
+ {
+ if (audc1 == POLY9) /* check for poly9 */
+ {
+ /* inc the poly9 counter */
+ chip->P9[1]++;
+ if (chip->P9[1] == POLY9_SIZE)
+ chip->P9[1] = 0;
+
+ if (chip->Bit9[chip->P9[1]])
+ outvol_1 = audv1;
+ else
+ outvol_1 = 0;
+ }
+ else if ( audc1 & 0x02 )
+ {
+ if (outvol_1 || audc1 & 0x01 )
+ outvol_1 = 0;
+ else
+ outvol_1 = audv1;
+ }
+ else
+ /* must be poly5 */
+ {
+ if (chip->Bit5[p5_1])
+ outvol_1 = audv1;
+ else
+ outvol_1 = 0;
+ }
+ }
+ else
+ /* poly4 is the only remaining option */
+ {
+ /* inc the poly4 counter */
+ chip->P4[1]++;
+ if (chip->P4[1] == POLY4_SIZE)
+ chip->P4[1] = 0;
+
+ if (chip->Bit4[chip->P4[1]])
+ outvol_1 = audv1;
+ else
+ outvol_1 = 0;
+ }
+ }
+ }
+
+ if (!chip->oversampling)
+ {
+ /* decrement the sample counter - value is 256 since the lower
+ * byte contains the fractional part */
+ chip->Samp_n_cnt -= 256;
+
+ /* if the count down has reached zero */
+ if (chip->Samp_n_cnt < 256)
+ {
+ /* adjust the sample counter */
+ chip->Samp_n_cnt += chip->Samp_n_max;
+
+ /* calculate the latest output value and place in buffer */
+ *buffer++ = outvol_0 + outvol_1;
+
+ /* and indicate one less byte to process */
+ length--;
+ }
+ }
+ else
+ {
+ do
+ {
+ /* decrement the sample counter - value is 256 since the lower
+ * byte contains the fractional part */
+ chip->Samp_n_cnt -= 256;
+ /* calculate the latest output value and place in buffer */
+ *buffer++ = outvol_0 + outvol_1;
+ length--;
+ }
+ while ((chip->Samp_n_cnt >= 256) && (length > 0));
+
+ /* adjust the sample counter if necessary */
+ if (chip->Samp_n_cnt < 256)
+ chip->Samp_n_cnt += chip->Samp_n_max;
+ }
+ }
+
+ /* save for next round */
+ chip->P5[0] = p5_0;
+ chip->P5[1] = p5_1;
+ chip->Outvol[0] = outvol_0;
+ chip->Outvol[1] = outvol_1;
+ chip->Div_n_cnt[0] = div_n_cnt0;
+ chip->Div_n_cnt[1] = div_n_cnt1;
+
+}
+
+static void poly_init(UINT8 *poly, int size, int f0, int f1)
+{
+ int mask = (1 << size) - 1;
+ int i, x = mask;
+
+ for (i = 0; i < mask; i++)
+ {
+ int bit0 = ( ( size - f0 ) ? ( x >> ( size - f0 ) ) : x ) & 0x01;
+ int bit1 = ( ( size - f1 ) ? ( x >> ( size - f1 ) ) : x ) & 0x01;
+ poly[i] = x & 1;
+ /* calculate next bit */
+ x = ( x >> 1 ) | ( ( bit0 ^ bit1 ) << ( size - 1) );
+ }
+}
+
+static void tia_save_state(device_t *device, tia *tia)
+{
+ device->save_item(NAME(tia->AUDC));
+ device->save_item(NAME(tia->AUDF));
+ device->save_item(NAME(tia->AUDV));
+ device->save_item(NAME(tia->Outvol));
+ device->save_item(NAME(tia->P4));
+ device->save_item(NAME(tia->P5));
+ device->save_item(NAME(tia->P9));
+ device->save_item(NAME(tia->Div_n_cnt));
+ device->save_item(NAME(tia->Div_n_max));
+ device->save_item(NAME(tia->Div_3_cnt));
+ device->save_item(NAME(tia->Samp_n_cnt));
+ device->save_item(NAME(tia->oversampling));
+}
+
+/*****************************************************************************/
+/* Module: tia_sh_start() */
+/* Purpose: to handle the power-up initialization functions */
+/* these functions should only be executed on a cold-restart */
+/* */
+/* Author: Ron Fries */
+/* Date: September 10, 1996 */
+/* */
+/* Inputs: sound_config *msound */
+/* is a pointer to the struct TIAInterface parameters */
+/* */
+/* Outputs: returns zero on success */
+/* */
+/*****************************************************************************/
+
+void *tia_sound_init(device_t *device, int clock, int sample_rate, int gain)
+{
+ struct tia *chip;
+ int chan;
+
+ chip = global_alloc_clear(struct tia);
+
+ /* set the gain factor (normally use TIA_DEFAULT_GAIN) */
+ chip->tia_gain = gain;
+
+ /* fill the polynomials */
+ poly_init(chip->Bit4, 4, 4, 3);
+ poly_init(chip->Bit5, 5, 5, 3);
+ poly_init(chip->Bit9, 9, 9, 5);
+
+ /* calculate the sample 'divide by N' value based on the playback freq. */
+ chip->Samp_n_max = ((UINT16)(UINT32)clock << 8) / sample_rate;
+ chip->Samp_n_cnt = chip->Samp_n_max; /* initialize all bits of the sample counter */
+
+ if (chip->Samp_n_max < 256) /* we need to use oversampling for sample_rate > clock_rate */
+ {
+ chip->Samp_n_max = ((UINT16)(UINT32)sample_rate << 8) / clock;
+ chip->Samp_n_cnt = chip->Samp_n_max;
+ chip->oversampling = 1;
+ }
+
+ /* initialize the local globals */
+ for (chan = CHAN1; chan <= CHAN2; chan++)
+ {
+ chip->Outvol[chan] = 0;
+ chip->Div_n_cnt[chan] = 0;
+ chip->Div_n_max[chan] = 0;
+ chip->Div_3_cnt[chan] = 3;
+ chip->AUDC[chan] = 0;
+ chip->AUDF[chan] = 0;
+ chip->AUDV[chan] = 0;
+ chip->P4[chan] = 0;
+ chip->P5[chan] = 0;
+ chip->P9[chan] = 0;
+ }
+
+ tia_save_state(device, chip);
+
+ return chip;
+}
+
+
+void tia_sound_free(void *chip)
+{
+ global_free((struct tia *)chip);
+}
diff --git a/src/devices/sound/tiasound.h b/src/devices/sound/tiasound.h
new file mode 100644
index 00000000000..a73b63c9cbe
--- /dev/null
+++ b/src/devices/sound/tiasound.h
@@ -0,0 +1,47 @@
+// license:GPL-2.0+
+// copyright-holders:Ron Fries,Dan Boris
+/*****************************************************************************/
+/* */
+/* Module: TIA Chip Sound Simulator Includes, V1.1 */
+/* Purpose: Define global function prototypes and structures for the TIA */
+/* Chip Sound Simulator. */
+/* Author: Ron Fries */
+/* */
+/* Revision History: */
+/* 10-Sep-96 - V1.0 - Initial Release */
+/* 14-Jan-97 - V1.1 - Added compiler directives to facilitate compilation */
+/* on a C++ compiler. */
+/* */
+/*****************************************************************************/
+/* */
+/* License Information and Copyright Notice */
+/* ======================================== */
+/* */
+/* TiaSound is Copyright Ron Fries */
+/* */
+/* This library is free software; you can redistribute it and/or modify it */
+/* under the terms of version 2 of the GNU Library General Public License */
+/* as published by the Free Software Foundation. */
+/* */
+/* This library is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library */
+/* General Public License for more details. */
+/* To obtain a copy of the GNU Library General Public License, write to the */
+/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+/* */
+/* Any permitted reproduction of these routines, in whole or in part, must */
+/* bear this legend. */
+/* */
+/*****************************************************************************/
+#pragma once
+
+#ifndef __TIASOUND_H__
+#define __TIASOUND_H__
+
+void *tia_sound_init(device_t *device, int clock, int sample_rate, int gain);
+void tia_sound_free(void *chip);
+void tia_process (void *chip, stream_sample_t *buffer, int length);
+void tia_write(void *chip, offs_t offset, UINT8 data);
+
+#endif /* __TIASOUND_H__ */
diff --git a/src/devices/sound/tms3615.c b/src/devices/sound/tms3615.c
new file mode 100644
index 00000000000..3ccf9159b9d
--- /dev/null
+++ b/src/devices/sound/tms3615.c
@@ -0,0 +1,109 @@
+// license:BSD-3-Clause
+// copyright-holders:Juergen Buchmueller
+#include "emu.h"
+#include "tms3615.h"
+
+#define VMIN 0x0000
+#define VMAX 0x7fff
+
+static const int divisor[TMS3615_TONES] = { 478, 451, 426, 402, 379, 358, 338, 319, 301, 284, 268, 253, 239 };
+
+
+// device type definition
+const device_type TMS3615 = &device_creator<tms3615_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// tms3615_device - constructor
+//-------------------------------------------------
+
+tms3615_device::tms3615_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, TMS3615, "TMS3615", tag, owner, clock, "tms3615", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_channel(0),
+ m_samplerate(0),
+ m_basefreq(0),
+ m_output8(0),
+ m_output16(0),
+ m_enable(0)
+{
+ memset(m_counter8, 0, TMS3615_TONES);
+ memset(m_counter16, 0, TMS3615_TONES);
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tms3615_device::device_start()
+{
+ m_channel = stream_alloc(0, 2, clock()/8);
+ m_samplerate = clock()/8;
+ m_basefreq = clock();
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void tms3615_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int samplerate = m_samplerate;
+ stream_sample_t *buffer8 = outputs[TMS3615_FOOTAGE_8];
+ stream_sample_t *buffer16 = outputs[TMS3615_FOOTAGE_16];
+
+ while( samples-- > 0 )
+ {
+ int sum8 = 0, sum16 = 0, tone = 0;
+
+ for (tone = 0; tone < TMS3615_TONES; tone++)
+ {
+ // 8'
+
+ m_counter8[tone] -= (m_basefreq / divisor[tone]);
+
+ while( m_counter8[tone] <= 0 )
+ {
+ m_counter8[tone] += samplerate;
+ m_output8 ^= 1 << tone;
+ }
+
+ if (m_output8 & m_enable & (1 << tone))
+ {
+ sum8 += VMAX;
+ }
+
+ // 16'
+
+ m_counter16[tone] -= (m_basefreq / divisor[tone] / 2);
+
+ while( m_counter16[tone] <= 0 )
+ {
+ m_counter16[tone] += samplerate;
+ m_output16 ^= 1 << tone;
+ }
+
+ if (m_output16 & m_enable & (1 << tone))
+ {
+ sum16 += VMAX;
+ }
+ }
+
+ *buffer8++ = sum8 / TMS3615_TONES;
+ *buffer16++ = sum16 / TMS3615_TONES;
+ }
+
+ m_enable = 0;
+}
+
+
+void tms3615_device::enable_w(int enable)
+{
+ m_enable = enable;
+}
diff --git a/src/devices/sound/tms3615.h b/src/devices/sound/tms3615.h
new file mode 100644
index 00000000000..f68a4718ef1
--- /dev/null
+++ b/src/devices/sound/tms3615.h
@@ -0,0 +1,61 @@
+// license:BSD-3-Clause
+// copyright-holders:Juergen Buchmueller
+#pragma once
+
+#ifndef __TMS3615_H__
+#define __TMS3615_H__
+
+#define TMS3615_TONES 13
+#define TMS3615_FOOTAGE_8 0
+#define TMS3615_FOOTAGE_16 1
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_TMS3615_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, TMS3615, _clock)
+#define MCFG_TMS3615_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, TMS3615, _clock)
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+
+// ======================> tms3615_device
+
+class tms3615_device : public device_t,
+ public device_sound_interface
+{
+public:
+ tms3615_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~tms3615_device() { }
+
+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);
+
+public:
+ void enable_w(int enable);
+
+private:
+ sound_stream *m_channel; /* returned by stream_create() */
+ int m_samplerate; /* output sample rate */
+ int m_basefreq; /* chip's base frequency */
+ int m_counter8[TMS3615_TONES]; /* tone frequency counter for 8' */
+ int m_counter16[TMS3615_TONES]; /* tone frequency counter for 16'*/
+ int m_output8; /* output signal bits for 8' */
+ int m_output16; /* output signal bits for 16' */
+ int m_enable; /* mask which tones to play */
+};
+
+extern ATTR_DEPRECATED const device_type TMS3615;
+
+
+#endif /* __TMS3615_H__ */
diff --git a/src/devices/sound/tms36xx.c b/src/devices/sound/tms36xx.c
new file mode 100644
index 00000000000..18c55ce6235
--- /dev/null
+++ b/src/devices/sound/tms36xx.c
@@ -0,0 +1,557 @@
+// license:BSD-3-Clause
+// copyright-holders:Juergen Buchmueller
+#include "emu.h"
+#include "tms36xx.h"
+
+#define VERBOSE 1
+
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+/* the frequencies are later adjusted by "* clock / FSCALE" */
+#define FSCALE 1024
+
+#define C(n) (int)((FSCALE<<(n-1))*1.18921) /* 2^(3/12) */
+#define Cx(n) (int)((FSCALE<<(n-1))*1.25992) /* 2^(4/12) */
+#define D(n) (int)((FSCALE<<(n-1))*1.33484) /* 2^(5/12) */
+#define Dx(n) (int)((FSCALE<<(n-1))*1.41421) /* 2^(6/12) */
+#define E(n) (int)((FSCALE<<(n-1))*1.49831) /* 2^(7/12) */
+#define F(n) (int)((FSCALE<<(n-1))*1.58740) /* 2^(8/12) */
+#define Fx(n) (int)((FSCALE<<(n-1))*1.68179) /* 2^(9/12) */
+#define G(n) (int)((FSCALE<<(n-1))*1.78180) /* 2^(10/12) */
+#define Gx(n) (int)((FSCALE<<(n-1))*1.88775) /* 2^(11/12) */
+#define A(n) (int)((FSCALE<<n)) /* A */
+#define Ax(n) (int)((FSCALE<<n)*1.05946) /* 2^(1/12) */
+#define B(n) (int)((FSCALE<<n)*1.12246) /* 2^(2/12) */
+
+/*
+ * Alarm sound?
+ * It is unknown what this sound is like. Until somebody manages
+ * trigger sound #1 of the Phoenix PCB sound chip I put just something
+ * 'alarming' in here.
+ */
+static const int tune1[96*6] = {
+ C(3), 0, 0, C(2), 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, 0, 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, 0, 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, 0, 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, C(4), 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, 0, 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, 0, 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, 0, 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, C(2), 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, 0, 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, 0, 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, 0, 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, C(4), 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, 0, 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, 0, 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+ C(3), 0, 0, 0, 0, 0,
+ G(3), 0, 0, 0, 0, 0,
+};
+
+/*
+ * Fuer Elise, Beethoven
+ * (Excuse my non-existent musical skill, Mr. B ;-)
+ */
+static const int tune2[96*6] = {
+ D(3), D(4), D(5), 0, 0, 0,
+ Cx(3), Cx(4), Cx(5), 0, 0, 0,
+ D(3), D(4), D(5), 0, 0, 0,
+ Cx(3), Cx(4), Cx(5), 0, 0, 0,
+ D(3), D(4), D(5), 0, 0, 0,
+ A(2), A(3), A(4), 0, 0, 0,
+ C(3), C(4), C(5), 0, 0, 0,
+ Ax(2), Ax(3), Ax(4), 0, 0, 0,
+ G(2), G(3), G(4), 0, 0, 0,
+ D(1), D(2), D(3), 0, 0, 0,
+ G(1), G(2), G(3), 0, 0, 0,
+ Ax(1), Ax(2), Ax(3), 0, 0, 0,
+
+ D(2), D(3), D(4), 0, 0, 0,
+ G(2), G(3), G(4), 0, 0, 0,
+ A(2), A(3), A(4), 0, 0, 0,
+ D(1), D(2), D(3), 0, 0, 0,
+ A(1), A(2), A(3), 0, 0, 0,
+ D(2), D(3), D(4), 0, 0, 0,
+ Fx(2), Fx(3), Fx(4), 0, 0, 0,
+ A(2), A(3), A(4), 0, 0, 0,
+ Ax(2), Ax(3), Ax(4), 0, 0, 0,
+ D(1), D(2), D(3), 0, 0, 0,
+ G(1), G(2), G(3), 0, 0, 0,
+ Ax(1), Ax(2), Ax(3), 0, 0, 0,
+
+ D(3), D(4), D(5), 0, 0, 0,
+ Cx(3), Cx(4), Cx(5), 0, 0, 0,
+ D(3), D(4), D(5), 0, 0, 0,
+ Cx(3), Cx(4), Cx(5), 0, 0, 0,
+ D(3), D(4), D(5), 0, 0, 0,
+ A(2), A(3), A(4), 0, 0, 0,
+ C(3), C(4), C(5), 0, 0, 0,
+ Ax(2), Ax(3), Ax(4), 0, 0, 0,
+ G(2), G(3), G(4), 0, 0, 0,
+ D(1), D(2), D(3), 0, 0, 0,
+ G(1), G(2), G(3), 0, 0, 0,
+ Ax(1), Ax(2), Ax(3), 0, 0, 0,
+
+ D(2), D(3), D(4), 0, 0, 0,
+ G(2), G(3), G(4), 0, 0, 0,
+ A(2), A(3), A(4), 0, 0, 0,
+ D(1), D(2), D(3), 0, 0, 0,
+ A(1), A(2), A(3), 0, 0, 0,
+ D(2), D(3), D(4), 0, 0, 0,
+ Ax(2), Ax(3), Ax(4), 0, 0, 0,
+ A(2), A(3), A(4), 0, 0, 0,
+ 0, 0, 0, G(2), G(3), G(4),
+ D(1), D(2), D(3), 0, 0, 0,
+ G(1), G(2), G(3), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * The theme from Phoenix, a sad little tune.
+ * Gerald Coy:
+ * The starting song from Phoenix comes from an old French movie and
+ * it's called : "Jeux interdits" which means "unallowed games" ;-)
+ * Mirko Buffoni:
+ * It's called "Sogni proibiti" in Italian, by Anonymous.
+ * Magic*:
+ * This song is a classical piece called "ESTUDIO" from M.A.Robira.
+ */
+static const int tune3[96*6] = {
+ A(2), A(3), A(4), D(1), D(2), D(3),
+ 0, 0, 0, 0, 0, 0,
+ A(2), A(3), A(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ A(2), A(3), A(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+
+ A(2), A(3), A(4), A(1), A(2), A(3),
+ 0, 0, 0, 0, 0, 0,
+ G(2), G(3), G(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ F(2), F(3), F(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+
+ F(2), F(3), F(4), F(1), F(2), F(3),
+ 0, 0, 0, 0, 0, 0,
+ E(2), E(3), E(4), F(1), F(2), F(3),
+ 0, 0, 0, 0, 0, 0,
+ D(2), D(3), D(4), F(1), F(2), F(3),
+ 0, 0, 0, 0, 0, 0,
+
+ D(2), D(3), D(4), A(1), A(2), A(3),
+ 0, 0, 0, 0, 0, 0,
+ F(2), F(3), F(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ A(2), A(3), A(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+
+ D(3), D(4), D(5), D(1), D(2), D(3),
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, D(1), D(2), D(3),
+ 0, 0, 0, F(1), F(2), F(3),
+ 0, 0, 0, A(1), A(2), A(3),
+ 0, 0, 0, D(2), D(2), D(2),
+
+ D(3), D(4), D(5), D(1), D(2), D(3),
+ 0, 0, 0, 0, 0, 0,
+ C(3), C(4), C(5), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ Ax(2), Ax(3), Ax(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+
+ Ax(2), Ax(3), Ax(4), Ax(1), Ax(2), Ax(3),
+ 0, 0, 0, 0, 0, 0,
+ A(2), A(3), A(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ G(2), G(3), G(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+
+ G(2), G(3), G(4), G(1), G(2), G(3),
+ 0, 0, 0, 0, 0, 0,
+ A(2), A(3), A(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ Ax(2), Ax(3), Ax(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+
+ A(2), A(3), A(4), A(1), A(2), A(3),
+ 0, 0, 0, 0, 0, 0,
+ Ax(2), Ax(3), Ax(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ A(2), A(3), A(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+
+ Cx(3), Cx(4), Cx(5), A(1), A(2), A(3),
+ 0, 0, 0, 0, 0, 0,
+ Ax(2), Ax(3), Ax(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ A(2), A(3), A(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+
+ A(2), A(3), A(4), F(1), F(2), F(3),
+ 0, 0, 0, 0, 0, 0,
+ G(2), G(3), G(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ F(2), F(3), F(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+
+ F(2), F(3), F(4), D(1), D(2), D(3),
+ 0, 0, 0, 0, 0, 0,
+ E(2), E(3), E(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ D(2), D(3), D(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+
+ E(2), E(3), E(4), E(1), E(2), E(3),
+ 0, 0, 0, 0, 0, 0,
+ E(2), E(3), E(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ E(2), E(3), E(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+
+ E(2), E(3), E(4), Ax(1), Ax(2), Ax(3),
+ 0, 0, 0, 0, 0, 0,
+ F(2), F(3), F(4), 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ E(2), E(3), E(4), F(1), F(2), F(3),
+ 0, 0, 0, 0, 0, 0,
+
+ D(2), D(3), D(4), D(1), D(2), D(3),
+ 0, 0, 0, 0, 0, 0,
+ F(2), F(3), F(4), A(1), A(2), A(3),
+ 0, 0, 0, 0, 0, 0,
+ A(2), A(3), A(4), F(1), F(2), F(3),
+ 0, 0, 0, 0, 0, 0,
+
+ D(3), D(4), D(5), D(1), D(2), D(3),
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0
+};
+
+/* This is used to play single notes for the TMS3615/TMS3617 */
+static const int tune4[13*6] = {
+/* 16' 8' 5 1/3' 4' 2 2/3' 2' */
+ B(0), B(1), Dx(2), B(2), Dx(3), B(3),
+ C(1), C(2), E(2), C(3), E(3), C(4),
+ Cx(1), Cx(2), F(2), Cx(3), F(3), Cx(4),
+ D(1), D(2), Fx(2), D(3), Fx(3), D(4),
+ Dx(1), Dx(2), G(2), Dx(3), G(3), Dx(4),
+ E(1), E(2), Gx(2), E(3), Gx(3), E(4),
+ F(1), F(2), A(2), F(3), A(3), F(4),
+ Fx(1), Fx(2), Ax(2), Fx(3), Ax(3), Fx(4),
+ G(1), G(2), B(2), G(3), B(3), G(4),
+ Gx(1), Gx(2), C(3), Gx(3), C(4), Gx(4),
+ A(1), A(2), Cx(3), A(3), Cx(4), A(4),
+ Ax(1), Ax(2), D(3), Ax(3), D(4), Ax(4),
+ B(1), B(2), Dx(3), B(3), Dx(4), B(4)
+};
+
+static const int *const tunes[] = {NULL,tune1,tune2,tune3,tune4};
+
+#define DECAY(voice) \
+ if( m_vol[voice] > TMS36XX_VMIN ) \
+ { \
+ /* decay of first voice */ \
+ m_vol_counter[voice] -= m_decay[voice]; \
+ while( m_vol_counter[voice] <= 0 ) \
+ { \
+ m_vol_counter[voice] += samplerate; \
+ if( m_vol[voice]-- <= TMS36XX_VMIN ) \
+ { \
+ m_frequency[voice] = 0; \
+ m_vol[voice] = TMS36XX_VMIN; \
+ break; \
+ } \
+ } \
+ }
+
+#define RESTART(voice) \
+ if( tunes[m_tune_num][m_tune_ofs*6+voice] ) \
+ { \
+ m_frequency[m_shift+voice] = \
+ tunes[m_tune_num][m_tune_ofs*6+voice] * \
+ (m_basefreq << m_octave) / FSCALE; \
+ m_vol[m_shift+voice] = TMS36XX_VMAX; \
+ }
+
+#define TONE(voice) \
+ if( (m_enable & (1<<voice)) && m_frequency[voice] ) \
+ { \
+ /* first note */ \
+ m_counter[voice] -= m_frequency[voice]; \
+ while( m_counter[voice] <= 0 ) \
+ { \
+ m_counter[voice] += samplerate; \
+ m_output ^= 1 << voice; \
+ } \
+ if (m_output & m_enable & (1 << voice)) \
+ sum += m_vol[voice]; \
+ }
+
+
+
+// device type definition
+const device_type TMS36XX = &device_creator<tms36xx_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// tms36xx_device - constructor
+//-------------------------------------------------
+
+tms36xx_device::tms36xx_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, TMS36XX, "TMS36XX", tag, owner, clock, "tms36xx", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_subtype(NULL),
+ m_channel(NULL),
+ m_samplerate(0),
+ m_basefreq(0),
+ m_octave(0),
+ m_speed(0),
+ m_tune_counter(0),
+ m_note_counter(0),
+ m_voices(0),
+ m_shift(0),
+ m_output(0),
+ m_enable(0),
+ m_tune_num(0),
+ m_tune_ofs(0),
+ m_tune_max(0)
+{
+ memset(m_vol, 0, sizeof(int)*12);
+ memset(m_vol_counter, 0, sizeof(int)*12);
+ memset(m_decay, 0, sizeof(int)*12);
+ memset(m_counter, 0, sizeof(int)*12);
+ memset(m_frequency, 0, sizeof(int)*12);
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tms36xx_device::device_start()
+{
+ int enable = 0;
+
+ m_channel = stream_alloc(0, 1, clock() * 64);
+ m_samplerate = clock() * 64;
+ m_basefreq = clock();
+
+ for (int j = 0; j < 6; j++)
+ {
+ if (m_decay_time[j] > 0)
+ {
+ m_decay[j+0] = m_decay[j+6] = TMS36XX_VMAX / m_decay_time[j];
+ enable |= 0x41 << j;
+ }
+ }
+ tms3617_enable(enable);
+
+ LOG(("TMS36xx samplerate %d\n", m_samplerate));
+ LOG(("TMS36xx basefreq %d\n", m_basefreq));
+ LOG(("TMS36xx decay %d,%d,%d,%d,%d,%d\n",
+ m_decay[0], m_decay[1], m_decay[2],
+ m_decay[3], m_decay[4], m_decay[5]));
+ LOG(("TMS36xx speed %d\n", m_speed));
+
+ save_item(NAME(m_octave));
+ save_item(NAME(m_tune_counter));
+ save_item(NAME(m_note_counter));
+ save_item(NAME(m_voices));
+ save_item(NAME(m_shift));
+ save_item(NAME(m_vol));
+ save_item(NAME(m_vol_counter));
+ save_item(NAME(m_counter));
+ save_item(NAME(m_frequency));
+ save_item(NAME(m_output));
+ save_item(NAME(m_enable));
+ save_item(NAME(m_tune_num));
+ save_item(NAME(m_tune_ofs));
+ save_item(NAME(m_tune_max));
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void tms36xx_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int samplerate = m_samplerate;
+ stream_sample_t *buffer = outputs[0];
+
+ /* no tune played? */
+ if( !tunes[m_tune_num] || m_voices == 0 )
+ {
+ while (--samples >= 0)
+ buffer[samples] = 0;
+ return;
+ }
+
+ while( samples-- > 0 )
+ {
+ int sum = 0;
+
+ /* decay the twelve voices */
+ DECAY( 0) DECAY( 1) DECAY( 2) DECAY( 3) DECAY( 4) DECAY( 5)
+ DECAY( 6) DECAY( 7) DECAY( 8) DECAY( 9) DECAY(10) DECAY(11)
+
+ /* musical note timing */
+ m_tune_counter -= m_speed;
+ if( m_tune_counter <= 0 )
+ {
+ int n = (-m_tune_counter / samplerate) + 1;
+ m_tune_counter += n * samplerate;
+
+ if( (m_note_counter -= n) <= 0 )
+ {
+ m_note_counter += TMS36XX_VMAX;
+ if (m_tune_ofs < m_tune_max)
+ {
+ /* shift to the other 'bank' of voices */
+ m_shift ^= 6;
+ /* restart one 'bank' of voices */
+ RESTART(0) RESTART(1) RESTART(2)
+ RESTART(3) RESTART(4) RESTART(5)
+ m_tune_ofs++;
+ }
+ }
+ }
+
+ /* update the twelve voices */
+ TONE( 0) TONE( 1) TONE( 2) TONE( 3) TONE( 4) TONE( 5)
+ TONE( 6) TONE( 7) TONE( 8) TONE( 9) TONE(10) TONE(11)
+
+ *buffer++ = sum / m_voices;
+ }
+}
+
+
+//-------------------------------------------------
+// MM6221AA interface functions
+//-------------------------------------------------
+
+void tms36xx_device::mm6221aa_tune_w(int tune)
+{
+ /* which tune? */
+ tune &= 3;
+ if( tune == m_tune_num )
+ return;
+
+ LOG(("%s tune:%X\n", m_subtype, tune));
+
+ /* update the stream before changing the tune */
+ m_channel->update();
+
+ m_tune_num = tune;
+ m_tune_ofs = 0;
+ m_tune_max = 96; /* fixed for now */
+}
+
+
+//-------------------------------------------------
+// TMS3615/17 interface functions
+//-------------------------------------------------
+
+void tms36xx_device::tms36xx_note_w(int octave, int note)
+{
+ octave &= 3;
+ note &= 15;
+
+ if (note > 12)
+ return;
+
+ LOG(("%s octave:%X note:%X\n", m_subtype, octave, note));
+
+ /* update the stream before changing the tune */
+ m_channel->update();
+
+ /* play a single note from 'tune 4', a list of the 13 tones */
+ tms36xx_reset_counters();
+ m_octave = octave;
+ m_tune_num = 4;
+ m_tune_ofs = note;
+ m_tune_max = note + 1;
+}
+
+
+//-------------------------------------------------
+// TMS3617 interface functions
+//-------------------------------------------------
+
+void tms36xx_device::tms3617_enable_w(int enable)
+{
+ tms3617_enable(enable);
+}
+
+
+//-------------------------------------------------
+// Locals
+//-------------------------------------------------
+
+void tms36xx_device::tms36xx_reset_counters()
+{
+ m_tune_counter = 0;
+ m_note_counter = 0;
+ memset(m_vol_counter, 0, sizeof(m_vol_counter));
+ memset(m_counter, 0, sizeof(m_counter));
+}
+
+
+void tms36xx_device::tms3617_enable(int enable)
+{
+ int i, bits = 0;
+
+ /* duplicate the 6 voice enable bits */
+ enable = (enable & 0x3f) | ((enable & 0x3f) << 6);
+ if (enable == m_enable)
+ return;
+
+ /* update the stream before changing the tune */
+ m_channel->update();
+
+ LOG(("%s enable voices", m_subtype));
+ for (i = 0; i < 6; i++)
+ {
+ if (enable & (1 << i))
+ {
+ bits += 2; /* each voice has two instances */
+
+ switch (i)
+ {
+ case 0: LOG((" 16'")); break;
+ case 1: LOG((" 8'")); break;
+ case 2: LOG((" 5 1/3'")); break;
+ case 3: LOG((" 4'")); break;
+ case 4: LOG((" 2 2/3'")); break;
+ case 5: LOG((" 2'")); break;
+ }
+ }
+ }
+ /* set the enable mask and number of active voices */
+ m_enable = enable;
+ m_voices = bits;
+ LOG(("%s\n", bits ? "" : " none"));
+}
diff --git a/src/devices/sound/tms36xx.h b/src/devices/sound/tms36xx.h
new file mode 100644
index 00000000000..b15e5908cc5
--- /dev/null
+++ b/src/devices/sound/tms36xx.h
@@ -0,0 +1,137 @@
+// license:BSD-3-Clause
+// copyright-holders:Juergen Buchmueller
+#pragma once
+
+#ifndef __TMS36XX_H__
+#define __TMS36XX_H__
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_TMS36XX_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, TMS36XX, _clock)
+#define MCFG_TMS36XX_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, TMS36XX, _clock)
+
+#define MCFG_TMS36XX_TYPE(_type) \
+ tms36xx_device::set_subtype(*device, _type);
+
+#define MCFG_TMS36XX_DECAY_TIMES(_dec0, _dec1, _dec2, _dec3, _dec4, _dec5) \
+ tms36xx_device::set_decays(*device, _dec0, _dec1, _dec2, _dec3, _dec4, _dec5);
+
+#define MCFG_TMS36XX_TUNE_SPEED(_speed) \
+ tms36xx_device::set_tune_speed(*device, _speed);
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// subtypes
+#define MM6221AA 21 // Phoenix (fixed melodies)
+#define TMS3615 15 // Naughty Boy, Pleiads (13 notes, one output)
+#define TMS3617 17 // Monster Bash (13 notes, six outputs)
+
+#define TMS36XX_VMIN 0x0000
+#define TMS36XX_VMAX 0x7fff
+
+
+// ======================> tms36xx_device
+
+class tms36xx_device : public device_t,
+ public device_sound_interface
+{
+public:
+ tms36xx_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~tms36xx_device() { }
+
+ static void set_subtype(device_t &device, int type)
+ {
+ tms36xx_device &dev = downcast<tms36xx_device &>(device);
+ switch (type)
+ {
+ case MM6221AA:
+ dev.m_subtype = "MM6221AA";
+ break;
+ case TMS3615:
+ dev.m_subtype = "TMS3615";
+ break;
+ case TMS3617:
+ dev.m_subtype = "TMS3617";
+ break;
+ default:
+ fatalerror("Invalid TMS36XX type: %d\n", type);
+ break;
+ }
+ }
+ static void set_tune_speed(device_t &device, double speed)
+ {
+ downcast<tms36xx_device &>(device).m_speed = (speed > 0) ? TMS36XX_VMAX / speed : TMS36XX_VMAX;
+ }
+ static void set_decays(device_t &device, double decay_0, double decay_1, double decay_2, double decay_3, double decay_4, double decay_5)
+ {
+ tms36xx_device &dev = downcast<tms36xx_device &>(device);
+ dev.m_decay_time[0] = decay_0;
+ dev.m_decay_time[1] = decay_1;
+ dev.m_decay_time[2] = decay_2;
+ dev.m_decay_time[3] = decay_3;
+ dev.m_decay_time[4] = decay_4;
+ dev.m_decay_time[5] = decay_5;
+ }
+
+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);
+
+public:
+ // MM6221AA interface functions
+ void mm6221aa_tune_w(int tune);
+
+ // TMS3615/17 interface functions
+ void tms36xx_note_w(int octave, int note);
+
+ // TMS3617 interface functions
+ void tms3617_enable_w(int enable);
+
+private:
+ void tms36xx_reset_counters();
+ void tms3617_enable(int enable);
+
+ double m_decay_time[6]; // decay times for the six harmonic notes
+
+ const char *m_subtype; // subtype name MM6221AA, TMS3615 or TMS3617
+ sound_stream *m_channel; // returned by stream_create()
+
+ int m_samplerate; // output sample rate
+
+ int m_basefreq; // chip's base frequency
+ int m_octave; // octave select of the TMS3615
+
+ int m_speed; // speed of the tune
+ int m_tune_counter; // tune counter
+ int m_note_counter; // note counter
+
+ int m_voices; // active voices
+ int m_shift; // shift toggles between 0 and 6 to allow decaying voices
+ int m_vol[12]; // (decaying) volume of harmonics notes
+ int m_vol_counter[12];// volume adjustment counter
+ int m_decay[12]; // volume adjustment rate - derived from m_intf_decay
+
+ int m_counter[12]; // tone frequency counter
+ int m_frequency[12]; // tone frequency
+ int m_output; // output signal bits
+ int m_enable; // mask which harmoics
+
+ int m_tune_num; // tune currently playing
+ int m_tune_ofs; // note currently playing
+ int m_tune_max; // end of tune
+};
+
+extern const device_type TMS36XX;
+
+
+#endif /* __TMS36XX_H__ */
diff --git a/src/devices/sound/tms5110.c b/src/devices/sound/tms5110.c
new file mode 100644
index 00000000000..481229a6bc3
--- /dev/null
+++ b/src/devices/sound/tms5110.c
@@ -0,0 +1,1620 @@
+// license:???
+// copyright-holders:Frank Palazzolo, Jarek Burczynski, Aaron Giles, Jonathan Gevaryahu, Couriersud
+/**********************************************************************************************
+
+ TMS5110 simulator (modified from TMS5220 by Jarek Burczynski)
+
+ Written for MAME by Frank Palazzolo
+ With help from Neill Corlett
+ Additional tweaking by Aaron Giles
+ Various fixes by Lord Nightmare
+ Additional enhancements by Couriersud
+ Sub-interpolation-cycle parameter updating added by Lord Nightmare
+ Read-bit and Output fixes by Lord Nightmare
+
+ Todo:
+ - implement CS
+ - TMS5110_CMD_TEST_TALK is only partially implemented
+
+ TMS5100:
+
+ +-----------------+
+ TST | 1 28 | CS
+ PDC | 2 27 | CTL8
+ ROM CK | 3 26 | ADD8
+ CPU CK | 4 25 | CTL1
+ VDD | 5 24 | ADD1
+ CR OSC | 6 23 | CTL2
+ RC OSC | 7 22 | ADD2
+ T11 | 8 21 | ADD4
+ NC | 9 20 | CTL4
+ I/O | 10 19 | M1
+ SPK1 | 11 18 | NC
+ SPK2 | 12 17 | NC
+ PROM OUT | 13 16 | NC
+ VSS | 14 15 | M0
+ +-----------------+
+
+ T11: Sync for serial data out
+
+
+ M58817
+
+ The following connections could be derived from radar scope schematics.
+ The M58817 is not 100% pin compatible to the 5100, but really close.
+
+ +-----------------+
+ (NC) | 1 28 | CS
+ PDC | 2 27 | CTL8
+ ROM CK | 3 26 | ADD8 (to 58819)
+ (NC) | 4 25 | CTL1
+ (VDD,-5) | 5 24 | ADD1 (to 58819)
+ (GND) | 6 23 | CTL2
+ Xin | 7 22 | ADD2 (to 58819)
+ Xout | 8 21 | ADD4 (to 58819)
+ (NC) | 9 20 | CTL4
+ (VDD,-5) | 10 19 | Status back to CPU
+ (NC) | 11 18 | C1 (to 58819)
+ SPKR | 12 17 | (NC)
+ SPKR | 13 16 | C0 (to 58819)
+ (NC) | 14 15 | (5V)
+ +-----------------+
+
+***********************************************************************************************/
+
+#include "emu.h"
+#include "tms5110.h"
+
+static INT16 clip_analog(INT16 cliptemp);
+
+/* *****optional defines***** */
+
+/* Hacky improvements which don't match patent: */
+/* Interpolation shift logic:
+ * One of the following two lines should be used, and the other commented
+ * The second line is more accurate mathematically but not accurate to the patent
+ */
+#define INTERP_SHIFT >> m_coeff->interp_coeff[m_IP]
+//define INTERP_SHIFT / (1<<m_coeff->interp_coeff[m_IP])
+
+/* Other hacks */
+/* HACK: if defined, outputs the low 4 bits of the lattice filter to the i/o
+ * or clip logic, even though the real hardware doesn't do this, partially verified by decap */
+#undef ALLOW_4_LSB
+
+/* forces m_TALK active instantly whenever m_SPEN would be activated, causing speech delay to be reduced by up to one frame time */
+/* for some reason, this hack makes snmath behave marginally more accurate to hardware, though it does not match the patent */
+#define FAST_START_HACK 1
+
+
+/* *****configuration of chip connection stuff***** */
+/* must be defined; if 0, output the waveform as if it was tapped on the speaker pin as usual, if 1, output the waveform as if it was tapped on the i/o pin (volume is much lower in the latter case) */
+#define FORCE_DIGITAL 0
+
+
+/* *****debugging defines***** */
+#undef VERBOSE
+// above is general, somewhat obsolete, catch all for debugs which don't fit elsewhere
+#undef DEBUG_PARSE_FRAME_DUMP
+// above dumps each frame to stderr: be sure to select one of the options below if you define it!
+#undef DEBUG_PARSE_FRAME_DUMP_BIN
+// dumps each speech frame as binary
+#undef DEBUG_PARSE_FRAME_DUMP_HEX
+// dumps each speech frame as hex
+#undef DEBUG_FRAME_ERRORS
+// above dumps info if a frame ran out of data
+#undef DEBUG_COMMAND_DUMP
+// above dumps all command writes and PDC-related state machine changes, plus command writes to VSMs
+#undef DEBUG_GENERATION
+// above dumps debug information related to the sample generation loop, i.e. whether interpolation is inhibited or not, and what the current and target values for each frame are.
+#undef DEBUG_GENERATION_VERBOSE
+// above dumps MUCH MORE debug information related to the sample generation loop, namely the excitation, energy, pitch, k*, and output values for EVERY SINGLE SAMPLE during a frame.
+#undef DEBUG_LATTICE
+// above dumps the lattice filter state data each sample.
+#undef DEBUG_CLIP
+// above dumps info to stderr whenever the analog clip hardware is (or would be) clipping the signal.
+
+
+#define MAX_SAMPLE_CHUNK 512
+
+/* 6 Variants, from tms5110r.inc */
+
+#define TMS5110_IS_TMC0281 (1)
+#define TMS5110_IS_TMC0281D (2)
+#define TMS5110_IS_CD2801 (3)
+#define TMS5110_IS_CD2802 (4)
+#define TMS5110_IS_TMS5110A (5)
+#define TMS5110_IS_M58817 (6)
+
+
+/* States for CTL */
+
+// ctl bus is input to tms51xx
+#define CTL_STATE_INPUT (0)
+// ctl bus is outputting a test talk command on CTL1(bit 0)
+#define CTL_STATE_TTALK_OUTPUT (1)
+// ctl bus is switching direction, next will be above
+#define CTL_STATE_NEXT_TTALK_OUTPUT (2)
+// ctl bus is outputting a read nybble 'output' command on CTL1,2,4,8 (bits 0-3)
+#define CTL_STATE_OUTPUT (3)
+// ctl bus is switching direction, next will be above
+#define CTL_STATE_NEXT_OUTPUT (4)
+
+
+
+/* Pull in the ROM tables */
+#include "tms5110r.inc"
+
+#define DEBUG_5110 0
+
+void tms5110_device::set_variant(int variant)
+{
+ switch (variant)
+ {
+ case TMS5110_IS_TMC0281:
+ m_coeff = &T0280B_0281A_coeff;
+ break;
+ case TMS5110_IS_TMC0281D:
+ m_coeff = &T0280D_0281D_coeff;
+ break;
+ case TMS5110_IS_CD2801:
+ m_coeff = &T0280F_2801A_coeff;
+ break;
+ case TMS5110_IS_M58817:
+ m_coeff = &M58817_coeff;
+ break;
+ case TMS5110_IS_CD2802:
+ m_coeff = &T0280F_2802_coeff;
+ break;
+ case TMS5110_IS_TMS5110A:
+ m_coeff = &tms5110a_coeff;
+ break;
+ default:
+ fatalerror("Unknown variant in tms5110_create\n");
+ }
+
+ m_variant = variant;
+}
+
+void tms5110_device::new_int_write(UINT8 rc, UINT8 m0, UINT8 m1, UINT8 addr)
+{
+ if (!m_m0_cb.isnull())
+ m_m0_cb(m0);
+ if (!m_m1_cb.isnull())
+ m_m1_cb(m1);
+ if (!m_addr_cb.isnull())
+ m_addr_cb((offs_t)0, addr);
+ if (!m_romclk_cb.isnull())
+ {
+ //printf("rc %d\n", rc);
+ m_romclk_cb(rc);
+ }
+}
+
+void tms5110_device::new_int_write_addr(UINT8 addr)
+{
+ new_int_write(1, 0, 1, addr); // romclk 1, m0 0, m1 1, addr bus nybble = xxxx
+ new_int_write(0, 0, 1, addr); // romclk 0, m0 0, m1 1, addr bus nybble = xxxx
+ new_int_write(1, 0, 0, addr); // romclk 1, m0 0, m1 0, addr bus nybble = xxxx
+ new_int_write(0, 0, 0, addr); // romclk 0, m0 0, m1 0, addr bus nybble = xxxx
+}
+
+UINT8 tms5110_device::new_int_read()
+{
+ new_int_write(1, 1, 0, 0); // romclk 1, m0 1, m1 0, addr bus nybble = 0/open bus
+ new_int_write(0, 1, 0, 0); // romclk 0, m0 1, m1 0, addr bus nybble = 0/open bus
+ new_int_write(1, 0, 0, 0); // romclk 1, m0 0, m1 0, addr bus nybble = 0/open bus
+ new_int_write(0, 0, 0, 0); // romclk 0, m0 0, m1 0, addr bus nybble = 0/open bus
+ if (!m_data_cb.isnull())
+ return m_data_cb();
+ if (DEBUG_5110) logerror("WARNING: CALLBACK MISSING, RETURNING 0!\n");
+ return 0;
+}
+
+void tms5110_device::register_for_save_states()
+{
+ save_item(NAME(m_variant));
+
+ save_item(NAME(m_PDC));
+ save_item(NAME(m_CTL_pins));
+ save_item(NAME(m_SPEN));
+ save_item(NAME(m_TALK));
+ save_item(NAME(m_TALKD));
+ save_item(NAME(m_state));
+
+ save_item(NAME(m_address));
+ save_item(NAME(m_next_is_address));
+ save_item(NAME(m_schedule_dummy_read));
+ save_item(NAME(m_addr_bit));
+ save_item(NAME(m_CTL_buffer));
+
+ save_item(NAME(m_OLDE));
+ save_item(NAME(m_OLDP));
+
+ save_item(NAME(m_new_frame_energy_idx));
+ save_item(NAME(m_new_frame_pitch_idx));
+ save_item(NAME(m_new_frame_k_idx));
+#ifdef PERFECT_INTERPOLATION_HACK
+ save_item(NAME(m_old_frame_energy_idx));
+ save_item(NAME(m_old_frame_pitch_idx));
+ save_item(NAME(m_old_frame_k_idx));
+ save_item(NAME(m_old_zpar));
+ save_item(NAME(m_old_uv_zpar));
+#endif
+ save_item(NAME(m_current_energy));
+ save_item(NAME(m_current_pitch));
+ save_item(NAME(m_current_k));
+
+ save_item(NAME(m_previous_energy));
+
+ save_item(NAME(m_subcycle));
+ save_item(NAME(m_subc_reload));
+ save_item(NAME(m_PC));
+ save_item(NAME(m_IP));
+ save_item(NAME(m_inhibit));
+ save_item(NAME(m_uv_zpar));
+ save_item(NAME(m_zpar));
+ save_item(NAME(m_pitch_zero));
+ save_item(NAME(m_pitch_count));
+
+ save_item(NAME(m_u));
+ save_item(NAME(m_x));
+
+ save_item(NAME(m_RNG));
+ save_item(NAME(m_excitation_data));
+
+ save_item(NAME(m_digital_select));
+
+ save_item(NAME(m_speech_rom_bitnum));
+
+ save_item(NAME(m_romclk_hack_timer_started));
+ save_item(NAME(m_romclk_hack_state));
+}
+
+/**********************************************************************************************
+
+ printbits helper function: takes a long int input and prints the resulting bits to stderr
+
+***********************************************************************************************/
+
+#ifdef DEBUG_PARSE_FRAME_DUMP_BIN
+static void printbits(long data, int num)
+{
+ int i;
+ for (i=(num-1); i>=0; i--)
+ fprintf(stderr,"%0ld", (data>>i)&1);
+}
+#endif
+#ifdef DEBUG_PARSE_FRAME_DUMP_HEX
+static void printbits(long data, int num)
+{
+ switch((num-1)&0xFC)
+ {
+ case 0:
+ fprintf(stderr,"%0lx", data);
+ break;
+ case 4:
+ fprintf(stderr,"%02lx", data);
+ break;
+ case 8:
+ fprintf(stderr,"%03lx", data);
+ break;
+ case 12:
+ fprintf(stderr,"%04lx", data);
+ break;
+ default:
+ fprintf(stderr,"%04lx", data);
+ break;
+ }
+}
+#endif
+
+/******************************************************************************************
+
+ extract_bits -- extract a specific number of bits from the VSM
+
+******************************************************************************************/
+
+int tms5110_device::extract_bits(int count)
+{
+ int val = 0;
+ if (DEBUG_5110) logerror("requesting %d bits", count);
+ for (int i = 0; i < count; i++)
+ {
+ val = (val<<1) | new_int_read();
+ if (DEBUG_5110) logerror("bit read: %d\n", val&1);
+ }
+ if (DEBUG_5110) logerror("returning: %02x\n", val);
+ return val;
+}
+
+
+void tms5110_device::perform_dummy_read()
+{
+ if (m_schedule_dummy_read)
+ {
+ int data = new_int_read();
+ if (DEBUG_5110) logerror("TMS5110 performing dummy read; value read = %1i\n", data & 1);
+ m_schedule_dummy_read = FALSE;
+ }
+}
+
+
+
+
+/**********************************************************************************************
+
+ tms5110_process -- fill the buffer with a specific number of samples
+
+***********************************************************************************************/
+
+void tms5110_device::process(INT16 *buffer, unsigned int size)
+{
+ int buf_count=0;
+ int i, bitout;
+ INT32 this_sample;
+
+ /* loop until the buffer is full or we've stopped speaking */
+ while (size > 0)
+ {
+ if(m_TALKD) // speaking
+ {
+ /* if we're ready for a new frame to be applied, i.e. when IP=0, PC=12, Sub=1
+ * (In reality, the frame was really loaded incrementally during the entire IP=0
+ * PC=x time period, but it doesn't affect anything until IP=0 PC=12 happens)
+ */
+ if ((m_IP == 0) && (m_PC == 12) && (m_subcycle == 1))
+ {
+ // HACK for regression testing, be sure to comment out before release!
+ //m_RNG = 0x1234;
+ // end HACK
+
+#ifdef PERFECT_INTERPOLATION_HACK
+ /* remember previous frame energy, pitch, and coefficients */
+ m_old_frame_energy_idx = m_new_frame_energy_idx;
+ m_old_frame_pitch_idx = m_new_frame_pitch_idx;
+ for (i = 0; i < m_coeff->num_k; i++)
+ m_old_frame_k_idx[i] = m_new_frame_k_idx[i];
+#endif
+
+ /* Parse a new frame into the new_target_energy, new_target_pitch and new_target_k[] */
+ parse_frame();
+
+ // if the new frame is unvoiced (or silenced via ZPAR), be sure to zero out the k5-k10 parameters
+ // NOTE: this is probably the bug the tms5100/tmc0280 has, pre-rev D, I think.
+ // GUESS: Pre-rev D versions start zeroing k5-k10 immediately upon new frame load regardless of interpolation inhibit
+ // I.e. ZPAR = /TALKD || (PC>5&&P=0)
+ // GUESS: D and later versions only start or stop zeroing k5-k10 at the IP7->IP0 transition AFTER the frame
+ // I.e. ZPAR = /TALKD || (PC>5&&OLDP)
+#ifdef PERFECT_INTERPOLATION_HACK
+ m_old_uv_zpar = m_uv_zpar;
+ m_old_zpar = m_zpar; // unset old zpar on new frame
+#endif
+ m_zpar = 0;
+ //m_uv_zpar = (OLD_FRAME_UNVOICED_FLAG||m_zpar); // GUESS: fixed version in tmc0280d/tms5100a/cd280x/tms5110
+ m_uv_zpar = (NEW_FRAME_UNVOICED_FLAG||m_zpar); // GUESS: buggy version in tmc0280/tms5100
+
+ /* if the new frame is a stop frame, unset both TALK and SPEN (via TCON). TALKD remains active while the energy is ramping to 0. */
+ if (NEW_FRAME_STOP_FLAG == 1)
+ {
+ m_TALK = m_SPEN = 0;
+ }
+
+ /* in all cases where interpolation would be inhibited, set the inhibit flag; otherwise clear it.
+ Interpolation inhibit cases:
+ * Old frame was voiced, new is unvoiced
+ * Old frame was silence/zero energy, new has nonzero energy
+ * Old frame was unvoiced, new is voiced (note this is the case on the patent but may not be correct on the real final chip)
+ */
+ if ( ((OLD_FRAME_UNVOICED_FLAG == 0) && (NEW_FRAME_UNVOICED_FLAG == 1))
+ || ((OLD_FRAME_UNVOICED_FLAG == 1) && (NEW_FRAME_UNVOICED_FLAG == 0))
+ || ((OLD_FRAME_SILENCE_FLAG == 1) && (NEW_FRAME_SILENCE_FLAG == 0)) )
+ m_inhibit = 1;
+ else // normal frame, normal interpolation
+ m_inhibit = 0;
+
+#ifdef DEBUG_GENERATION
+ /* Debug info for current parsed frame */
+ fprintf(stderr, "OLDE=0: %d; OLDP=0: %d; E=0: %d; P=0: %d; ", m_OLDE, m_OLDP, (m_new_frame_energy_idx==0), (m_new_frame_pitch_idx==0));
+ fprintf(stderr,"Processing new frame: ");
+ if (m_inhibit == 0)
+ fprintf(stderr, "Normal Frame\n");
+ else
+ fprintf(stderr,"Interpolation Inhibited\n");
+ fprintf(stderr,"*** current Energy, Pitch and Ks = %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",m_current_energy, m_current_pitch, m_current_k[0], m_current_k[1], m_current_k[2], m_current_k[3], m_current_k[4], m_current_k[5], m_current_k[6], m_current_k[7], m_current_k[8], m_current_k[9]);
+ fprintf(stderr,"*** target Energy(idx), Pitch, and Ks = %04d(%x),%04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",
+ (m_coeff->energytable[m_new_frame_energy_idx] * (1-m_zpar)),
+ m_new_frame_energy_idx,
+ (m_coeff->pitchtable[m_new_frame_pitch_idx] * (1-m_zpar)),
+ (m_coeff->ktable[0][m_new_frame_k_idx[0]] * (1-m_zpar)),
+ (m_coeff->ktable[1][m_new_frame_k_idx[1]] * (1-m_zpar)),
+ (m_coeff->ktable[2][m_new_frame_k_idx[2]] * (1-m_zpar)),
+ (m_coeff->ktable[3][m_new_frame_k_idx[3]] * (1-m_zpar)),
+ (m_coeff->ktable[4][m_new_frame_k_idx[4]] * (1-m_uv_zpar)),
+ (m_coeff->ktable[5][m_new_frame_k_idx[5]] * (1-m_uv_zpar)),
+ (m_coeff->ktable[6][m_new_frame_k_idx[6]] * (1-m_uv_zpar)),
+ (m_coeff->ktable[7][m_new_frame_k_idx[7]] * (1-m_uv_zpar)),
+ (m_coeff->ktable[8][m_new_frame_k_idx[8]] * (1-m_uv_zpar)),
+ (m_coeff->ktable[9][m_new_frame_k_idx[9]] * (1-m_uv_zpar)) );
+#endif
+
+ }
+ else // Not a new frame, just interpolate the existing frame.
+ {
+ int inhibit_state = ((m_inhibit==1)&&(m_IP != 0)); // disable inhibit when reaching the last interp period, but don't overwrite the m_inhibit value
+#ifdef PERFECT_INTERPOLATION_HACK
+ int samples_per_frame = m_subc_reload?175:266; // either (13 A cycles + 12 B cycles) * 7 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 7 interps for SPKSLOW
+ //int samples_per_frame = m_subc_reload?200:304; // either (13 A cycles + 12 B cycles) * 8 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 8 interps for SPKSLOW
+ int current_sample = (m_subcycle - m_subc_reload)+(m_PC*(3-m_subc_reload))+((m_subc_reload?25:38)*((m_IP-1)&7));
+ //fprintf(stderr, "CS: %03d", current_sample);
+ // reset the current energy, pitch, etc to what it was at frame start
+ m_current_energy = (m_coeff->energytable[m_old_frame_energy_idx] * (1-m_old_zpar));
+ m_current_pitch = (m_coeff->pitchtable[m_old_frame_pitch_idx] * (1-m_old_zpar));
+ for (i = 0; i < m_coeff->num_k; i++)
+ m_current_k[i] = (m_coeff->ktable[i][m_old_frame_k_idx[i]] * (1-((i<4)?m_old_zpar:m_old_uv_zpar)));
+ // now adjust each value to be exactly correct for each of the samples per frame
+ if (m_IP != 0) // if we're still interpolating...
+ {
+ m_current_energy = (m_current_energy + (((m_coeff->energytable[m_new_frame_energy_idx] - m_current_energy)*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-m_zpar);
+ m_current_pitch = (m_current_pitch + (((m_coeff->pitchtable[m_new_frame_pitch_idx] - m_current_pitch)*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-m_zpar);
+ for (i = 0; i < m_coeff->num_k; i++)
+ m_current_k[i] = (m_current_k[i] + (((m_coeff->ktable[i][m_new_frame_k_idx[i]] - m_current_k[i])*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-((i<4)?m_zpar:m_uv_zpar));
+ }
+ else // we're done, play this frame for 1/8 frame.
+ {
+ m_current_energy = (m_coeff->energytable[m_new_frame_energy_idx] * (1-m_zpar));
+ m_current_pitch = (m_coeff->pitchtable[m_new_frame_pitch_idx] * (1-m_zpar));
+ for (i = 0; i < m_coeff->num_k; i++)
+ m_current_k[i] = (m_coeff->ktable[i][m_new_frame_k_idx[i]] * (1-((i<4)?m_zpar:m_uv_zpar)));
+ }
+#else
+ //Updates to parameters only happen on subcycle '2' (B cycle) of PCs.
+ if (m_subcycle == 2)
+ {
+ switch(m_PC)
+ {
+ case 0: /* PC = 0, B cycle, write updated energy */
+ m_current_energy = (m_current_energy + (((m_coeff->energytable[m_new_frame_energy_idx] - m_current_energy)*(1-inhibit_state)) INTERP_SHIFT))*(1-m_zpar);
+ break;
+ case 1: /* PC = 1, B cycle, write updated pitch */
+ m_current_pitch = (m_current_pitch + (((m_coeff->pitchtable[m_new_frame_pitch_idx] - m_current_pitch)*(1-inhibit_state)) INTERP_SHIFT))*(1-m_zpar);
+ break;
+ case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11:
+ /* PC = 2 through 11, B cycle, write updated K1 through K10 */
+ m_current_k[m_PC-2] = (m_current_k[m_PC-2] + (((m_coeff->ktable[m_PC-2][m_new_frame_k_idx[m_PC-2]] - m_current_k[m_PC-2])*(1-inhibit_state)) INTERP_SHIFT))*(1-(((m_PC-2)<4)?m_zpar:m_uv_zpar));
+ break;
+ case 12: /* PC = 12 */
+ /* we should NEVER reach this point, PC=12 doesn't have a subcycle 2 */
+ break;
+ }
+ }
+#endif
+ }
+
+ // calculate the output
+ if (OLD_FRAME_UNVOICED_FLAG == 1)
+ {
+ // generate unvoiced samples here
+ if (m_RNG & 1)
+ m_excitation_data = ~0x3F; /* according to the patent it is (either + or -) half of the maximum value in the chirp table, so either 01000000(0x40) or 11000000(0xC0)*/
+ else
+ m_excitation_data = 0x40;
+ }
+ else /* (OLD_FRAME_UNVOICED_FLAG == 0) */
+ {
+ // generate voiced samples here
+ /* US patent 4331836 Figure 14B shows, and logic would hold, that a pitch based chirp
+ * function has a chirp/peak and then a long chain of zeroes.
+ * The last entry of the chirp rom is at address 0b110011 (51d), the 52nd sample,
+ * and if the address reaches that point the ADDRESS incrementer is
+ * disabled, forcing all samples beyond 51d to be == 51d
+ */
+ if (m_pitch_count >= 51)
+ m_excitation_data = (INT8)m_coeff->chirptable[51];
+ else /*m_pitch_count < 51*/
+ m_excitation_data = (INT8)m_coeff->chirptable[m_pitch_count];
+ }
+
+ // Update LFSR *20* times every sample (once per T cycle), like patent shows
+ for (i=0; i<20; i++)
+ {
+ bitout = ((m_RNG >> 12) & 1) ^
+ ((m_RNG >> 3) & 1) ^
+ ((m_RNG >> 2) & 1) ^
+ ((m_RNG >> 0) & 1);
+ m_RNG <<= 1;
+ m_RNG |= bitout;
+ }
+ this_sample = lattice_filter(); /* execute lattice filter */
+#ifdef DEBUG_GENERATION_VERBOSE
+ //fprintf(stderr,"C:%01d; ",m_subcycle);
+ fprintf(stderr,"IP:%01d PC:%02d X:%04d E:%03d P:%03d Pc:%03d ",m_IP, m_PC, m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count);
+ //fprintf(stderr,"X:%04d E:%03d P:%03d Pc:%03d ", m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count);
+ for (i=0; i<10; i++)
+ fprintf(stderr,"K%d:%04d ", i+1, m_current_k[i]);
+ fprintf(stderr,"Out:%06d ", this_sample);
+//#ifdef PERFECT_INTERPOLATION_HACK
+// fprintf(stderr,"%d%d%d%d",m_old_zpar,m_zpar,m_old_uv_zpar,m_uv_zpar);
+//#else
+// fprintf(stderr,"x%dx%d",m_zpar,m_uv_zpar);
+//#endif
+ fprintf(stderr,"\n");
+#endif
+ /* next, force result to 14 bits (since its possible that the addition at the final (k1) stage of the lattice overflowed) */
+ while (this_sample > 16383) this_sample -= 32768;
+ while (this_sample < -16384) this_sample += 32768;
+ if (m_digital_select == 0) // analog SPK pin output is only 8 bits, with clipping
+ buffer[buf_count] = clip_analog(this_sample);
+ else // digital I/O pin output is 12 bits
+ {
+#ifdef ALLOW_4_LSB
+ // input: ssss ssss ssss ssss ssnn nnnn nnnn nnnn
+ // N taps: ^ = 0x2000;
+ // output: ssss ssss ssss ssss snnn nnnn nnnn nnnN
+ buffer[buf_count] = (this_sample<<1)|((this_sample&0x2000)>>13);
+#else
+ this_sample &= ~0xF;
+ // input: ssss ssss ssss ssss ssnn nnnn nnnn 0000
+ // N taps: ^^ ^^^ = 0x3E00;
+ // output: ssss ssss ssss ssss snnn nnnn nnnN NNNN
+ buffer[buf_count] = (this_sample<<1)|((this_sample&0x3E00)>>9);
+#endif
+ }
+ // Update all counts
+
+ m_subcycle++;
+ if ((m_subcycle == 2) && (m_PC == 12)) // RESETF3
+ {
+ /* Circuit 412 in the patent acts a reset, resetting the pitch counter to 0
+ * if INHIBIT was true during the most recent frame transition.
+ * The exact time this occurs is betwen IP=7, PC=12 sub=0, T=t12
+ * and m_IP = 0, PC=0 sub=0, T=t12, a period of exactly 20 cycles,
+ * which overlaps the time OLDE and OLDP are updated at IP=7 PC=12 T17
+ * (and hence INHIBIT itself 2 t-cycles later). We do it here because it is
+ * convenient and should make no difference in output.
+ */
+ if ((m_IP == 7)&&(m_inhibit==1)) m_pitch_zero = 1;
+ if ((m_IP == 0)&&(m_pitch_zero==1)) m_pitch_zero = 0;
+ if (m_IP == 7) // RESETL4
+ {
+ // Latch OLDE and OLDP
+ OLD_FRAME_SILENCE_FLAG = NEW_FRAME_SILENCE_FLAG; // m_OLDE
+ OLD_FRAME_UNVOICED_FLAG = NEW_FRAME_UNVOICED_FLAG; // m_OLDP
+ /* if TALK was clear last frame, halt speech now, since TALKD (latched from TALK on new frame) just went inactive. */
+#ifdef DEBUG_GENERATION
+ if (m_TALK == 0)
+ fprintf(stderr,"tms5110_process: processing frame: TALKD = 0 caused by stop frame or buffer empty, halting speech.\n");
+#endif
+ m_TALKD = m_TALK; // TALKD is latched from TALK
+ m_TALK = m_SPEN; // TALK is latched from SPEN
+ }
+ m_subcycle = m_subc_reload;
+ m_PC = 0;
+ m_IP++;
+ m_IP&=0x7;
+ }
+ else if (m_subcycle == 3)
+ {
+ m_subcycle = m_subc_reload;
+ m_PC++;
+ }
+ m_pitch_count++;
+ if ((m_pitch_count >= m_current_pitch)||(m_pitch_zero == 1)) m_pitch_count = 0;
+ m_pitch_count &= 0x1FF;
+ }
+ else // m_TALKD == 0
+ {
+ m_subcycle++;
+ if ((m_subcycle == 2) && (m_PC == 12)) // RESETF3
+ {
+ if (m_IP == 7) // RESETL4
+ {
+ m_TALKD = m_TALK; // TALKD is latched from TALK
+ m_TALK = m_SPEN; // TALK is latched from SPEN
+ }
+ m_subcycle = m_subc_reload;
+ m_PC = 0;
+ m_IP++;
+ m_IP&=0x7;
+ }
+ else if (m_subcycle == 3)
+ {
+ m_subcycle = m_subc_reload;
+ m_PC++;
+ }
+ buffer[buf_count] = -1; /* should be just -1; actual chip outputs -1 every idle sample; (cf note in data sheet, p 10, table 4) */
+ }
+ buf_count++;
+ size--;
+ }
+}
+
+/**********************************************************************************************
+
+ clip_analog -- clips the 14 bit return value from the lattice filter to its final 10 bit value (-512 to 511), and upshifts/range extends this to 16 bits
+
+***********************************************************************************************/
+
+static INT16 clip_analog(INT16 cliptemp)
+{
+ /* clipping, just like the patent shows:
+ * the top 10 bits of this result are visible on the digital output IO pin.
+ * next, if the top 3 bits of the 14 bit result are all the same, the lowest of those 3 bits plus the next 7 bits are the signed analog output, otherwise the low bits are all forced to match the inverse of the topmost bit, i.e.:
+ * 1x xxxx xxxx xxxx -> 0b10000000
+ * 11 1bcd efgh xxxx -> 0b1bcdefgh
+ * 00 0bcd efgh xxxx -> 0b0bcdefgh
+ * 0x xxxx xxxx xxxx -> 0b01111111
+ */
+#ifdef DEBUG_CLIP
+ if ((cliptemp > 2047) || (cliptemp < -2048)) fprintf(stderr,"clipping cliptemp to range; was %d\n", cliptemp);
+#endif
+ if (cliptemp > 2047) cliptemp = 2047;
+ else if (cliptemp < -2048) cliptemp = -2048;
+ /* at this point the analog output is tapped */
+#ifdef ALLOW_4_LSB
+ // input: ssss snnn nnnn nnnn
+ // N taps: ^^^ ^ = 0x0780
+ // output: snnn nnnn nnnn NNNN
+ return (cliptemp << 4)|((cliptemp&0x780)>>7); // upshift and range adjust
+#else
+ cliptemp &= ~0xF;
+ // input: ssss snnn nnnn 0000
+ // N taps: ^^^ ^^^^ = 0x07F0
+ // P taps: ^ = 0x0400
+ // output: snnn nnnn NNNN NNNP
+ return (cliptemp << 4)|((cliptemp&0x7F0)>>3)|((cliptemp&0x400)>>10); // upshift and range adjust
+#endif
+}
+
+
+/**********************************************************************************************
+
+ matrix_multiply -- does the proper multiply and shift
+ a is the k coefficient and is clamped to 10 bits (9 bits plus a sign)
+ b is the running result and is clamped to 14 bits.
+ output is 14 bits, but note the result LSB bit is always 1.
+ Because the low 4 bits of the result are trimmed off before
+ output, this makes almost no difference in the computation.
+
+**********************************************************************************************/
+static INT32 matrix_multiply(INT32 a, INT32 b)
+{
+ INT32 result;
+ while (a>511) { a-=1024; }
+ while (a<-512) { a+=1024; }
+ while (b>16383) { b-=32768; }
+ while (b<-16384) { b+=32768; }
+ result = ((a*b)>>9)|1;//&(~1);
+#ifdef VERBOSE
+ if (result>16383) fprintf(stderr,"matrix multiplier overflowed! a: %x, b: %x, result: %x", a, b, result);
+ if (result<-16384) fprintf(stderr,"matrix multiplier underflowed! a: %x, b: %x, result: %x", a, b, result);
+#endif
+ return result;
+}
+
+/**********************************************************************************************
+
+ lattice_filter -- executes one 'full run' of the lattice filter on a specific byte of
+ excitation data, and specific values of all the current k constants, and returns the
+ resulting sample.
+
+***********************************************************************************************/
+
+INT32 tms5110_device::lattice_filter()
+{
+ // Lattice filter here
+ // Aug/05/07: redone as unrolled loop, for clarity - LN
+ /* Originally Copied verbatim from table I in US patent 4,209,804, now updated to be in same order as the actual chip does it, not that it matters.
+ notation equivalencies from table:
+ Yn(i) == m_u[n-1]
+ Kn = m_current_k[n-1]
+ bn = m_x[n-1]
+ */
+ /*
+ int ep = matrix_multiply(m_previous_energy, (m_excitation_data<<6)); //Y(11)
+ m_u[10] = ep;
+ for (int i = 0; i < 10; i++)
+ {
+ int ii = 10-i; // for m = 10, this would be 11 - i, and since i is from 1 to 10, then ii ranges from 10 to 1
+ //int jj = ii+1; // this variable, even on the fortran version, is never used. it probably was intended to be used on the two lines below the next one to save some redundant additions on each.
+ ep = ep - (((m_current_k[ii-1] * m_x[ii-1])>>9)|1); // subtract reflection from lower stage 'top of lattice'
+ m_u[ii-1] = ep;
+ m_x[ii] = m_x[ii-1] + (((m_current_k[ii-1] * ep)>>9)|1); // add reflection from upper stage 'bottom of lattice'
+ }
+ m_x[0] = ep; // feed the last section of the top of the lattice directly to the bottom of the lattice
+ */
+ m_u[10] = matrix_multiply(m_previous_energy, (m_excitation_data<<6)); //Y(11)
+ m_u[9] = m_u[10] - matrix_multiply(m_current_k[9], m_x[9]);
+ m_u[8] = m_u[9] - matrix_multiply(m_current_k[8], m_x[8]);
+ m_u[7] = m_u[8] - matrix_multiply(m_current_k[7], m_x[7]);
+ m_u[6] = m_u[7] - matrix_multiply(m_current_k[6], m_x[6]);
+ m_u[5] = m_u[6] - matrix_multiply(m_current_k[5], m_x[5]);
+ m_u[4] = m_u[5] - matrix_multiply(m_current_k[4], m_x[4]);
+ m_u[3] = m_u[4] - matrix_multiply(m_current_k[3], m_x[3]);
+ m_u[2] = m_u[3] - matrix_multiply(m_current_k[2], m_x[2]);
+ m_u[1] = m_u[2] - matrix_multiply(m_current_k[1], m_x[1]);
+ m_u[0] = m_u[1] - matrix_multiply(m_current_k[0], m_x[0]);
+ m_x[9] = m_x[8] + matrix_multiply(m_current_k[8], m_u[8]);
+ m_x[8] = m_x[7] + matrix_multiply(m_current_k[7], m_u[7]);
+ m_x[7] = m_x[6] + matrix_multiply(m_current_k[6], m_u[6]);
+ m_x[6] = m_x[5] + matrix_multiply(m_current_k[5], m_u[5]);
+ m_x[5] = m_x[4] + matrix_multiply(m_current_k[4], m_u[4]);
+ m_x[4] = m_x[3] + matrix_multiply(m_current_k[3], m_u[3]);
+ m_x[3] = m_x[2] + matrix_multiply(m_current_k[2], m_u[2]);
+ m_x[2] = m_x[1] + matrix_multiply(m_current_k[1], m_u[1]);
+ m_x[1] = m_x[0] + matrix_multiply(m_current_k[0], m_u[0]);
+ m_x[0] = m_u[0];
+ m_previous_energy = m_current_energy;
+#ifdef DEBUG_LATTICE
+ int i;
+ fprintf(stderr,"V:%04d ", m_u[10]);
+ for (i = 9; i >= 0; i--)
+ {
+ fprintf(stderr,"Y%d:%04d ", i+1, m_u[i]);
+ fprintf(stderr,"b%d:%04d ", i+1, m_x[i]);
+ if ((i % 5) == 0) fprintf(stderr,"\n");
+ }
+#endif
+ return m_u[0];
+}
+
+
+
+
+/******************************************************************************************
+
+ PDC_set -- set Processor Data Clock. Execute CTL_pins command on hi-lo transition.
+
+******************************************************************************************/
+
+void tms5110_device::PDC_set(int data)
+{
+ if (m_PDC != (data & 0x1) )
+ {
+ m_PDC = data & 0x1;
+ if (m_PDC == 0) /* toggling 1->0 processes command on CTL_pins */
+ {
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"PDC falling edge(%02X): ",m_state);
+#endif
+ /* first pdc toggles output, next toggles input */
+ switch (m_state)
+ {
+ case CTL_STATE_INPUT:
+ /* continue */
+ break;
+ case CTL_STATE_NEXT_TTALK_OUTPUT:
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"Switching CTL bus direction to output for Test Talk\n");
+#endif
+ m_state = CTL_STATE_TTALK_OUTPUT;
+ return;
+ case CTL_STATE_TTALK_OUTPUT:
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"Switching CTL bus direction back to input from Test Talk\n");
+#endif
+ m_state = CTL_STATE_INPUT;
+ return;
+ case CTL_STATE_NEXT_OUTPUT:
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"Switching CTL bus direction for Read Bit Buffer Output\n");
+#endif
+ m_state = CTL_STATE_OUTPUT;
+ return;
+ case CTL_STATE_OUTPUT:
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"Switching CTL bus direction back to input from Read Bit Buffer Output\n");
+#endif
+ m_state = CTL_STATE_INPUT;
+ return;
+ }
+ /* the only real commands we handle now are SPEAK and RESET */
+ if (m_next_is_address)
+ {
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"Loading address nybble %02x to VSMs\n", m_CTL_pins);
+#endif
+ m_next_is_address = FALSE;
+ m_address = m_address | ((m_CTL_pins & 0x0F)<<m_addr_bit);
+ m_addr_bit = (m_addr_bit + 4) % 12;
+ m_schedule_dummy_read = TRUE;
+ new_int_write_addr(m_CTL_pins & 0x0F);
+ }
+ else
+ {
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"Got command nybble %02x: ", m_CTL_pins);
+#endif
+ switch (m_CTL_pins & 0xe) /*CTL1 - don't care*/
+ {
+ case TMS5110_CMD_RESET:
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"RESET\n");
+#endif
+ perform_dummy_read();
+ reset();
+ break;
+
+ case TMS5110_CMD_LOAD_ADDRESS:
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"LOAD ADDRESS\n");
+#endif
+ m_next_is_address = TRUE;
+ break;
+
+ case TMS5110_CMD_OUTPUT:
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"OUTPUT (from read-bit buffer)\n");
+#endif
+ m_state = CTL_STATE_NEXT_OUTPUT;
+ break;
+
+ case TMS5110_CMD_SPKSLOW:
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"SPKSLOW\n");
+#endif
+ perform_dummy_read();
+ m_SPEN = 1; /* start immediately */
+#ifdef FAST_START_HACK
+ m_TALK = 1;
+#endif
+ /* clear out variables before speaking */
+ m_zpar = 1; // zero all the parameters
+ m_uv_zpar = 1; // zero k4-k10 as well
+ m_OLDE = 1; // 'silence/zpar' frames are zero energy
+ m_OLDP = 1; // 'silence/zpar' frames are zero pitch
+#ifdef PERFECT_INTERPOLATION_HACK
+ m_old_zpar = 1; // zero all the old parameters
+ m_old_uv_zpar = 1; // zero old k4-k10 as well
+#endif
+ m_subc_reload = 0; // SPKSLOW means this is 0
+ break;
+
+ case TMS5110_CMD_READ_BIT:
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"READ BIT\n");
+#endif
+ if (m_schedule_dummy_read)
+ perform_dummy_read();
+ else
+ {
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"actually reading a bit now\n");
+#endif
+ m_CTL_buffer >>= 1;
+ m_CTL_buffer |= (extract_bits(1)<<3);
+ m_CTL_buffer &= 0xF;
+ }
+ break;
+
+ case TMS5110_CMD_SPEAK:
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"SPEAK\n");
+#endif
+ perform_dummy_read();
+ m_SPEN = 1; /* start immediately */
+#ifdef FAST_START_HACK
+ m_TALK = 1;
+#endif
+ /* clear out variables before speaking */
+ m_zpar = 1; // zero all the parameters
+ m_uv_zpar = 1; // zero k4-k10 as well
+ m_OLDE = 1; // 'silence/zpar' frames are zero energy
+ m_OLDP = 1; // 'silence/zpar' frames are zero pitch
+#ifdef PERFECT_INTERPOLATION_HACK
+ m_old_zpar = 1; // zero all the old parameters
+ m_old_uv_zpar = 1; // zero old k4-k10 as well
+#endif
+ m_subc_reload = 1; // SPEAK means this is 1
+ break;
+
+ case TMS5110_CMD_READ_BRANCH:
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"READ AND BRANCH\n");
+#endif
+ new_int_write(0,1,1,0);
+ new_int_write(1,1,1,0);
+ new_int_write(0,1,1,0);
+ new_int_write(0,0,0,0);
+ new_int_write(1,0,0,0);
+ new_int_write(0,0,0,0);
+ m_schedule_dummy_read = FALSE;
+ break;
+
+ case TMS5110_CMD_TEST_TALK:
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"TEST TALK\n");
+#endif
+ m_state = CTL_STATE_NEXT_TTALK_OUTPUT;
+ break;
+
+ default:
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"tms5110.c: unknown command: 0x%02x\n", m_CTL_pins);
+#endif
+ break;
+ }
+
+ }
+ }
+ }
+}
+
+
+
+/******************************************************************************************
+
+ parse_frame -- parse a new frame's worth of data; returns 0 if not enough bits in buffer
+
+******************************************************************************************/
+
+void tms5110_device::parse_frame()
+{
+ int i, rep_flag;
+
+ // attempt to extract the energy index
+ m_new_frame_energy_idx = extract_bits(m_coeff->energy_bits);
+#ifdef DEBUG_PARSE_FRAME_DUMP
+ printbits(m_new_frame_energy_idx,m_coeff->energy_bits);
+ fprintf(stderr," ");
+#endif
+
+ // if the energy index is 0 or 15, we're done
+ if ((m_new_frame_energy_idx == 0) || (m_new_frame_energy_idx == 15))
+ return;
+
+ rep_flag = extract_bits(1);
+#ifdef DEBUG_PARSE_FRAME_DUMP
+ printbits(rep_flag, 1);
+ fprintf(stderr," ");
+#endif
+
+ m_new_frame_pitch_idx = extract_bits(m_coeff->pitch_bits);
+#ifdef DEBUG_PARSE_FRAME_DUMP
+ printbits(m_new_frame_pitch_idx,m_coeff->pitch_bits);
+ fprintf(stderr," ");
+#endif
+ // if this is a repeat frame, just do nothing, it will reuse the old coefficients
+ if (rep_flag)
+ return;
+
+ // extract first 4 K coefficients
+ for (i = 0; i < 4; i++)
+ {
+ m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]);
+#ifdef DEBUG_PARSE_FRAME_DUMP
+ printbits(m_new_frame_k_idx[i],m_coeff->kbits[i]);
+ fprintf(stderr," ");
+#endif
+ }
+
+ // if the pitch index was zero, we only need 4 K's...
+ if (m_new_frame_pitch_idx == 0)
+ {
+ /* and the rest of the coefficients are zeroed, but that's done in the generator code */
+ return;
+ }
+
+ // If we got here, we need the remaining 6 K's
+ for (i = 4; i < m_coeff->num_k; i++)
+ {
+ m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]);
+#ifdef DEBUG_PARSE_FRAME_DUMP
+ printbits(m_new_frame_k_idx[i],m_coeff->kbits[i]);
+ fprintf(stderr," ");
+#endif
+ }
+#ifdef VERBOSE
+ logerror("Parsed a frame successfully in ROM\n");
+#endif
+ return;
+}
+
+
+#if 0
+/*This is an example word TEN taken from the TMS5110A datasheet*/
+static const unsigned int example_word_TEN[619]={
+/* 1*/1,0,0,0, 0, 0,0,0,0,0, 1,1,0,0,0, 0,0,0,1,0, 0,1,1,1, 0,1,0,1,
+/* 2*/1,0,0,0, 0, 0,0,0,0,0, 1,0,0,1,0, 0,0,1,1,0, 0,0,1,1, 0,1,0,1,
+/* 3*/1,1,0,0, 0, 1,0,0,0,0, 1,0,1,0,0, 0,1,0,1,0, 0,1,0,0, 1,0,1,0, 1,0,0,0, 1,0,0,1, 0,1,0,1, 0,0,1, 0,1,0, 0,1,1,
+/* 4*/1,1,1,0, 0, 0,1,1,1,1, 1,0,1,0,1, 0,1,1,1,0, 0,1,0,1, 0,1,1,1, 0,1,1,1, 1,0,1,1, 1,0,1,0, 0,1,1, 0,1,0, 0,1,1,
+/* 5*/1,1,1,0, 0, 1,0,0,0,0, 1,0,1,0,0, 0,1,1,1,0, 0,1,0,1, 1,0,1,0, 1,0,0,0, 1,1,0,0, 1,0,1,1, 1,0,0, 0,1,0, 0,1,1,
+/* 6*/1,1,1,0, 0, 1,0,0,0,1, 1,0,1,0,1, 0,1,1,0,1, 0,1,1,0, 0,1,1,1, 0,1,1,1, 1,0,1,0, 1,0,1,0, 1,1,0, 0,0,1, 1,0,0,
+/* 7*/1,1,1,0, 0, 1,0,0,1,0, 1,0,1,1,1, 0,1,1,1,0, 0,1,1,1, 0,1,1,1, 0,1,0,1, 0,1,1,0, 1,0,0,1, 1,1,0, 0,1,0, 0,1,1,
+/* 8*/1,1,1,0, 1, 1,0,1,0,1,
+/* 9*/1,1,1,0, 0, 1,1,0,0,1, 1,0,1,1,1, 0,1,0,1,1, 1,0,1,1, 0,1,1,1, 0,1,0,0, 1,0,0,0, 1,0,0,0, 1,1,0, 0,1,1, 0,1,1,
+/*10*/1,1,0,1, 0, 1,1,0,1,0, 1,0,1,0,1, 0,1,1,0,1, 1,0,1,1, 0,1,0,1, 0,1,0,0, 1,0,0,0, 1,0,1,0, 1,1,0, 0,1,0, 1,0,0,
+/*11*/1,0,1,1, 0, 1,1,0,1,1, 1,0,0,1,1, 1,0,0,1,0, 0,1,1,0, 0,0,1,1, 0,1,0,1, 1,0,0,1, 1,0,1,0, 1,0,0, 0,1,1, 0,1,1,
+/*12*/1,0,0,0, 0, 1,1,1,0,0, 1,0,0,1,1, 0,0,1,1,0, 0,1,0,0, 0,1,1,0, 1,1,0,0, 0,1,0,1, 1,0,0,0, 1,0,0, 0,1,0, 1,0,1,
+/*13*/0,1,1,1, 1, 1,1,1,0,1,
+/*14*/0,1,1,1, 0, 1,1,1,1,0, 1,0,0,1,1, 0,0,1,1,1, 0,1,0,1, 0,1,0,1, 1,1,0,0, 0,1,1,1, 1,0,0,0, 1,0,0, 0,1,0, 1,0,1,
+/*15*/0,1,1,0, 0, 1,1,1,1,0, 1,0,1,0,1, 0,0,1,1,0, 0,1,0,0, 0,0,1,1, 1,1,0,0, 1,0,0,1, 0,1,1,1, 1,0,1, 0,1,0, 1,0,1,
+/*16*/1,1,1,1
+};
+#endif
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tms5110_device::device_start()
+{
+ m_table = region()->base();
+
+ set_variant(TMS5110_IS_TMS5110A);
+
+ /* resolve lines */
+ m_m0_cb.resolve();
+ m_m1_cb.resolve();
+ m_romclk_cb.resolve();
+ m_addr_cb.resolve();
+ m_data_cb.resolve();
+
+ /* initialize a stream */
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / 80);
+
+ m_state = CTL_STATE_INPUT; /* most probably not defined */
+ m_romclk_hack_timer = timer_alloc(0);
+
+ register_for_save_states();
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tms5100_device::device_start()
+{
+ tms5110_device::device_start();
+ set_variant(TMS5110_IS_TMC0281);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tmc0281_device::device_start()
+{
+ tms5110_device::device_start();
+ set_variant(TMS5110_IS_TMC0281);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tms5100a_device::device_start()
+{
+ tms5110_device::device_start();
+ set_variant(TMS5110_IS_TMC0281D);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tmc0281d_device::device_start()
+{
+ tms5110_device::device_start();
+ set_variant(TMS5110_IS_TMC0281D);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void cd2801_device::device_start()
+{
+ tms5110_device::device_start();
+ set_variant(TMS5110_IS_CD2801);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void cd2802_device::device_start()
+{
+ tms5110_device::device_start();
+ set_variant(TMS5110_IS_CD2802);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tms5110a_device::device_start()
+{
+ tms5110_device::device_start();
+ set_variant(TMS5110_IS_TMS5110A);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void m58817_device::device_start()
+{
+ tms5110_device::device_start();
+ set_variant(TMS5110_IS_M58817);
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void tms5110_device::device_reset()
+{
+ m_digital_select = FORCE_DIGITAL; // assume analog output
+
+ /* initialize the chip state */
+ m_SPEN = m_TALK = m_TALKD = 0;
+ m_CTL_pins = 0;
+ m_RNG = 0x1fff;
+ m_CTL_buffer = 0;
+ m_PDC = 0;
+
+ /* initialize the energy/pitch/k states */
+#ifdef PERFECT_INTERPOLATION_HACK
+ m_old_frame_energy_idx = m_old_frame_pitch_idx = 0;
+ memset(m_old_frame_k_idx, 0, sizeof(m_old_frame_k_idx));
+ m_old_zpar = m_old_uv_zpar = 0;
+#endif
+ m_new_frame_energy_idx = m_current_energy = m_previous_energy = 0;
+ m_new_frame_pitch_idx = m_current_pitch = 0;
+ m_zpar = m_uv_zpar = 0;
+ memset(m_new_frame_k_idx, 0, sizeof(m_new_frame_k_idx));
+ memset(m_current_k, 0, sizeof(m_current_k));
+
+ /* initialize the sample generators */
+ m_inhibit = 1;
+ m_subcycle = m_pitch_count = m_pitch_zero = m_PC = m_zpar = 0;
+ m_subc_reload = 1;
+ m_OLDE = m_OLDP = 1;
+ m_IP = 0;
+ m_RNG = 0x1FFF;
+ memset(m_u, 0, sizeof(m_u));
+ memset(m_x, 0, sizeof(m_x));
+ if (m_table != NULL)
+ {
+ /* legacy interface */
+ m_schedule_dummy_read = TRUE;
+ }
+ else
+ {
+ /* no dummy read! This makes bagman and ad2083 speech fail
+ * with the new cycle and transition exact interfaces
+ */
+ m_schedule_dummy_read = FALSE;
+ }
+ m_next_is_address = FALSE;
+ m_address = 0;
+ m_addr_bit = 0;
+}
+
+
+
+/******************************************************************************
+
+ tms5110_ctl_w -- write Control Command to the sound chip
+commands like Speech, Reset, etc., are loaded into the chip via the CTL pins
+
+******************************************************************************/
+
+WRITE8_MEMBER( tms5110_device::ctl_w )
+{
+ /* bring up to date first */
+ m_stream->update();
+ m_CTL_pins = data & 0xf;
+}
+
+
+/******************************************************************************
+
+ tms5110_pdc_w -- write to PDC pin on the sound chip
+
+******************************************************************************/
+
+WRITE_LINE_MEMBER( tms5110_device::pdc_w )
+{
+ /* bring up to date first */
+ m_stream->update();
+ PDC_set(state);
+}
+
+
+
+/******************************************************************************
+
+ tms5110_ctl_r -- read from the VSP (51xx) control bus
+ The CTL bus can be in three states:
+ 1. Test talk output:
+ bit 0 = TS - Talk Status is active (high) when the VSP is processing speech data.
+ Talk Status goes active at the initiation of a SPEAK command.
+ It goes inactive (low) when the stop code (Energy=1111) is processed, or
+ immediately(?????? not TMS5110) by a RESET command.
+ other bits may be open bus
+ 2. 'read bit' buffer contents output:
+ bits 0-3 = buffer contents
+ 3. Input 'open bus' state:
+ bits 0-3 = high-z
+
+******************************************************************************/
+
+READ8_MEMBER( tms5110_device::ctl_r )
+{
+ /* bring up to date first */
+ m_stream->update();
+ if (m_state == CTL_STATE_TTALK_OUTPUT)
+ {
+ if (DEBUG_5110) logerror("Status read while outputting Test Talk (status=%2d)\n", TALK_STATUS);
+ return (TALK_STATUS << 0); /*CTL1 = still talking ? */
+ }
+ else if (m_state == CTL_STATE_OUTPUT)
+ {
+ if (DEBUG_5110) logerror("Status read while outputting buffer (buffer=%2d)\n", m_CTL_buffer);
+ return (m_CTL_buffer);
+ }
+ else // we're reading with the bus in input mode! just return the last thing written to the bus
+ {
+ if (DEBUG_5110) logerror("Status read (not in output mode), returning %02x\n", m_CTL_pins);
+ return (m_CTL_pins);
+ }
+}
+
+READ8_MEMBER( m58817_device::status_r )
+{
+ /* bring up to date first */
+ m_stream->update();
+ return (TALK_STATUS << 0); /*CTL1 = still talking ? */
+}
+
+/******************************************************************************
+
+ tms5110_romclk_hack_r -- read status of romclk
+
+******************************************************************************/
+
+void tms5110_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ m_romclk_hack_state = !m_romclk_hack_state;
+}
+
+READ8_MEMBER( tms5110_device::romclk_hack_r )
+{
+ /* bring up to date first */
+ m_stream->update();
+
+ /* create and start timer if necessary */
+ if (!m_romclk_hack_timer_started)
+ {
+ m_romclk_hack_timer_started = TRUE;
+ m_romclk_hack_timer->adjust(attotime::from_hz(clock() / 40), 0, attotime::from_hz(clock() / 40));
+ }
+ return m_romclk_hack_state;
+}
+
+
+/******************************************************************************
+
+ tms5110_update -- update the sound chip so that it is in sync with CPU execution
+
+******************************************************************************/
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void tms5110_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ INT16 sample_data[MAX_SAMPLE_CHUNK];
+ stream_sample_t *buffer = outputs[0];
+
+ /* loop while we still have samples to generate */
+ while (samples)
+ {
+ int length = (samples > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : samples;
+ int index;
+
+ /* generate the samples and copy to the target buffer */
+ process(sample_data, length);
+ for (index = 0; index < length; index++)
+ *buffer++ = sample_data[index];
+
+ /* account for the samples */
+ samples -= length;
+ }
+}
+
+
+
+/******************************************************************************
+
+ tms5110_set_frequency -- adjusts the playback frequency
+ TODO: kill this function; we should be adjusting the tms51xx device clock itself,
+ not setting it here!
+
+******************************************************************************/
+
+void tms5110_device::set_frequency(int frequency)
+{
+ m_stream->set_sample_rate(frequency / 80);
+}
+
+
+
+/* from here on in this file is a VSM 'Emulator' circuit used by bagman and ad2083 */
+
+/*
+ *
+ * General Interface design (Bagman)
+ *
+ * +------------------------------------------------------------------------+
+ * | |
+ * +-------------+ | +-------------+ +-------------+ +-------------+ |
+ * | TMS5100 | | | Counters | | Rom(s) | | Decoder | |
+ * | ADD8 |<--+ | LS393s | | | | | |
+ * | | | | | | | Out |--+
+ * | M0 |---+ | Address |======>| Address | | IN1 |
+ * | | | | | | Data |======>| ... |
+ * M | | +---------->| Clk | | | | IN8 |
+ * A-->| CTL1 | | | | | | |
+ * P-->| CTL2 | +--->| Reset | | | | |
+ * P-->| CTL3 | | | | | | | A B C |
+ * E-->| CTL4 | | +-------------+ +-------------+ +-------------+
+ * D-->| PDC | | ^ ^ ^
+ * | | +-------------------------------------------------+ | | |
+ * | | | Bit Select
+ * | ROMCLK |---+ +-------------+ +-------------+ |
+ * | | | | Counter | | PROM | |
+ * +-------------+ | | LS393 | | D1 | M --+ Reset Bit
+ * | | Q0 |------>| A0 | A
+ * +---------->| Clk Q1 |------>| A1 | P ==> CTL1 ... CTL4
+ * | Q2 |------>| A2 | P --> PDC
+ * | Reset Q3 |------>| A3 | E --+ Stop Bit
+ * | | +-->| A4 D8 | D |
+ * +-------------+ | +-------------+ |
+ * | |
+ * | +---+ |
+ * | | |<-----------------+
+ * +---| & |
+ * | |<-------- Enable
+ * +---+
+ *
+ */
+
+/******************************************************************************
+
+ device_start( tmsprom ) -- allocate buffers initialize
+
+******************************************************************************/
+
+void tmsprom_device::register_for_save_states()
+{
+ save_item(NAME(m_address));
+ save_item(NAME(m_base_address));
+ save_item(NAME(m_bit));
+ save_item(NAME(m_enable));
+ save_item(NAME(m_prom_cnt));
+ save_item(NAME(m_m0));
+}
+
+void tmsprom_device::update_prom_cnt()
+{
+ UINT8 prev_val = m_prom[m_prom_cnt] | 0x0200;
+ if (m_enable && (prev_val & (1<<m_stop_bit)))
+ m_prom_cnt |= 0x10;
+ else
+ m_prom_cnt &= 0x0f;
+}
+
+void tmsprom_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ /* only 16 bytes needed ... The original dump is bad. This
+ * is what is needed to get speech to work. The prom data has
+ * been updated and marked as BAD_DUMP. The information below
+ * is given for reference once another dump should surface.
+ *
+ * static const int prom[16] = {0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00,
+ * 0x02, 0x00, 0x40, 0x00, 0x04, 0x06, 0x04, 0x84 };
+ */
+ UINT16 ctrl;
+
+ update_prom_cnt();
+ ctrl = (m_prom[m_prom_cnt] | 0x200);
+
+ //if (m_enable && m_prom_cnt < 0x10) printf("ctrl %04x, enable %d cnt %d\n", ctrl, m_enable, m_prom_cnt);
+ m_prom_cnt = ((m_prom_cnt + 1) & 0x0f) | (m_prom_cnt & 0x10);
+
+ if (ctrl & (1 << m_reset_bit))
+ m_address = 0;
+
+ m_ctl_cb((offs_t)0, BITSWAP8(ctrl,0,0,0,0,m_ctl8_bit,
+ m_ctl4_bit,m_ctl2_bit,m_ctl1_bit));
+
+ m_pdc_cb((ctrl >> m_pdc_bit) & 0x01);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tmsprom_device::device_start()
+{
+ /* resolve lines */
+ m_pdc_cb.resolve_safe();
+ m_ctl_cb.resolve_safe();
+
+ m_rom = region()->base();
+ assert_always(m_rom != NULL, "Error creating TMSPROM chip: No rom region found");
+ m_prom = owner()->memregion(m_prom_region)->base();
+ assert_always(m_prom != NULL, "Error creating TMSPROM chip: No prom region found");
+
+ m_romclk_timer = timer_alloc(0);
+ m_romclk_timer->adjust(attotime::zero, 0, attotime::from_hz(clock()));
+
+ m_bit = 0;
+ m_base_address = 0;
+ m_address = 0;
+ m_enable = 0;
+ m_m0 = 0;
+ m_prom_cnt = 0;
+
+ register_for_save_states();
+}
+
+WRITE_LINE_MEMBER( tmsprom_device::m0_w )
+{
+ /* falling edge counts */
+ if (m_m0 && !state)
+ {
+ m_address += 1;
+ m_address &= (m_rom_size-1);
+ }
+ m_m0 = state;
+}
+
+READ_LINE_MEMBER( tmsprom_device::data_r )
+{
+ return (m_rom[m_base_address + m_address] >> m_bit) & 0x01;
+}
+
+
+WRITE8_MEMBER( tmsprom_device::rom_csq_w )
+{
+ if (!data)
+ m_base_address = offset * m_rom_size;
+}
+
+WRITE8_MEMBER( tmsprom_device::bit_w )
+{
+ m_bit = data;
+}
+
+WRITE_LINE_MEMBER( tmsprom_device::enable_w )
+{
+ if (state != m_enable)
+ {
+ m_enable = state;
+ update_prom_cnt();
+
+ /* the following is needed for ad2084.
+ * It is difficult to derive the actual connections from
+ * pcb pictures but the reset pin of the LS393 driving
+ * the prom address line is connected somewhere.
+ *
+ * This does not affect bagman. It just simulates that a
+ * write to ads3 is always happening when the four lower
+ * counter bits are 0!
+ */
+ if (state)
+ m_prom_cnt &= 0x10;
+ }
+}
+
+
+/*-------------------------------------------------
+ TMS 5110 device definition
+-------------------------------------------------*/
+
+const device_type TMS5110 = &device_creator<tms5110_device>;
+
+tms5110_device::tms5110_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, TMS5110, "TMS5110", tag, owner, clock, "tms5110", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_m0_cb(*this),
+ m_m1_cb(*this),
+ m_addr_cb(*this),
+ m_data_cb(*this),
+ m_romclk_cb(*this)
+{
+}
+
+tms5110_device::tms5110_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_sound_interface(mconfig, *this),
+ m_m0_cb(*this),
+ m_m1_cb(*this),
+ m_addr_cb(*this),
+ m_data_cb(*this),
+ m_romclk_cb(*this)
+{
+}
+
+
+const device_type TMS5100 = &device_creator<tms5100_device>;
+
+tms5100_device::tms5100_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms5110_device(mconfig, TMS5100, "TMS5100", tag, owner, clock, "tms5100", __FILE__)
+{
+}
+
+const device_type TMC0281 = &device_creator<tmc0281_device>;
+
+tmc0281_device::tmc0281_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms5110_device(mconfig, TMC0281, "TMC0281", tag, owner, clock, "tmc0281", __FILE__)
+{
+}
+
+const device_type TMS5100A = &device_creator<tms5100a_device>;
+
+tms5100a_device::tms5100a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms5110_device(mconfig, TMS5100A, "TMS5100A", tag, owner, clock, "tms5100a", __FILE__)
+{
+}
+
+const device_type TMC0281D = &device_creator<tmc0281d_device>;
+
+tmc0281d_device::tmc0281d_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms5110_device(mconfig, TMC0281D, "TMC0281D", tag, owner, clock, "tmc0281d", __FILE__)
+{
+}
+
+const device_type CD2801 = &device_creator<cd2801_device>;
+
+cd2801_device::cd2801_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms5110_device(mconfig, CD2801, "CD2801", tag, owner, clock, "cd2801", __FILE__)
+{
+}
+
+const device_type CD2802 = &device_creator<cd2802_device>;
+
+cd2802_device::cd2802_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms5110_device(mconfig, CD2802, "CD2802", tag, owner, clock, "cd2802", __FILE__)
+{
+}
+
+const device_type TMS5110A = &device_creator<tms5110a_device>;
+
+tms5110a_device::tms5110a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms5110_device(mconfig, TMS5110A, "TMS5110A", tag, owner, clock, "tms5110a", __FILE__)
+{
+}
+
+const device_type M58817 = &device_creator<m58817_device>;
+
+m58817_device::m58817_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms5110_device(mconfig, M58817, "M58817", tag, owner, clock, "m58817", __FILE__)
+{
+}
+
+
+const device_type TMSPROM = &device_creator<tmsprom_device>;
+
+tmsprom_device::tmsprom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, TMSPROM, "TMSPROM", tag, owner, clock, "tmsprom", __FILE__),
+ m_prom_region(""),
+ m_rom_size(0),
+ m_pdc_bit(0),
+ m_ctl1_bit(0),
+ m_ctl2_bit(0),
+ m_ctl4_bit(0),
+ m_ctl8_bit(0),
+ m_reset_bit(0),
+ m_stop_bit(0),
+ m_pdc_cb(*this),
+ m_ctl_cb(*this)
+{
+}
diff --git a/src/devices/sound/tms5110.h b/src/devices/sound/tms5110.h
new file mode 100644
index 00000000000..dffb19b29d7
--- /dev/null
+++ b/src/devices/sound/tms5110.h
@@ -0,0 +1,397 @@
+// license:???
+// copyright-holders:Frank Palazzolo, Jarek Burczynski, Aaron Giles, Jonathan Gevaryahu, Couriersud
+#pragma once
+
+#ifndef __TMS5110_H__
+#define __TMS5110_H__
+
+#include "emu.h"
+
+/* HACK: if defined, uses impossibly perfect 'straight line' interpolation */
+#undef PERFECT_INTERPOLATION_HACK
+
+/* TMS5110 commands */
+ /* CTL8 CTL4 CTL2 CTL1 | PDC's */
+ /* (MSB) (LSB) | required */
+#define TMS5110_CMD_RESET (0) /* 0 0 0 x | 1 */
+#define TMS5110_CMD_LOAD_ADDRESS (2) /* 0 0 1 x | 2 */
+#define TMS5110_CMD_OUTPUT (4) /* 0 1 0 x | 3 */
+#define TMS5110_CMD_SPKSLOW (6) /* 0 1 1 x | 1 | Note: this command is undocumented on the datasheets, it only appears on the patents. It might not actually work properly on some of the real chips as manufactured. Acts the same as CMD_SPEAK, but makes the interpolator take three A cycles whereever it would normally only take one, effectively making speech of any given word take twice as long as normal. */
+#define TMS5110_CMD_READ_BIT (8) /* 1 0 0 x | 1 */
+#define TMS5110_CMD_SPEAK (10) /* 1 0 1 x | 1 */
+#define TMS5110_CMD_READ_BRANCH (12) /* 1 1 0 x | 1 */
+#define TMS5110_CMD_TEST_TALK (14) /* 1 1 1 x | 3 */
+
+/* clock rate = 80 * output sample rate, */
+/* usually 640000 for 8000 Hz sample rate or */
+/* usually 800000 for 10000 Hz sample rate. */
+
+#define MCFG_TMS5110_M0_CB(_devcb) \
+ devcb = &tms5110_device::set_m0_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_TMS5110_M1_CB(_devcb) \
+ devcb = &tms5110_device::set_m1_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_TMS5110_ADDR_CB(_devcb) \
+ devcb = &tms5110_device::set_addr_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_TMS5110_DATA_CB(_devcb) \
+ devcb = &tms5110_device::set_data_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_TMS5110_ROMCLK_CB(_devcb) \
+ devcb = &tms5110_device::set_romclk_callback(*device, DEVCB_##_devcb);
+
+
+class tms5110_device : public device_t,
+ public device_sound_interface
+{
+public:
+ tms5110_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ tms5110_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ template<class _Object> static devcb_base &set_m0_callback(device_t &device, _Object object) { return downcast<tms5110_device &>(device).m_m0_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_m1_callback(device_t &device, _Object object) { return downcast<tms5110_device &>(device).m_m1_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_addr_callback(device_t &device, _Object object) { return downcast<tms5110_device &>(device).m_addr_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_data_callback(device_t &device, _Object object) { return downcast<tms5110_device &>(device).m_data_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_romclk_callback(device_t &device, _Object object) { return downcast<tms5110_device &>(device).m_romclk_cb.set_callback(object); }
+
+ DECLARE_WRITE8_MEMBER( ctl_w );
+ DECLARE_READ8_MEMBER( ctl_r );
+ DECLARE_WRITE_LINE_MEMBER( pdc_w );
+
+ /* this is only used by cvs.c
+ * it is not related at all to the speech generation
+ * and conflicts with the new rom controller interface.
+ */
+ DECLARE_READ8_MEMBER( romclk_hack_r );
+
+ void set_frequency(int frequency);
+
+ int _speech_rom_read_bit();
+ void _speech_rom_set_addr(int addr);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ void set_variant(int variant);
+
+ UINT8 m_SPEN; /* set on speak command, cleared on stop command or reset command */
+ UINT8 m_TALK; /* set on SPEN & RESETL4(pc12->pc0 transition), cleared on stop command or reset command */
+#define TALK_STATUS (m_SPEN|m_TALKD)
+ UINT8 m_TALKD; /* TALK(TCON) value, latched every RESETL4 */
+ sound_stream *m_stream;
+
+private:
+ void new_int_write(UINT8 rc, UINT8 m0, UINT8 m1, UINT8 addr);
+ void new_int_write_addr(UINT8 addr);
+ UINT8 new_int_read();
+ void register_for_save_states();
+ int extract_bits(int count);
+ void perform_dummy_read();
+ INT32 lattice_filter();
+ void process(INT16 *buffer, unsigned int size);
+ void PDC_set(int data);
+ void parse_frame();
+
+ // internal state
+ /* coefficient tables */
+ int m_variant; /* Variant of the 5110 - see tms5110.h */
+
+ /* coefficient tables */
+ const struct tms5100_coeffs *m_coeff;
+
+ /* these contain global status bits */
+ UINT8 m_PDC;
+ UINT8 m_CTL_pins;
+ UINT8 m_state;
+
+ /* Rom interface */
+ UINT32 m_address;
+ UINT8 m_next_is_address;
+ UINT8 m_schedule_dummy_read;
+ UINT8 m_addr_bit;
+ /* read byte */
+ UINT8 m_CTL_buffer;
+
+ /* callbacks */
+ devcb_write_line m_m0_cb; // the M0 line
+ devcb_write_line m_m1_cb; // the M1 line
+ devcb_write8 m_addr_cb; // Write to ADD1,2,4,8 - 4 address bits
+ devcb_read_line m_data_cb; // Read one bit from ADD8/Data - voice data
+ // On a real chip rom_clk is running all the time
+ // Here, we only use it to properly emulate the protocol.
+ // Do not rely on it to be a timed signal.
+ devcb_write_line m_romclk_cb; // rom clock - Only used to drive the data lines
+
+ /* these contain data describing the current and previous voice frames */
+#define OLD_FRAME_SILENCE_FLAG m_OLDE // 1 if E=0, 0 otherwise.
+#define OLD_FRAME_UNVOICED_FLAG m_OLDP // 1 if P=0 (unvoiced), 0 if voiced
+ UINT8 m_OLDE;
+ UINT8 m_OLDP;
+
+#define NEW_FRAME_STOP_FLAG (m_new_frame_energy_idx == 0xF) // 1 if this is a stop (Energy = 0xF) frame
+#define NEW_FRAME_SILENCE_FLAG (m_new_frame_energy_idx == 0) // ditto as above
+#define NEW_FRAME_UNVOICED_FLAG (m_new_frame_pitch_idx == 0) // ditto as above
+ UINT8 m_new_frame_energy_idx;
+ UINT8 m_new_frame_pitch_idx;
+ UINT8 m_new_frame_k_idx[10];
+
+
+ /* these are all used to contain the current state of the sound generation */
+#ifndef PERFECT_INTERPOLATION_HACK
+ INT16 m_current_energy;
+ INT16 m_current_pitch;
+ INT16 m_current_k[10];
+#else
+ UINT8 m_old_frame_energy_idx;
+ UINT8 m_old_frame_pitch_idx;
+ UINT8 m_old_frame_k_idx[10];
+ UINT8 m_old_zpar;
+ UINT8 m_old_uv_zpar;
+
+ INT32 m_current_energy;
+ INT32 m_current_pitch;
+ INT32 m_current_k[10];
+#endif
+
+ UINT16 m_previous_energy; /* needed for lattice filter to match patent */
+
+ UINT8 m_subcycle; /* contains the current subcycle for a given PC: 0 is A' (only used on SPKSLOW mode on 51xx), 1 is A, 2 is B */
+ UINT8 m_subc_reload; /* contains 1 for normal speech, 0 when SPKSLOW is active */
+ UINT8 m_PC; /* current parameter counter (what param is being interpolated), ranges from 0 to 12 */
+ /* NOTE: the interpolation period counts 1,2,3,4,5,6,7,0 for divide by 8,8,8,4,4,2,2,1 */
+ UINT8 m_IP; /* the current interpolation period */
+ UINT8 m_inhibit; /* If 1, interpolation is inhibited until the DIV1 period */
+ UINT8 m_uv_zpar; /* If 1, zero k5 thru k10 coefficients */
+ UINT8 m_zpar; /* If 1, zero ALL parameters. */
+ UINT8 m_pitch_zero; /* circuit 412; pitch is forced to zero under certain circumstances */
+ UINT16 m_pitch_count; /* pitch counter; provides chirp rom address */
+
+ INT32 m_u[11];
+ INT32 m_x[10];
+
+ UINT16 m_RNG; /* the random noise generator configuration is: 1 + x + x^3 + x^4 + x^13 TODO: no it isn't */
+ INT16 m_excitation_data;
+
+ /* The TMS51xx has two different ways of providing output data: the
+ analog speaker pins (which were usually used) and the Digital I/O pin.
+ The internal DAC used to feed the analog pins is only 8 bits, and has the
+ funny clipping/clamping logic, while the digital pin gives full 10 bit
+ resolution of the output data.
+ TODO: add a way to set/reset this other than the FORCE_DIGITAL define
+ */
+ UINT8 m_digital_select;
+
+ INT32 m_speech_rom_bitnum;
+
+ UINT8 m_romclk_hack_timer_started;
+ UINT8 m_romclk_hack_state;
+
+ emu_timer *m_romclk_hack_timer;
+ const UINT8 *m_table;
+};
+
+extern const device_type TMS5110;
+
+class tms5100_device : public tms5110_device
+{
+public:
+ tms5100_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type TMS5100;
+
+class tmc0281_device : public tms5110_device
+{
+public:
+ tmc0281_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type TMC0281;
+
+class tms5100a_device : public tms5110_device
+{
+public:
+ tms5100a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type TMS5100A;
+
+class tmc0281d_device : public tms5110_device
+{
+public:
+ tmc0281d_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type TMC0281D;
+
+class cd2801_device : public tms5110_device
+{
+public:
+ cd2801_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type CD2801;
+
+class cd2802_device : public tms5110_device
+{
+public:
+ cd2802_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type CD2802;
+
+class tms5110a_device : public tms5110_device
+{
+public:
+ tms5110a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type TMS5110A;
+
+class m58817_device : public tms5110_device
+{
+public:
+ m58817_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_READ8_MEMBER( status_r );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type M58817;
+
+
+
+/* PROM controlled TMS5110 interface */
+
+class tmsprom_device : public device_t
+{
+public:
+ tmsprom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void set_region(device_t &device, const char *region) { downcast<tmsprom_device &>(device).m_prom_region = region; }
+ static void set_rom_size(device_t &device, UINT32 rom_size) { downcast<tmsprom_device &>(device).m_rom_size = rom_size; }
+ static void set_pdc_bit(device_t &device, UINT8 pdc_bit) { downcast<tmsprom_device &>(device).m_pdc_bit = pdc_bit; }
+ static void set_ctl1_bit(device_t &device, UINT8 ctl1_bit) { downcast<tmsprom_device &>(device).m_ctl1_bit = ctl1_bit; }
+ static void set_ctl2_bit(device_t &device, UINT8 ctl2_bit) { downcast<tmsprom_device &>(device).m_ctl2_bit = ctl2_bit; }
+ static void set_ctl4_bit(device_t &device, UINT8 ctl4_bit) { downcast<tmsprom_device &>(device).m_ctl4_bit = ctl4_bit; }
+ static void set_ctl8_bit(device_t &device, UINT8 ctl8_bit) { downcast<tmsprom_device &>(device).m_ctl8_bit = ctl8_bit; }
+ static void set_reset_bit(device_t &device, UINT8 reset_bit) { downcast<tmsprom_device &>(device).m_reset_bit = reset_bit; }
+ static void set_stop_bit(device_t &device, UINT8 stop_bit) { downcast<tmsprom_device &>(device).m_stop_bit = stop_bit; }
+ template<class _Object> static devcb_base &set_pdc_callback(device_t &device, _Object object) { return downcast<tmsprom_device &>(device).m_pdc_cb.set_callback(object); }
+ template<class _Object> static devcb_base &set_ctl_callback(device_t &device, _Object object) { return downcast<tmsprom_device &>(device).m_ctl_cb.set_callback(object); }
+
+ DECLARE_WRITE_LINE_MEMBER( m0_w );
+ DECLARE_READ_LINE_MEMBER( data_r );
+
+ /* offset is rom # */
+ DECLARE_WRITE8_MEMBER( rom_csq_w );
+ DECLARE_WRITE8_MEMBER( bit_w );
+ DECLARE_WRITE_LINE_MEMBER( enable_w );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+private:
+ void register_for_save_states();
+ void update_prom_cnt();
+
+ // internal state
+ /* Rom interface */
+ UINT32 m_address;
+ /* ctl lines */
+ UINT8 m_m0;
+ UINT8 m_enable;
+ UINT32 m_base_address;
+ UINT8 m_bit;
+
+ int m_prom_cnt;
+
+ const char *m_prom_region; /* prom memory region - sound region is automatically assigned */
+ UINT32 m_rom_size; /* individual rom_size */
+ UINT8 m_pdc_bit; /* bit # of pdc line */
+ /* virtual bit 8: constant 0, virtual bit 9:constant 1 */
+ UINT8 m_ctl1_bit; /* bit # of ctl1 line */
+ UINT8 m_ctl2_bit; /* bit # of ctl2 line */
+ UINT8 m_ctl4_bit; /* bit # of ctl4 line */
+ UINT8 m_ctl8_bit; /* bit # of ctl8 line */
+ UINT8 m_reset_bit; /* bit # of rom reset */
+ UINT8 m_stop_bit; /* bit # of stop */
+ devcb_write_line m_pdc_cb; /* tms pdc func */
+ devcb_write8 m_ctl_cb; /* tms ctl func */
+
+ emu_timer *m_romclk_timer;
+ const UINT8 *m_rom;
+ const UINT8 *m_prom;
+};
+
+extern const device_type TMSPROM;
+
+#define MCFG_TMSPROM_REGION(_region) \
+ tmsprom_device::set_region(*device, _region);
+
+#define MCFG_TMSPROM_ROM_SIZE(_size) \
+ tmsprom_device::set_rom_size(*device, _size);
+
+#define MCFG_TMSPROM_PDC_BIT(_bit) \
+ tmsprom_device::set_pdc_bit(*device, _bit);
+
+#define MCFG_TMSPROM_CTL1_BIT(_bit) \
+ tmsprom_device::set_ctl1_bit(*device, _bit);
+
+#define MCFG_TMSPROM_CTL2_BIT(_bit) \
+ tmsprom_device::set_ctl2_bit(*device, _bit);
+
+#define MCFG_TMSPROM_CTL4_BIT(_bit) \
+ tmsprom_device::set_ctl4_bit(*device, _bit);
+
+#define MCFG_TMSPROM_CTL8_BIT(_bit) \
+ tmsprom_device::set_ctl8_bit(*device, _bit);
+
+#define MCFG_TMSPROM_RESET_BIT(_bit) \
+ tmsprom_device::set_reset_bit(*device, _bit);
+
+#define MCFG_TMSPROM_STOP_BIT(_bit) \
+ tmsprom_device::set_stop_bit(*device, _bit);
+
+#define MCFG_TMSPROM_PDC_CB(_devcb) \
+ devcb = &tmsprom_device::set_pdc_callback(*device, DEVCB_##_devcb);
+
+#define MCFG_TMSPROM_CTL_CB(_devcb) \
+ devcb = &tmsprom_device::set_ctl_callback(*device, DEVCB_##_devcb);
+
+#endif /* __TMS5110_H__ */
diff --git a/src/devices/sound/tms5110r.inc b/src/devices/sound/tms5110r.inc
new file mode 100644
index 00000000000..7e370853db0
--- /dev/null
+++ b/src/devices/sound/tms5110r.inc
@@ -0,0 +1,705 @@
+// license:BSD-3-Clause
+// copyright-holders:Frank Palazzolo, Couriersud, Jonathan Gevaryahu
+/* TMS51xx and TMS52xx ROM Tables */
+
+/* The following table is assumed to be for TMS5100
+ *
+ * US Patent 4209836
+ * 4331836
+ * 4304964
+ * 4234761
+ * 4189779
+ * 4449233
+ *
+ * All patents give interpolation coefficients
+ * { 1, 8, 8, 8, 4, 4, 2, 2 }
+ * This sequence will not calculate the published
+ * fractions:
+ * 1 8 0.125
+ * 2 8 0.234
+ * 3 8 0.330
+ * 4 4 0.498
+ * 5 4 0.623
+ * 6 2 0.717
+ * 7 2 0.859
+ * 0 1 1.000
+ * (remember, 1 is the FIRST entry!)
+ *
+ * Instead, { 1, 8, 8, 8, 4, 4, 4, 2 }
+ * will calculate those coefficients.
+ * Howeever, after simulating the actual circuit from the patent in pspice,
+ * the { 1, 8, 8, 8, 4, 4, 2, 2 } pattern is revealed as the correct one.
+ * Since the real chip uses shifters and not true division to achieve those
+ * factors, they have been replaced by the shifting coefficients:
+ * { 0, 3, 3, 3, 2, 2, 1, 1 }
+ */
+
+ /* quick note on derivative analysis:
+ Judging by all the TI chips I (Lord Nightmare) have done this test on, the first derivative between successive values of the LPC tables should follow a roughly triangular or sine shaped curve, the second derivative should start at a value, increase slightly, then decrease smoothly and become negative right around where the LPC curve passes 0, finally increase slightly right near the end. If it doesn't do this, there is probably a wrong value in there somewhere. The pitch and energy tables follow similar patterns but aren't the same since they never cross 0. The chirp table doesn't follow this pattern at all.
+ */
+
+ /* Chip types based on die marks from decap:
+ chip type
+ |||||| rom number
+ |||||| |||||
+ VVVVVV VVVVV
+ T0280A 0281 = 1978 speak & spell, unknown difference to below, assumed same? uses old chirp
+ T0280B 0281A = 1979 speak & spell, also == TMS5100, uses old chirp
+ ?????? ????? (no decap; likely 'T0280D 0281D') = 1980 speak & spell, 1981 speak & spell compact, changed energy table, otherwise same as above, uses old chirp
+ T0280F 2801A = 1980 speak & math, 1980 speak and read, uses old chirp
+ T0280F 2802 = touch and tell, language translator; uses a unique chirp rom.
+ ?????? ????? = TMS5110
+ T0280F 5110A = TMS5110AN2L
+
+
+ */
+
+/* chip rom contents defines */
+#define SUBTYPE_0281A 1
+#define SUBTYPE_0281D 2
+#define SUBTYPE_2801A 4
+#define SUBTYPE_M58817 8
+#define SUBTYPE_2802 16
+#define SUBTYPE_5110 32
+#define SUBTYPE_2501E 64
+#define SUBTYPE_5220 128
+#define SUBTYPE_PAT4335277 256
+#define SUBTYPE_VLM5030 512
+
+/* coefficient defines */
+#define MAX_K 10
+#define MAX_SCALE_BITS 6
+#define MAX_SCALE (1<<MAX_SCALE_BITS)
+#define MAX_CHIRP_SIZE 52
+
+struct tms5100_coeffs
+{
+ int subtype;
+ int num_k;
+ int energy_bits;
+ int pitch_bits;
+ int kbits[MAX_K];
+ unsigned short energytable[MAX_SCALE];
+ unsigned short pitchtable[MAX_SCALE];
+ int ktable[MAX_K][MAX_SCALE];
+ INT16 chirptable[MAX_CHIRP_SIZE];
+ INT8 interp_coeff[8];
+};
+
+/* common, shared coefficients */
+/* energy */
+#define TI_0280_PATENT_ENERGY \
+ /* E */\
+ { 0, 0, 1, 1, 2, 3, 5, 7, \
+ 10, 15, 21, 30, 43, 61, 86, 0 },
+
+#define TI_028X_LATER_ENERGY \
+ /* E */\
+ { 0, 1, 2, 3, 4, 6, 8, 11, \
+ 16, 23, 33, 47, 63, 85,114, 0 },
+
+/* pitch */
+#define TI_0280_2801_PATENT_PITCH \
+ /* P */\
+ { 0, 41, 43, 45, 47, 49, 51, 53, \
+ 55, 58, 60, 63, 66, 70, 73, 76, \
+ 79, 83, 87, 90, 94, 99, 103, 107, \
+ 112, 118, 123, 129, 134, 140, 147, 153 },
+
+#define TI_2802_PITCH \
+ /* P */\
+ { 0, 16, 18, 19, 21, 24, 26, 28, \
+ 31, 35, 37, 42, 44, 47, 50, 53, \
+ 56, 59, 63, 67, 71, 75, 79, 84, \
+ 89, 94, 100, 106, 112, 126, 141, 150},
+
+#define TI_5110_PITCH \
+ /* P */\
+ { 0, 15, 16, 17, 19, 21, 22, 25, \
+ 26, 29, 32, 36, 40, 42, 46, 50, \
+ 55, 60, 64, 68, 72, 76, 80, 84, \
+ 86, 93, 101, 110, 120, 132, 144, 159},
+
+#define TI_2501E_PITCH \
+ /* P */\
+ { 0, 14, 15, 16, 17, 18, 19, 20, \
+ 21, 22, 23, 24, 25, 26, 27, 28, \
+ 29, 30, 31, 32, 34, 36, 38, 40, \
+ 41, 43, 45, 48, 49, 51, 54, 55, \
+ 57, 60, 62, 64, 68, 72, 74, 76, \
+ 81, 85, 87, 90, 96, 99, 103, 107, \
+ 112, 117, 122, 127, 133, 139, 145, 151, \
+ 157, 164, 171, 178, 186, 194, 202, 211},
+
+#define TI_5220_PITCH \
+ /* P */\
+ { 0, 15, 16, 17, 18, 19, 20, 21, \
+ 22, 23, 24, 25, 26, 27, 28, 29, \
+ 30, 31, 32, 33, 34, 35, 36, 37, \
+ 38, 39, 40, 41, 42, 44, 46, 48, \
+ 50, 52, 53, 56, 58, 60, 62, 65, \
+ 68, 70, 72, 76, 78, 80, 84, 86, \
+ 91, 94, 98, 101, 105, 109, 114, 118, \
+ 122, 127, 132, 137, 142, 148, 153, 159},
+
+/* LPC */
+#define TI_0280_PATENT_LPC \
+ /* K1 */\
+ { -501, -497, -493, -488, -480, -471, -460, -446,\
+ -427, -405, -378, -344, -305, -259, -206, -148,\
+ -86, -21, 45, 110, 171, 227, 277, 320,\
+ 357, 388, 413, 434, 451, 464, 474, 498 },\
+ /* K2 */\
+ { -349, -328, -305, -280, -252, -223, -192, -158,\
+ -124, -88, -51, -14, 23, 60, 97, 133,\
+ 167, 199, 230, 259, 286, 310, 333, 354,\
+ 372, 389, 404, 417, 429, 439, 449, 506 },\
+ /* K3 */\
+ { -397, -365, -327, -282, -229, -170, -104, -36,\
+ 35, 104, 169, 228, 281, 326, 364, 396 },\
+ /* K4 */\
+ { -369, -334, -293, -245, -191, -131, -67, -1,\
+ 64, 128, 188, 243, 291, 332, 367, 397 },\
+ /* K5 */\
+ { -319, -286, -250, -211, -168, -122, -74, -25,\
+ 24, 73, 121, 167, 210, 249, 285, 318 },\
+ /* K6 */\
+ { -290, -252, -209, -163, -114, -62, -9, 44,\
+ 97, 147, 194, 238, 278, 313, 344, 371 },\
+ /* K7 */\
+ { -291, -256, -216, -174, -128, -80, -31, 19,\
+ 69, 117, 163, 206, 246, 283, 316, 345 },\
+ /* K8 */\
+ { -218, -133, -38, 59, 152, 235, 305, 361 },\
+ /* K9 */\
+ { -226, -157, -82, -3, 76, 151, 220, 280 },\
+ /* K10 */\
+ { -179, -122, -61, 1, 62, 123, 179, 231 },
+
+#define TI_2801_2501E_LPC \
+ /* K1 */\
+ { -501, -498, -495, -490, -485, -478, -469, -459,\
+ -446, -431, -412, -389, -362, -331, -295, -253,\
+ -207, -156, -102, -45, 13, 70, 126, 179,\
+ 228, 272, 311, 345, 374, 399, 420, 437 },\
+ /* K2 */\
+ { -376, -357, -335, -312, -286, -258, -227, -195,\
+ -161, -124, -87, -49, -10, 29, 68, 106,\
+ 143, 178, 212, 243, 272, 299, 324, 346,\
+ 366, 384, 400, 414, 427, 438, 448, 506 },\
+ /* K3 */\
+ { -407, -381, -349, -311, -268, -218, -162, -102,\
+ -39, 25, 89, 149, 206, 257, 302, 341 },\
+ /* K4 */\
+ { -290, -252, -209, -163, -114, -62, -9, 44,\
+ 97, 147, 194, 238, 278, 313, 344, 371 },\
+ /* K5 */\
+ { -318, -283, -245, -202, -156, -107, -56, -3,\
+ 49, 101, 150, 196, 239, 278, 313, 344 },\
+ /* K6 */\
+ { -193, -152, -109, -65, -20, 26, 71, 115,\
+ 158, 198, 235, 270, 301, 330, 355, 377 },\
+ /* K7 */\
+ { -254, -218, -180, -140, -97, -53, -8, 36,\
+ 81, 124, 165, 204, 240, 274, 304, 332 },\
+ /* K8 */\
+ { -205, -112, -10, 92, 187, 269, 336, 387 },\
+ /* K9 */\
+ { -249, -183, -110, -32, 48, 126, 198, 261 }, /* on patents 4,403,965 and 4,946,391 the 4th entry is 0x3ED (-19) which is a typo of the correct value of 0x3E0 (-32)*/\
+ /* K10 */\
+ { -190, -133, -73, -10, 53, 115, 173, 227 },
+
+// below is the same as 2801/2501E above EXCEPT for K4 which is completely different.
+#define TI_2802_LPC \
+ /* K1 */\
+ { -501, -498, -495, -490, -485, -478, -469, -459,\
+ -446, -431, -412, -389, -362, -331, -295, -253,\
+ -207, -156, -102, -45, 13, 70, 126, 179,\
+ 228, 272, 311, 345, 374, 399, 420, 437},\
+ /* K2 */\
+ { -376, -357, -335, -312, -286, -258, -227, -195,\
+ -161, -124, -87, -49, -10, 29, 68, 106,\
+ 143, 178, 212, 243, 272, 299, 324, 346,\
+ 366, 384, 400, 414, 427, 438, 448, 506},\
+ /* K3 */\
+ { -407, -381, -349, -311, -268, -218, -162, -102,\
+ -39, 25, 89, 149, 206, 257, 302, 341},\
+ /* K4 */\
+ { -289, -248, -202, -152, -98, -43, 14, 71,\
+ 125, 177, 225, 269, 307, 341, 371, 506},\
+ /* K5 */\
+ { -318, -283, -245, -202, -156, -107, -56, -3,\
+ 49, 101, 150, 196, 239, 278, 313, 344},\
+ /* K6 */\
+ { -193, -152, -109, -65, -20, 26, 71, 115,\
+ 158, 198, 235, 270, 301, 330, 355, 377},\
+ /* K7 */\
+ { -254, -218, -180, -140, -97, -53, -8, 36,\
+ 81, 124, 165, 204, 240, 274, 304, 332},\
+ /* K8 */\
+ { -205, -112, -10, 92, 187, 269, 336, 387},\
+ /* K9 */\
+ { -249, -183, -110, -32, 48, 126, 198, 261},\
+ /* K10 */\
+ { -190, -133, -73, -10, 53, 115, 173, 227},
+
+#define TI_5110_5220_LPC \
+ /* K1 */\
+ { -501, -498, -497, -495, -493, -491, -488, -482,\
+ -478, -474, -469, -464, -459, -452, -445, -437,\
+ -412, -380, -339, -288, -227, -158, -81, -1,\
+ 80, 157, 226, 287, 337, 379, 411, 436 },\
+ /* K2 */\
+ { -328, -303, -274, -244, -211, -175, -138, -99,\
+ -59, -18, 24, 64, 105, 143, 180, 215,\
+ 248, 278, 306, 331, 354, 374, 392, 408,\
+ 422, 435, 445, 455, 463, 470, 476, 506 },\
+ /* K3 */\
+ { -441, -387, -333, -279, -225, -171, -117, -63,\
+ -9, 45, 98, 152, 206, 260, 314, 368 },\
+ /* K4 */\
+ { -328, -273, -217, -161, -106, -50, 5, 61,\
+ 116, 172, 228, 283, 339, 394, 450, 506 },\
+ /* K5 */\
+ { -328, -282, -235, -189, -142, -96, -50, -3,\
+ 43, 90, 136, 182, 229, 275, 322, 368 },\
+ /* K6 */\
+ { -256, -212, -168, -123, -79, -35, 10, 54,\
+ 98, 143, 187, 232, 276, 320, 365, 409 },\
+ /* K7 */\
+ { -308, -260, -212, -164, -117, -69, -21, 27,\
+ 75, 122, 170, 218, 266, 314, 361, 409 },\
+ /* K8 */\
+ { -256, -161, -66, 29, 124, 219, 314, 409 },\
+ /* K9 */\
+ { -256, -176, -96, -15, 65, 146, 226, 307 },\
+ /* K10 */\
+ { -205, -132, -59, 14, 87, 160, 234, 307 },
+
+/* chirp */
+#define TI_0280_PATENT_CHIRP \
+ /* Chirp table */\
+ { 0x00, 0x2a, 0xd4, 0x32, 0xb2, 0x12, 0x25, 0x14,\
+ 0x02, 0xe1, 0xc5, 0x02, 0x5f, 0x5a, 0x05, 0x0f,\
+ 0x26, 0xfc, 0xa5, 0xa5, 0xd6, 0xdd, 0xdc, 0xfc,\
+ 0x25, 0x2b, 0x22, 0x21, 0x0f, 0xff, 0xf8, 0xee,\
+ 0xed, 0xef, 0xf7, 0xf6, 0xfa, 0x00, 0x03, 0x02,\
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00 },
+
+// almost, but not exactly the same as the patent chirp above (25 bits differ)
+#define TI_2801_CHIRP \
+ /* Chirp table */\
+ { 0x00, 0x2b, 0xd4, 0x33, 0xb3, 0x12, 0x25, 0x14,\
+ 0x02, 0xe2, 0xc6, 0x03, 0x60, 0x5b, 0x05, 0x0f,\
+ 0x26, 0xfc, 0xa6, 0xa5, 0xd6, 0xdd, 0xdd, 0xfd,\
+ 0x25, 0x2b, 0x23, 0x22, 0x0f, 0xff, 0xf8, 0xef,\
+ 0xed, 0xef, 0xf7, 0xf7, 0xfa, 0x01, 0x04, 0x03,\
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00 },
+
+#define TI_2802_CHIRP \
+ /* Chirp table */\
+ { 0x00, 0xa5, 0xbd, 0xee, 0x34, 0x73, 0x7e, 0x3d,\
+ 0xe8, 0xea, 0x34, 0x24, 0xd1, 0x01, 0x13, 0xc3,\
+ 0x0c, 0xd2, 0xe7, 0xdd, 0xd9, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00 },
+
+#define TI_LATER_CHIRP \
+ /* Chirp table */\
+ { 0x00, 0x03, 0x0f, 0x28, 0x4c, 0x6c, 0x71, 0x50,\
+ 0x25, 0x26, 0x4c, 0x44, 0x1a, 0x32, 0x3b, 0x13,\
+ 0x37, 0x1a, 0x25, 0x1f, 0x1d, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00 },
+
+/* Interpolation Table */
+#define TI_INTERP \
+ /* interpolation shift coefficients */\
+ { 0, 3, 3, 3, 2, 2, 1, 1 }
+
+/* TMS5100/TMC0281:
+ (Die revs A, B; 1977?-1981?)
+ The TMS5100NL was decapped and imaged by digshadow in April, 2013.
+ The LPC table is verified to match the decap.
+ It also matches the intended contents of US Patent 4,209,836 and several others.
+ The chirp table is verified to match the decap, and also matches the patents.
+ In April, 2013, digshadow decapped a TMS5100 chip from 1980: http://siliconpr0n.org/map/ti/tms5100nl/
+ The TMS5100 had the die markings: "0281 B 281A"
+ In December 2014, Sean riddle decapped a TMC0281 chip from 1978 from an early speak
+ and spell.
+ The TMC0281 die had the die markings "0281 A 281"
+ The chirp table matches what digshadow had decapped earlier.
+ The LPC table hasn't been fully typed yet.
+ Digitally dumped via PROMOUT by PlgDavid in 2014 for verification.
+*/
+static const struct tms5100_coeffs T0280B_0281A_coeff =
+{
+ /* subtype */
+ SUBTYPE_0281A,
+ 10,
+ 4,
+ 5,
+ { 5, 5, 4, 4, 4, 4, 4, 3, 3, 3 },
+ TI_0280_PATENT_ENERGY
+ TI_0280_2801_PATENT_PITCH
+ {
+ TI_0280_PATENT_LPC
+ },
+ TI_0280_PATENT_CHIRP
+ TI_INTERP
+};
+
+/* TMS5110A/TMC0281D:
+ This chip is used on the later speak & spell, and speak & spell compact;
+ The energy table differs from the original tmc0281/tms5100, as does the interpolation behavior,
+ which is the 'alternate' behavior.
+ The chips have datecodes in the 1983-1984 range, probably 1982 also.
+ Digitally dumped via PROMOUT by PlgDavid in 2014
+ */
+static const struct tms5100_coeffs T0280D_0281D_coeff =
+{
+ /* subtype */
+ SUBTYPE_0281D,
+ 10,
+ 4,
+ 5,
+ { 5, 5, 4, 4, 4, 4, 4, 3, 3, 3 },
+ TI_028X_LATER_ENERGY
+ TI_0280_2801_PATENT_PITCH
+ {
+ TI_0280_PATENT_LPC
+ },
+ TI_0280_PATENT_CHIRP
+ TI_INTERP
+};
+
+/* TMC0280/CD2801:
+ Used in the Speak & Math, Speak & Read, and Language Translator
+ Decapped by Digshadow in 2014 http://siliconpr0n.org/map/ti/tmc0280fnl/
+ Digitally dumped via PROMOUT by PlgDavid in 2014
+ The coefficients are exactly the same as the TMS5200.
+ The coefficients also come from US Patents 4,403,965 and 4,946,391 (with
+ one typo in the patent).
+ The chirp table is very slightly different from the 4,209,836 patent one,
+ but matches the table in the 4,403,965 and 4,946,391 patents.
+ The Mitsubishi M58817 also seems to work best with these coefficients, so
+ it is possible the engineers of that chip copied them from the TI patents.
+ ***TODO: there are 2 versions of this chip, and the interpolation
+ behavior between the two differs slightly:
+ * TMC0280NLP // CD2801 with datecodes around 1980 has the same
+ interpolation inhibit behavior as 5100/TMC0281 on unvoiced->silent
+ transition.
+ * CD2801A-NL with datecodes around 1982 have the 'alternate behavior'
+ */
+static const struct tms5100_coeffs T0280F_2801A_coeff =
+{
+ /* subtype */
+ SUBTYPE_2801A,
+ 10,
+ 4,
+ 5,
+ { 5, 5, 4, 4, 4, 4, 4, 3, 3, 3 },
+ TI_028X_LATER_ENERGY
+ TI_0280_2801_PATENT_PITCH
+ {
+ TI_2801_2501E_LPC
+ },
+ TI_2801_CHIRP
+ TI_INTERP
+};
+
+/* Mitsubishi M58817
+The Mitsubishi M58817 seems to have (partly?) copied the coefficients from the
+TMC0280/CD2801 above, but has some slight differences to it within the chip:
+the main accumulator seems to have 1 extra bit and the digital values are
+tapped 1 bit higher than on the TI chips. This is emulated within tms5110.c
+ */
+static const struct tms5100_coeffs M58817_coeff =
+{
+ /* subtype */
+ SUBTYPE_M58817,
+ 10,
+ 4,
+ 5,
+ { 5, 5, 4, 4, 4, 4, 4, 3, 3, 3 },
+ TI_028X_LATER_ENERGY
+ TI_0280_2801_PATENT_PITCH
+ {
+ TI_2801_2501E_LPC
+ },
+ TI_2801_CHIRP
+ TI_INTERP
+};
+
+/* CD2802:
+ (1984 era?)
+ Used in Touch and Tell only (and Vocaid?), this chip has a unique pitch, LPC and chirp table.
+ Has the 'alternate' interpolation behavior.
+ Digitally dumped via PROMOUT by PlgDavid in 2014
+ Decapped by Sean Riddle in 2015
+ */
+static const struct tms5100_coeffs T0280F_2802_coeff =
+{
+ /* subtype */
+ SUBTYPE_2802,
+ 10,
+ 4,
+ 5,
+ { 5, 5, 4, 4, 4, 4, 4, 3, 3, 3 },
+ TI_028X_LATER_ENERGY
+ TI_2802_PITCH
+ {
+ TI_2802_LPC
+ },
+ TI_2802_CHIRP
+ TI_INTERP
+};
+
+/* TMS5110A:
+ (1984-90 era? early chips may be called TMS5110C; later chips past 1988 or so may be called TSP5110A)
+ The TMS5110A LPC coefficients were originally read from an actual TMS5110A
+ chip by Jarek Burczynski using the PROMOUT pin, later verified/redumped
+ by PlgDavid.
+ NullMoogleCable decapped a TMS5110AN2L in 2015: http://wtfmoogle.com/wp-content/uploads/2015/03/0317_1.jpg
+ which was used to verify the chirp table.
+ The slightly older but otherwise identical TMS5111NLL was decapped and imaged by digshadow in April, 2013.
+ The die is marked "TMS5110AJ"
+ The LPC table is verified from decap to match the values from Jarek and PlgDavid's PROMOUT dumps of the TMS5110.
+ The LPC table matches that of the TMS5220.
+ It uses the 'newer' 5200-style chirp table.
+ It has the 'alternate' interpolation behavor (tested on 5110a; 5111 behavior is unknown)
+*/
+static const struct tms5100_coeffs tms5110a_coeff =
+{
+ /* subtype */
+ SUBTYPE_5110,
+ 10,
+ 4,
+ 5,
+ { 5, 5, 4, 4, 4, 4, 4, 3, 3, 3 },
+ TI_028X_LATER_ENERGY
+ TI_5110_PITCH
+ {
+ TI_5110_5220_LPC
+ },
+ TI_LATER_CHIRP
+ TI_INTERP
+};
+
+/* The following coefficients come from US Patent 4,335,277 and 4,581,757.
+However, the K10 row of coefficients are entirely missing from both of those
+patents.
+The K values don't match the values read from any of the TI chips so far, but
+might match some other undiscovered chip? Or may be complete garbage put as a red
+herring in the patent?
+*/
+ // k* is followed by d if done transcription, c if checked for derivative aberrations
+static const struct tms5100_coeffs pat4335277_coeff =
+{
+ /* subtype */
+ SUBTYPE_PAT4335277,
+ 10,
+ 4,
+ 6,
+ { 5, 5, 4, 4, 4, 4, 4, 3, 3, 3 },
+ TI_028X_LATER_ENERGY
+ TI_2501E_PITCH
+ {
+ /* K1dc */
+ { -507, -505, -503, -501, -497, -493, -488, -481,
+ -473, -463, -450, -434, -414, -390, -362, -328,
+ -288, -242, -191, -135, -75, -13, 49, 110,
+ 168, 221, 269, 311, 348, 379, 404, 426 },
+ /* K2dc */
+ { -294, -266, -235, -202, -167, -130, -92, -52,
+ -12, 28, 68, 108, 145, 182, 216, 248,
+ 278, 305, 330, 352, 372, 390, 406, 420,
+ 432, 443, 453, 461, 468, 474, 479, 486 },
+ /* K3dc */
+ { -449, -432, -411, -385, -354, -317, -273, -223,
+ -167, -107, -43, 22, 87, 148, 206, 258 },
+ /* K4dc (first 4-5 values are probably wrong but close) */
+ { -321, -270, -220, -157, -97, -40, 25, 89,
+ 150, 207, 259, 304, 343, 376, 403, 425 },
+ /* K5dc */
+ { -373, -347, -318, -284, -247, -206, -162, -115,
+ -65, -15, 36, 86, 135, 181, 224, 263 },
+ /* K6dc */
+ { -213, -176, -137, -96, -54, -11, 33, 75,
+ 117, 157, 195, 231, 264, 294, 322, 347 },
+ /* K7dc */
+ { -294, -264, -232, -198, -161, -122, -82, -41,
+ 1, 43, 84, 125, 163, 200, 234, 266 },
+ /* K8dc */
+ { -195, -117, -32, 54, 137, 213, 279, 335 },
+ /* K9dc */
+ { -122, -55, 15, 83, 149, 210, 264, 311 },
+ /* K10 - this was entirely missing from the patent, and I've simply copied the real TMS5220 one, which is wrong */
+ { -205, -132, -59, 14, 87, 160, 234, 307 },
+ },
+ TI_0280_PATENT_CHIRP
+ TI_INTERP
+};
+
+/* TMS5200/CD2501E
+ (1979-1983 era)
+The TMS5200NL was decapped and imaged by digshadow in March, 2013.
+It is equivalent to the CD2501E (internally: "TMC0285") chip used
+ on the TI 99/4(A) speech module.
+The LPC table is verified to match the decap.
+ (It was previously dumped with PROMOUT which matches as well)
+The chirp table is verified to match the decap. (sum = 0x3da)
+Note that the K coefficients are VERY different from the coefficients given
+ in the US 4,335,277 patent, which may have been for some sort of prototype or
+ otherwise intentionally scrambled. The energy and pitch tables, however, are
+ identical to that patent.
+Also note, that the K coefficients are identical to the coefficients from the
+ CD2801 (which itself is almost identical to the CD2802).
+NOTE FROM DECAP: immediately to the left of each of the K1,2,3,4,5,and 6
+ coefficients in the LPC rom are extra columns containing the constants
+ -510, -502, 313, 318, or in hex 0x202, 0x20A, 0x139, 0x13E.
+ Those EXACT constants DO appear (rather nonsensically) on the lpc table in US
+ patent 4,335,277. They are likely related to the multiplicative interpolator
+ described in us patent 4,419,540; whether the 5200/2501E and the 5220 or 5220C
+ actually implement this interpolator or not is unclear. This interpolator
+ seems intended for chips with variable frame rate, so it may only exist
+ on the TMS/TSP5220C and CD2501ECD.
+*/
+
+static const struct tms5100_coeffs T0285_2501E_coeff =
+{
+ /* subtype */
+ SUBTYPE_2501E,
+ 10,
+ 4,
+ 6,
+ { 5, 5, 4, 4, 4, 4, 4, 3, 3, 3 },
+ TI_028X_LATER_ENERGY
+ TI_2501E_PITCH
+ {
+ TI_2801_2501E_LPC
+ },
+ TI_LATER_CHIRP
+ TI_INTERP
+};
+
+/* TMS5220/5220C:
+(1983 era for 5220, 1986-1992 era for 5220C; 5220C may also be called TSP5220C)
+The TMS5220NL was decapped and imaged by digshadow in April, 2013.
+The LPC table table is verified to match the decap.
+The chirp table is verified to match the decap. (sum = 0x3da)
+Note that all the LPC K* values match the TMS5110a table (as read via PROMOUT)
+exactly.
+The TMS5220CNL was decapped and imaged by digshadow in April, 2013.
+The LPC table table is verified to match the decap and exactly matches TMS5220NL.
+The chirp table is verified to match the decap. (sum = 0x3da)
+*/
+static const struct tms5100_coeffs tms5220_coeff =
+{
+ /* subtype */
+ SUBTYPE_5220,
+ 10,
+ 4,
+ 6,
+ { 5, 5, 4, 4, 4, 4, 4, 3, 3, 3 },
+ TI_028X_LATER_ENERGY
+ TI_5220_PITCH
+ {
+ TI_5110_5220_LPC
+ },
+ TI_LATER_CHIRP
+ TI_INTERP
+};
+
+/* The following Sanyo VLM5030 coefficients are derived from decaps of the chip
+done by ogoun, plus image stitching done by John McMaster. The organization of
+coefficients beyond k2 is derived from work by Tatsuyuki Satoh.
+The actual coefficient rom on the chip die has 5 groups of bits only:
+Address | K1A | K1B | K2 | Energy | Pitch |
+Decoder | K1A | K1B | K2 | Energy | Pitch |
+K1A, K1B and K2 are 10 bits wide, 32 bits long each.
+Energy and pitch are both 7 bits wide, 32 bits long each.
+K1A holds odd values of K1, K1B holds even values.
+K2 holds values for K2 only
+K3 and K4 are actually the table index values <<6
+K5 thru K10 are actually the table index values <<7
+The concept of only having non-binary weighted reflection coefficients for the
+first two k stages is mentioned in Markel & Gray "Linear Prediction of Speech"
+and in Thomas Parsons' "Voice and Speech Processing"
+ */
+static const struct tms5100_coeffs vlm5030_coeff =
+{
+ /* subtype */
+ SUBTYPE_VLM5030,
+ 10,
+ 5,
+ 5,
+ { 6, 5, 4, 4, 3, 3, 3, 3, 3, 3 },
+ /* E */
+ { 0, 1, 2, 3, 5, 6, 7, 9,
+ 11, 13, 15, 17, 19, 22, 24, 27,
+ 31, 34, 38, 42, 47, 51, 57, 62,
+ 68, 75, 82, 89, 98,107,116,127},
+ /* P */
+ { 0, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 31, 33, 35, 37, 39, 41,
+ 43, 45, 49, 53, 57, 61, 65, 69,
+ 73, 77, 85, 93, 101, 109, 117, 125 },
+ {
+ /* K1 */
+ /* (NOTE: the order of each table is correct, despite that the index MSb
+ looks backwards) */
+ { 390, 403, 414, 425, 434, 443, 450, 457,
+ 463, 469, 474, 478, 482, 485, 488, 491,
+ 494, 496, 498, 499, 501, 502, 503, 504,
+ 505, 506, 507, 507, 508, 508, 509, 509,
+ -390,-376,-360,-344,-325,-305,-284,-261,
+ -237,-211,-183,-155,-125, -95, -64, -32,
+ 0, 32, 64, 95, 125, 155, 183, 211,
+ 237, 261, 284, 305, 325, 344, 360, 376 },
+ /* K2 */
+ { 0, 50, 100, 149, 196, 241, 284, 325,
+ 362, 396, 426, 452, 473, 490, 502, 510,
+ 0,-510,-502,-490,-473,-452,-426,-396, /* entry 16(0x10) either has some special function, purpose unknown, or is a manufacturing error and should have been -512 */
+ -362,-325,-284,-241,-196,-149,-100, -50 },
+ /* K3 */
+ /*{ 0, 100, 196, 284, 362, 426, 473, 502,
+ -510,-490,-452,-396,-325,-241,-149, -50 },*/
+ { 0, 64, 128, 192, 256, 320, 384, 448,
+ -512,-448,-384,-320,-256,-192,-128, -64 },
+ /* K4 */
+ /*{ 0, 100, 196, 284, 362, 426, 473, 502,
+ -510,-490,-452,-396,-325,-241,-149, -50 },*/
+ { 0, 64, 128, 192, 256, 320, 384, 448,
+ -512,-448,-384,-320,-256,-192,-128, -64 },
+ /* K5 */
+ { 0, 128, 256, 384,-512,-384,-256,-128 },
+ /* K6 */
+ { 0, 128, 256, 384,-512,-384,-256,-128 },
+ /* K7 */
+ { 0, 128, 256, 384,-512,-384,-256,-128 },
+ /* K8 */
+ { 0, 128, 256, 384,-512,-384,-256,-128 },
+ /* K9 */
+ { 0, 128, 256, 384,-512,-384,-256,-128 },
+ /* K10 */
+ /*{ 0, 196, 362, 473,-510,-452,-325,-149 },*/
+ { 0, 128, 256, 384,-512,-384,-256,-128 },
+ },
+ /* Chirp table */
+ { 0,127,127, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0 },
+ /* interpolation coefficients */
+ { 3, 3, 3, 2, 2, 1, 1, 0 }
+};
diff --git a/src/devices/sound/tms5220.c b/src/devices/sound/tms5220.c
new file mode 100644
index 00000000000..12b718c98c7
--- /dev/null
+++ b/src/devices/sound/tms5220.c
@@ -0,0 +1,1953 @@
+// license:BSD-3-Clause
+// copyright-holders:Frank Palazzolo, Aaron Giles, Jonathan Gevaryahu, Raphael Nabet, Couriersud, Michael Zapf
+/**********************************************************************************************
+
+ TMS5200/5220 simulator
+
+ Written for MAME by Frank Palazzolo
+ With help from Neill Corlett
+ Additional tweaking by Aaron Giles
+ TMS6100 Speech Rom support added by Raphael Nabet
+ PRNG code by Jarek Burczynski backported from tms5110.c by Lord Nightmare
+ Chirp/excitation table fixes by Lord Nightmare
+ Various fixes by Lord Nightmare
+ Modularization by Lord Nightmare
+ Sub-interpolation-cycle parameter updating added by Lord Nightmare
+ Preliminary MASSIVE merge of tms5110 and tms5220 cores by Lord Nightmare
+ Lattice Filter, Multiplier, and clipping redone by Lord Nightmare
+ TMS5220C multi-rate feature added by Lord Nightmare
+ Massive rewrite and reorganization by Lord Nightmare
+ Additional IP, PC, subcycle timing rewrite by Lord Nightmare
+ Updated based on the chip decaps done by digshadow
+
+ Much information regarding the lpc encoding used here comes from US patent 4,209,844
+ US patent 4,331,836 describes the complete 51xx chip
+ US patent 4,335,277 describes the complete 52xx chip
+ Special Thanks to Larry Brantingham for answering questions regarding the chip details
+
+ TMS5200/TMS5220/TMS5220C/CD2501E/CD2501ECD:
+
+ +-----------------+
+ D7(d0) | 1 28 | /RS
+ ADD1 | 2 27 | /WS
+ ROMCLK | 3 26 | D6(d1)
+ VDD(-5) | 4 25 | ADD2
+ VSS(+5) | 5 24 | D5(d2)
+ OSC | 6 23 | ADD4
+ T11 | 7 22 | D4(d3)
+ SPKR | 8 21 | ADD8/DATA
+ I/O | 9 20 | TEST
+ PROMOUT | 10 19 | D3(d4)
+ VREF(GND)| 11 18 | /READY
+ D2(d5) | 12 17 | /INT
+ D1(d6) | 13 16 | M1
+ D0(d7) | 14 15 | M0
+ +-----------------+
+Note the standard naming for d* data bits with 7 as MSB and 0 as LSB is in lowercase.
+TI's naming has D7 as LSB and D0 as MSB and is in uppercase
+
+TODO:
+ * Ever since the big rewrite, there are glitches on certain frame transitions
+ for example in the word 'rid' during the eprom attract mode,
+ I (LN) am not entirely sure why the real chip doesn't have these as well.
+ Needs more real hardware testing/dumps for comparison.
+ * Implement a ready callback for pc interfaces
+ - this will be quite a challenge since for it to be really accurate
+ the whole emulation has to run in sync (lots of timers) with the
+ cpu cores.
+ * If a command is still executing, /READY will be kept high until the command has
+ finished if the next command is written.
+ * tomcat has a 5220 which is not hooked up at all
+
+Pedantic detail from observation of real chip:
+The 5200 and 5220 chips outputs the following coefficients over PROMOUT while
+'idle' and not speaking, in this order:
+e[0 or f] p[0] k1[0] k2[0] k3[0] k4[0] k5[f] k6[f] k7[f] k8[7] k9[7] k10[7]
+
+Patent notes (important timing info for interpolation):
+* TCycle ranges from 1 to 20, is clocked based on the clock input or RC clock
+ to the chip / 4. This emulation core completely ignores TCycle, as it isn't
+ very relevant.
+ Every full TCycle count (i.e. overflow from 20 to 1), Subcycle is
+ incremented.
+* Subcycle ranges from 0 to 2, reload is 0 in SPKSLOW mode, 1 normally, and
+ corresponds to whether an interpolation value is being calculated (0 or 1)
+ or being written to ram (2). 0 and 1 correspond to 'A' cycles on the
+ patent, while 2 corresponds to 'B' cycles.
+ Every Subcycle full count (i.e. overflow from 2 to (0 or 1)), PC is
+ incremented. (NOTE: if PC=12, overflow happens on the 1->2 transition,
+ not 2->0; PC=12 has no B cycle.)
+* PC ranges from 0 to 12, and corresponds to the parameter being interpolated
+ or otherwise read from rom using PROMOUT.
+ The order is:
+ 0 = Energy
+ 1 = Pitch
+ 2 = K1
+ 3 = K2
+ ...
+ 11 = K10
+ 12 = nothing
+ Every PC full count (i.e. overflow from 12 to 0), IP (aka "Interpolation Period")
+ is incremented.
+* IP (aka "Interpolation Period") ranges from 0 to 7, and corresponds with the amount
+ of rightshift that the difference between current and target for a given
+ parameter will have applied to it, before being added to the current
+ parameter. Note that when interpolation is inhibited, only IP=0 will
+ cause any change to the current values of the coefficients.
+ The order is, after new frame parse (last ip was 0 before parse):
+ 1 = >>3 (/8)
+ 2 = >>3 (/8)
+ 3 = >>3 (/8)
+ 4 = >>2 (/4)
+ 5 = >>2 (/4)
+ 6 = >>1 (/2) (NOTE: the patent has an error regarding this value on one table implying it should be /4, but circuit simulation of parts of the patent shows that the /2 value is correct.)
+ 7 = >>1 (/2)
+ 0 = >>0 (/1, forcing current values to equal target values)
+ Every IP full count, a new frame is parsed, but ONLY on the 0->*
+ transition.
+ NOTE: on TMS5220C ONLY, the datasheet IMPLIES the following:
+ Upon new frame parse (end of IP=0), the IP is forced to a value depending
+ on the TMS5220C-specific rate setting. For rate settings 0, 1, 2, 3, it
+ will be forced to 1, 3, 5 or 7 respectively. On non-TMS5220 chips, it
+ counts as expected (IP=1 follows IP=0) always.
+ This means, the tms5220c with rates set to n counts IP as follows:
+ (new frame parse is indicated with a #)
+ Rate IP Count
+ 00 7 0#1 2 3 4 5 6 7 0#1 2 3 4 5 6 7 <- non-tms5220c chips always follow this pattern
+ 01 7 0#3 4 5 6 7 0#3 4 5 6 7 0#3 4 5
+ 10 7 0#5 6 7 0#5 6 7 0#5 6 7 0#5 6 7
+ 11 7 0#7 0#7 0#7 0#7 0#7 0#7 0#7 0#7
+ Based on the behavior tested on the CD2501ECD this is assumed to be the same for that chip as well.
+
+Most of the following is based on figure 8c of 4,331,836, which is the
+ TMS5100/TMC0280 patent, but the same information applies to the TMS52xx
+ as well.
+
+OLDP is a status flag which controls whether unvoiced or voiced excitation is
+ being generated. It is latched from "P=0" at IP=7 PC=12 T=16.
+ (This means that, during normal operation, between IP=7 PC=12 T16 and
+ IP=0 PC=1 T17, OLDP and P=0 are the same)
+"P=0" is a status flag which is set if the index value for pitch for the new
+ frame being parsed (which will become the new target frame) is zero.
+ It is used for determining whether interpolation of the next frame is
+ inhibited or not. It is updated at IP=0 PC=1 T17. See next section.
+OLDE is a status flag which is only used for determining whether
+ interpolation is inhibited or not.
+ It is latched from "E=0" at IP=7 PC=12 T=16.
+ (This means that, during normal operation, between IP=7 PC=12 T16 and
+ IP=0 PC=0 T17, OLDE and E=0 are the same)
+"E=0" is a status flag which is set if the index value for energy for the new
+ frame being parsed (which will become the new target frame) is zero.
+ It is used for determining whether interpolation of the next frame is
+ inhibited or not. It is updated at IP=0 PC=0 T17. See next section.
+
+Interpolation is inhibited (i.e. interpolation at IP frames will not happen
+ except for IP=0) under the following circumstances:
+ "P=0" != "OLDP" ("P=0" = 1, and OLDP = 0; OR "P=0" = 0, and OLDP = 1)
+ This means the new frame is unvoiced and the old one was voiced, or vice
+ versa.
+* TODO the 5100 and 5200 patents are inconsistent about the above. Trace the decaps!
+ "OLDE" = 1 and "E=0" = 0
+ This means the new frame is not silent, and the old frame was silent.
+
+
+
+****Documentation of chip commands:***
+ x0x0xbcc : on 5200/5220: NOP (does nothing); on 5220C and CD2501ECD: Select frame length by cc, and b selects whether every frame is preceded by 2 bits to select the frame length (instead of using the value set by cc); the default (and after a reset command) is as if '0x00' was written, i.e. for frame length (200 samples) and 0 for whether the preceding 2 bits are enabled (off)
+
+ x001xxxx: READ BYTE (RDBY) Sends eight read bit commands (M0 high M1 low) to VSM and reads the resulting bits serially into a temporary register, which becomes readable as the next byte read from the tms52xx once ready goes active. Note the bit order of the byte read from the TMS52xx is BACKWARDS as compared to the actual data order as in the rom on the VSM chips; the read byte command of the tms5100 reads the bits in the 'correct' order. This was IMHO a rather silly design decision of TI. (I (LN) asked Larry Brantingham about this but he wasn't involved with the TMS52xx chips, just the 5100); There's ASCII data in the TI 99/4 speech module VSMs which has the bit order reversed on purpose because of this!
+ TALK STATUS must be CLEAR for this command to work; otherwise it is treated as a NOP.
+
+ x011xxxx: READ AND BRANCH (RB) Sends a read and branch command (M0 high, M1 high) to force VSM to set its data pointer to whatever the data is at its current pointer location is)
+ TALK STATUS must be CLEAR for this command to work; otherwise it is treated as a NOP.
+
+ x100aaaa: LOAD ADDRESS (LA) Send a load address command (M0 low M1 high) to VSM with the 4 'a' bits; Note you need to send four or five of these in sequence to actually specify an address to the vsm.
+ TALK STATUS must be CLEAR for this command to work; otherwise it is treated as a NOP.
+
+ x101xxxx: SPEAK (SPK) Begins speaking, pulling spech data from the current address pointer location of the VSM modules.
+
+ x110xxxx: SPEAK EXTERNAL (SPKEXT) Clears the FIFO using SPKEE line, then sets TALKD (TALKST remains zero) until 8 bytes have been written to the FIFO, at which point it begins speaking, pulling data from the 16 byte fifo.
+ The patent implies TALK STATUS must be CLEAR for this command to work; otherwise it is treated as a NOP, but the decap shows that this is not true, and is an error on the patent diagram.
+
+ x111xxxx: RESET (RST) Resets the speech synthesis core immediately, and clears the FIFO.
+
+
+ Other chip differences:
+ The 5220C (and CD2501ECD maybe?) are quieter due to a better dac arrangement on die which allows less crossover between bits, based on the decap differences.
+
+
+***MAME Driver specific notes:***
+
+ Victory's initial audio selftest is pretty brutal to the FIFO: it sends a
+ sequence of bytes to the FIFO and checks the status bits after each one; if
+ even one bit is in the wrong state (i.e. speech starts one byte too early or
+ late), the test fails!
+ The sample in Victory 'Shields up!' after you activate shields, the 'up' part
+ of the sample is missing the STOP frame at the end of it; this causes the
+ speech core to run out of bits to parse from the FIFO, cutting the sample off
+ by one frame. This appears to be an original game code bug.
+
+Progress list for drivers using old vs new interface:
+starwars: uses new interface (couriersud)
+gauntlet: uses new interface (couriersud)
+atarisy1: uses new interface (Lord Nightmare)
+atarisy2: uses new interface (Lord Nightmare)
+atarijsa: uses new interface (Lord Nightmare)
+firefox: uses new interface (couriersud)
+mhavoc: uses old interface, and is in the machine file instead of the driver.
+monymony/jackrabt(zaccaria.c): uses new interface (couriersud)
+victory(audio/exidy.c): uses new interface (couriersud)
+looping: uses old interface
+portraits: uses *NO* interface; the i/o cpu hasn't been hooked to anything!
+dotron and midwayfb(mcr.c): uses old interface
+
+
+As for which games used which chips:
+
+TMS5200 AKA TMC0285 AKA CD2501E: (1980 to 1983)
+ Arcade: Zaccaria's 'money money' and 'jack rabbit'; Bally/Midway's
+'Discs of Tron' (all environmental cabs and a few upright cabs; the code
+exists on all versions for the speech though, and upright cabs can be
+upgraded to add it by hacking on a 'Squawk & Talk' pinball speech board
+(which is also TMS5200 based) with a few modded components)
+ Pinball: All Bally/Midway machines which uses the 'Squawk & Talk' board.
+ Home computer: TI 99/4 PHP1500 Speech module (along with two VSM
+serial chips); Street Electronics Corp.'s Apple II 'Echo 2' Speech
+synthesizer (early cards only)
+
+CD2501ECD: (1983)
+ Home computer: TI 99/8 (prototypes only)
+
+TMS5220: (mostly on things made between 1981 and 1984-1985)
+ Arcade: Bally/Midway's 'NFL Football'; Atari's 'Star Wars',
+'Firefox', 'Return of the Jedi', 'Road Runner', 'The Empire Strikes
+Back' (all verified with schematics); Venture Line's 'Looping' and 'Sky
+Bumper' (need verify for both); Olympia's 'Portraits' (need verify);
+Exidy's 'Victory' and 'Victor Banana' (need verify for both)
+ Pinball: Several (don't know names offhand, have not checked schematics; likely Zaccaria's 'Farfalla')
+ Home computer: Street Electronics Corp.'s Apple II 'Echo 2' Speech
+synthesizer (later cards only); Texas Instruments' 'Speak and Learn'
+scanner wand unit.
+
+TMS5220C AKA TSP5220C: (on stuff made from 1984 to 1992 or so)
+ Arcade: Atari's 'Indiana Jones and the Temple of Doom', '720',
+'Gauntlet', 'Gauntlet II', 'A.P.B.', 'Paperboy', 'RoadBlasters',
+'Vindicators Pt II'(verify?), and 'Escape from the Planet of the Robot
+Monsters' (all verified except for vindicators pt 2)
+ Pinball: Several (less common than the tms5220? (not sure about
+this), mostly on later pinballs with LPC speech)
+ Home computer: Street Electronics Corp.'s 'ECHO' parallel/hobbyist
+module (6511 based), IBM PS/2 Speech adapter (parallel port connection
+device), PES Speech adapter (serial port connection)
+
+Street electronics had a later 1989-era ECHO appleII card which is TSP50c0x/1x
+MCU based speech and not tms5xxx based (though it is likely emulating the tms5220
+in MCU code). Look for a 16-pin chip at U6 labeled "ECHO-3 SN".
+
+***********************************************************************************************/
+
+#include "emu.h"
+#include "tms5220.h"
+
+static INT16 clip_analog(INT16 cliptemp);
+
+/* *****optional defines***** */
+
+/* Hacky improvements which don't match patent: */
+/* Interpolation shift logic:
+ * One of the following two lines should be used, and the other commented
+ * The second line is more accurate mathematically but not accurate to the patent
+ */
+#define INTERP_SHIFT >> m_coeff->interp_coeff[m_IP]
+//define INTERP_SHIFT / (1<<m_coeff->interp_coeff[m_IP])
+
+/* Other hacks */
+/* HACK: if defined, outputs the low 4 bits of the lattice filter to the i/o
+ * or clip logic, even though the real hardware doesn't do this, partially verified by decap */
+#undef ALLOW_4_LSB
+
+/* forces m_TALK active instantly whenever m_SPEN would be activated, causing speech delay to be reduced by up to one frame time */
+/* for some reason, this hack makes victory behave better, though it does not match the patent */
+#define FAST_START_HACK 1
+
+
+/* *****configuration of chip connection stuff***** */
+/* must be defined; if 0, output the waveform as if it was tapped on the speaker pin as usual, if 1, output the waveform as if it was tapped on the i/o pin (volume is much lower in the latter case) */
+#define FORCE_DIGITAL 0
+
+/* must be defined; if 1, normal speech (one A cycle, one B cycle per interpolation step); if 0; speak as if SPKSLOW was used (two A cycles, one B cycle per interpolation step) */
+#define FORCE_SUBC_RELOAD 1
+
+
+/* *****debugging defines***** */
+#undef VERBOSE
+// above is general, somewhat obsolete, catch all for debugs which don't fit elsewhere
+#undef DEBUG_DUMP_INPUT_DATA
+// above dumps the data input to the tms52xx to stdout, useful for making logged data dumps for real hardware tests
+#undef DEBUG_FIFO
+// above debugs fifo stuff: writes, reads and flag updates
+#undef DEBUG_PARSE_FRAME_DUMP
+// above dumps each frame to stderr: be sure to select one of the options below if you define it!
+#undef DEBUG_PARSE_FRAME_DUMP_BIN
+// dumps each speech frame as binary
+#undef DEBUG_PARSE_FRAME_DUMP_HEX
+// dumps each speech frame as hex
+#undef DEBUG_FRAME_ERRORS
+// above dumps info if a frame ran out of data
+#undef DEBUG_COMMAND_DUMP
+// above dumps all non-speech-data command writes
+#undef DEBUG_PIN_READS
+// above spams the errorlog with i/o ready messages whenever the ready or irq pin is read
+#undef DEBUG_GENERATION
+// above dumps debug information related to the sample generation loop, i.e. whether interpolation is inhibited or not, and what the current and target values for each frame are.
+#undef DEBUG_GENERATION_VERBOSE
+// above dumps MUCH MORE debug information related to the sample generation loop, namely the excitation, energy, pitch, k*, and output values for EVERY SINGLE SAMPLE during a frame.
+#undef DEBUG_LATTICE
+// above dumps the lattice filter state data each sample.
+#undef DEBUG_CLIP
+// above dumps info to stderr whenever the analog clip hardware is (or would be) clipping the signal.
+#undef DEBUG_IO_READY
+// above debugs the io ready callback
+#undef DEBUG_RS_WS
+// above debugs the tms5220_data_r and data_w access methods which actually respect rs and ws
+
+#define MAX_SAMPLE_CHUNK 512
+
+/* Variants */
+
+#define TMS5220_IS_5220C (4)
+#define TMS5220_IS_5200 (5)
+#define TMS5220_IS_5220 (6)
+#define TMS5220_IS_CD2501ECD (7)
+
+#define TMS5220_IS_CD2501E TMS5220_IS_5200
+
+#define TMS5220_HAS_RATE_CONTROL ((m_variant == TMS5220_IS_5220C) || (m_variant == TMS5220_IS_CD2501ECD))
+#define TMS5220_IS_52xx ((m_variant == TMS5220_IS_5220C) || (m_variant == TMS5220_IS_5200) || (m_variant == TMS5220_IS_5220) || (m_variant == TMS5220_IS_CD2501ECD))
+
+static const UINT8 reload_table[4] = { 0, 2, 4, 6 }; //sample count reload for 5220c and cd2501ecd only; 5200 and 5220 always reload with 0; keep in mind this is loaded on IP=0 PC=12 subcycle=1 so it immediately will increment after one sample, effectively being 1,3,5,7 as in the comments above.
+
+// Pull in the ROM tables
+#include "tms5110r.inc"
+
+
+void tms5220_device::set_variant(int variant)
+{
+ switch (variant)
+ {
+ case TMS5220_IS_5200:
+ case TMS5220_IS_CD2501ECD:
+ m_coeff = &T0285_2501E_coeff;
+ break;
+ case TMS5220_IS_5220C:
+ case TMS5220_IS_5220:
+ m_coeff = &tms5220_coeff;
+ break;
+ default:
+ fatalerror("Unknown variant in tms5220_set_variant\n");
+ }
+ m_variant = variant;
+}
+
+
+void tms5220_device::register_for_save_states()
+{
+ save_item(NAME(m_variant));
+
+ save_item(NAME(m_fifo));
+ save_item(NAME(m_fifo_head));
+ save_item(NAME(m_fifo_tail));
+ save_item(NAME(m_fifo_count));
+ save_item(NAME(m_fifo_bits_taken));
+
+ save_item(NAME(m_previous_TALK_STATUS));
+ save_item(NAME(m_SPEN));
+ save_item(NAME(m_DDIS));
+ save_item(NAME(m_TALK));
+ save_item(NAME(m_TALKD));
+ save_item(NAME(m_buffer_low));
+ save_item(NAME(m_buffer_empty));
+ save_item(NAME(m_irq_pin));
+ save_item(NAME(m_ready_pin));
+
+ save_item(NAME(m_OLDE));
+ save_item(NAME(m_OLDP));
+
+ save_item(NAME(m_new_frame_energy_idx));
+ save_item(NAME(m_new_frame_pitch_idx));
+ save_item(NAME(m_new_frame_k_idx));
+#ifdef PERFECT_INTERPOLATION_HACK
+ save_item(NAME(m_old_frame_energy_idx));
+ save_item(NAME(m_old_frame_pitch_idx));
+ save_item(NAME(m_old_frame_k_idx));
+#endif
+ save_item(NAME(m_current_energy));
+ save_item(NAME(m_current_pitch));
+ save_item(NAME(m_current_k));
+
+ save_item(NAME(m_previous_energy));
+
+ save_item(NAME(m_subcycle));
+ save_item(NAME(m_subc_reload));
+ save_item(NAME(m_PC));
+ save_item(NAME(m_IP));
+ save_item(NAME(m_inhibit));
+ save_item(NAME(m_uv_zpar));
+ save_item(NAME(m_zpar));
+ save_item(NAME(m_pitch_zero));
+ save_item(NAME(m_c_variant_rate));
+ save_item(NAME(m_pitch_count));
+
+ save_item(NAME(m_u));
+ save_item(NAME(m_x));
+
+ save_item(NAME(m_RNG));
+ save_item(NAME(m_excitation_data));
+
+ save_item(NAME(m_schedule_dummy_read));
+ save_item(NAME(m_data_register));
+ save_item(NAME(m_RDB_flag));
+ save_item(NAME(m_digital_select));
+
+ save_item(NAME(m_io_ready));
+
+ save_item(NAME(m_true_timing));
+
+ save_item(NAME(m_rs_ws));
+ save_item(NAME(m_read_latch));
+ save_item(NAME(m_write_latch));
+}
+
+
+/**********************************************************************************************
+
+ printbits helper function: takes a long int input and prints the resulting bits to stderr
+
+***********************************************************************************************/
+
+#ifdef DEBUG_PARSE_FRAME_DUMP_BIN
+static void printbits(long data, int num)
+{
+ int i;
+ for (i=(num-1); i>=0; i--)
+ fprintf(stderr,"%0ld", (data>>i)&1);
+}
+#endif
+#ifdef DEBUG_PARSE_FRAME_DUMP_HEX
+static void printbits(long data, int num)
+{
+ switch((num-1)&0xFC)
+ {
+ case 0:
+ fprintf(stderr,"%0lx", data);
+ break;
+ case 4:
+ fprintf(stderr,"%02lx", data);
+ break;
+ case 8:
+ fprintf(stderr,"%03lx", data);
+ break;
+ case 12:
+ fprintf(stderr,"%04lx", data);
+ break;
+ default:
+ fprintf(stderr,"%04lx", data);
+ break;
+ }
+}
+#endif
+
+/**********************************************************************************************
+
+ tms5220_data_write -- handle a write to the TMS5220
+
+***********************************************************************************************/
+
+void tms5220_device::data_write(int data)
+{
+ int old_buffer_low = m_buffer_low;
+#ifdef DEBUG_DUMP_INPUT_DATA
+ fprintf(stdout, "%c",data);
+#endif
+ if (m_DDIS) // If we're in speak external mode
+ {
+ // add this byte to the FIFO
+ if (m_fifo_count < FIFO_SIZE)
+ {
+ m_fifo[m_fifo_tail] = data;
+ m_fifo_tail = (m_fifo_tail + 1) % FIFO_SIZE;
+ m_fifo_count++;
+#ifdef DEBUG_FIFO
+ fprintf(stderr,"data_write: Added byte to FIFO (current count=%2d)\n", m_fifo_count);
+#endif
+ update_fifo_status_and_ints();
+ // if we just unset buffer low with that last write, and SPEN *was* zero (see circuit 251, sheet 12)
+ if ((m_SPEN == 0) && ((old_buffer_low == 1) && (m_buffer_low == 0))) // MUST HAVE EDGE DETECT
+ {
+ int i;
+#ifdef DEBUG_FIFO
+ fprintf(stderr,"data_write triggered SPEN to go active!\n");
+#endif
+ // ...then we now have enough bytes to start talking; set zpar and clear out the new frame parameters (it will become old frame just before the first call to parse_frame() )
+ m_zpar = 1;
+ m_uv_zpar = 1; // zero k4-k10 as well
+ m_OLDE = 1; // 'silence/zpar' frames are zero energy
+ m_OLDP = 1; // 'silence/zpar' frames are zero pitch
+#ifdef PERFECT_INTERPOLATION_HACK
+ m_old_zpar = 1; // zero all the old parameters
+ m_old_uv_zpar = 1; // zero old k4-k10 as well
+#endif
+ m_SPEN = 1;
+#ifdef FAST_START_HACK
+ m_TALK = 1;
+#endif
+ m_new_frame_energy_idx = 0;
+ m_new_frame_pitch_idx = 0;
+ for (i = 0; i < 4; i++)
+ m_new_frame_k_idx[i] = 0;
+ for (i = 4; i < 7; i++)
+ m_new_frame_k_idx[i] = 0xF;
+ for (i = 7; i < m_coeff->num_k; i++)
+ m_new_frame_k_idx[i] = 0x7;
+
+ }
+ }
+ else
+ {
+#ifdef DEBUG_FIFO
+ fprintf(stderr,"data_write: Ran out of room in the tms52xx FIFO! this should never happen!\n");
+ // at this point, /READY should remain HIGH/inactive until the fifo has at least one byte open in it.
+#endif
+ }
+
+
+ }
+ else //(! m_DDIS)
+ // R Nabet : we parse commands at once. It is necessary for such commands as read.
+ process_command(data);
+}
+
+/**********************************************************************************************
+
+ update_fifo_status_and_ints -- check to see if the various flags should be on or off
+ Description of flags, and their position in the status register:
+ From the data sheet:
+ bit D0(bit 7) = TS - Talk Status is active (high) when the VSP is processing speech data.
+ Talk Status goes active at the initiation of a Speak command or after nine
+ bytes of data are loaded into the FIFO following a Speak External command. It
+ goes inactive (low) when the stop code (Energy=1111) is processed, or
+ immediately by a buffer empty condition or a reset command.
+ bit D1(bit 6) = BL - Buffer Low is active (high) when the FIFO buffer is more than half empty.
+ Buffer Low is set when the "Last-In" byte is shifted down past the half-full
+ boundary of the stack. Buffer Low is cleared when data is loaded to the stack
+ so that the "Last-In" byte lies above the half-full boundary and becomes the
+ eighth data byte of the stack.
+ bit D2(bit 5) = BE - Buffer Empty is active (high) when the FIFO buffer has run out of data
+ while executing a Speak External command. Buffer Empty is set when the last bit
+ of the "Last-In" byte is shifted out to the Synthesis Section. This causes
+ Talk Status to be cleared. Speech is terminated at some abnormal point and the
+ Speak External command execution is terminated.
+
+***********************************************************************************************/
+
+void tms5220_device::update_fifo_status_and_ints()
+{
+ /* update 52xx fifo flags and set ints if needed */
+ if (!TMS5220_IS_52xx) return; // bail out if not a 52xx chip
+ update_ready_state();
+
+ /* BL is set if neither byte 9 nor 8 of the fifo are in use; this
+ translates to having fifo_count (which ranges from 0 bytes in use to 16
+ bytes used) being less than or equal to 8. Victory/Victorba depends on this. */
+ if (m_fifo_count <= 8)
+ {
+ // generate an interrupt if necessary; if /BL was inactive and is now active, set int.
+ if (!m_buffer_low)
+ set_interrupt_state(1);
+ m_buffer_low = 1;
+ }
+ else
+ m_buffer_low = 0;
+
+ /* BE is set if neither byte 15 nor 14 of the fifo are in use; this
+ translates to having fifo_count equal to exactly 0
+ */
+ if (m_fifo_count == 0)
+ {
+ // generate an interrupt if necessary; if /BE was inactive and is now active, set int.
+ if (!m_buffer_empty)
+ set_interrupt_state(1);
+ m_buffer_empty = 1;
+ m_TALK = m_SPEN = 0; // /BE being active clears the TALK(TCON) status which in turn clears SPEN
+ }
+ else
+ m_buffer_empty = 0;
+
+ // generate an interrupt if /TS was active, and is now inactive.
+ // also, in this case, regardless if DDIS was set, unset it.
+ if (m_previous_TALK_STATUS == 1 && (TALK_STATUS == 0))
+ {
+#ifdef VERBOSE
+ fprintf(stderr,"Talk status WAS 1, is now 0, unsetting DDIS and firing an interrupt!\n");
+#endif
+ set_interrupt_state(1);
+ m_DDIS = 0;
+ }
+ m_previous_TALK_STATUS = TALK_STATUS;
+
+}
+
+/**********************************************************************************************
+
+ extract_bits -- extract a specific number of bits from the current input stream (FIFO or VSM)
+
+***********************************************************************************************/
+
+int tms5220_device::extract_bits(int count)
+{
+ int val = 0;
+
+ if (m_DDIS)
+ {
+ // extract from FIFO
+ while (count--)
+ {
+ val = (val << 1) | ((m_fifo[m_fifo_head] >> m_fifo_bits_taken) & 1);
+ m_fifo_bits_taken++;
+ if (m_fifo_bits_taken >= 8)
+ {
+ m_fifo_count--;
+ m_fifo[m_fifo_head] = 0; // zero the newly depleted fifo head byte
+ m_fifo_head = (m_fifo_head + 1) % FIFO_SIZE;
+ m_fifo_bits_taken = 0;
+ update_fifo_status_and_ints();
+ }
+ }
+ }
+ else
+ {
+ // extract from VSM (speech ROM)
+ if (m_speechrom)
+ val = m_speechrom->read(count);
+ }
+ return val;
+}
+
+/**********************************************************************************************
+
+ tms5220_status_read -- read status or data from the TMS5220
+
+***********************************************************************************************/
+
+int tms5220_device::status_read()
+{
+ if (m_RDB_flag)
+ { /* if last command was read, return data register */
+ m_RDB_flag = FALSE;
+ return(m_data_register);
+ }
+ else
+ { /* read status */
+ /* clear the interrupt pin on status read */
+ set_interrupt_state(0);
+#ifdef DEBUG_PIN_READS
+ fprintf(stderr,"Status read: TS=%d BL=%d BE=%d\n", TALK_STATUS, m_buffer_low, m_buffer_empty);
+#endif
+ return (TALK_STATUS << 7) | (m_buffer_low << 6) | (m_buffer_empty << 5);
+ }
+}
+
+
+/**********************************************************************************************
+
+ tms5220_ready_read -- returns the ready state of the TMS5220
+
+***********************************************************************************************/
+
+int tms5220_device::ready_read()
+{
+#ifdef DEBUG_PIN_READS
+ fprintf(stderr,"ready_read: ready pin read, io_ready is %d, fifo count is %d, DDIS(speak external) is %d\n", m_io_ready, m_fifo_count, m_DDIS);
+#endif
+ return ((m_fifo_count < FIFO_SIZE)||(!m_DDIS)) && m_io_ready;
+}
+
+
+/**********************************************************************************************
+
+ tms5220_cycles_to_ready -- returns the number of cycles until ready is asserted
+ NOTE: this function is deprecated and is known to be VERY inaccurate.
+ Use at your own peril!
+
+***********************************************************************************************/
+
+int tms5220_device::cycles_to_ready()
+{
+ int answer;
+
+
+ if (ready_read())
+ answer = 0;
+ else
+ {
+ int val;
+ int samples_per_frame = m_subc_reload?200:304; // either (13 A cycles + 12 B cycles) * 8 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 8 interps for SPKSLOW
+ int current_sample = ((m_PC*(3-m_subc_reload))+((m_subc_reload?38:25)*m_IP));
+ answer = samples_per_frame-current_sample+8;
+
+ // total number of bits available in current byte is (8 - m_fifo_bits_taken)
+ // if more than 4 are available, we need to check the energy
+ if (m_fifo_bits_taken < 4)
+ {
+ // read energy
+ val = (m_fifo[m_fifo_head] >> m_fifo_bits_taken) & 0xf;
+ if (val == 0)
+ /* 0 -> silence frame: we will only read 4 bits, and we will
+ * therefore need to read another frame before the FIFO is not
+ * full any more */
+ answer += m_subc_reload?200:304;
+ /* 15 -> stop frame, we will only read 4 bits, but the FIFO will
+ * we cleared; otherwise, we need to parse the repeat flag (1 bit)
+ * and the pitch (6 bits), so everything will be OK. */
+ }
+ }
+
+ return answer;
+}
+
+
+/**********************************************************************************************
+
+ tms5220_int_read -- returns the interrupt state of the TMS5220
+
+***********************************************************************************************/
+
+int tms5220_device::int_read()
+{
+#ifdef DEBUG_PIN_READS
+ fprintf(stderr,"int_read: irq pin read, state is %d\n", m_irq_pin);
+#endif
+ return m_irq_pin;
+}
+
+
+/**********************************************************************************************
+
+ tms5220_process -- fill the buffer with a specific number of samples
+
+***********************************************************************************************/
+
+void tms5220_device::process(INT16 *buffer, unsigned int size)
+{
+ int buf_count=0;
+ int i, bitout;
+ INT32 this_sample;
+
+#ifdef VERBOSE
+ fprintf(stderr,"process called with size of %d; IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", size, m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
+#endif
+
+ /* loop until the buffer is full or we've stopped speaking */
+ while (size > 0)
+ {
+ if(m_TALKD) // speaking
+ {
+ /* if we're ready for a new frame to be applied, i.e. when IP=0, PC=12, Sub=1
+ * (In reality, the frame was really loaded incrementally during the entire IP=0
+ * PC=x time period, but it doesn't affect anything until IP=0 PC=12 happens)
+ */
+ if ((m_IP == 0) && (m_PC == 12) && (m_subcycle == 1))
+ {
+ // HACK for regression testing, be sure to comment out before release!
+ //m_RNG = 0x1234;
+ // end HACK
+
+ /* appropriately override the interp count if needed; this will be incremented after the frame parse! */
+ m_IP = reload_table[m_c_variant_rate&0x3];
+
+#ifdef PERFECT_INTERPOLATION_HACK
+ /* remember previous frame energy, pitch, and coefficients */
+ m_old_frame_energy_idx = m_new_frame_energy_idx;
+ m_old_frame_pitch_idx = m_new_frame_pitch_idx;
+ for (i = 0; i < m_coeff->num_k; i++)
+ m_old_frame_k_idx[i] = m_new_frame_k_idx[i];
+#endif
+
+ /* Parse a new frame into the new_target_energy, new_target_pitch and new_target_k[] */
+ parse_frame();
+
+ // if the new frame is unvoiced (or silenced via ZPAR), be sure to zero out the k5-k10 parameters
+ // NOTE: this is probably the bug the tms5100/tmc0280 has, pre-rev D, I think.
+ // GUESS: Pre-rev D versions start zeroing k5-k10 immediately upon new frame load regardless of interpolation inhibit
+ // I.e. ZPAR = /TALKD || (PC>5&&P=0)
+ // GUESS: D and later versions only start or stop zeroing k5-k10 at the IP7->IP0 transition AFTER the frame
+ // I.e. ZPAR = /TALKD || (PC>5&&OLDP)
+#ifdef PERFECT_INTERPOLATION_HACK
+ m_old_uv_zpar = m_uv_zpar;
+ m_old_zpar = m_zpar; // unset old zpar on new frame
+#endif
+ m_zpar = 0;
+ //m_uv_zpar = (OLD_FRAME_UNVOICED_FLAG||m_zpar); // GUESS: fixed version in tmc0280d/tms5100a/cd280x/tms5110
+ m_uv_zpar = (NEW_FRAME_UNVOICED_FLAG||m_zpar); // GUESS: buggy version in tmc0280/tms5100
+
+ /* if the new frame is a stop frame, unset both TALK and SPEN (via TCON). TALKD remains active while the energy is ramping to 0. */
+ if (NEW_FRAME_STOP_FLAG == 1)
+ {
+ m_TALK = m_SPEN = 0;
+ }
+
+ /* in all cases where interpolation would be inhibited, set the inhibit flag; otherwise clear it.
+ Interpolation inhibit cases:
+ * Old frame was voiced, new is unvoiced
+ * Old frame was silence/zero energy, new has nonzero energy
+ * Old frame was unvoiced, new is voiced
+ * Old frame was unvoiced, new frame is silence/zero energy (unique to tms52xx)
+ */
+ if ( ((OLD_FRAME_UNVOICED_FLAG == 0) && (NEW_FRAME_UNVOICED_FLAG == 1))
+ || ((OLD_FRAME_UNVOICED_FLAG == 1) && (NEW_FRAME_UNVOICED_FLAG == 0))
+ || ((OLD_FRAME_SILENCE_FLAG == 1) && (NEW_FRAME_SILENCE_FLAG == 0))
+ || ((OLD_FRAME_UNVOICED_FLAG == 1) && (NEW_FRAME_SILENCE_FLAG == 1)) )
+ m_inhibit = 1;
+ else // normal frame, normal interpolation
+ m_inhibit = 0;
+
+#ifdef DEBUG_GENERATION
+ /* Debug info for current parsed frame */
+ fprintf(stderr, "OLDE: %d; OLDP: %d; ", m_OLDE, m_OLDP);
+ fprintf(stderr,"Processing new frame: ");
+ if (m_inhibit == 0)
+ fprintf(stderr, "Normal Frame\n");
+ else
+ fprintf(stderr,"Interpolation Inhibited\n");
+ fprintf(stderr,"*** current Energy, Pitch and Ks = %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",m_current_energy, m_current_pitch, m_current_k[0], m_current_k[1], m_current_k[2], m_current_k[3], m_current_k[4], m_current_k[5], m_current_k[6], m_current_k[7], m_current_k[8], m_current_k[9]);
+ fprintf(stderr,"*** target Energy(idx), Pitch, and Ks = %04d(%x),%04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",
+ (m_coeff->energytable[m_new_frame_energy_idx] * (1-m_zpar)),
+ m_new_frame_energy_idx,
+ (m_coeff->pitchtable[m_new_frame_pitch_idx] * (1-m_zpar)),
+ (m_coeff->ktable[0][m_new_frame_k_idx[0]] * (1-m_zpar)),
+ (m_coeff->ktable[1][m_new_frame_k_idx[1]] * (1-m_zpar)),
+ (m_coeff->ktable[2][m_new_frame_k_idx[2]] * (1-m_zpar)),
+ (m_coeff->ktable[3][m_new_frame_k_idx[3]] * (1-m_zpar)),
+ (m_coeff->ktable[4][m_new_frame_k_idx[4]] * (1-m_uv_zpar)),
+ (m_coeff->ktable[5][m_new_frame_k_idx[5]] * (1-m_uv_zpar)),
+ (m_coeff->ktable[6][m_new_frame_k_idx[6]] * (1-m_uv_zpar)),
+ (m_coeff->ktable[7][m_new_frame_k_idx[7]] * (1-m_uv_zpar)),
+ (m_coeff->ktable[8][m_new_frame_k_idx[8]] * (1-m_uv_zpar)),
+ (m_coeff->ktable[9][m_new_frame_k_idx[9]] * (1-m_uv_zpar)) );
+#endif
+
+ }
+ else // Not a new frame, just interpolate the existing frame.
+ {
+ int inhibit_state = ((m_inhibit==1)&&(m_IP != 0)); // disable inhibit when reaching the last interp period, but don't overwrite the m_inhibit value
+#ifdef PERFECT_INTERPOLATION_HACK
+ int samples_per_frame = m_subc_reload?175:266; // either (13 A cycles + 12 B cycles) * 7 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 7 interps for SPKSLOW
+ //int samples_per_frame = m_subc_reload?200:304; // either (13 A cycles + 12 B cycles) * 8 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 8 interps for SPKSLOW
+ int current_sample = (m_subcycle - m_subc_reload)+(m_PC*(3-m_subc_reload))+((m_subc_reload?25:38)*((m_IP-1)&7));
+ //fprintf(stderr, "CS: %03d", current_sample);
+ // reset the current energy, pitch, etc to what it was at frame start
+ m_current_energy = (m_coeff->energytable[m_old_frame_energy_idx] * (1-m_old_zpar));
+ m_current_pitch = (m_coeff->pitchtable[m_old_frame_pitch_idx] * (1-m_old_zpar));
+ for (i = 0; i < m_coeff->num_k; i++)
+ m_current_k[i] = (m_coeff->ktable[i][m_old_frame_k_idx[i]] * (1-((i<4)?m_old_zpar:m_old_uv_zpar)));
+ // now adjust each value to be exactly correct for each of the samples per frame
+ if (m_IP != 0) // if we're still interpolating...
+ {
+ m_current_energy = (m_current_energy + (((m_coeff->energytable[m_new_frame_energy_idx] - m_current_energy)*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-m_zpar);
+ m_current_pitch = (m_current_pitch + (((m_coeff->pitchtable[m_new_frame_pitch_idx] - m_current_pitch)*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-m_zpar);
+ for (i = 0; i < m_coeff->num_k; i++)
+ m_current_k[i] = (m_current_k[i] + (((m_coeff->ktable[i][m_new_frame_k_idx[i]] - m_current_k[i])*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-((i<4)?m_zpar:m_uv_zpar));
+ }
+ else // we're done, play this frame for 1/8 frame.
+ {
+ m_current_energy = (m_coeff->energytable[m_new_frame_energy_idx] * (1-m_zpar));
+ m_current_pitch = (m_coeff->pitchtable[m_new_frame_pitch_idx] * (1-m_zpar));
+ for (i = 0; i < m_coeff->num_k; i++)
+ m_current_k[i] = (m_coeff->ktable[i][m_new_frame_k_idx[i]] * (1-((i<4)?m_zpar:m_uv_zpar)));
+ }
+#else
+ //Updates to parameters only happen on subcycle '2' (B cycle) of PCs.
+ if (m_subcycle == 2)
+ {
+ switch(m_PC)
+ {
+ case 0: /* PC = 0, B cycle, write updated energy */
+ m_current_energy = (m_current_energy + (((m_coeff->energytable[m_new_frame_energy_idx] - m_current_energy)*(1-inhibit_state)) INTERP_SHIFT))*(1-m_zpar);
+ break;
+ case 1: /* PC = 1, B cycle, write updated pitch */
+ m_current_pitch = (m_current_pitch + (((m_coeff->pitchtable[m_new_frame_pitch_idx] - m_current_pitch)*(1-inhibit_state)) INTERP_SHIFT))*(1-m_zpar);
+ break;
+ case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11:
+ /* PC = 2 through 11, B cycle, write updated K1 through K10 */
+ m_current_k[m_PC-2] = (m_current_k[m_PC-2] + (((m_coeff->ktable[m_PC-2][m_new_frame_k_idx[m_PC-2]] - m_current_k[m_PC-2])*(1-inhibit_state)) INTERP_SHIFT))*(1-(((m_PC-2)<4)?m_zpar:m_uv_zpar));
+ break;
+ case 12: /* PC = 12 */
+ /* we should NEVER reach this point, PC=12 doesn't have a subcycle 2 */
+ break;
+ }
+ }
+#endif
+ }
+
+ // calculate the output
+ if (OLD_FRAME_UNVOICED_FLAG == 1)
+ {
+ // generate unvoiced samples here
+ if (m_RNG & 1)
+ m_excitation_data = ~0x3F; /* according to the patent it is (either + or -) half of the maximum value in the chirp table, so either 01000000(0x40) or 11000000(0xC0)*/
+ else
+ m_excitation_data = 0x40;
+ }
+ else /* (OLD_FRAME_UNVOICED_FLAG == 0) */
+ {
+ // generate voiced samples here
+ /* US patent 4331836 Figure 14B shows, and logic would hold, that a pitch based chirp
+ * function has a chirp/peak and then a long chain of zeroes.
+ * The last entry of the chirp rom is at address 0b110011 (51d), the 52nd sample,
+ * and if the address reaches that point the ADDRESS incrementer is
+ * disabled, forcing all samples beyond 51d to be == 51d
+ */
+ if (m_pitch_count >= 51)
+ m_excitation_data = (INT8)m_coeff->chirptable[51];
+ else /*m_pitch_count < 51*/
+ m_excitation_data = (INT8)m_coeff->chirptable[m_pitch_count];
+ }
+
+ // Update LFSR *20* times every sample (once per T cycle), like patent shows
+ for (i=0; i<20; i++)
+ {
+ bitout = ((m_RNG >> 12) & 1) ^
+ ((m_RNG >> 3) & 1) ^
+ ((m_RNG >> 2) & 1) ^
+ ((m_RNG >> 0) & 1);
+ m_RNG <<= 1;
+ m_RNG |= bitout;
+ }
+ this_sample = lattice_filter(); /* execute lattice filter */
+#ifdef DEBUG_GENERATION_VERBOSE
+ //fprintf(stderr,"C:%01d; ",m_subcycle);
+ fprintf(stderr,"IP:%01d PC:%02d X:%04d E:%03d P:%03d Pc:%03d ",m_IP, m_PC, m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count);
+ //fprintf(stderr,"X:%04d E:%03d P:%03d Pc:%03d ", m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count);
+ for (i=0; i<10; i++)
+ fprintf(stderr,"K%d:%04d ", i+1, m_current_k[i]);
+ fprintf(stderr,"Out:%06d ", this_sample);
+//#ifdef PERFECT_INTERPOLATION_HACK
+// fprintf(stderr,"%d%d%d%d",m_old_zpar,m_zpar,m_old_uv_zpar,m_uv_zpar);
+//#else
+// fprintf(stderr,"x%dx%d",m_zpar,m_uv_zpar);
+//#endif
+ fprintf(stderr,"\n");
+#endif
+ /* next, force result to 14 bits (since its possible that the addition at the final (k1) stage of the lattice overflowed) */
+ while (this_sample > 16383) this_sample -= 32768;
+ while (this_sample < -16384) this_sample += 32768;
+ if (m_digital_select == 0) // analog SPK pin output is only 8 bits, with clipping
+ buffer[buf_count] = clip_analog(this_sample);
+ else // digital I/O pin output is 12 bits
+ {
+#ifdef ALLOW_4_LSB
+ // input: ssss ssss ssss ssss ssnn nnnn nnnn nnnn
+ // N taps: ^ = 0x2000;
+ // output: ssss ssss ssss ssss snnn nnnn nnnn nnnN
+ buffer[buf_count] = (this_sample<<1)|((this_sample&0x2000)>>13);
+#else
+ this_sample &= ~0xF;
+ // input: ssss ssss ssss ssss ssnn nnnn nnnn 0000
+ // N taps: ^^ ^^^ = 0x3E00;
+ // output: ssss ssss ssss ssss snnn nnnn nnnN NNNN
+ buffer[buf_count] = (this_sample<<1)|((this_sample&0x3E00)>>9);
+#endif
+ }
+ // Update all counts
+
+ m_subcycle++;
+ if ((m_subcycle == 2) && (m_PC == 12)) // RESETF3
+ {
+ /* Circuit 412 in the patent acts a reset, resetting the pitch counter to 0
+ * if INHIBIT was true during the most recent frame transition.
+ * The exact time this occurs is betwen IP=7, PC=12 sub=0, T=t12
+ * and m_IP = 0, PC=0 sub=0, T=t12, a period of exactly 20 cycles,
+ * which overlaps the time OLDE and OLDP are updated at IP=7 PC=12 T17
+ * (and hence INHIBIT itself 2 t-cycles later). We do it here because it is
+ * convenient and should make no difference in output.
+ */
+ if ((m_IP == 7)&&(m_inhibit==1)) m_pitch_zero = 1;
+ if ((m_IP == 0)&&(m_pitch_zero==1)) m_pitch_zero = 0;
+ if (m_IP == 7) // RESETL4
+ {
+ // Latch OLDE and OLDP
+ OLD_FRAME_SILENCE_FLAG = NEW_FRAME_SILENCE_FLAG; // m_OLDE
+ OLD_FRAME_UNVOICED_FLAG = NEW_FRAME_UNVOICED_FLAG; // m_OLDP
+ /* if TALK was clear last frame, halt speech now, since TALKD (latched from TALK on new frame) just went inactive. */
+#ifdef DEBUG_GENERATION
+ fprintf(stderr,"RESETL4, about to update status: IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
+#endif
+#ifdef DEBUG_GENERATION
+ if (m_TALK == 0)
+ fprintf(stderr,"tms5220_process: processing frame: TALKD = 0 caused by stop frame or buffer empty, halting speech.\n");
+#endif
+ m_TALKD = m_TALK; // TALKD is latched from TALK
+ update_fifo_status_and_ints(); // to trigger an interrupt if TALK_STATUS is now inactive
+ m_TALK = m_SPEN; // TALK is latched from SPEN
+#ifdef DEBUG_GENERATION
+ fprintf(stderr,"RESETL4, status updated: IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
+#endif
+ }
+ m_subcycle = m_subc_reload;
+ m_PC = 0;
+ m_IP++;
+ m_IP&=0x7;
+ }
+ else if (m_subcycle == 3)
+ {
+ m_subcycle = m_subc_reload;
+ m_PC++;
+ }
+ m_pitch_count++;
+ if ((m_pitch_count >= m_current_pitch)||(m_pitch_zero == 1)) m_pitch_count = 0;
+ m_pitch_count &= 0x1FF;
+ }
+ else // m_TALKD == 0
+ {
+ m_subcycle++;
+ if ((m_subcycle == 2) && (m_PC == 12)) // RESETF3
+ {
+ if (m_IP == 7) // RESETL4
+ {
+ m_TALKD = m_TALK; // TALKD is latched from TALK
+ m_TALK = m_SPEN; // TALK is latched from SPEN
+ }
+ m_subcycle = m_subc_reload;
+ m_PC = 0;
+ m_IP++;
+ m_IP&=0x7;
+ }
+ else if (m_subcycle == 3)
+ {
+ m_subcycle = m_subc_reload;
+ m_PC++;
+ }
+ buffer[buf_count] = -1; /* should be just -1; actual chip outputs -1 every idle sample; (cf note in data sheet, p 10, table 4) */
+ }
+ buf_count++;
+ size--;
+ }
+}
+
+/**********************************************************************************************
+
+ clip_analog -- clips the 14 bit return value from the lattice filter to its final 10 bit value (-512 to 511), and upshifts/range extends this to 16 bits
+
+***********************************************************************************************/
+
+static INT16 clip_analog(INT16 cliptemp)
+{
+ /* clipping, just like the patent shows:
+ * the top 10 bits of this result are visible on the digital output IO pin.
+ * next, if the top 3 bits of the 14 bit result are all the same, the lowest of those 3 bits plus the next 7 bits are the signed analog output, otherwise the low bits are all forced to match the inverse of the topmost bit, i.e.:
+ * 1x xxxx xxxx xxxx -> 0b10000000
+ * 11 1bcd efgh xxxx -> 0b1bcdefgh
+ * 00 0bcd efgh xxxx -> 0b0bcdefgh
+ * 0x xxxx xxxx xxxx -> 0b01111111
+ */
+#ifdef DEBUG_CLIP
+ if ((cliptemp > 2047) || (cliptemp < -2048)) fprintf(stderr,"clipping cliptemp to range; was %d\n", cliptemp);
+#endif
+ if (cliptemp > 2047) cliptemp = 2047;
+ else if (cliptemp < -2048) cliptemp = -2048;
+ /* at this point the analog output is tapped */
+#ifdef ALLOW_4_LSB
+ // input: ssss snnn nnnn nnnn
+ // N taps: ^^^ ^ = 0x0780
+ // output: snnn nnnn nnnn NNNN
+ return (cliptemp << 4)|((cliptemp&0x780)>>7); // upshift and range adjust
+#else
+ cliptemp &= ~0xF;
+ // input: ssss snnn nnnn 0000
+ // N taps: ^^^ ^^^^ = 0x07F0
+ // P taps: ^ = 0x0400
+ // output: snnn nnnn NNNN NNNP
+ return (cliptemp << 4)|((cliptemp&0x7F0)>>3)|((cliptemp&0x400)>>10); // upshift and range adjust
+#endif
+}
+
+
+/**********************************************************************************************
+
+ matrix_multiply -- does the proper multiply and shift
+ a is the k coefficient and is clamped to 10 bits (9 bits plus a sign)
+ b is the running result and is clamped to 14 bits.
+ output is 14 bits, but note the result LSB bit is always 1.
+ Because the low 4 bits of the result are trimmed off before
+ output, this makes almost no difference in the computation.
+
+**********************************************************************************************/
+static INT32 matrix_multiply(INT32 a, INT32 b)
+{
+ INT32 result;
+ while (a>511) { a-=1024; }
+ while (a<-512) { a+=1024; }
+ while (b>16383) { b-=32768; }
+ while (b<-16384) { b+=32768; }
+ result = ((a*b)>>9)|1;//&(~1);
+#ifdef VERBOSE
+ if (result>16383) fprintf(stderr,"matrix multiplier overflowed! a: %x, b: %x, result: %x", a, b, result);
+ if (result<-16384) fprintf(stderr,"matrix multiplier underflowed! a: %x, b: %x, result: %x", a, b, result);
+#endif
+ return result;
+}
+
+/**********************************************************************************************
+
+ lattice_filter -- executes one 'full run' of the lattice filter on a specific byte of
+ excitation data, and specific values of all the current k constants, and returns the
+ resulting sample.
+
+***********************************************************************************************/
+
+INT32 tms5220_device::lattice_filter()
+{
+ // Lattice filter here
+ // Aug/05/07: redone as unrolled loop, for clarity - LN
+ /* Originally Copied verbatim from table I in US patent 4,209,804, now updated to be in same order as the actual chip does it, not that it matters.
+ notation equivalencies from table:
+ Yn(i) == m_u[n-1]
+ Kn = m_current_k[n-1]
+ bn = m_x[n-1]
+ */
+ /*
+ int ep = matrix_multiply(m_previous_energy, (m_excitation_data<<6)); //Y(11)
+ m_u[10] = ep;
+ for (int i = 0; i < 10; i++)
+ {
+ int ii = 10-i; // for m = 10, this would be 11 - i, and since i is from 1 to 10, then ii ranges from 10 to 1
+ //int jj = ii+1; // this variable, even on the fortran version, is never used. it probably was intended to be used on the two lines below the next one to save some redundant additions on each.
+ ep = ep - (((m_current_k[ii-1] * m_x[ii-1])>>9)|1); // subtract reflection from lower stage 'top of lattice'
+ m_u[ii-1] = ep;
+ m_x[ii] = m_x[ii-1] + (((m_current_k[ii-1] * ep)>>9)|1); // add reflection from upper stage 'bottom of lattice'
+ }
+ m_x[0] = ep; // feed the last section of the top of the lattice directly to the bottom of the lattice
+ */
+ m_u[10] = matrix_multiply(m_previous_energy, (m_excitation_data<<6)); //Y(11)
+ m_u[9] = m_u[10] - matrix_multiply(m_current_k[9], m_x[9]);
+ m_u[8] = m_u[9] - matrix_multiply(m_current_k[8], m_x[8]);
+ m_u[7] = m_u[8] - matrix_multiply(m_current_k[7], m_x[7]);
+ m_u[6] = m_u[7] - matrix_multiply(m_current_k[6], m_x[6]);
+ m_u[5] = m_u[6] - matrix_multiply(m_current_k[5], m_x[5]);
+ m_u[4] = m_u[5] - matrix_multiply(m_current_k[4], m_x[4]);
+ m_u[3] = m_u[4] - matrix_multiply(m_current_k[3], m_x[3]);
+ m_u[2] = m_u[3] - matrix_multiply(m_current_k[2], m_x[2]);
+ m_u[1] = m_u[2] - matrix_multiply(m_current_k[1], m_x[1]);
+ m_u[0] = m_u[1] - matrix_multiply(m_current_k[0], m_x[0]);
+ m_x[9] = m_x[8] + matrix_multiply(m_current_k[8], m_u[8]);
+ m_x[8] = m_x[7] + matrix_multiply(m_current_k[7], m_u[7]);
+ m_x[7] = m_x[6] + matrix_multiply(m_current_k[6], m_u[6]);
+ m_x[6] = m_x[5] + matrix_multiply(m_current_k[5], m_u[5]);
+ m_x[5] = m_x[4] + matrix_multiply(m_current_k[4], m_u[4]);
+ m_x[4] = m_x[3] + matrix_multiply(m_current_k[3], m_u[3]);
+ m_x[3] = m_x[2] + matrix_multiply(m_current_k[2], m_u[2]);
+ m_x[2] = m_x[1] + matrix_multiply(m_current_k[1], m_u[1]);
+ m_x[1] = m_x[0] + matrix_multiply(m_current_k[0], m_u[0]);
+ m_x[0] = m_u[0];
+ m_previous_energy = m_current_energy;
+#ifdef DEBUG_LATTICE
+ int i;
+ fprintf(stderr,"V:%04d ", m_u[10]);
+ for (i = 9; i >= 0; i--)
+ {
+ fprintf(stderr,"Y%d:%04d ", i+1, m_u[i]);
+ fprintf(stderr,"b%d:%04d ", i+1, m_x[i]);
+ if ((i % 5) == 0) fprintf(stderr,"\n");
+ }
+#endif
+ return m_u[0];
+}
+
+
+/**********************************************************************************************
+
+ process_command -- extract a byte from the FIFO and interpret it as a command
+
+***********************************************************************************************/
+
+void tms5220_device::process_command(unsigned char cmd)
+{
+ int i;
+#ifdef DEBUG_COMMAND_DUMP
+ fprintf(stderr,"process_command called with parameter %02X\n",cmd);
+#endif
+ /* parse the command */
+ switch (cmd & 0x70)
+ {
+ case 0x10 : /* read byte */
+ if (TALK_STATUS == 0) /* TALKST must be clear for RDBY */
+ {
+ if (m_schedule_dummy_read)
+ {
+ m_schedule_dummy_read = FALSE;
+ if (m_speechrom)
+ m_speechrom->read(1);
+ }
+ if (m_speechrom)
+ m_data_register = m_speechrom->read(8); /* read one byte from speech ROM... */
+ m_RDB_flag = TRUE;
+ }
+ break;
+
+ case 0x00: case 0x20: /* set rate (tms5220c and cd2501ecd only), otherwise NOP */
+ if (TMS5220_HAS_RATE_CONTROL)
+ {
+ m_c_variant_rate = cmd&0x0F;
+ }
+ break;
+
+ case 0x30 : /* read and branch */
+ if (TALK_STATUS == 0) /* TALKST must be clear for RB */
+ {
+#ifdef VERBOSE
+ fprintf(stderr,"read and branch command received\n");
+#endif
+ m_RDB_flag = FALSE;
+ if (m_speechrom)
+ m_speechrom->read_and_branch();
+ }
+ break;
+
+ case 0x40 : /* load address */
+ if (TALK_STATUS == 0) /* TALKST must be clear for LA */
+ {
+ /* tms5220 data sheet says that if we load only one 4-bit nibble, it won't work.
+ This code does not care about this. */
+ if (m_speechrom)
+ m_speechrom->load_address(cmd & 0x0f);
+ m_schedule_dummy_read = TRUE;
+ }
+ break;
+
+ case 0x50 : /* speak */
+ if (m_schedule_dummy_read)
+ {
+ m_schedule_dummy_read = FALSE;
+ if (m_speechrom)
+ m_speechrom->read(1);
+ }
+ m_SPEN = 1;
+#ifdef FAST_START_HACK
+ m_TALK = 1;
+#endif
+ m_DDIS = 0;
+ m_zpar = 1; // zero all the parameters
+ m_uv_zpar = 1; // zero k4-k10 as well
+ m_OLDE = 1; // 'silence/zpar' frames are zero energy
+ m_OLDP = 1; // 'silence/zpar' frames are zero pitch
+#ifdef PERFECT_INTERPOLATION_HACK
+ m_old_zpar = 1; // zero all the old parameters
+ m_old_uv_zpar = 1; // zero old k4-k10 as well
+#endif
+ // following is semi-hack but matches idle state observed on chip
+ m_new_frame_energy_idx = 0;
+ m_new_frame_pitch_idx = 0;
+ for (i = 0; i < 4; i++)
+ m_new_frame_k_idx[i] = 0;
+ for (i = 4; i < 7; i++)
+ m_new_frame_k_idx[i] = 0xF;
+ for (i = 7; i < m_coeff->num_k; i++)
+ m_new_frame_k_idx[i] = 0x7;
+ break;
+
+ case 0x60 : /* speak external */
+ // SPKEXT going active activates SPKEE which clears the fifo
+ m_fifo_head = m_fifo_tail = m_fifo_count = m_fifo_bits_taken = 0;
+ // SPEN is enabled when the fifo passes half full (falling edge of BL signal)
+ m_DDIS = 1;
+ m_zpar = 1; // zero all the parameters
+ m_uv_zpar = 1; // zero k4-k10 as well
+ m_OLDE = 1; // 'silence/zpar' frames are zero energy
+ m_OLDP = 1; // 'silence/zpar' frames are zero pitch
+#ifdef PERFECT_INTERPOLATION_HACK
+ m_old_zpar = 1; // zero all the old parameters
+ m_old_uv_zpar = 1; // zero old k4-k10 as well
+#endif
+ // following is semi-hack but matches idle state observed on chip
+ m_new_frame_energy_idx = 0;
+ m_new_frame_pitch_idx = 0;
+ for (i = 0; i < 4; i++)
+ m_new_frame_k_idx[i] = 0;
+ for (i = 4; i < 7; i++)
+ m_new_frame_k_idx[i] = 0xF;
+ for (i = 7; i < m_coeff->num_k; i++)
+ m_new_frame_k_idx[i] = 0x7;
+ m_RDB_flag = FALSE;
+ break;
+
+ case 0x70 : /* reset */
+ if (m_schedule_dummy_read)
+ {
+ m_schedule_dummy_read = FALSE;
+ if (m_speechrom)
+ m_speechrom->read(1);
+ }
+ reset();
+ break;
+ }
+
+ /* update the buffer low state */
+ update_fifo_status_and_ints();
+}
+
+/******************************************************************************************
+
+ parse_frame -- parse a new frame's worth of data; returns 0 if not enough bits in buffer
+
+******************************************************************************************/
+
+void tms5220_device::parse_frame()
+{
+ int i, rep_flag;
+
+ // We actually don't care how many bits are left in the fifo here; the frame subpart will be processed normally, and any bits extracted 'past the end' of the fifo will be read as zeroes; the fifo being emptied will set the /BE latch which will halt speech exactly as if a stop frame had been encountered (instead of whatever partial frame was read); the same exact circuitry is used for both on the real chip, see us patent 4335277 sheet 16, gates 232a (decode stop frame) and 232b (decode /BE plus DDIS (decode disable) which is active during speak external).
+
+ /* if the chip is a tms5220C, and the rate mode is set to that each frame (0x04 bit set)
+ has a 2 bit rate preceding it, grab two bits here and store them as the rate; */
+ if ((TMS5220_HAS_RATE_CONTROL) && (m_c_variant_rate & 0x04))
+ {
+ i = extract_bits(2);
+#ifdef DEBUG_PARSE_FRAME_DUMP
+ printbits(i,2);
+ fprintf(stderr," ");
+#endif
+ m_IP = reload_table[i];
+ }
+ else // non-5220C and 5220C in fixed rate mode
+ m_IP = reload_table[m_c_variant_rate&0x3];
+
+ update_fifo_status_and_ints();
+ if (m_DDIS && m_buffer_empty) goto ranout;
+
+ // attempt to extract the energy index
+ m_new_frame_energy_idx = extract_bits(m_coeff->energy_bits);
+#ifdef DEBUG_PARSE_FRAME_DUMP
+ printbits(m_new_frame_energy_idx,m_coeff->energy_bits);
+ fprintf(stderr," ");
+#endif
+ update_fifo_status_and_ints();
+ if (m_DDIS && m_buffer_empty) goto ranout;
+ // if the energy index is 0 or 15, we're done
+ if ((m_new_frame_energy_idx == 0) || (m_new_frame_energy_idx == 15))
+ return;
+
+
+ // attempt to extract the repeat flag
+ rep_flag = extract_bits(1);
+#ifdef DEBUG_PARSE_FRAME_DUMP
+ printbits(rep_flag, 1);
+ fprintf(stderr," ");
+#endif
+
+ // attempt to extract the pitch
+ m_new_frame_pitch_idx = extract_bits(m_coeff->pitch_bits);
+#ifdef DEBUG_PARSE_FRAME_DUMP
+ printbits(m_new_frame_pitch_idx,m_coeff->pitch_bits);
+ fprintf(stderr," ");
+#endif
+ update_fifo_status_and_ints();
+ if (m_DDIS && m_buffer_empty) goto ranout;
+ // if this is a repeat frame, just do nothing, it will reuse the old coefficients
+ if (rep_flag)
+ return;
+
+ // extract first 4 K coefficients
+ for (i = 0; i < 4; i++)
+ {
+ m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]);
+#ifdef DEBUG_PARSE_FRAME_DUMP
+ printbits(m_new_frame_k_idx[i],m_coeff->kbits[i]);
+ fprintf(stderr," ");
+#endif
+ update_fifo_status_and_ints();
+ if (m_DDIS && m_buffer_empty) goto ranout;
+ }
+
+ // if the pitch index was zero, we only need 4 K's...
+ if (m_new_frame_pitch_idx == 0)
+ {
+ /* and the rest of the coefficients are zeroed, but that's done in the generator code */
+ return;
+ }
+
+ // If we got here, we need the remaining 6 K's
+ for (i = 4; i < m_coeff->num_k; i++)
+ {
+ m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]);
+#ifdef DEBUG_PARSE_FRAME_DUMP
+ printbits(m_new_frame_k_idx[i],m_coeff->kbits[i]);
+ fprintf(stderr," ");
+#endif
+ update_fifo_status_and_ints();
+ if (m_DDIS && m_buffer_empty) goto ranout;
+ }
+#ifdef DEBUG_PARSE_FRAME_DUMP
+ fprintf(stderr,"\n");
+#endif
+#ifdef VERBOSE
+ if (m_DDIS)
+ fprintf(stderr,"Parsed a frame successfully in FIFO - %d bits remaining\n", (m_fifo_count*8)-(m_fifo_bits_taken));
+ else
+ fprintf(stderr,"Parsed a frame successfully in ROM\n");
+#endif
+ return;
+
+ ranout:
+#ifdef DEBUG_FRAME_ERRORS
+ fprintf(stderr,"Ran out of bits on a parse!\n");
+#endif
+ return;
+}
+
+/**********************************************************************************************
+
+ set_interrupt_state -- generate an interrupt
+
+***********************************************************************************************/
+
+void tms5220_device::set_interrupt_state(int state)
+{
+ if (!TMS5220_IS_52xx) return; // bail out if not a 52xx chip, since there's no int pin
+#ifdef DEBUG_PIN_READS
+ fprintf(stderr,"irq pin set to state %d\n", state);
+#endif
+ if (!m_irq_handler.isnull() && state != m_irq_pin)
+ m_irq_handler(!state);
+ m_irq_pin = state;
+}
+
+/**********************************************************************************************
+
+ update_ready_state -- update the ready line
+
+***********************************************************************************************/
+
+void tms5220_device::update_ready_state()
+{
+ int state = ready_read();
+#ifdef DEBUG_PIN_READS
+ fprintf(stderr,"ready pin set to state %d\n", state);
+#endif
+ if (!m_readyq_handler.isnull() && state != m_ready_pin)
+ m_readyq_handler(!state);
+ m_ready_pin = state;
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tms5220_device::device_start()
+{
+ if (m_speechrom_tag)
+ {
+ m_speechrom = siblingdevice<speechrom_device>( m_speechrom_tag );
+ if( !m_speechrom )
+ {
+ throw new emu_fatalerror("Error: %s '%s' can't find speechrom '%s'\n", shortname(), tag(), m_speechrom_tag );
+ }
+ }
+ else
+ {
+ m_speechrom = NULL;
+ }
+
+ set_variant(TMS5220_IS_5220);
+ m_clock = clock();
+
+ /* resolve irq and readyq line */
+ m_irq_handler.resolve();
+ m_readyq_handler.resolve();
+
+ /* initialize a stream */
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / 80);
+
+ m_timer_io_ready = timer_alloc(0);
+
+ /* not during reset which is called frm within a write! */
+ m_io_ready = 1;
+ m_true_timing = 0;
+ m_rs_ws = 0x03; // rs and ws are assumed to be inactive on device startup
+
+ register_for_save_states();
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tms5220c_device::device_start()
+{
+ tms5220_device::device_start();
+ set_variant(TMS5220_IS_5220C);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void cd2501e_device::device_start()
+{
+ tms5220_device::device_start();
+ set_variant(TMS5220_IS_CD2501E);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void tms5200_device::device_start()
+{
+ tms5220_device::device_start();
+ set_variant(TMS5220_IS_5200);
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void cd2501ecd_device::device_start()
+{
+ tms5220_device::device_start();
+ set_variant(TMS5220_IS_CD2501ECD);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void tms5220_device::device_reset()
+{
+ m_digital_select = FORCE_DIGITAL; // assume analog output
+ /* initialize the FIFO */
+ /*memset(m_fifo, 0, sizeof(m_fifo));*/
+ m_fifo_head = m_fifo_tail = m_fifo_count = m_fifo_bits_taken = 0;
+
+ /* initialize the chip state */
+ /* Note that we do not actually clear IRQ on start-up : IRQ is even raised if m_buffer_empty or m_buffer_low are 0 */
+ m_SPEN = m_DDIS = m_TALK = m_TALKD = m_previous_TALK_STATUS = m_irq_pin = m_ready_pin = 0;
+ set_interrupt_state(0);
+ update_ready_state();
+ m_buffer_empty = m_buffer_low = 1;
+
+ m_RDB_flag = FALSE;
+
+ /* initialize the energy/pitch/k states */
+#ifdef PERFECT_INTERPOLATION_HACK
+ m_old_frame_energy_idx = m_old_frame_pitch_idx = 0;
+ memset(m_old_frame_k_idx, 0, sizeof(m_old_frame_k_idx));
+ m_old_zpar = 0;
+#endif
+ m_new_frame_energy_idx = m_current_energy = m_previous_energy = 0;
+ m_new_frame_pitch_idx = m_current_pitch = 0;
+ m_zpar = m_uv_zpar = 0;
+ memset(m_new_frame_k_idx, 0, sizeof(m_new_frame_k_idx));
+ memset(m_current_k, 0, sizeof(m_current_k));
+
+ /* initialize the sample generators */
+ m_inhibit = 1;
+ m_subcycle = m_c_variant_rate = m_pitch_count = m_PC = 0;
+ m_subc_reload = FORCE_SUBC_RELOAD;
+ m_OLDE = m_OLDP = 1;
+ m_IP = reload_table[m_c_variant_rate&0x3];
+ m_RNG = 0x1FFF;
+ memset(m_u, 0, sizeof(m_u));
+ memset(m_x, 0, sizeof(m_x));
+ m_schedule_dummy_read = 0;
+
+ if (m_speechrom)
+ {
+ m_speechrom->load_address(0);
+ // MZ: Do the dummy read immediately. The previous line will cause a
+ // shift in the address pointer in the VSM. When the next command is a
+ // load_address, no dummy read will occur, hence the address will be
+ // falsely shifted.
+ m_speechrom->read(1);
+ m_schedule_dummy_read = FALSE;
+ }
+}
+
+/**********************************************************************************************
+
+ True timing
+
+***********************************************************************************************/
+
+void tms5220_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case 0:
+ if (param)
+ {
+ switch (m_rs_ws)
+ {
+ case 0x02:
+ /* Write */
+ /* bring up to date first */
+#ifdef DEBUG_IO_READY
+ fprintf(stderr,"Serviced write: %02x\n", m_write_latch);
+ //fprintf(stderr, "Processed write data: %02X\n", m_write_latch);
+#endif
+ m_stream->update();
+ data_write(m_write_latch);
+ break;
+ case 0x01:
+ /* Read */
+ /* bring up to date first */
+ m_stream->update();
+ m_read_latch = status_read();
+ break;
+ case 0x03:
+ /* High Impedance */
+ case 0x00:
+ /* illegal */
+ break;
+ }
+ }
+
+ m_io_ready = param;
+ update_ready_state();
+ break;
+ }
+}
+
+/*
+ * /RS line write handler
+ */
+WRITE_LINE_MEMBER( tms5220_device::rsq_w )
+{
+ UINT8 new_val;
+
+ m_true_timing = 1;
+ state &= 0x01;
+#ifdef DEBUG_RS_WS
+ fprintf(stderr,"/RS written with data: %d\n", state);
+#endif
+ new_val = (m_rs_ws & 0x01) | (state<<1);
+ if (new_val != m_rs_ws)
+ {
+ m_rs_ws = new_val;
+ if (new_val == 0)
+ {
+ if (TMS5220_HAS_RATE_CONTROL) // correct for 5220c, ? for cd2501ecd
+ reset();
+#ifdef DEBUG_RS_WS
+ else
+ /* illegal */
+ fprintf(stderr,"tms5220_rs_w: illegal\n");
+#endif
+ return;
+ }
+ else if ( new_val == 3)
+ {
+ /* high impedance */
+ m_read_latch = 0xff;
+ return;
+ }
+ if (state)
+ {
+ /* low to high */
+ }
+ else
+ {
+ /* high to low - schedule ready cycle */
+#ifdef DEBUG_RS_WS
+ fprintf(stderr,"Scheduling ready cycle for /RS...\n");
+#endif
+ /* upon /RS being activated, /READY goes inactive after 100 nsec from data sheet, through 3 asynchronous gates on patent. This is effectively within one clock, so we immediately set io_ready to 0 and activate the callback. */
+ m_io_ready = 0;
+ update_ready_state();
+ /* How long does /READY stay inactive, when /RS is pulled low? I believe its almost always ~16 clocks (25 usec at 800khz as shown on the datasheet) */
+ m_timer_io_ready->adjust(attotime::from_hz(clock()/16), 1); // this should take around 10-16 (closer to ~11?) cycles to complete
+ }
+ }
+}
+
+/*
+ * /WS line write handler
+ */
+WRITE_LINE_MEMBER( tms5220_device::wsq_w )
+{
+ UINT8 new_val;
+
+ m_true_timing = 1;
+ state &= 0x01;
+#ifdef DEBUG_RS_WS
+ fprintf(stderr,"/WS written with data: %d\n", state);
+#endif
+ new_val = (m_rs_ws & 0x02) | (state<<0);
+ if (new_val != m_rs_ws)
+ {
+ m_rs_ws = new_val;
+ if (new_val == 0)
+ {
+ if (TMS5220_HAS_RATE_CONTROL) // correct for 5220c, ? for cd2501ecd
+ reset();
+#ifdef DEBUG_RS_WS
+ else
+ /* illegal */
+ fprintf(stderr,"tms5220_ws_w: illegal\n");
+#endif
+ return;
+ }
+ else if ( new_val == 3)
+ {
+ /* high impedance */
+ m_read_latch = 0xff;
+ return;
+ }
+ if (state)
+ {
+ /* low to high */
+ }
+ else
+ {
+ /* high to low - schedule ready cycle */
+#ifdef DEBUG_RS_WS
+ fprintf(stderr,"Scheduling ready cycle for /WS...\n");
+#endif
+ /* upon /WS being activated, /READY goes inactive after 100 nsec from data sheet, through 3 asynchronous gates on patent. This is effectively within one clock, so we immediately set io_ready to 0 and activate the callback. */
+ m_io_ready = 0;
+ update_ready_state();
+ /* Now comes the complicated part: long does /READY stay inactive, when /WS is pulled low? This depends ENTIRELY on the command written, or whether the chip is in speak external mode or not...
+ Speak external mode: ~16 cycles
+ Command Mode:
+ SPK: ? cycles
+ SPKEXT: ? cycles
+ RDBY: between 60 and 140 cycles
+ RB: ? cycles (80?)
+ RST: between 60 and 140 cycles
+ SET RATE (5220C and CD2501ECD only): ? cycles (probably ~16)
+ */
+ // TODO: actually HANDLE the timing differences! currently just assuming always 16 cycles
+ m_timer_io_ready->adjust(attotime::from_hz(clock()/16), 1); // this should take around 10-16 (closer to ~15) cycles to complete for fifo writes, TODO: but actually depends on what command is written if in command mode
+ }
+ }
+}
+
+/**********************************************************************************************
+
+ tms5220_data_w -- write data to the sound chip
+
+***********************************************************************************************/
+
+WRITE8_MEMBER( tms5220_device::data_w )
+{
+ // prevent debugger from changing the internal state
+ if (space.debugger_access()) return;
+
+#ifdef DEBUG_RS_WS
+ fprintf(stderr,"tms5220_data_w: data %02x\n", data);
+#endif
+ if (!m_true_timing)
+ {
+ /* bring up to date first */
+ m_stream->update();
+ data_write(data);
+ }
+ else
+ {
+ /* actually in a write ? */
+#ifdef DEBUG_RS_WS
+ if (!(m_rs_ws == 0x02))
+ fprintf(stderr,"tms5220_data_w: data written outside ws, status: %02x!\n", m_rs_ws);
+#endif
+ m_write_latch = data;
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ tms5220_status_r -- read status or data from the sound chip
+
+***********************************************************************************************/
+
+READ8_MEMBER( tms5220_device::status_r )
+{
+ // prevent debugger from changing the internal state
+ if (space.debugger_access()) return 0;
+
+ if (!m_true_timing)
+ {
+ /* bring up to date first */
+ m_stream->update();
+ return status_read();
+ }
+ else
+ {
+ /* actually in a read ? */
+ if (m_rs_ws == 0x01)
+ return m_read_latch;
+#ifdef DEBUG_RS_WS
+ else
+ fprintf(stderr,"tms5220_status_r: data read outside rs!\n");
+#endif
+ return 0xff;
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ tms5220_ready_r -- return the not ready status from the sound chip
+
+***********************************************************************************************/
+
+READ_LINE_MEMBER( tms5220_device::readyq_r )
+{
+ /* bring up to date first */
+ m_stream->update();
+ return !ready_read();
+}
+
+
+
+/**********************************************************************************************
+
+ tms5220_time_to_ready -- return the time in seconds until the ready line is asserted
+
+***********************************************************************************************/
+
+double tms5220_device::time_to_ready()
+{
+ double cycles;
+
+ /* bring up to date first */
+ m_stream->update();
+ cycles = cycles_to_ready();
+ return cycles * 80.0 / m_clock;
+}
+
+
+
+/**********************************************************************************************
+
+ tms5220_int_r -- return the int status from the sound chip
+
+***********************************************************************************************/
+
+READ_LINE_MEMBER( tms5220_device::intq_r )
+{
+ /* bring up to date first */
+ m_stream->update();
+ return !int_read();
+}
+
+
+
+/**********************************************************************************************
+
+ tms5220_update -- update the sound chip so that it is in sync with CPU execution
+
+***********************************************************************************************/
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void tms5220_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ INT16 sample_data[MAX_SAMPLE_CHUNK];
+ stream_sample_t *buffer = outputs[0];
+
+ /* loop while we still have samples to generate */
+ while (samples)
+ {
+ int length = (samples > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : samples;
+ int index;
+
+ /* generate the samples and copy to the target buffer */
+ process(sample_data, length);
+ for (index = 0; index < length; index++)
+ *buffer++ = sample_data[index];
+
+ /* account for the samples */
+ samples -= length;
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ tms5220_set_frequency -- adjusts the playback frequency
+
+***********************************************************************************************/
+
+void tms5220_device::set_frequency(int frequency)
+{
+ m_stream->set_sample_rate(frequency / 80);
+ m_clock = frequency;
+}
+
+const device_type TMS5220C = &device_creator<tms5220c_device>;
+
+tms5220c_device::tms5220c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms5220_device(mconfig, TMS5220C, "TMS5220C", tag, owner, clock, "tms5220c", __FILE__)
+{
+}
+
+
+const device_type TMS5220 = &device_creator<tms5220_device>;
+
+tms5220_device::tms5220_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, TMS5220, "TMS5220", tag, owner, clock, "tms5220", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_irq_handler(*this),
+ m_readyq_handler(*this),
+ m_speechrom_tag(NULL)
+{
+}
+
+tms5220_device::tms5220_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_sound_interface(mconfig, *this),
+ m_irq_handler(*this),
+ m_readyq_handler(*this),
+ m_speechrom_tag(NULL)
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void tms5220_device::device_config_complete()
+{
+}
+
+
+const device_type CD2501E = &device_creator<cd2501e_device>;
+
+cd2501e_device::cd2501e_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms5220_device(mconfig, CD2501E, "CD2501E", tag, owner, clock, "cd2501e", __FILE__)
+{
+}
+
+
+const device_type TMS5200 = &device_creator<tms5200_device>;
+
+tms5200_device::tms5200_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms5220_device(mconfig, TMS5200, "TMS5200", tag, owner, clock, "tms5200", __FILE__)
+{
+}
+
+
+const device_type CD2501ECD = &device_creator<cd2501ecd_device>;
+
+cd2501ecd_device::cd2501ecd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : tms5220_device(mconfig, CD2501ECD, "CD2501ECD", tag, owner, clock, "cd2501ecd", __FILE__)
+{
+}
diff --git a/src/devices/sound/tms5220.h b/src/devices/sound/tms5220.h
new file mode 100644
index 00000000000..12edc66e5c3
--- /dev/null
+++ b/src/devices/sound/tms5220.h
@@ -0,0 +1,254 @@
+// license:BSD-3-Clause
+// copyright-holders:Frank Palazzolo, Aaron Giles, Jonathan Gevaryahu, Raphael Nabet, Couriersud, Michael Zapf
+#pragma once
+
+#ifndef __TMS5220_H__
+#define __TMS5220_H__
+
+#include "emu.h"
+#include "machine/spchrom.h"
+
+/* HACK: if defined, uses impossibly perfect 'straight line' interpolation */
+#undef PERFECT_INTERPOLATION_HACK
+
+#define FIFO_SIZE 16
+
+/* clock rate = 80 * output sample rate, */
+/* usually 640000 for 8000 Hz sample rate or */
+/* usually 800000 for 10000 Hz sample rate. */
+
+/* IRQ callback function, active low, i.e. state=0 */
+#define MCFG_TMS52XX_IRQ_HANDLER(_devcb) \
+ devcb = &tms5220_device::set_irq_handler(*device, DEVCB_##_devcb);
+
+/* Ready callback function, active low, i.e. state=0 */
+#define MCFG_TMS52XX_READYQ_HANDLER(_devcb) \
+ devcb = &tms5220_device::set_readyq_handler(*device, DEVCB_##_devcb);
+
+#define MCFG_TMS52XX_SPEECHROM(_tag) \
+ tms5220_device::set_speechrom_tag(*device, _tag);
+
+class tms5220_device : public device_t,
+ public device_sound_interface
+{
+public:
+ tms5220_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ tms5220_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<tms5220_device &>(device).m_irq_handler.set_callback(object); }
+ template<class _Object> static devcb_base &set_readyq_handler(device_t &device, _Object object) { return downcast<tms5220_device &>(device).m_readyq_handler.set_callback(object); }
+ static void set_speechrom_tag(device_t &device, const char *_tag) { downcast<tms5220_device &>(device).m_speechrom_tag = _tag; }
+
+ /* Control lines - once written to will switch interface into
+ * "true" timing behaviour.
+ */
+
+ /* all lines with suffix q are active low! */
+
+ WRITE_LINE_MEMBER( rsq_w );
+ WRITE_LINE_MEMBER( wsq_w );
+
+ DECLARE_WRITE8_MEMBER( data_w );
+ DECLARE_READ8_MEMBER( status_r );
+
+ READ_LINE_MEMBER( readyq_r );
+ READ_LINE_MEMBER( intq_r );
+
+ double time_to_ready();
+
+ void set_frequency(int frequency);
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+ virtual void device_reset();
+
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ void set_variant(int variant);
+
+private:
+ void register_for_save_states();
+ void data_write(int data);
+ void update_fifo_status_and_ints();
+ int extract_bits(int count);
+ int status_read();
+ int ready_read();
+ int cycles_to_ready();
+ int int_read();
+ void process(INT16 *buffer, unsigned int size);
+ INT32 lattice_filter();
+ void process_command(unsigned char cmd);
+ void parse_frame();
+ void set_interrupt_state(int state);
+ void update_ready_state();
+
+ // internal state
+
+ /* coefficient tables */
+ int m_variant; /* Variant of the 5xxx - see tms5110r.h */
+
+ /* coefficient tables */
+ const struct tms5100_coeffs *m_coeff;
+
+ /* these contain data that describes the 128-bit data FIFO */
+ UINT8 m_fifo[FIFO_SIZE];
+ UINT8 m_fifo_head;
+ UINT8 m_fifo_tail;
+ UINT8 m_fifo_count;
+ UINT8 m_fifo_bits_taken;
+
+
+ /* these contain global status bits */
+ UINT8 m_previous_TALK_STATUS; /* this is the OLD value of TALK_STATUS (i.e. previous value of m_SPEN|m_TALKD), needed for generating interrupts on a falling TALK_STATUS edge */
+ UINT8 m_SPEN; /* set on speak(or speak external and BL falling edge) command, cleared on stop command, reset command, or buffer out */
+ UINT8 m_DDIS; /* If 1, DDIS is 1, i.e. Speak External command in progress, writes go to FIFO. */
+ UINT8 m_TALK; /* set on SPEN & RESETL4(pc12->pc0 transition), cleared on stop command or reset command */
+#define TALK_STATUS (m_SPEN|m_TALKD)
+ UINT8 m_TALKD; /* TALK(TCON) value, latched every RESETL4 */
+ UINT8 m_buffer_low; /* If 1, FIFO has less than 8 bytes in it */
+ UINT8 m_buffer_empty; /* If 1, FIFO is empty */
+ UINT8 m_irq_pin; /* state of the IRQ pin (output) */
+ UINT8 m_ready_pin; /* state of the READY pin (output) */
+
+ /* these contain data describing the current and previous voice frames */
+#define OLD_FRAME_SILENCE_FLAG m_OLDE // 1 if E=0, 0 otherwise.
+#define OLD_FRAME_UNVOICED_FLAG m_OLDP // 1 if P=0 (unvoiced), 0 if voiced
+ UINT8 m_OLDE;
+ UINT8 m_OLDP;
+
+#define NEW_FRAME_STOP_FLAG (m_new_frame_energy_idx == 0xF) // 1 if this is a stop (Energy = 0xF) frame
+#define NEW_FRAME_SILENCE_FLAG (m_new_frame_energy_idx == 0) // ditto as above
+#define NEW_FRAME_UNVOICED_FLAG (m_new_frame_pitch_idx == 0) // ditto as above
+ UINT8 m_new_frame_energy_idx;
+ UINT8 m_new_frame_pitch_idx;
+ UINT8 m_new_frame_k_idx[10];
+
+
+ /* these are all used to contain the current state of the sound generation */
+#ifndef PERFECT_INTERPOLATION_HACK
+ INT16 m_current_energy;
+ INT16 m_current_pitch;
+ INT16 m_current_k[10];
+#else
+ UINT8 m_old_frame_energy_idx;
+ UINT8 m_old_frame_pitch_idx;
+ UINT8 m_old_frame_k_idx[10];
+ UINT8 m_old_zpar;
+ UINT8 m_old_uv_zpar;
+
+ INT32 m_current_energy;
+ INT32 m_current_pitch;
+ INT32 m_current_k[10];
+#endif
+
+ UINT16 m_previous_energy; /* needed for lattice filter to match patent */
+
+ UINT8 m_subcycle; /* contains the current subcycle for a given PC: 0 is A' (only used on SPKSLOW mode on 51xx), 1 is A, 2 is B */
+ UINT8 m_subc_reload; /* contains 1 for normal speech, 0 when SPKSLOW is active */
+ UINT8 m_PC; /* current parameter counter (what param is being interpolated), ranges from 0 to 12 */
+ /* NOTE: the interpolation period counts 1,2,3,4,5,6,7,0 for divide by 8,8,8,4,4,2,2,1 */
+ UINT8 m_IP; /* the current interpolation period */
+ UINT8 m_inhibit; /* If 1, interpolation is inhibited until the DIV1 period */
+ UINT8 m_uv_zpar; /* If 1, zero k5 thru k10 coefficients */
+ UINT8 m_zpar; /* If 1, zero ALL parameters. */
+ UINT8 m_pitch_zero; /* circuit 412; pitch is forced to zero under certain circumstances */
+ UINT8 m_c_variant_rate; /* only relevant for tms5220C's multi frame rate feature; is the actual 4 bit value written on a 0x2* or 0x0* command */
+ UINT16 m_pitch_count; /* pitch counter; provides chirp rom address */
+
+ INT32 m_u[11];
+ INT32 m_x[10];
+
+ UINT16 m_RNG; /* the random noise generator configuration is: 1 + x + x^3 + x^4 + x^13 TODO: no it isn't */
+ INT16 m_excitation_data;
+
+ /* R Nabet : These have been added to emulate speech Roms */
+ UINT8 m_schedule_dummy_read; /* set after each load address, so that next read operation is preceded by a dummy read */
+ UINT8 m_data_register; /* data register, used by read command */
+ UINT8 m_RDB_flag; /* whether we should read data register or status register */
+
+ /* The TMS52xx has two different ways of providing output data: the
+ analog speaker pin (which was usually used) and the Digital I/O pin.
+ The internal DAC used to feed the analog pin is only 8 bits, and has the
+ funny clipping/clamping logic, while the digital pin gives full 10 bit
+ resolution of the output data.
+ TODO: add a way to set/reset this other than the FORCE_DIGITAL define
+ */
+ UINT8 m_digital_select;
+
+ /* io_ready: page 3 of the datasheet specifies that READY will be asserted until
+ * data is available or processed by the system.
+ */
+ UINT8 m_io_ready;
+
+ /* flag for "true" timing involving rs/ws */
+ UINT8 m_true_timing;
+
+ /* rsws - state, rs bit 1, ws bit 0 */
+ UINT8 m_rs_ws;
+ UINT8 m_read_latch;
+ UINT8 m_write_latch;
+
+ sound_stream *m_stream;
+ int m_clock;
+ emu_timer *m_timer_io_ready;
+
+ /* callbacks */
+ devcb_write_line m_irq_handler;
+ devcb_write_line m_readyq_handler;
+ const char *m_speechrom_tag;
+ speechrom_device *m_speechrom;
+};
+
+extern const device_type TMS5220;
+
+class tms5220c_device : public tms5220_device
+{
+public:
+ tms5220c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type TMS5220C;
+
+class cd2501e_device : public tms5220_device
+{
+public:
+ cd2501e_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type CD2501E;
+
+class tms5200_device : public tms5220_device
+{
+public:
+ tms5200_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type TMS5200;
+
+class cd2501ecd_device : public tms5220_device
+{
+public:
+ cd2501ecd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+extern const device_type CD2501ECD;
+
+#endif
diff --git a/src/devices/sound/tms5220.txt b/src/devices/sound/tms5220.txt
new file mode 100644
index 00000000000..d7a8b1a178c
--- /dev/null
+++ b/src/devices/sound/tms5220.txt
@@ -0,0 +1,86 @@
+*****************************
+
+ TI TMS5220 Emulator
+ (c) Frank Palazzolo
+ Updated by Raphael Nabet
+
+*****************************
+
+The TI TMS5220 Speech chip uses Linear-Predictive decoding scheme to produce
+speech from very compact data. This scheme is very similar to the U.S.
+Federal Standard LPC-10e coding system, which was developed soon after
+this chip.
+
+It is virtually identical to the chip used in the landmark "Speak 'N Spell"
+toy produced in the '70s.
+
+Acknowledgements:
+-----------------
+
+I would like to thank Larry Brantingham, the original designer of the chip,
+for his technical help.
+
+I would also like to thank Neill Cortlett, who first showed that this chip
+could be emulated in real-time in his Multi-Gauntlet Emulator.
+
+Theory of operation:
+--------------------
+
+The TI speech chip contains a 128-bit parallel-in, serial-out FIFO, a
+10-pole digital lattice filter which models the vocal tract, and a D/A
+converter. It was originally design to operate either with a microcomputer
+interface, or with a serial speech ROM. The Speech ROM functionality is
+now emulated, although no arcade games require it currently.
+
+The input data is writen a byte at a time into the chip, and it is
+decoded bitwise into variable length frames.
+
+Possible Frame Types are as follows:
+
+ Energy RF Pitch K1 K2 K3 K4 K5 K6 K7 K8 K9 K10
+ -----------------------------------------------------------------
+
+Silent 0000
+Stop 1111
+Repeat XXXX 1 XXXXXX
+Unvoiced XXXX 0 000000 XXXXX XXXXX XXXX XXXX
+Voiced XXXX 0 XXXXXX XXXXX XXXXX XXXX XXXX XXXX XXXX XXXX XXX XXX XXX
+
+Stop Frame: Stops the current speech
+Repeat Frame: Uses the digital filter coefficients from the previous frame,
+ with new Energy and Pitch values
+Unvoiced Frame: Uses Noise generator to feed 4 pole digital filter
+ (All other coefficients are set to zero)
+Voiced Frame: Uses Pulse Generator to feed 10 pole digital filter
+
+All parameters (Energy, Pitch, K1-K10) are indexes into a lookup table for
+actual values (see TMS5220R.c)
+
+K1-K10 are reflection coefficients for the lattice filter.
+
+Each frame is used to generate 200 samples, and 8 times during each frame,
+(every 25 samples), these values are linearly interpolated to smooth out
+frame transitions.
+
+The Noise generator is based on a shift-register type random-bit generator.
+The Pulse generator is based on a lookup table.
+
+API:
+----
+
+TBD
+
+More:
+-----
+
+For further technical info, the data sheet is floating around on the net.
+(I believe the name is TMS.PDF) If you can't find a copy, email me and
+I'll point you towards it. Feel free to contact me if you have a question.
+
+Frank Palazzolo
+
+palazzol@home.com
+
+
+
+
diff --git a/src/devices/sound/upd7752.c b/src/devices/sound/upd7752.c
new file mode 100644
index 00000000000..bebe180f722
--- /dev/null
+++ b/src/devices/sound/upd7752.c
@@ -0,0 +1,195 @@
+// license:BSD-3-Clause
+// copyright-holders:Angelo Salese
+/***************************************************************************
+
+NEC uPD7752 Voice Synthesizing LSI
+
+skeleton device
+
+***************************************************************************/
+
+#include "emu.h"
+#include "sound/upd7752.h"
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+const device_type UPD7752 = &device_creator<upd7752_device>;
+
+/* TODO: unknown exact size */
+static ADDRESS_MAP_START( upd7752_ram, AS_0, 8, upd7752_device )
+// AM_RANGE(0x0000, 0x7fff) AM_ROM
+ AM_RANGE(0x0000, 0xffff) AM_RAM
+ADDRESS_MAP_END
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// upd7752_device - constructor
+//-------------------------------------------------
+
+upd7752_device::upd7752_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, UPD7752, "uPD7752", tag, owner, clock, "upd7752", __FILE__),
+ device_sound_interface(mconfig, *this),
+ device_memory_interface(mconfig, *this),
+ m_space_config("ram", ENDIANNESS_LITTLE, 8, 16, 0, NULL, *ADDRESS_MAP_NAME(upd7752_ram))
+{
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *upd7752_device::memory_space_config(address_spacenum spacenum) const
+{
+ return (spacenum == AS_0) ? &m_space_config : NULL;
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void upd7752_device::device_start()
+{
+ /* TODO: clock */
+ m_stream = stream_alloc(0, 1, clock() / 64);
+
+ m_status = 0;
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void upd7752_device::device_reset()
+{
+}
+
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+
+void upd7752_device::device_stop()
+{
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void upd7752_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+}
+
+//**************************************************************************
+// INLINE HELPERS
+//**************************************************************************
+
+inline UINT8 upd7752_device::readbyte(offs_t address)
+{
+ return space().read_byte(address);
+}
+
+//-------------------------------------------------
+// writebyte - write a byte at the given address
+//-------------------------------------------------
+
+inline void upd7752_device::writebyte(offs_t address, UINT8 data)
+{
+ space().write_byte(address, data);
+}
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+void upd7752_device::status_change(UINT8 flag,bool type)
+{
+ if(type == true)
+ m_status |= flag;
+ else
+ m_status &= ~flag;
+}
+
+READ8_MEMBER( upd7752_device::read )
+{
+ switch(offset & 3)
+ {
+ //[0x00]: status register
+ //x--- ---- BSY busy status (1) processing (0) stopped
+ //-x-- ---- REQ audio parameter (1) input request (0) prohibited (???)
+ //--x- ---- ~INT / EXT message data (1) Outside (0) Inside
+ //---x ---- ERR error flag
+ case 0x00: return m_status;
+ //[0x02]: port 0xe2 latch?
+ case 0x02: return 0xff;
+ //[0x03]: port 0xe3 latch?
+ case 0x03: return 0xff;
+ }
+ return 0xff;
+}
+
+WRITE8_MEMBER( upd7752_device::write )
+{
+ switch(offset & 3)
+ {
+ // [0x00]: audio parameter transfer
+ case 0x00:
+ if(m_status & EXT)
+ {
+ /*
+ [0] xxxx x--- number of frames (times) to apply next table (N1)
+ ---- -x-- Quantized Magnification Data (QMAG)
+ ---- --x- Selective Interpolation Data (SI)
+ ---- ---x Voicing/Unvoicing Data (VU)
+ [1] xxxx ---- amp Voice source amplitude
+ ---- x--- Fricative Voice data
+ ---- -xxx Pitch
+ (repeat for N1 times)
+ if [0] & 0xf8 == 0 then command stop
+ */
+ writebyte(m_ram_addr++,data);
+ }
+ //else
+ // ...
+
+ break;
+
+ // [0x02]: mode set
+ // ---- -x-- Frame periodic analysis (0) 10 ms / frame (1) 20 ms / frame
+ // ---- --xx Utterance (tempo?) speed
+ // 00 : NORMAL SPEED
+ // 01 : SLOW SPEED
+ // 10 : FAST SPEED
+ // 11 : Setting prohibited
+
+ case 0x02:
+ m_mode = data & 7;
+ break;
+
+ case 0x03: //command set
+ switch(data)
+ {
+ case 0xfe: // external message select cmd
+ status_change(EXT,true);
+ status_change(REQ,true);
+ //TODO: BSY flag too
+ m_ram_addr = 0;
+ break;
+ }
+
+ break;
+
+ }
+}
diff --git a/src/devices/sound/upd7752.h b/src/devices/sound/upd7752.h
new file mode 100644
index 00000000000..eabd705e047
--- /dev/null
+++ b/src/devices/sound/upd7752.h
@@ -0,0 +1,78 @@
+// license:BSD-3-Clause
+// copyright-holders:Angelo Salese
+/***************************************************************************
+
+Template for skeleton device
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __UPD7752DEV_H__
+#define __UPD7752DEV_H__
+
+/* status flags */
+#define BSY 1<<7
+#define REQ 1<<6
+#define EXT 1<<5
+#define ERR 1<<4
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_UPD7752_ADD(_tag,_freq) \
+ MCFG_DEVICE_ADD(_tag, UPD7752, _freq)
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> upd7752_device
+
+class upd7752_device : public device_t,
+ public device_sound_interface,
+ public device_memory_interface
+{
+public:
+ // construction/destruction
+ upd7752_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // I/O operations
+ DECLARE_WRITE8_MEMBER( write );
+ DECLARE_READ8_MEMBER( read );
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const;
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_stop();
+ virtual void device_reset();
+
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ sound_stream *m_stream;
+ const address_space_config m_space_config;
+ UINT8 m_status;
+ UINT16 m_ram_addr;
+ UINT8 m_mode;
+ void status_change(UINT8 flag,bool type);
+ inline UINT8 readbyte(offs_t address);
+ inline void writebyte(offs_t address, UINT8 data);
+};
+
+
+// device type definition
+extern const device_type UPD7752;
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+
+
+#endif
diff --git a/src/devices/sound/upd7759.c b/src/devices/sound/upd7759.c
new file mode 100644
index 00000000000..4b7d4e13a18
--- /dev/null
+++ b/src/devices/sound/upd7759.c
@@ -0,0 +1,874 @@
+// license:BSD-3-Clause
+// copyright-holders:Juergen Buchmueller, Mike Balfour, Howie Cohen, Olivier Galibert, Aaron Giles
+/************************************************************
+
+ NEC uPD7759/55/56/P56/57/58 ADPCM Speech Processor
+ by: Juergen Buchmueller, Mike Balfour, Howie Cohen,
+ Olivier Galibert, and Aaron Giles
+
+ TODO:
+ - is there a doable method to dump the internal maskrom? :(
+ As far as we know, decapping is the only option
+ - low-level emulation
+ - watchdog? - according to uPD775x datasheet, the chip goes into standy mode
+ if CS/ST/RESET have not been accessed for more than 3 seconds
+ - convert to MAME modern device
+
+*************************************************************
+
+ uPD7759 Description:
+
+ The uPD7759 is a speech processing LSI that utilizes ADPCM to produce
+ speech or other sampled sounds. It can directly address up to 1Mbit
+ (128k) of external data ROM, or the host CPU can control the speech
+ data transfer. The uPD7759 is usually hooked up to a 640 kHz clock and
+ has one 8-bit input port, a start pin, a busy pin, and a clock output.
+
+ The chip is composed of 3 parts:
+ - a clock divider
+ - a rom-reading engine
+ - an adpcm engine
+ - a 4-to-9 bit adpcm converter
+
+ The clock divider takes the base 640KHz clock and divides it first
+ by a fixed divisor of 4 and then by a value between 9 and 32. The
+ result gives a clock between 5KHz and 17.78KHz. It's probably
+ possible, but not recommended and certainly out-of-spec, to push the
+ chip harder by reducing the divider.
+
+ The rom-reading engine reads one byte every two divided clock cycles.
+ The factor two comes from the fact that a byte has two nibbles, i.e.
+ two samples.
+
+ The apdcm engine takes bytes and interprets them as commands:
+
+ 00000000 sample end
+ 00dddddd silence
+ 01ffffff send the 256 following nibbles to the converter
+ 10ffffff nnnnnnnn send the n+1 following nibbles to the converter
+ 11---rrr --ffffff nnnnnnnn send the n+1 following nibbles to the converter, and repeat r+1 times
+
+ "ffffff" is sent to the clock divider to be the base clock for the
+ adpcm converter, i.e., it's the sampling rate. If the number of
+ nibbles to send is odd the last nibble is ignored. The commands
+ are always 8-bit aligned.
+
+ "dddddd" is the duration of the silence. The base speed is unknown,
+ 1ms sounds reasonably. It does not seem linked to the adpcm clock
+ speed because there often is a silence before any 01 or 10 command.
+
+ The adpcm converter converts nibbles into 9-bit DAC values. It has
+ an internal state of 4 bits that's used in conjunction with the
+ nibble to lookup which of the 256 possible steps is used. Then
+ the state is changed according to the nibble value. Essentially, the
+ higher the state, the bigger the steps are, and using big steps
+ increase the state. Conversely, using small steps reduces the state.
+ This allows the engine to be a little more adaptative than a
+ classical ADPCM algorithm.
+
+ The uPD7759 can run in two modes, master (also known as standalone)
+ and slave. The mode is selected through the "md" pin. No known
+ game changes modes on the fly, and it's unsure if that's even
+ possible to do.
+
+
+ Master mode:
+
+ The output of the rom reader is directly connected to the adpcm
+ converter. The controlling cpu only sends a sample number and the
+ 7759 plays it.
+
+ The sample rom has a header at the beginning of the form
+
+ nn 5a a5 69 55
+
+ where nn is the number of the last sample. This is then followed by
+ a vector of 2-bytes msb-first values, one per sample. Multiplying
+ them by two gives the sample start offset in the rom. A 0x00 marks
+ the end of each sample.
+
+ It seems that the uPD7759 reads at least part of the rom header at
+ startup. Games doing rom banking are careful to reset the chip after
+ each change.
+
+
+ Slave mode:
+
+ The rom reader is completely disconnected. The input port is
+ connected directly to the adpcm engine. The first write to the input
+ port activates the engine (the value itself is ignored). The engine
+ activates the clock output and waits for commands. The clock speed
+ is unknown, but its probably a divider of 640KHz. We use 40KHz here
+ because 80KHz crashes altbeast. The chip probably has an internal
+ fifo to the converter and suspends the clock when the fifo is full.
+ The first command is always 0xFF. A second 0xFF marks the end of the
+ sample and the engine stops. OTOH, there is a 0x00 at the end too.
+ Go figure.
+
+*************************************************************
+
+ The other chip models don't support slave mode, and have an internal ROM.
+ Other than that, they are thought to be nearly identical to uPD7759.
+
+ 55C 18-pin DIP 96 Kbit ROM
+ 55G 24-pin SOP 96 Kbit ROM
+ 56C 18-pin DIP 256 Kbit ROM
+ 56G 24-pin SOP 256 Kbit ROM
+ P56CR 20-pin DIP 256 Kbit ROM (OTP) - dumping the ROM is trivial
+ P56G 24-pin SOP 256 Kbit ROM (OTP) - "
+ 57C 18-pin DIP 512 Kbit ROM
+ 57G 24-pin SOP 512 Kbit ROM
+ 58C 18-pin DIP 1 Mbit ROM
+ 58G 24-pin SOP 1 Mbit ROM
+
+*************************************************************/
+
+#include "emu.h"
+#include "upd7759.h"
+
+
+#define DEBUG_STATES (0)
+#define DEBUG_METHOD osd_printf_debug
+
+
+
+/************************************************************
+
+ Constants
+
+*************************************************************/
+
+/* step value fractional bits */
+#define FRAC_BITS 20
+#define FRAC_ONE (1 << FRAC_BITS)
+#define FRAC_MASK (FRAC_ONE - 1)
+
+
+upd775x_device::upd775x_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_sound_interface(mconfig, *this),
+ m_channel(NULL),
+ m_sample_offset_shift(0),
+ m_pos(0),
+ m_step(0),
+ m_fifo_in(0),
+ m_reset(0),
+ m_start(0),
+ m_drq(0),
+ m_state(0),
+ m_clocks_left(0),
+ m_nibbles_left(0),
+ m_repeat_count(0),
+ m_post_drq_state(0),
+ m_post_drq_clocks(0),
+ m_req_sample(0),
+ m_last_sample(0),
+ m_block_header(0),
+ m_sample_rate(0),
+ m_first_valid_header(0),
+ m_offset(0),
+ m_repeat_offset(0),
+ m_adpcm_state(0),
+ m_adpcm_data(0),
+ m_sample(0),
+ m_rom(NULL),
+ m_rombase(NULL),
+ m_romoffset(0),
+ m_rommask(0),
+ m_drqcallback(*this)
+{
+}
+
+const device_type UPD7759 = &device_creator<upd7759_device>;
+
+upd7759_device::upd7759_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : upd775x_device(mconfig, UPD7759, "uPD7759", tag, owner, clock, "upd7759", __FILE__),
+ m_timer(NULL)
+{
+}
+
+
+upd7759_device::upd7759_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : upd775x_device(mconfig, type, name, tag, owner, clock, shortname, source),
+ m_timer(NULL)
+{
+}
+
+
+const device_type UPD7756 = &device_creator<upd7756_device>;
+
+upd7756_device::upd7756_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : upd775x_device(mconfig, UPD7756, "uPD7756", tag, owner, clock, "upd7756", __FILE__)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void upd775x_device::device_start()
+{
+}
+
+void upd7759_device::device_start()
+{
+ m_drqcallback.resolve_safe();
+
+ /* chip configuration */
+ m_sample_offset_shift = (type() == UPD7759) ? 1 : 0;
+
+ /* allocate a stream channel */
+ m_channel = machine().sound().stream_alloc(*this, 0, 1, clock()/4);
+
+ /* compute the stepping rate based on the chip's clock speed */
+ m_step = 4 * FRAC_ONE;
+
+ /* compute the clock period */
+ m_clock_period = attotime::from_hz(clock());
+
+ /* set the intial state */
+ m_state = STATE_IDLE;
+
+ /* compute the ROM base or allocate a timer */
+ m_romoffset = 0;
+ m_rom = m_rombase = region()->base();
+ if (m_rombase == NULL)
+ {
+ assert(type() == UPD7759); // other chips do not support slave mode
+ m_timer = timer_alloc(TIMER_SLAVE_UPDATE);
+ m_rommask = 0;
+ }
+ else
+ {
+ UINT32 romsize = region()->bytes();
+ if (romsize >= 0x20000) m_rommask = 0x1ffff;
+ else m_rommask = romsize - 1;
+
+ m_drqcallback.set_callback(DEVCB_NULL);
+ }
+
+ /* assume /RESET and /START are both high */
+ m_reset = 1;
+ m_start = 1;
+
+ /* toggle the reset line to finish the reset */
+ device_reset();
+
+ save_item(NAME(m_pos));
+ save_item(NAME(m_step));
+
+ save_item(NAME(m_fifo_in));
+ save_item(NAME(m_reset));
+ save_item(NAME(m_start));
+ save_item(NAME(m_drq));
+
+ save_item(NAME(m_state));
+ save_item(NAME(m_clocks_left));
+ save_item(NAME(m_nibbles_left));
+ save_item(NAME(m_repeat_count));
+ save_item(NAME(m_post_drq_state));
+ save_item(NAME(m_post_drq_clocks));
+ save_item(NAME(m_req_sample));
+ save_item(NAME(m_last_sample));
+ save_item(NAME(m_block_header));
+ save_item(NAME(m_sample_rate));
+ save_item(NAME(m_first_valid_header));
+ save_item(NAME(m_offset));
+ save_item(NAME(m_repeat_offset));
+
+ save_item(NAME(m_adpcm_state));
+ save_item(NAME(m_adpcm_data));
+ save_item(NAME(m_sample));
+
+ save_item(NAME(m_romoffset));
+ machine().save().register_postload(save_prepost_delegate(FUNC(upd7759_device::postload), this));
+}
+
+
+void upd7756_device::device_start()
+{
+ m_drqcallback.resolve_safe();
+
+ /* chip configuration */
+ m_sample_offset_shift = (type() == UPD7759) ? 1 : 0;
+
+ /* allocate a stream channel */
+ m_channel = machine().sound().stream_alloc(*this, 0, 1, clock()/4);
+
+ /* compute the stepping rate based on the chip's clock speed */
+ m_step = 4 * FRAC_ONE;
+
+ /* compute the clock period */
+ m_clock_period = attotime::from_hz(clock());
+
+ /* set the intial state */
+ m_state = STATE_IDLE;
+
+ /* compute the ROM base or allocate a timer */
+ m_romoffset = 0;
+ m_rom = m_rombase = region()->base();
+ if (m_rombase == NULL)
+ {
+ m_rommask = 0;
+ }
+ else
+ {
+ UINT32 romsize = region()->bytes();
+ if (romsize >= 0x20000) m_rommask = 0x1ffff;
+ else m_rommask = romsize - 1;
+
+ m_drqcallback.set_callback(DEVCB_NULL);
+ }
+
+ /* assume /RESET and /START are both high */
+ m_reset = 1;
+ m_start = 1;
+
+ /* toggle the reset line to finish the reset */
+ device_reset();
+
+ save_item(NAME(m_pos));
+ save_item(NAME(m_step));
+
+ save_item(NAME(m_fifo_in));
+ save_item(NAME(m_reset));
+ save_item(NAME(m_start));
+ save_item(NAME(m_drq));
+
+ save_item(NAME(m_state));
+ save_item(NAME(m_clocks_left));
+ save_item(NAME(m_nibbles_left));
+ save_item(NAME(m_repeat_count));
+ save_item(NAME(m_post_drq_state));
+ save_item(NAME(m_post_drq_clocks));
+ save_item(NAME(m_req_sample));
+ save_item(NAME(m_last_sample));
+ save_item(NAME(m_block_header));
+ save_item(NAME(m_sample_rate));
+ save_item(NAME(m_first_valid_header));
+ save_item(NAME(m_offset));
+ save_item(NAME(m_repeat_offset));
+
+ save_item(NAME(m_adpcm_state));
+ save_item(NAME(m_adpcm_data));
+ save_item(NAME(m_sample));
+
+ save_item(NAME(m_romoffset));
+ machine().save().register_postload(save_prepost_delegate(FUNC(upd7759_device::postload), this));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void upd775x_device::device_reset()
+{
+}
+
+void upd7759_device::device_reset()
+{
+ m_pos = 0;
+ m_fifo_in = 0;
+ m_drq = 0;
+ m_state = STATE_IDLE;
+ m_clocks_left = 0;
+ m_nibbles_left = 0;
+ m_repeat_count = 0;
+ m_post_drq_state = STATE_IDLE;
+ m_post_drq_clocks = 0;
+ m_req_sample = 0;
+ m_last_sample = 0;
+ m_block_header = 0;
+ m_sample_rate = 0;
+ m_first_valid_header = 0;
+ m_offset = 0;
+ m_repeat_offset = 0;
+ m_adpcm_state = 0;
+ m_adpcm_data = 0;
+ m_sample = 0;
+
+ /* turn off any timer */
+ if (m_timer)
+ m_timer->adjust(attotime::never);
+}
+
+void upd7756_device::device_reset()
+{
+ m_pos = 0;
+ m_fifo_in = 0;
+ m_drq = 0;
+ m_state = STATE_IDLE;
+ m_clocks_left = 0;
+ m_nibbles_left = 0;
+ m_repeat_count = 0;
+ m_post_drq_state = STATE_IDLE;
+ m_post_drq_clocks = 0;
+ m_req_sample = 0;
+ m_last_sample = 0;
+ m_block_header = 0;
+ m_sample_rate = 0;
+ m_first_valid_header = 0;
+ m_offset = 0;
+ m_repeat_offset = 0;
+ m_adpcm_state = 0;
+ m_adpcm_data = 0;
+ m_sample = 0;
+}
+
+
+/************************************************************
+
+ Local variables
+
+*************************************************************/
+
+static const int upd775x_step[16][16] =
+{
+ { 0, 0, 1, 2, 3, 5, 7, 10, 0, 0, -1, -2, -3, -5, -7, -10 },
+ { 0, 1, 2, 3, 4, 6, 8, 13, 0, -1, -2, -3, -4, -6, -8, -13 },
+ { 0, 1, 2, 4, 5, 7, 10, 15, 0, -1, -2, -4, -5, -7, -10, -15 },
+ { 0, 1, 3, 4, 6, 9, 13, 19, 0, -1, -3, -4, -6, -9, -13, -19 },
+ { 0, 2, 3, 5, 8, 11, 15, 23, 0, -2, -3, -5, -8, -11, -15, -23 },
+ { 0, 2, 4, 7, 10, 14, 19, 29, 0, -2, -4, -7, -10, -14, -19, -29 },
+ { 0, 3, 5, 8, 12, 16, 22, 33, 0, -3, -5, -8, -12, -16, -22, -33 },
+ { 1, 4, 7, 10, 15, 20, 29, 43, -1, -4, -7, -10, -15, -20, -29, -43 },
+ { 1, 4, 8, 13, 18, 25, 35, 53, -1, -4, -8, -13, -18, -25, -35, -53 },
+ { 1, 6, 10, 16, 22, 31, 43, 64, -1, -6, -10, -16, -22, -31, -43, -64 },
+ { 2, 7, 12, 19, 27, 37, 51, 76, -2, -7, -12, -19, -27, -37, -51, -76 },
+ { 2, 9, 16, 24, 34, 46, 64, 96, -2, -9, -16, -24, -34, -46, -64, -96 },
+ { 3, 11, 19, 29, 41, 57, 79, 117, -3, -11, -19, -29, -41, -57, -79, -117 },
+ { 4, 13, 24, 36, 50, 69, 96, 143, -4, -13, -24, -36, -50, -69, -96, -143 },
+ { 4, 16, 29, 44, 62, 85, 118, 175, -4, -16, -29, -44, -62, -85, -118, -175 },
+ { 6, 20, 36, 54, 76, 104, 144, 214, -6, -20, -36, -54, -76, -104, -144, -214 },
+};
+
+static const int upd775x_state_table[16] = { -1, -1, 0, 0, 1, 2, 2, 3, -1, -1, 0, 0, 1, 2, 2, 3 };
+
+
+/************************************************************
+
+ ADPCM sample updater
+
+*************************************************************/
+
+void upd775x_device::update_adpcm(int data)
+{
+ /* update the sample and the state */
+ m_sample += upd775x_step[m_adpcm_state][data];
+ m_adpcm_state += upd775x_state_table[data];
+
+ /* clamp the state to 0..15 */
+ if (m_adpcm_state < 0)
+ m_adpcm_state = 0;
+ else if (m_adpcm_state > 15)
+ m_adpcm_state = 15;
+}
+
+
+
+/************************************************************
+
+ Master chip state machine
+
+*************************************************************/
+
+void upd775x_device::advance_state()
+{
+ switch (m_state)
+ {
+ /* Idle state: we stick around here while there's nothing to do */
+ case STATE_IDLE:
+ m_clocks_left = 4;
+ break;
+
+ /* drop DRQ state: update to the intended state */
+ case STATE_DROP_DRQ:
+ m_drq = 0;
+
+ m_clocks_left = m_post_drq_clocks;
+ m_state = m_post_drq_state;
+ break;
+
+ /* Start state: we begin here as soon as a sample is triggered */
+ case STATE_START:
+ m_req_sample = m_rom ? m_fifo_in : 0x10;
+ if (DEBUG_STATES) DEBUG_METHOD("uPD7759: req_sample = %02X\n", m_req_sample);
+
+ /* 35+ cycles after we get here, the /DRQ goes low
+ * (first byte (number of samples in ROM) should be sent in response)
+ *
+ * (35 is the minimum number of cycles I found during heavy tests.
+ * Depending on the state the chip was in just before the /MD was set to 0 (reset, standby
+ * or just-finished-playing-previous-sample) this number can range from 35 up to ~24000).
+ * It also varies slightly from test to test, but not much - a few cycles at most.) */
+ m_clocks_left = 70; /* 35 - breaks cotton */
+ m_state = STATE_FIRST_REQ;
+ break;
+
+ /* First request state: issue a request for the first byte */
+ /* The expected response will be the index of the last sample */
+ case STATE_FIRST_REQ:
+ if (DEBUG_STATES) DEBUG_METHOD("uPD7759: first data request\n");
+ m_drq = 1;
+
+ /* 44 cycles later, we will latch this value and request another byte */
+ m_clocks_left = 44;
+ m_state = STATE_LAST_SAMPLE;
+ break;
+
+ /* Last sample state: latch the last sample value and issue a request for the second byte */
+ /* The second byte read will be just a dummy */
+ case STATE_LAST_SAMPLE:
+ m_last_sample = m_rom ? m_rom[0] : m_fifo_in;
+ if (DEBUG_STATES) DEBUG_METHOD("uPD7759: last_sample = %02X, requesting dummy 1\n", m_last_sample);
+ m_drq = 1;
+
+ /* 28 cycles later, we will latch this value and request another byte */
+ m_clocks_left = 28; /* 28 - breaks cotton */
+ m_state = (m_req_sample > m_last_sample) ? STATE_IDLE : STATE_DUMMY1;
+ break;
+
+ /* First dummy state: ignore any data here and issue a request for the third byte */
+ /* The expected response will be the MSB of the sample address */
+ case STATE_DUMMY1:
+ if (DEBUG_STATES) DEBUG_METHOD("uPD7759: dummy1, requesting offset_hi\n");
+ m_drq = 1;
+
+ /* 32 cycles later, we will latch this value and request another byte */
+ m_clocks_left = 32;
+ m_state = STATE_ADDR_MSB;
+ break;
+
+ /* Address MSB state: latch the MSB of the sample address and issue a request for the fourth byte */
+ /* The expected response will be the LSB of the sample address */
+ case STATE_ADDR_MSB:
+ m_offset = (m_rom ? m_rom[m_req_sample * 2 + 5] : m_fifo_in) << (8 + m_sample_offset_shift);
+ if (DEBUG_STATES) DEBUG_METHOD("uPD7759: offset_hi = %02X, requesting offset_lo\n", m_offset >> (8 + m_sample_offset_shift));
+ m_drq = 1;
+
+ /* 44 cycles later, we will latch this value and request another byte */
+ m_clocks_left = 44;
+ m_state = STATE_ADDR_LSB;
+ break;
+
+ /* Address LSB state: latch the LSB of the sample address and issue a request for the fifth byte */
+ /* The expected response will be just a dummy */
+ case STATE_ADDR_LSB:
+ m_offset |= (m_rom ? m_rom[m_req_sample * 2 + 6] : m_fifo_in) << m_sample_offset_shift;
+ if (DEBUG_STATES) DEBUG_METHOD("uPD7759: offset_lo = %02X, requesting dummy 2\n", (m_offset >> m_sample_offset_shift) & 0xff);
+ if (m_offset > m_rommask) logerror("uPD7759 offset %X > rommask %X\n",m_offset, m_rommask);
+ m_drq = 1;
+
+ /* 36 cycles later, we will latch this value and request another byte */
+ m_clocks_left = 36;
+ m_state = STATE_DUMMY2;
+ break;
+
+ /* Second dummy state: ignore any data here and issue a request for the sixth byte */
+ /* The expected response will be the first block header */
+ case STATE_DUMMY2:
+ m_offset++;
+ m_first_valid_header = 0;
+ if (DEBUG_STATES) DEBUG_METHOD("uPD7759: dummy2, requesting block header\n");
+ m_drq = 1;
+
+ /* 36?? cycles later, we will latch this value and request another byte */
+ m_clocks_left = 36;
+ m_state = STATE_BLOCK_HEADER;
+ break;
+
+ /* Block header state: latch the header and issue a request for the first byte afterwards */
+ case STATE_BLOCK_HEADER:
+
+ /* if we're in a repeat loop, reset the offset to the repeat point and decrement the count */
+ if (m_repeat_count)
+ {
+ m_repeat_count--;
+ m_offset = m_repeat_offset;
+ }
+ m_block_header = m_rom ? m_rom[m_offset++ & m_rommask] : m_fifo_in;
+ if (DEBUG_STATES) DEBUG_METHOD("uPD7759: header (@%05X) = %02X, requesting next byte\n", m_offset, m_block_header);
+ m_drq = 1;
+
+ /* our next step depends on the top two bits */
+ switch (m_block_header & 0xc0)
+ {
+ case 0x00: /* silence */
+ m_clocks_left = 1024 * ((m_block_header & 0x3f) + 1);
+ m_state = (m_block_header == 0 && m_first_valid_header) ? STATE_IDLE : STATE_BLOCK_HEADER;
+ m_sample = 0;
+ m_adpcm_state = 0;
+ break;
+
+ case 0x40: /* 256 nibbles */
+ m_sample_rate = (m_block_header & 0x3f) + 1;
+ m_nibbles_left = 256;
+ m_clocks_left = 36; /* just a guess */
+ m_state = STATE_NIBBLE_MSN;
+ break;
+
+ case 0x80: /* n nibbles */
+ m_sample_rate = (m_block_header & 0x3f) + 1;
+ m_clocks_left = 36; /* just a guess */
+ m_state = STATE_NIBBLE_COUNT;
+ break;
+
+ case 0xc0: /* repeat loop */
+ m_repeat_count = (m_block_header & 7) + 1;
+ m_repeat_offset = m_offset;
+ m_clocks_left = 36; /* just a guess */
+ m_state = STATE_BLOCK_HEADER;
+ break;
+ }
+
+ /* set a flag when we get the first non-zero header */
+ if (m_block_header != 0)
+ m_first_valid_header = 1;
+ break;
+
+ /* Nibble count state: latch the number of nibbles to play and request another byte */
+ /* The expected response will be the first data byte */
+ case STATE_NIBBLE_COUNT:
+ m_nibbles_left = (m_rom ? m_rom[m_offset++ & m_rommask] : m_fifo_in) + 1;
+ if (DEBUG_STATES) DEBUG_METHOD("uPD7759: nibble_count = %u, requesting next byte\n", (unsigned)m_nibbles_left);
+ m_drq = 1;
+
+ /* 36?? cycles later, we will latch this value and request another byte */
+ m_clocks_left = 36; /* just a guess */
+ m_state = STATE_NIBBLE_MSN;
+ break;
+
+ /* MSN state: latch the data for this pair of samples and request another byte */
+ /* The expected response will be the next sample data or another header */
+ case STATE_NIBBLE_MSN:
+ m_adpcm_data = m_rom ? m_rom[m_offset++ & m_rommask] : m_fifo_in;
+ update_adpcm(m_adpcm_data >> 4);
+ m_drq = 1;
+
+ /* we stay in this state until the time for this sample is complete */
+ m_clocks_left = m_sample_rate * 4;
+ if (--m_nibbles_left == 0)
+ m_state = STATE_BLOCK_HEADER;
+ else
+ m_state = STATE_NIBBLE_LSN;
+ break;
+
+ /* LSN state: process the lower nibble */
+ case STATE_NIBBLE_LSN:
+ update_adpcm(m_adpcm_data & 15);
+
+ /* we stay in this state until the time for this sample is complete */
+ m_clocks_left = m_sample_rate * 4;
+ if (--m_nibbles_left == 0)
+ m_state = STATE_BLOCK_HEADER;
+ else
+ m_state = STATE_NIBBLE_MSN;
+ break;
+ }
+
+ /* if there's a DRQ, fudge the state */
+ if (m_drq)
+ {
+ m_post_drq_state = m_state;
+ m_post_drq_clocks = m_clocks_left - 21;
+ m_state = STATE_DROP_DRQ;
+ m_clocks_left = 21;
+ }
+}
+
+/************************************************************
+
+ DRQ callback
+
+*************************************************************/
+
+void upd7759_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ UINT8 olddrq = m_drq;
+
+ switch (id)
+ {
+ case TIMER_SLAVE_UPDATE:
+
+ /* update the stream */
+ m_channel->update();
+
+ /* advance the state */
+ advance_state();
+
+ /* if the DRQ changed, update it */
+ logerror("upd7759_slave_update: DRQ %d->%d\n", olddrq, m_drq);
+ if (olddrq != m_drq)
+ m_drqcallback(m_drq);
+
+ /* set a timer to go off when that is done */
+ if (m_state != STATE_IDLE)
+ m_timer->adjust(m_clock_period * m_clocks_left);
+ break;
+
+ default:
+ assert_always(FALSE, "Unknown id in upd7759_device::device_timer");
+ }
+}
+
+/************************************************************
+
+ Sound startup
+
+*************************************************************/
+
+void upd775x_device::postload()
+{
+ if (m_rombase)
+ m_rom = m_rombase + m_romoffset;
+}
+
+/************************************************************
+
+ I/O handlers
+
+*************************************************************/
+
+WRITE_LINE_MEMBER( upd775x_device::reset_w )
+{
+ /* update the reset value */
+ UINT8 oldreset = m_reset;
+ m_reset = (state != 0);
+
+ /* update the stream first */
+ m_channel->update();
+
+ /* on the falling edge, reset everything */
+ if (oldreset && !m_reset)
+ device_reset();
+}
+
+WRITE_LINE_MEMBER( upd7759_device::start_w )
+{
+ /* update the start value */
+ UINT8 oldstart = m_start;
+ m_start = (state != 0);
+
+ logerror("upd7759_start_w: %d->%d\n", oldstart, m_start);
+
+ /* update the stream first */
+ m_channel->update();
+
+ /* on the rising edge, if we're idle, start going, but not if we're held in reset */
+ if (m_state == STATE_IDLE && !oldstart && m_start && m_reset)
+ {
+ m_state = STATE_START;
+
+ /* for slave mode, start the timer going */
+ if (m_timer)
+ m_timer->adjust(attotime::zero);
+ }
+}
+
+WRITE_LINE_MEMBER( upd7756_device::start_w )
+{
+ /* update the start value */
+ UINT8 oldstart = m_start;
+ m_start = (state != 0);
+
+ logerror("upd7759_start_w: %d->%d\n", oldstart, m_start);
+
+ /* update the stream first */
+ m_channel->update();
+
+ /* on the rising edge, if we're idle, start going, but not if we're held in reset */
+ if (m_state == STATE_IDLE && !oldstart && m_start && m_reset)
+ {
+ m_state = STATE_START;
+ }
+}
+
+
+WRITE8_MEMBER( upd775x_device::port_w )
+{
+ /* update the FIFO value */
+ m_fifo_in = data;
+}
+
+
+READ_LINE_MEMBER( upd775x_device::busy_r )
+{
+ /* return /BUSY */
+ return (m_state == STATE_IDLE);
+}
+
+
+void upd775x_device::set_bank_base(UINT32 base)
+{
+ assert(m_rombase != NULL);
+ m_rom = m_rombase + base;
+ m_romoffset = base;
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void upd775x_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ INT32 clocks_left = m_clocks_left;
+ INT16 sample = m_sample;
+ UINT32 step = m_step;
+ UINT32 pos = m_pos;
+ stream_sample_t *buffer = outputs[0];
+
+ /* loop until done */
+ if (m_state != STATE_IDLE)
+ while (samples != 0)
+ {
+ /* store the current sample */
+ *buffer++ = sample << 7;
+ samples--;
+
+ /* advance by the number of clocks/output sample */
+ pos += step;
+
+ /* handle clocks, but only in standalone mode */
+ while (m_rom && pos >= FRAC_ONE)
+ {
+ int clocks_this_time = pos >> FRAC_BITS;
+ if (clocks_this_time > clocks_left)
+ clocks_this_time = clocks_left;
+
+ /* clock once */
+ pos -= clocks_this_time * FRAC_ONE;
+ clocks_left -= clocks_this_time;
+
+ /* if we're out of clocks, time to handle the next state */
+ if (clocks_left == 0)
+ {
+ /* advance one state; if we hit idle, bail */
+ advance_state();
+ if (m_state == STATE_IDLE)
+ break;
+
+ /* reimport the variables that we cached */
+ clocks_left = m_clocks_left;
+ sample = m_sample;
+ }
+ }
+ }
+
+ /* if we got out early, just zap the rest of the buffer */
+ if (samples != 0)
+ memset(buffer, 0, samples * sizeof(*buffer));
+
+ /* flush the state back */
+ m_clocks_left = clocks_left;
+ m_pos = pos;
+}
+
+void upd7759_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ upd775x_device::sound_stream_update(stream, inputs, outputs, samples);
+}
+
+void upd7756_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ upd775x_device::sound_stream_update(stream, inputs, outputs, samples);
+}
diff --git a/src/devices/sound/upd7759.h b/src/devices/sound/upd7759.h
new file mode 100644
index 00000000000..94286e7cd57
--- /dev/null
+++ b/src/devices/sound/upd7759.h
@@ -0,0 +1,151 @@
+// license:BSD-3-Clause
+// copyright-holders:Juergen Buchmueller, Mike Balfour, Howie Cohen, Olivier Galibert, Aaron Giles
+#pragma once
+
+#ifndef __UPD7759_H__
+#define __UPD7759_H__
+
+/* chip states */
+enum
+{
+ STATE_IDLE,
+ STATE_DROP_DRQ,
+ STATE_START,
+ STATE_FIRST_REQ,
+ STATE_LAST_SAMPLE,
+ STATE_DUMMY1,
+ STATE_ADDR_MSB,
+ STATE_ADDR_LSB,
+ STATE_DUMMY2,
+ STATE_BLOCK_HEADER,
+ STATE_NIBBLE_COUNT,
+ STATE_NIBBLE_MSN,
+ STATE_NIBBLE_LSN
+};
+
+/* NEC uPD7759/55/56/P56/57/58 ADPCM Speech Processor */
+
+/* There are two modes for the uPD7759, selected through the !MD pin.
+ This is the mode select input. High is stand alone, low is slave.
+ We're making the assumption that nobody switches modes through
+ software.
+*/
+
+#define UPD7759_STANDARD_CLOCK XTAL_640kHz
+
+class upd775x_device : public device_t,
+ public device_sound_interface
+{
+public:
+ upd775x_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ ~upd775x_device() {}
+
+ template<class _Object> static devcb_base &set_drq_callback(device_t &device, _Object object) { return downcast<upd775x_device &>(device).m_drqcallback.set_callback(object); }
+
+ void set_bank_base(offs_t base);
+
+ DECLARE_WRITE_LINE_MEMBER( reset_w );
+ DECLARE_READ_LINE_MEMBER( busy_r );
+ virtual DECLARE_WRITE8_MEMBER( port_w );
+ void postload();
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ // internal state
+ sound_stream *m_channel; /* stream channel for playback */
+
+ /* chip configuration */
+ UINT8 m_sample_offset_shift; /* header sample address shift (access data > 0xffff) */
+
+ /* internal clock to output sample rate mapping */
+ UINT32 m_pos; /* current output sample position */
+ UINT32 m_step; /* step value per output sample */
+ attotime m_clock_period; /* clock period */
+
+ /* I/O lines */
+ UINT8 m_fifo_in; /* last data written to the sound chip */
+ UINT8 m_reset; /* current state of the RESET line */
+ UINT8 m_start; /* current state of the START line */
+ UINT8 m_drq; /* current state of the DRQ line */
+
+ /* internal state machine */
+ INT8 m_state; /* current overall chip state */
+ INT32 m_clocks_left; /* number of clocks left in this state */
+ UINT16 m_nibbles_left; /* number of ADPCM nibbles left to process */
+ UINT8 m_repeat_count; /* number of repeats remaining in current repeat block */
+ INT8 m_post_drq_state; /* state we will be in after the DRQ line is dropped */
+ INT32 m_post_drq_clocks; /* clocks that will be left after the DRQ line is dropped */
+ UINT8 m_req_sample; /* requested sample number */
+ UINT8 m_last_sample; /* last sample number available */
+ UINT8 m_block_header; /* header byte */
+ UINT8 m_sample_rate; /* number of UPD clocks per ADPCM nibble */
+ UINT8 m_first_valid_header; /* did we get our first valid header yet? */
+ UINT32 m_offset; /* current ROM offset */
+ UINT32 m_repeat_offset; /* current ROM repeat offset */
+
+ /* ADPCM processing */
+ INT8 m_adpcm_state; /* ADPCM state index */
+ UINT8 m_adpcm_data; /* current byte of ADPCM data */
+ INT16 m_sample; /* current sample value */
+
+ /* ROM access */
+ UINT8 * m_rom; /* pointer to ROM data or NULL for slave mode */
+ UINT8 * m_rombase; /* pointer to ROM data or NULL for slave mode */
+ UINT32 m_romoffset; /* ROM offset to make save/restore easier */
+ UINT32 m_rommask; /* maximum address offset */
+
+ devcb_write_line m_drqcallback;
+
+ void update_adpcm(int data);
+ virtual void advance_state();
+};
+
+class upd7759_device : public upd775x_device
+{
+public:
+ upd7759_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ upd7759_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+
+ enum
+ {
+ TIMER_SLAVE_UPDATE
+ };
+
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ emu_timer *m_timer; /* timer */
+
+ DECLARE_WRITE_LINE_MEMBER( start_w );
+};
+
+class upd7756_device : public upd775x_device
+{
+public:
+ upd7756_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ DECLARE_WRITE_LINE_MEMBER( start_w );
+};
+
+extern const device_type UPD7759;
+extern const device_type UPD7756;
+
+#define MCFG_UPD7759_DRQ_CALLBACK(_write) \
+ devcb = &upd7759_device::set_drq_callback(*device, DEVCB_##_write);
+
+#define MCFG_UPD7756_DRQ_CALLBACK(_write) \
+ devcb = &upd7756_device::set_drq_callback(*device, DEVCB_##_write);
+
+#endif /* __UPD7759_H__ */
diff --git a/src/devices/sound/vlm5030.c b/src/devices/sound/vlm5030.c
new file mode 100644
index 00000000000..6a66cfab937
--- /dev/null
+++ b/src/devices/sound/vlm5030.c
@@ -0,0 +1,655 @@
+// license:BSD-3-Clause
+// copyright-holders:Tatsuyuki Satoh
+/*
+ vlm5030.c
+
+ Sanyo VLM5030 emulator
+
+ Written by Tatsuyuki Satoh
+ Based on TMS5220 simulator (tms5220.c)
+
+ +-------,_,-------+
+ GND -- | 1 40 | <- RST
+ (gnd) TST1 -> | 2 39 | ?? TST4
+ OSC2 ck | 3 _ 38 | ?? TST3
+ OSC1 ck | 4 (_) 37 | ?? TST2
+ D0 -> | 5 36 | -> DAO
+ D1 -> | 6 35 | -- VREF (+5v through 5.6k resistor)
+ D2 -> | 7 34 | -> MTE
+ D3 -> | 8 V 33 | -> /ME
+ D4 -> | 9 L 32 | <- VCU
+ D5 -> | 10 M 31 | <- START
+ D6 -> | 11 5 30 | -> BSY
+ D7 -> | 12 0 29 | -- Vdd (+5v)
+ A0 <- | 13 3 28 | -> A15
+ A1 <- | 14 0 27 | -> A14
+ A2 <- | 15 26 | -> A13
+ A3 <- | 16 _ 25 | -> A12
+ A4 <- | 17 (_) 24 | -> A11
+ A5 <- | 18 23 | -> A10
+ A6 <- | 19 22 | -> A9
+ A7 <- | 20 21 | -> A8
+ +-----------------+
+
+TST1 is probably a test mode enable pin, must be grounded for normal operation.
+TST2-4 are some sort of test pins but can be left floating?
+VREF is probably the 0v ref for the output dac
+DAO is the output dac
+/ME is connected to the voice data rom /OE enable
+START strobes in a byte of data over the data bus from host cpu
+OSC1/2 are to both ends of a 3.579545MHz xtal with a 100pf cap from each end to gnd
+VCU makes the data bus select the upper 8 bits of the word register internally instead of the lower 8 bits. it is only useful if you need more than 256 phrases in rom? (recheck this)
+MTE is an output for roms which need to be clocked to latch address before use, or for a latch sitting in front of the voice rom address lines? (recheck this)
+RST not only resets the chip on its rising edge but grabs a byte of mode state data from the data bus on its falling edge? (recheck this)
+
+ note:
+ memory read cycle(==sampling rate) = 122.9u(440clock)
+ interpolator (LC8109 = 2.5ms) = 20 * samples(125us)
+ frame time (20ms) = 4 * interpolator
+ 9bit DAC is composed of 5bit Physical and 3bitPWM.
+
+ todo:
+ Noise Generator circuit without 'machine.rand()' function.
+
+----------- command format (Analytical result) ----------
+
+1)end of speech (8bit)
+:00000011:
+
+2)silent some frame (8bit)
+:????SS01:
+
+SS : number of silent frames
+ 00 = 2 frame
+ 01 = 4 frame
+ 10 = 6 frame
+ 11 = 8 frame
+
+3)-speech frame (48bit)
+function: 6th : 5th : 4th : 3rd : 2nd : 1st :
+end : --- : --- : --- : --- : --- :00000011:
+silent : --- : --- : --- : --- : --- :0000SS01:
+speech :11111122:22233334:44455566:67778889:99AAAEEE:EEPPPPP0:
+
+EEEEE : energy : volume 0=off,0x1f=max
+PPPPP : pitch : 0=noize , 1=fast,0x1f=slow
+111111 : K1 : 48=off
+22222 : K2 : 0=off,1=+min,0x0f=+max,0x10=off,0x11=+max,0x1f=-min
+ : 16 == special function??
+3333 : K3 : 0=off,1=+min,0x07=+max,0x08=-max,0x0f=-min
+4444 : K4 :
+555 : K5 : 0=off,1=+min,0x03=+max,0x04=-max,0x07=-min
+666 : K6 :
+777 : K7 :
+888 : K8 :
+999 : K9 :
+AAA : K10 :
+
+ ---------- chirp table information ----------
+
+DAC PWM cycle == 88system clock , (11clock x 8 pattern) = 40.6KHz
+one chirp == 5 x PWM cycle == 440systemclock(8,136Hz)
+
+chirp 0 : volume 10- 8 : with filter
+chirp 1 : volume 8- 6 : with filter
+chirp 2 : volume 6- 4 : with filter
+chirp 3 : volume 4 : no filter ??
+chirp 4- 5: volume 4- 2 : with filter
+chirp 6-11: volume 2- 0 : with filter
+chirp 12-..: vokume 0 : silent
+
+ ---------- digial output information ----------
+ when ME pin = high , some status output to A0..15 pins
+
+ A0..8 : DAC output value (abs)
+ A9 : DAC sign flag , L=minus,H=Plus
+ A10 : energy reload flag (pitch pulse)
+ A11..15 : unknown
+
+ [DAC output value(signed 6bit)] = A9 ? A0..8 : -(A0..8)
+
+*/
+#include "emu.h"
+#include "vlm5030.h"
+
+/* interpolator per frame */
+#define FR_SIZE 4
+/* samples per interpolator */
+#define IP_SIZE_SLOWER (240/FR_SIZE)
+#define IP_SIZE_SLOW (200/FR_SIZE)
+#define IP_SIZE_NORMAL (160/FR_SIZE)
+#define IP_SIZE_FAST (120/FR_SIZE)
+#define IP_SIZE_FASTER ( 80/FR_SIZE)
+
+
+/* phase value */
+enum {
+ PH_RESET,
+ PH_IDLE,
+ PH_SETUP,
+ PH_WAIT,
+ PH_RUN,
+ PH_STOP,
+ PH_END
+};
+
+/* Pull in the ROM tables */
+#include "tms5110r.inc"
+
+/*
+ speed parameter
+SPC SPB SPA
+ 1 0 1 more slow (05h) : 42ms (150%) : 60sample
+ 1 1 x slow (06h,07h) : 34ms (125%) : 50sample
+ x 0 0 normal (00h,04h) : 25.6ms (100%) : 40samplme
+ 0 0 1 fast (01h) : 20.2ms (75%) : 30sample
+ 0 1 x more fast (02h,03h) : 12.2ms (50%) : 20sample
+*/
+static const int vlm5030_speed_table[8] =
+{
+ IP_SIZE_NORMAL,
+ IP_SIZE_FAST,
+ IP_SIZE_FASTER,
+ IP_SIZE_FASTER,
+ IP_SIZE_NORMAL,
+ IP_SIZE_SLOWER,
+ IP_SIZE_SLOW,
+ IP_SIZE_SLOW
+};
+
+const device_type VLM5030 = &device_creator<vlm5030_device>;
+
+vlm5030_device::vlm5030_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, VLM5030, "VLM5030", tag, owner, clock, "vlm5030", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_channel(NULL),
+ m_coeff(NULL),
+ m_rom(NULL),
+ m_address_mask(0),
+ m_address(0),
+ m_pin_BSY(0),
+ m_pin_ST(0),
+ m_pin_VCU(0),
+ m_pin_RST(0),
+ m_latch_data(0),
+ m_vcu_addr_h(0),
+ m_parameter(0),
+ m_phase(PH_RESET),
+ m_frame_size(0),
+ m_pitch_offset(0),
+ m_interp_step(0),
+ m_interp_count(0),
+ m_sample_count(0),
+ m_pitch_count(0),
+ m_old_energy(0),
+ m_old_pitch(0),
+ m_target_energy(0),
+ m_target_pitch(0),
+ m_new_energy(0),
+ m_new_pitch(0),
+ m_current_energy(0),
+ m_current_pitch(0)
+{
+ memset(m_old_k, 0, sizeof(m_old_k));
+ memset(m_new_k, 0, sizeof(m_new_k));
+ memset(m_current_k, 0, sizeof(m_current_k));
+ memset(m_target_k, 0, sizeof(m_target_k));
+ memset(m_x, 0, sizeof(m_x));
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+/* start VLM5030 with sound rom */
+/* speech_rom == 0 -> use sampling data mode */
+
+void vlm5030_device::device_start()
+{
+ m_coeff = &vlm5030_coeff;
+
+ /* reset input pins */
+ m_pin_RST = m_pin_ST = m_pin_VCU= 0;
+ m_latch_data = 0;
+
+ device_reset();
+ m_phase = PH_IDLE;
+
+ m_rom = region()->base();
+ m_address_mask = (region()->bytes() - 1) & 0xffff;
+
+ m_channel = machine().sound().stream_alloc(*this, 0, 1, clock() / 440);
+
+ /* don't restore "UINT8 *m_rom" when use vlm5030_set_rom() */
+
+ save_item(NAME(m_address));
+ save_item(NAME(m_pin_BSY));
+ save_item(NAME(m_pin_ST));
+ save_item(NAME(m_pin_VCU));
+ save_item(NAME(m_pin_RST));
+ save_item(NAME(m_latch_data));
+ save_item(NAME(m_vcu_addr_h));
+ save_item(NAME(m_parameter));
+ save_item(NAME(m_phase));
+ save_item(NAME(m_interp_count));
+ save_item(NAME(m_sample_count));
+ save_item(NAME(m_pitch_count));
+ save_item(NAME(m_old_energy));
+ save_item(NAME(m_old_pitch));
+ save_item(NAME(m_old_k));
+ save_item(NAME(m_target_energy));
+ save_item(NAME(m_target_pitch));
+ save_item(NAME(m_target_k));
+ save_item(NAME(m_x));
+ machine().save().register_postload(save_prepost_delegate(FUNC(vlm5030_device::restore_state), this));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void vlm5030_device::device_reset()
+{
+ m_phase = PH_RESET;
+ m_address = 0;
+ m_vcu_addr_h = 0;
+ m_pin_BSY = 0;
+
+ m_old_energy = m_old_pitch = 0;
+ m_new_energy = m_new_pitch = 0;
+ m_current_energy = m_current_pitch = 0;
+ m_target_energy = m_target_pitch = 0;
+ memset(m_old_k, 0, sizeof(m_old_k));
+ memset(m_new_k, 0, sizeof(m_new_k));
+ memset(m_current_k, 0, sizeof(m_current_k));
+ memset(m_target_k, 0, sizeof(m_target_k));
+ m_interp_count = m_sample_count = m_pitch_count = 0;
+ memset(m_x, 0, sizeof(m_x));
+ /* reset parameters */
+ setup_parameter( 0x00);
+}
+
+int vlm5030_device::get_bits(int sbit,int bits)
+{
+ int offset = m_address + (sbit>>3);
+ int data;
+
+ data = m_rom[offset&m_address_mask] +
+ (((int)m_rom[(offset+1)&m_address_mask])*256);
+ data >>= (sbit&7);
+ data &= (0xff>>(8-bits));
+
+ return data;
+}
+
+/* get next frame */
+int vlm5030_device::parse_frame()
+{
+ unsigned char cmd;
+ int i;
+
+ /* remember previous frame */
+ m_old_energy = m_new_energy;
+ m_old_pitch = m_new_pitch;
+ for(i=0;i<=9;i++)
+ m_old_k[i] = m_new_k[i];
+
+ /* command byte check */
+ cmd = m_rom[m_address&m_address_mask];
+ if( cmd & 0x01 )
+ { /* extend frame */
+ m_new_energy = m_new_pitch = 0;
+ for(i=0;i<=9;i++)
+ m_new_k[i] = 0;
+ m_address++;
+ if( cmd & 0x02 )
+ { /* end of speech */
+
+ /* logerror("VLM5030 %04X end \n",m_address ); */
+ return 0;
+ }
+ else
+ { /* silent frame */
+ int nums = ( (cmd>>2)+1 )*2;
+ /* logerror("VLM5030 %04X silent %d frame\n",m_address,nums ); */
+ return nums * FR_SIZE;
+ }
+ }
+ /* pitch */
+ m_new_pitch = ( m_coeff->pitchtable[get_bits(1,m_coeff->pitch_bits)] + m_pitch_offset )&0xff;
+ /* energy */
+ m_new_energy = m_coeff->energytable[get_bits(6,m_coeff->energy_bits)];
+
+ /* 10 K's */
+ m_new_k[9] = m_coeff->ktable[9][get_bits(11,m_coeff->kbits[9])];
+ m_new_k[8] = m_coeff->ktable[8][get_bits(14,m_coeff->kbits[8])];
+ m_new_k[7] = m_coeff->ktable[7][get_bits(17,m_coeff->kbits[7])];
+ m_new_k[6] = m_coeff->ktable[6][get_bits(20,m_coeff->kbits[6])];
+ m_new_k[5] = m_coeff->ktable[5][get_bits(23,m_coeff->kbits[5])];
+ m_new_k[4] = m_coeff->ktable[4][get_bits(26,m_coeff->kbits[4])];
+ m_new_k[3] = m_coeff->ktable[3][get_bits(29,m_coeff->kbits[3])];
+ m_new_k[2] = m_coeff->ktable[2][get_bits(33,m_coeff->kbits[2])];
+ m_new_k[1] = m_coeff->ktable[1][get_bits(37,m_coeff->kbits[1])];
+ m_new_k[0] = m_coeff->ktable[0][get_bits(42,m_coeff->kbits[0])];
+
+ m_address+=6;
+ logerror("VLM5030 %04X voice \n",m_address );
+ //fprintf(stderr,"*** target Energy, Pitch, and Ks = %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",m_new_energy, m_new_pitch, m_new_k[0], m_new_k[1], m_new_k[2], m_new_k[3], m_new_k[4], m_new_k[5], m_new_k[6], m_new_k[7], m_new_k[8], m_new_k[9]);
+ return FR_SIZE;
+}
+
+/* realtime update */
+void vlm5030_device::update()
+{
+ m_channel->update();
+}
+
+/* setup parameteroption when RST=H */
+void vlm5030_device::setup_parameter(UINT8 param)
+{
+ /* latch parameter value */
+ m_parameter = param;
+
+ /* bit 0,1 : 4800bps / 9600bps , interporator step */
+ if(param&2) /* bit 1 = 1 , 9600bps */
+ m_interp_step = 4; /* 9600bps : no interporator */
+ else if(param&1) /* bit1 = 0 & bit0 = 1 , 4800bps */
+ m_interp_step = 2; /* 4800bps : 2 interporator */
+ else /* bit1 = bit0 = 0 : 2400bps */
+ m_interp_step = 1; /* 2400bps : 4 interporator */
+
+ /* bit 3,4,5 : speed (frame size) */
+ m_frame_size = vlm5030_speed_table[(param>>3) &7];
+
+ /* bit 6,7 : low / high pitch */
+ if(param&0x80) /* bit7=1 , high pitch */
+ m_pitch_offset = -8;
+ else if(param&0x40) /* bit6=1 , low pitch */
+ m_pitch_offset = 8;
+ else
+ m_pitch_offset = 0;
+}
+
+
+void vlm5030_device::restore_state()
+{
+ int i;
+
+ int interp_effect = FR_SIZE - (m_interp_count%FR_SIZE);
+ /* restore parameter data */
+ setup_parameter( m_parameter);
+
+ /* restore current energy,pitch & filter */
+ m_current_energy = m_old_energy + (m_target_energy - m_old_energy) * interp_effect / FR_SIZE;
+ if (m_old_pitch > 1)
+ m_current_pitch = m_old_pitch + (m_target_pitch - m_old_pitch) * interp_effect / FR_SIZE;
+ for (i = 0; i <= 9 ; i++)
+ m_current_k[i] = m_old_k[i] + (m_target_k[i] - m_old_k[i]) * interp_effect / FR_SIZE;
+}
+
+/* set speech rom address */
+void vlm5030_device::set_rom(void *speech_rom)
+{
+ m_rom = (UINT8 *)speech_rom;
+}
+
+/* get BSY pin level */
+READ_LINE_MEMBER( vlm5030_device::bsy )
+{
+ update();
+ return m_pin_BSY;
+}
+
+/* latch contoll data */
+WRITE8_MEMBER( vlm5030_device::data_w )
+{
+ m_latch_data = (UINT8)data;
+}
+
+/* set RST pin level : reset / set table address A8-A15 */
+WRITE_LINE_MEMBER( vlm5030_device::rst )
+{
+ if( m_pin_RST )
+ {
+ if( !state )
+ { /* H -> L : latch parameters */
+ m_pin_RST = 0;
+ setup_parameter( m_latch_data);
+ }
+ }
+ else
+ {
+ if( state )
+ { /* L -> H : reset chip */
+ m_pin_RST = 1;
+ if( m_pin_BSY )
+ {
+ device_reset();
+ }
+ }
+ }
+}
+
+/* set VCU pin level : ?? unknown */
+WRITE_LINE_MEMBER( vlm5030_device::vcu )
+{
+ /* direct mode / indirect mode */
+ m_pin_VCU = state;
+}
+
+/* set ST pin level : set table address A0-A7 / start speech */
+WRITE_LINE_MEMBER( vlm5030_device::st )
+{
+ int table;
+
+ if( m_pin_ST != state )
+ {
+ /* pin level is change */
+ if( !state )
+ { /* H -> L */
+ m_pin_ST = 0;
+
+ if( m_pin_VCU )
+ { /* direct access mode & address High */
+ m_vcu_addr_h = ((int)m_latch_data<<8) + 0x01;
+ }
+ else
+ {
+ /* start speech */
+ /* check access mode */
+ if( m_vcu_addr_h )
+ { /* direct access mode */
+ m_address = (m_vcu_addr_h&0xff00) + m_latch_data;
+ m_vcu_addr_h = 0;
+ }
+ else
+ { /* indirect accedd mode */
+ table = (m_latch_data&0xfe) + (((int)m_latch_data&1)<<8);
+ m_address = (((int)m_rom[table&m_address_mask])<<8)
+ | m_rom[(table+1)&m_address_mask];
+#if 0
+/* show unsupported parameter message */
+if( m_interp_step != 1)
+ popmessage("No %d %dBPS parameter",table/2,m_interp_step*2400);
+#endif
+ }
+ update();
+ /* logerror("VLM5030 %02X start adr=%04X\n",table/2,m_address ); */
+ /* reset process status */
+ m_sample_count = m_frame_size;
+ m_interp_count = FR_SIZE;
+ /* clear filter */
+ /* start after 3 sampling cycle */
+ m_phase = PH_RUN;
+ }
+ }
+ else
+ { /* L -> H */
+ m_pin_ST = 1;
+ /* setup speech , BSY on after 30ms? */
+ m_phase = PH_SETUP;
+ m_sample_count = 1; /* wait time for busy on */
+ m_pin_BSY = 1; /* */
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void vlm5030_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int buf_count=0;
+ int interp_effect;
+ int i;
+ int u[11];
+ stream_sample_t *buffer = outputs[0];
+
+ /* running */
+ if( m_phase == PH_RUN || m_phase == PH_STOP )
+ {
+ /* playing speech */
+ while (samples > 0)
+ {
+ int current_val;
+
+ /* check new interpolator or new frame */
+ if( m_sample_count == 0 )
+ {
+ if( m_phase == PH_STOP )
+ {
+ m_phase = PH_END;
+ m_sample_count = 1;
+ goto phase_stop; /* continue to end phase */
+ }
+ m_sample_count = m_frame_size;
+ /* interpolator changes */
+ if ( m_interp_count == 0 )
+ {
+ /* change to new frame */
+ m_interp_count = parse_frame(); /* with change phase */
+ if ( m_interp_count == 0 )
+ { /* end mark found */
+ m_interp_count = FR_SIZE;
+ m_sample_count = m_frame_size; /* end -> stop time */
+ m_phase = PH_STOP;
+ }
+ /* Set old target as new start of frame */
+ m_current_energy = m_old_energy;
+ m_current_pitch = m_old_pitch;
+ for(i=0;i<=9;i++)
+ m_current_k[i] = m_old_k[i];
+ /* is this a zero energy frame? */
+ if (m_current_energy == 0)
+ {
+ /*osd_printf_debug("processing frame: zero energy\n");*/
+ m_target_energy = 0;
+ m_target_pitch = m_current_pitch;
+ for(i=0;i<=9;i++)
+ m_target_k[i] = m_current_k[i];
+ }
+ else
+ {
+ /*osd_printf_debug("processing frame: Normal\n");*/
+ /*osd_printf_debug("*** Energy = %d\n",m_current_energy);*/
+ /*osd_printf_debug("proc: %d %d\n",last_fbuf_head,fbuf_head);*/
+ m_target_energy = m_new_energy;
+ m_target_pitch = m_new_pitch;
+ for(i=0;i<=9;i++)
+ m_target_k[i] = m_new_k[i];
+ }
+ }
+ /* next interpolator */
+ /* Update values based on step values 25% , 50% , 75% , 100% */
+ m_interp_count -= m_interp_step;
+ /* 3,2,1,0 -> 1,2,3,4 */
+ interp_effect = FR_SIZE - (m_interp_count%FR_SIZE);
+ m_current_energy = m_old_energy + (m_target_energy - m_old_energy) * interp_effect / FR_SIZE;
+ if (m_old_pitch > 1)
+ m_current_pitch = m_old_pitch + (m_target_pitch - m_old_pitch) * interp_effect / FR_SIZE;
+ for (i = 0; i <= 9 ; i++)
+ m_current_k[i] = m_old_k[i] + (m_target_k[i] - m_old_k[i]) * interp_effect / FR_SIZE;
+ }
+ /* calcrate digital filter */
+ if (m_old_energy == 0)
+ {
+ /* generate silent samples here */
+ current_val = 0x00;
+ }
+ else if (m_old_pitch <= 1)
+ { /* generate unvoiced samples here */
+ current_val = (machine().rand()&1) ? m_current_energy : -m_current_energy;
+ }
+ else
+ {
+ /* generate voiced samples here */
+ current_val = ( m_pitch_count == 0) ? m_current_energy : 0;
+ }
+
+ /* Lattice filter here */
+ u[10] = current_val;
+ for (i = 9; i >= 0; i--)
+ u[i] = u[i+1] - ((-m_current_k[i] * m_x[i]) / 512);
+ for (i = 9; i >= 1; i--)
+ m_x[i] = m_x[i-1] + ((-m_current_k[i-1] * u[i-1]) / 512);
+ m_x[0] = u[0];
+
+ /* clipping, buffering */
+ if (u[0] > 511)
+ buffer[buf_count] = 511<<6;
+ else if (u[0] < -511)
+ buffer[buf_count] = -511<<6;
+ else
+ buffer[buf_count] = (u[0] << 6);
+ buf_count++;
+
+ /* sample count */
+ m_sample_count--;
+ /* pitch */
+ m_pitch_count++;
+ if (m_pitch_count >= m_current_pitch )
+ m_pitch_count = 0;
+ /* size */
+ samples--;
+ }
+/* return;*/
+ }
+ /* stop phase */
+phase_stop:
+ switch( m_phase )
+ {
+ case PH_SETUP:
+ if( m_sample_count <= samples)
+ {
+ m_sample_count = 0;
+ /* logerror("VLM5030 BSY=H\n" ); */
+ /* pin_BSY = 1; */
+ m_phase = PH_WAIT;
+ }
+ else
+ {
+ m_sample_count -= samples;
+ }
+ break;
+ case PH_END:
+ if( m_sample_count <= samples)
+ {
+ m_sample_count = 0;
+ /* logerror("VLM5030 BSY=L\n" ); */
+ m_pin_BSY = 0;
+ m_phase = PH_IDLE;
+ }
+ else
+ {
+ m_sample_count -= samples;
+ }
+ }
+ /* silent buffering */
+ while (samples > 0)
+ {
+ buffer[buf_count++] = 0x00;
+ samples--;
+ }
+}
diff --git a/src/devices/sound/vlm5030.h b/src/devices/sound/vlm5030.h
new file mode 100644
index 00000000000..94a5b846b39
--- /dev/null
+++ b/src/devices/sound/vlm5030.h
@@ -0,0 +1,100 @@
+// license:BSD-3-Clause
+// copyright-holders:Tatsuyuki Satoh
+#pragma once
+
+#ifndef __VLM5030_H__
+#define __VLM5030_H__
+
+ class vlm5030_device : public device_t,
+ public device_sound_interface
+ {
+ public:
+ vlm5030_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~vlm5030_device() {}
+
+ /* set speech rom address */
+ void set_rom(void *speech_rom);
+
+ /* get BSY pin level */
+ DECLARE_READ_LINE_MEMBER( bsy );
+
+ /* latch contoll data */
+ DECLARE_WRITE8_MEMBER( data_w );
+
+ /* set RST pin level : reset / set table address A8-A15 */
+ DECLARE_WRITE_LINE_MEMBER( rst );
+
+ /* set VCU pin level : ?? unknown */
+ DECLARE_WRITE_LINE_MEMBER( vcu );
+
+ /* set ST pin level : set table address A0-A7 / start speech */
+ DECLARE_WRITE_LINE_MEMBER( st );
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ // internal state
+ sound_stream * m_channel;
+
+ /* coefficient tables */
+ const struct tms5100_coeffs *m_coeff;
+
+ /* need to save state */
+
+ UINT8 *m_rom;
+ int m_address_mask;
+ UINT16 m_address;
+ UINT8 m_pin_BSY;
+ UINT8 m_pin_ST;
+ UINT8 m_pin_VCU;
+ UINT8 m_pin_RST;
+ UINT8 m_latch_data;
+ UINT16 m_vcu_addr_h;
+ UINT8 m_parameter;
+ UINT8 m_phase;
+
+ /* state of option paramter */
+ int m_frame_size;
+ int m_pitch_offset;
+ UINT8 m_interp_step;
+
+ UINT8 m_interp_count; /* number of interp periods */
+ UINT8 m_sample_count; /* sample number within interp */
+ UINT8 m_pitch_count;
+
+ /* these contain data describing the current and previous voice frames */
+ UINT16 m_old_energy;
+ UINT8 m_old_pitch;
+ INT16 m_old_k[10];
+ UINT16 m_target_energy;
+ UINT8 m_target_pitch;
+ INT16 m_target_k[10];
+
+ UINT16 m_new_energy;
+ UINT8 m_new_pitch;
+ INT16 m_new_k[10];
+
+ /* these are all used to contain the current state of the sound generation */
+ unsigned int m_current_energy;
+ unsigned int m_current_pitch;
+ int m_current_k[10];
+
+ INT32 m_x[10];
+
+ int get_bits(int sbit,int bits);
+ int parse_frame();
+ void update();
+ void setup_parameter(UINT8 param);
+ void restore_state();
+};
+
+extern const device_type VLM5030;
+
+
+#endif /* __VLM5030_H__ */
diff --git a/src/devices/sound/votrax.c b/src/devices/sound/votrax.c
new file mode 100644
index 00000000000..f6b857c5dea
--- /dev/null
+++ b/src/devices/sound/votrax.c
@@ -0,0 +1,1362 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ votrax.c
+
+ Simple VOTRAX SC-01 simulator based on sample fragments.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "votrax.h"
+
+
+//**************************************************************************
+// DEBUGGING
+//**************************************************************************
+
+#define TEMP_HACKS (1)
+
+#define LOG_TIMING (0)
+#define LOG_LOWPARAM (0)
+#define LOG_GLOTTAL (0)
+#define LOG_TRANSITION (0)
+
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+// note that according to the patent timing circuit, p1/p2 and phi1/phi2
+// run 4x faster than all references in the patent text
+const UINT32 P_CLOCK_BIT = 5; // 5 according to timing diagram
+const UINT32 PHI_CLOCK_BIT = 3; // 3 according to timing diagram
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+const device_type VOTRAX_SC01 = &device_creator<votrax_sc01_device>;
+
+// ROM definition for the Votrax phoneme ROM
+ROM_START( votrax_sc01 )
+ ROM_REGION( 0x200, "phoneme", 0 )
+ ROM_LOAD( "sc01.bin", 0x0000, 0x200, CRC(0353dd6c) SHA1(00e8e497b96a10bd9f4d7e559433c3c209b0d3a8) )
+ROM_END
+
+// textual phoneme names for debugging
+const char *const votrax_sc01_device::s_phoneme_table[64] =
+{
+ "EH3", "EH2", "EH1", "PA0", "DT", "A1", "A2", "ZH",
+ "AH2", "I3", "I2", "I1", "M", "N", "B", "V",
+ "CH", "SH", "Z", "AW1", "NG", "AH1", "OO1", "OO",
+ "L", "K", "J", "H", "G", "F", "D", "S",
+ "A", "AY", "Y1", "UH3", "AH", "P", "O", "I",
+ "U", "Y", "T", "R", "E", "W", "AE", "AE1",
+ "AW2", "UH2", "UH1", "UH", "O2", "O1", "IU", "U1",
+ "THV", "TH", "ER", "EH", "E1", "AW", "PA1", "STOP"
+};
+
+// this waveform is derived from measuring fig. 10 in the patent
+// it is only an approximation
+const double votrax_sc01_device::s_glottal_wave[16] =
+{
+ 0,
+ 16.0/22.0,
+ -22.0/22.0,
+ -17.0/22.0,
+ -15.0/22.0,
+ -10.0/22.0,
+ -7.0/22.0,
+ -4.0/22.0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+};
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// votrax_sc01_device - constructor
+//-------------------------------------------------
+
+votrax_sc01_device::votrax_sc01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, VOTRAX_SC01, "Votrax SC-01", tag, owner, clock, "votrax", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_phoneme_timer(NULL),
+ m_request_cb(*this)
+{
+}
+
+//**************************************************************************
+// READ/WRITE HANDLERS
+//**************************************************************************
+
+//-------------------------------------------------
+// write - handle a write to the control register
+//-------------------------------------------------
+
+WRITE8_MEMBER( votrax_sc01_device::write )
+{
+ // flush out anything currently processing
+ m_stream->update();
+
+ // only 6 bits matter
+ m_phoneme = data & 0x3f;
+const UINT8 *rom = m_rom + (m_phoneme << 3);
+osd_printf_debug("%s: STROBE %s (F1=%X F2=%X FC=%X F3=%X F2Q=%X VA=%X FA=%X CL=%X CLD=%X VD=%X PAC=%X PH=%02X)\n",
+ machine().time().as_string(3), s_phoneme_table[m_phoneme],
+ rom[0] >> 4, rom[1] >> 4, rom[2] >> 4, rom[3] >> 4, rom[4] >> 4, rom[5] >> 4, rom[6] >> 4,
+ rom[3] & 0xf, rom[4] & 0xf, rom[5] & 0xf, rom[6] & 0xf, rom[7]);
+
+ // the STROBE signal resets the phoneme counter
+ m_counter_84 = 0xf;
+
+ // not in the schematics, but necessary to fully reset the request latch
+ m_latch_92 = 0;
+
+ // clear the request signal
+ m_request_cb(m_request_state = m_internal_request = CLEAR_LINE);
+ m_phoneme_timer->adjust(attotime::zero);
+}
+
+
+//-------------------------------------------------
+// inflection_w - handle a write to the
+// inflection bits
+//-------------------------------------------------
+
+WRITE8_MEMBER( votrax_sc01_device::inflection_w )
+{
+ // only 2 bits matter
+ data &= 3;
+ if (m_inflection == data)
+ return;
+
+ // append an inflection marker
+ m_stream->update();
+ m_inflection = data;
+}
+
+
+
+//**************************************************************************
+// CORE LOGIC
+//**************************************************************************
+
+//-------------------------------------------------
+// update_subphoneme_clock_period - re-compute the
+// period of the sub-phoneme clock, as a multiple
+// of the master clock
+//-------------------------------------------------
+
+void votrax_sc01_device::update_subphoneme_clock_period()
+{
+ assert(m_latch_80 < 128);
+
+/*
+ The sub-phoneme timing circuit is based off the switching capacitor
+ technique described in the Votrax patent. Replacing the capacitor
+ ladder with [Rx] representing the effective resistance, the circuit
+ becomes essentially a pair of op-amps:
+
+ VM
+ | i1
+ [R1]
+ | Vc
+ +----------------------+
+ | +---|C1|---+ |
+ [R2] | | | |\
+ |Vb i2 | |\ | +--++\
+ +--[Rx]--+----+-\ | | >
+ | | >--+-----+-/
+ [R3] +----++/ Vc |/
+ |i3 | |/
+ +--------+ Va
+ |
+ [R4]
+ |
+ 0
+
+ We have two op-amps, the left used as a standard amplifier, the right
+ one as a comparator. The circuit triggers when the two inputs of the
+ right op-amp are equal.
+
+ The left part of the circuit (before C1) is simply a current injector.
+ It's all made of resistors, there's no modulated input, so everything
+ is going to be constant. If you don't know about op-amps used as
+ amplifiers, you just need to know that it forces its two inputs to
+ have the same voltage while not sending or providing any current
+ through there (only though its output in fact).
+
+ In the schema, the injected current is i2. Basic equations apply:
+ Va = R4.i3
+ Vb = Va + R3.i3
+ Vb = Va + Rx.i2
+ Vc = Vb + R2.i1
+ VM = Vc + R1.i1
+ i1 = i2 + i3
+
+ And the tipping happens when the voltage on the right of C1 reaches
+ Vc, so:
+ Vc = Va + i2.T/C1
+
+ (i2 being a constant, the integration is kinda easy)
+
+ Some maths later:
+ R3.i3 = Rx.i2 -> i3 = Rx/R3.i2
+ i1 = (1+Rx/R3).i2
+ Va + (Rx + R2 + R2.Rx/R3).i2 = Va + T/C1.i2
+ T = C1*(Rx*(1+R2/R3) + R2)
+
+ Which isn't, interestingly though not surprisingly, dependant on Vm,
+ R1 or R4. And you have to round it to the next multiple of
+ 0.2ms+0.1ms due to the clocking on p2 and its offset to p1 (charging
+ only happens on p1 active), and add one p1/p2 cycle (0.2ms) for the
+ discharge.
+
+ So now you have your base clock, which you have to multiply by 16 to
+ get the phoneme length.
+
+ r2 = 9e3
+ r3 = 1e3
+ c1 = 1000e-12
+ rx = 1/(5KHz * cx)
+*/
+
+ // determine total capacitance
+ double cx = 0;
+ if ((m_latch_80 & 0x01) != 0) cx += 5e-12;
+ if ((m_latch_80 & 0x02) != 0) cx += 11e-12;
+ if ((m_latch_80 & 0x04) != 0) cx += 21e-12;
+ if ((m_latch_80 & 0x08) != 0) cx += 43e-12;
+ if ((m_latch_80 & 0x10) != 0) cx += 86e-12;
+ if ((m_latch_80 & 0x20) != 0) cx += 173e-12;
+ if ((m_latch_80 & 0x40) != 0) cx += 345e-12;
+
+ // apply the equation above to determine charging time
+ // note that the 5kHz listed above for P1 is for a nominal master
+ // clock frequency of 1.28MHz, meaning it is master clock / 128
+ // which should be the P1 clock but appears to be a bit different
+ double p1_frequency = double(m_master_clock_freq) / double(1 << (P_CLOCK_BIT + 2));
+ double rx = 1.0 / (p1_frequency * cx);
+ double period = 1000e-12 * (rx * (1.0 + 9e3 / 1e3) + 9e3);
+
+ // convert to master clock cycles and round up
+ m_subphoneme_period = UINT32(ceil(period * double(m_master_clock_freq)));
+}
+
+//-------------------------------------------------
+// bits_to_caps - compute the final capacity from
+// a grid of bit-selected caps
+//-------------------------------------------------
+
+double votrax_sc01_device::bits_to_caps(UINT32 value, int caps_count, const double *caps_values)
+{
+ double sum = 0;
+ for(int i=0; i<caps_count; i++)
+ if(value & (1<<i))
+ sum += caps_values[i];
+ return sum;
+}
+
+/*
+ Playing with analog filters, or where all the magic filter formulas are coming from.
+
+ First you start with an analog circuit, for instance this one:
+
+ | +--[R2]--+
+ | | |
+ | +--|C2|--+<V1 +--|C3|--+
+ | | | | |
+ | Vi +--[R1]--+ | |\ | | |\ |
+ | -----+ +----+--+-\ | +--+-\ |
+ | +--|C1|--+ | >--+--[Rx]--+ | >--+----- Vo
+ | | 0-++/ 0-++/ |
+ | | |/ +--[R0]--+ |/ |
+ | | | | |
+ | | | /| | |
+ | | | /-+--+--[R0]--+
+ | +--[R4]-------+--< |
+ | V2^ \++-0
+ | \|
+
+ You need to determine the transfer function H(s) of the circuit, which is
+ defined as the ratio Vo/Vi. To do that, you use some properties:
+
+ - The intensity through an element is equal to the voltage
+ difference through the element divided by the impedence
+
+ - The impedence of a resistance is equal to its resistance
+
+ - The impedence of a capacitor is 1/(s*C) where C is its capacitance
+
+ - The impedence of elements in series is the sum of the impedences
+
+ - The impedence of elements in parallel is the inverse of the sum of
+ the inverses
+
+ - The sum of all intensities flowing into a node is 0 (there's no
+ charge accumulation in a wire)
+
+ - An operational amplifier in looped mode is an interesting beast:
+ the intensity at its two inputs is always 0, and the voltage is
+ forced identical between the inputs. In our case, since the '+'
+ inputs are all tied to ground, that means that the '-' inputs are at
+ voltage 0, intensity 0.
+
+ From here we can build some equations. Noting:
+ X1 = 1/(1/R1 + s*C1)
+ X2 = 1/(1/R2 + s*C2)
+ X3 = 1/(s*C3)
+
+ Then computing the intensity flow at each '-' input we have:
+ Vi/X1 + V2/R4 + V1/X2 = 0
+ V2/R0 + Vo/R0 = 0
+ V1/Rx + Vo/X3 = 0
+
+ Wrangling the equations, one eventually gets:
+ | 1 + s * C1*R1
+ | Vo/Vi = H(s) = (R4/R1) * -------------------------------------------
+ | 1 + s * C3*Rx*R4/R2 + s^2 * C2*C3*Rx*R4
+
+ To check the mathematics between the 's' stuff, check "Laplace
+ transform". In short, it's a nice way of manipulating derivatives
+ and integrals without having to manipulate derivatives and
+ integrals.
+
+ With that transfer function, we first can compute what happens to
+ every frequency in the input signal. You just compute H(2i*pi*f)
+ where f is the frequency, which will give you a complex number
+ representing the amplitude and phase effect. To get the usual dB
+ curves, compute 20*log10(abs(v))).
+
+ Now, once you have an analog transfer function, you can build a
+ digital filter from it using what is called the bilinear transform.
+
+ In our case, we have an analog filter with the transfer function:
+ | 1 + k[0]*s
+ | H(s) = -------------------------
+ | 1 + k[1]*s + k[2]*s^2
+
+ We can always reintroduce the global multipler later, and it's 1 in
+ most of our cases anyway.
+
+ The we pose:
+ | z-1
+ | s(z) = zc * ---
+ | z+1
+
+ where zc = 2*pi*fr/tan(pi*fr/fs)
+ with fs = sampling frequency
+ and fr = most interesting frequency
+
+ Then we rewrite H in function of negative integer powers of z.
+
+ Noting m0 = zc*k[0], m1 = zc*k[1], m2=zc*zc*k[2],
+
+ a little equation wrangling then gives:
+
+ | (1+m0) + (3+m0) *z^-1 + (3-m0) *z^-2 + (1-m0)*z^-3
+ | H(z) = ----------------------------------------------------------------
+ | (1+m1+m2) + (3+m1-m2)*z^-1 + (3-m1-m2)*z^-2 + (1-m1+m2)*z^-3
+
+ That beast in the digital transfer function, of which you can
+ extract response curves by posing z = exp(2*i*pi*f/fs).
+
+ Note that the bilinear transform is an approximation, and H(z(f)) =
+ H(s(f)) only at frequency fr. And the shape of the filter will be
+ better respected around fr. If you look at the curves of the
+ filters we're interested in, the frequency:
+ fr = sqrt(abs(k[0]*k[1]-k[2]))/(2*pi*k[2])
+
+ which is a (good) approximation of the filter peak position is a
+ good choice.
+
+ Note that terminology wise, the "standard" bilinear transform is
+ with fr = fs/2, and using a different fr is called "pre-warping".
+
+ So now we have a digital transfer function of the generic form:
+
+ | a[0] + a[1]*z^-1 + a[2]*z^-2 + a[3]*z^-3
+ | H(z) = --------------------------------------------
+ | b[0] + b[1]*z^-1 + b[2]*z^-2 + b[3]*z^-3
+
+ The magic then is that the powers of z represent time in samples.
+ Noting x the input stream and y the output stream, you have:
+ H(z) = y(z)/x(z)
+
+ or in other words:
+ y*b[0]*z^0 + y*b[1]*z^-1 + y*b[2]*z^-2 + y*b[3]*z^-3 = x*a[0]*z^0 + x*a[1]*z^-1 + x*a[2]*z^-2 + x*a[3]*z^-3
+
+ i.e.
+
+ y*z^0 = (x*a[0]*z^0 + x*a[1]*z^-1 + x*a[2]*z^-2 + x*a[3]*z^-3 - y*b[1]*z^-1 - y*b[2]*z^-2 - y*b[3]*z^-3) / b[0]
+
+ and powers of z being time in samples,
+
+ y[0] = (x[0]*a[0] + x[-1]*a[1] + x[-2]*a[2] + x[-3]*a[3] - y[-1]*b[1] - y[-2]*b[2] - y[-3]*b[3]) / b[0]
+
+ So you have a filter you can apply. Note that this is why you want
+ negative powers of z. Positive powers would mean looking into the
+ future (which is possible in some cases, in particular with x, and
+ has some very interesting properties, but is not very useful in
+ analog circuit simulation).
+
+ Note that if you have multiple inputs, all this stuff is linear.
+ Or, in other words, you just have to split it in multiple circuits
+ with only one input connected each time and sum the results. It
+ will be correct.
+
+ Also, since we're in practice in a dynamic system, for an amplifying
+ filter (i.e. where things like r4/r1 is not 1), it's better to
+ proceed in two steps:
+
+ - amplify the input by the current value of the coefficient, and
+ historize it
+ - apply the now non-amplifying filter to the historized amplified
+ input
+
+ That way reduces the probability of the output boucing all over the
+ place.
+
+*/
+
+
+//-------------------------------------------------------------
+// filter_s_to_z - analog to digital filter transformation
+//-------------------------------------------------------------
+
+void votrax_sc01_device::filter_s_to_z(const double *k, double fs, double *a, double *b)
+{
+ double fpeak = sqrt(fabs(k[0]*k[1]-k[2]))/(2*M_PI*k[2]);
+ double zc = 2*M_PI*fpeak/tan(M_PI*fpeak/fs);
+
+ double m0 = zc*k[0];
+ double m1 = zc*k[1];
+ double m2 = zc*zc*k[2];
+
+ a[0] = 1+m0;
+ a[1] = 3+m0;
+ a[2] = 3-m0;
+ a[3] = 1-m0;
+ b[0] = 1+m1+m2;
+ b[1] = 3+m1-m2;
+ b[2] = 3-m1-m2;
+ b[3] = 1-m1+m2;
+}
+
+
+//-------------------------------------------------------------
+// apply_filter - apply the digital filter (before output
+// shifting, so y[0] is one step in the past)
+//-------------------------------------------------------------
+double votrax_sc01_device::apply_filter(const double *x, const double *y, const double *a, const double *b)
+{
+ return (x[0]*a[0] + x[1]*a[1] + x[2]*a[2] + x[3]*a[3] - y[0]*b[1] - y[1]*b[2] - y[2]*b[3]) / b[0];
+}
+
+
+//-------------------------------------------------------------
+// shift_hist - shift a value in an output history
+//-------------------------------------------------------------
+
+void votrax_sc01_device::shift_hist(double val, double *hist_array, int hist_size)
+{
+ for(int i = 0; i < hist_size-1; i++)
+ hist_array[hist_size-1-i] = hist_array[hist_size-2-i];
+ hist_array[0] = val;
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests
+// for our sound stream
+//-------------------------------------------------
+
+void votrax_sc01_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ // determine how many master half-clocks per sample
+ int half_clocks_per_sample = (m_master_clock_freq * 2) / stream.sample_rate();
+
+ // iterate over clocks (samples)
+ stream_sample_t *dest = outputs[0];
+ while (samples--)
+ {
+ // run the digital logic at the master clock rate
+ double glottal_out = 0;
+ UINT8 noise_out_digital = 0;
+ for (int curclock = 0; curclock < half_clocks_per_sample; curclock++)
+ {
+if (LOG_TIMING | LOG_LOWPARAM | LOG_GLOTTAL | LOG_TRANSITION)
+{
+ if (m_counter_34 % 32 == 0 && m_master_clock == 0)
+ {
+ if (LOG_TIMING)
+ osd_printf_debug("MCLK C034 L070 L072 BET1 P1 P2 PHI1 PHI2 PH1' PH2' SUBC C088 C084 L092 IIRQ ");
+ if (LOG_LOWPARAM)
+ osd_printf_debug("F132 F114 F112 F142 L080 ");
+ if (LOG_GLOTTAL)
+ osd_printf_debug("C220 C222 C224 C234 C236 FGAT GLSY ");
+ if (LOG_TRANSITION)
+ osd_printf_debug("0625 C046 L046 A0-2 L168 L170 FC VA FA F1 F2 F3 F2Q ");
+ osd_printf_debug("\n");
+ }
+ if (LOG_TIMING)
+ osd_printf_debug("%4X %4X %4X %4X %4X %4X %4X %4X %4X %4X %4X %4X %4X %4X %4X %4X ", m_master_clock, m_counter_34, m_latch_70, m_latch_72, m_beta1, m_p1, m_p2, m_phi1, m_phi2, m_phi1_20, m_phi2_20, m_subphoneme_count, m_clock_88, m_counter_84, m_latch_92, m_internal_request);
+ if (LOG_LOWPARAM)
+ osd_printf_debug("%4X %4X %4X %4X %4X ", m_srff_132, m_srff_114, m_srff_112, m_srff_142, m_latch_80);
+ if (LOG_GLOTTAL)
+ osd_printf_debug("%4X %4X %4X %4X %4X %4X %4X ", m_counter_220, m_counter_222, m_counter_224, m_counter_234, m_counter_236, m_fgate, m_glottal_sync);
+ if (LOG_TRANSITION)
+ osd_printf_debug("%4X %4X %4X %4X %4X %4X %4X %4X %4X %4X %4X %4X %4X ", m_0625_clock, m_counter_46, m_latch_46, m_latch_72 & 7, m_latch_168, m_latch_170, m_fc, m_va, m_fa, m_f1, m_f2, m_f3, m_f2q);
+ osd_printf_debug("\n");
+}
+
+ //==============================================
+ //
+ // Timing circuit (patent figure 2a)
+ //
+ //==============================================
+
+ // update master clock
+ m_master_clock ^= 1;
+
+ // on the falling edge of the master clock, advance the 10-bit counter at 34
+ UINT8 old_latch_72 = m_latch_72;
+ if (m_master_clock == 0)
+ m_counter_34 = (m_counter_34 + 1) & 0x3ff;
+ else
+ {
+ m_latch_70 = m_counter_34 & 0xf;
+ m_latch_72 = ((m_counter_34 >> 4) & 7) | ((m_counter_34 >> 6) & 8);
+ }
+
+ // derive beta 1 clock:
+ // set if m_latch_70.0 == 1
+ // reset if m_latch_70.0 == 0
+// UINT8 old_beta1 = m_beta1;
+ m_beta1 = BIT(m_latch_70, 0);
+
+ // derive p2 clock:
+ // set if (m_counter_34.P_CLOCK_BIT & clock) == 1
+ // reset if (m_counter_34.P_CLOCK_BIT == 0)
+ UINT8 old_p2 = m_p2;
+ if (BIT(m_counter_34, P_CLOCK_BIT) & m_master_clock)
+ m_p2 = 1;
+ else if (!BIT(m_counter_34, P_CLOCK_BIT))
+ m_p2 = 0;
+
+ // derive p1 clock:
+ // set if (!m_counter_34.P_CLOCK_BIT & clock) == 1
+ // reset if (m_counter_34.P_CLOCK_BIT == 1)
+// UINT8 old_p1 = m_p1;
+ if (BIT(~m_counter_34, P_CLOCK_BIT) & m_master_clock)
+ m_p1 = 1;
+ else if (BIT(m_counter_34, P_CLOCK_BIT))
+ m_p1 = 0;
+
+ // derive phi2 clock:
+ // set if (m_counter_34.PHI_CLOCK_BIT & clock) == 1
+ // reset if (m_counter_34.PHI_CLOCK_BIT == 0)
+ UINT8 old_phi2 = m_phi2;
+ if (BIT(m_counter_34, PHI_CLOCK_BIT) & m_master_clock)
+ m_phi2 = 1;
+ else if (!BIT(m_counter_34, PHI_CLOCK_BIT))
+ m_phi2 = 0;
+
+ // derive phi1 clock:
+ // set if (!m_counter_34.PHI_CLOCK_BIT & clock) == 1
+ // reset if (m_counter_34.PHI_CLOCK_BIT == 1)
+ UINT8 old_phi1 = m_phi1;
+ if (BIT(~m_counter_34, PHI_CLOCK_BIT) & m_master_clock)
+ m_phi1 = 1;
+ else if (BIT(m_counter_34, PHI_CLOCK_BIT))
+ m_phi1 = 0;
+
+ // derive alternate phi2 clock:
+ // set if (m_counter_34.PHI_CLOCK_BIT & clock) == 1
+ // reset if (m_counter_34.PHI_CLOCK_BIT == 0)
+ UINT8 old_phi2_20 = m_phi2_20;
+ if (BIT(m_counter_34, PHI_CLOCK_BIT + 2) & m_master_clock)
+ m_phi2_20 = 1;
+ else if (!BIT(m_counter_34, PHI_CLOCK_BIT + 2))
+ m_phi2_20 = 0;
+
+ // derive alternate phi1 clock:
+ // set if (!m_counter_34.PHI_CLOCK_BIT & clock) == 1
+ // reset if (m_counter_34.PHI_CLOCK_BIT == 1)
+// UINT8 old_phi1_20 = m_phi1_20;
+ if (BIT(~m_counter_34, PHI_CLOCK_BIT + 2) & m_master_clock)
+ m_phi1_20 = 1;
+ else if (BIT(m_counter_34, PHI_CLOCK_BIT + 2))
+ m_phi1_20 = 0;
+
+ // determine rising edges of each clock of interest
+// UINT8 beta1_rising = (old_beta1 ^ m_beta1) & m_beta1;
+ UINT8 p2_rising = (old_p2 ^ m_p2) & m_p2;
+// UINT8 p1_rising = (old_p1 ^ m_p1) & m_p1;
+ UINT8 phi2_rising = (old_phi2 ^ m_phi2) & m_phi2;
+ UINT8 phi1_rising = (old_phi1 ^ m_phi1) & m_phi1;
+ UINT8 phi2_20_rising = (old_phi2_20 ^ m_phi2_20) & m_phi2_20;
+// UINT8 phi1_20_rising = (old_phi1_20 ^ m_phi1_20) & m_phi1_20;
+ UINT8 a0_rising = BIT((old_latch_72 ^ m_latch_72) & m_latch_72, 0);
+ UINT8 a2_rising = BIT((old_latch_72 ^ m_latch_72) & m_latch_72, 2);
+ UINT8 _125k_rising = BIT((old_latch_72 ^ m_latch_72) & m_latch_72, 3);
+
+ // track subphoneme counter state
+ if (!(m_latch_42 | m_phi1))
+ m_subphoneme_count = 0;
+ else
+ m_subphoneme_count++;
+ if (p2_rising)
+ m_latch_42 = (m_subphoneme_count < m_subphoneme_period);
+
+ // update the state of the subphoneme clock line
+ UINT8 old_clock_88 = m_clock_88;
+ m_clock_88 = !m_latch_42; //!(m_latch_42 | m_phi1); -- figure 7 seems to be wrong here
+ UINT8 clock_88_rising = (old_clock_88 ^ m_clock_88) & m_clock_88;
+
+ // the A/R line holds the counter in reset except during phoneme processing,
+ // when it is clocked on the rising edge of the subphoneme timer clock
+ if (m_internal_request != CLEAR_LINE)
+ m_counter_84 = 0xf;
+ else if (clock_88_rising)
+ {
+ m_counter_84 = (m_counter_84 - 1) & 0x0f;
+osd_printf_debug("counter=%d\n", m_counter_84);
+ }
+
+ // clock the zero count latch
+ if (p2_rising)
+ m_latch_92 = ((m_counter_84 == 0) | (m_latch_92 << 1)) & 3;
+
+ // once both bits are set, the request line goes high
+ if (m_latch_92 == 3)
+ {
+ // if the request line was previously low, reset the VD/CLD flip-flops
+ if (m_internal_request == CLEAR_LINE)
+ m_srff_112 = m_srff_114 = 0;
+ m_internal_request = ASSERT_LINE;
+ }
+
+ //==============================================
+ //
+ // Low parameter clocking (patent figure 2b)
+ //
+ //==============================================
+
+ // fetch ROM data; note that the address lines come directly from
+ // counter_34 and not from the latches, which are 1 cycle delayed
+ UINT8 romdata = m_rom[(m_phoneme << 3) | ((m_counter_34 >> 4) & 7)];
+
+ // update the ROM data; ROM format is (upper nibble/lower nibble)
+ // +00 = F1 parameter / 0
+ // +01 = F2 parameter / 0
+ // +02 = FC parameter / 0
+ // +03 = F3 parameter / CL
+ // +04 = F2Q Parameter / CLD
+ // +05 = VA Parameter / VD
+ // +06 = FA Parameter / PAC
+ // +07 = Phoneme timing (full 7 bits)
+
+ // latch a new value from ROM on phi2
+ UINT8 a = m_latch_72 & 7;
+ UINT8 romdata_swapped;
+ if (phi2_rising)
+ {
+ switch (a)
+ {
+ // update CL
+ case 3:
+ m_srff_132 = m_srff_114 & BIT(~romdata, 3);
+ break;
+
+ // update CLD
+ case 4:
+ romdata_swapped = (BIT(romdata, 0) << 3) | (BIT(romdata, 1) << 2) | (BIT(romdata, 2) << 1) | (BIT(romdata, 3) << 0);
+ if (m_counter_84 != 0 && romdata_swapped == (m_counter_84 ^ 0xf))
+ m_srff_114 = 1;
+ break;
+
+ // update VD
+ case 5:
+ romdata_swapped = (BIT(romdata, 0) << 3) | (BIT(romdata, 1) << 2) | (BIT(romdata, 2) << 1) | (BIT(romdata, 3) << 0);
+ if (m_counter_84 != 0 && romdata_swapped == (m_counter_84 ^ 0xf))
+ m_srff_112 = 1;
+ break;
+
+ // update FF == PAC & (VA | FA)
+ case 6:
+ m_srff_142 = BIT(romdata, 3);
+ break;
+
+ // update PH
+ case 7:
+ if (m_latch_80 != (romdata & 0x7f))
+ {
+ m_latch_80 = romdata & 0x7f;
+osd_printf_debug("[PH=%02X]\n", m_latch_80);
+ UINT32 old_period = m_subphoneme_period;
+ update_subphoneme_clock_period();
+ m_subphoneme_count = (m_subphoneme_count * m_subphoneme_period) / old_period;
+ m_phoneme_timer->adjust(attotime::zero);
+ }
+ break;
+ }
+ }
+
+ //==============================================
+ //
+ // Glottal circuit (patent figure 6)
+ //
+ //==============================================
+
+ // determine the TC output from the counters (note that TC requires ET)
+ UINT8 counter_222_tc = (m_counter_222 == 0xf);
+ UINT8 counter_220_tc = (m_counter_220 == 0xf && counter_222_tc);
+ UINT8 counter_224_tc = (m_counter_224 == 0xf && counter_222_tc);
+
+ // clock glottal counter 224 on rising edge of a0
+ if (a0_rising)
+ {
+ // counter 224 is only enabled if TC of counter 222 is 1
+ if (counter_222_tc)
+ {
+ // if counter 220's TC is 1, do a load instead of a count
+ if (counter_220_tc)
+ m_counter_224 = (m_inflection << 1) | ((~m_f1 & 0x8) >> 3);
+ else
+ m_counter_224 = (m_counter_224 + 1) & 0xf;
+ }
+ }
+
+ // clock remaining glottal counters (220, 222, 236) on rising edge of phi2
+ if (phi2_20_rising)
+ {
+ // counter 220 is only enabled if TC of counter 222 is 1
+ if (counter_222_tc)
+ {
+ // if counter 220's TC is 1, do a load instead of a count
+ if (counter_220_tc)
+ m_counter_220 = (m_inflection << 1) | ((~m_f1 & 0x8) >> 3);
+ else
+ m_counter_220 = (m_counter_220 + 1) & 0xf;
+ }
+
+ // counter 222 is always enabled
+ if (1)
+ {
+ // if counter 220's TC is 1, do a load instead of a count
+ if (counter_220_tc)
+ m_counter_222 = (~m_f1 & 0x7) << 1;
+ else
+ m_counter_222 = (m_counter_222 + 1) & 0xf;
+ }
+
+ // counter 236 is always enabled
+ if (1)
+ {
+ m_counter_236 = (m_counter_236 + 1) & 0xf;
+
+ // rising edge of Q1 from counter 236 clocks counter 234
+ if ((m_counter_236 & 0x3) == 0x2)
+ {
+ // counter 234 is only enabled if it has not reached terminal
+ if (m_counter_234 != 0xf)
+ m_counter_234 = (m_counter_234 + 1) & 0xf;
+ }
+ }
+ }
+
+ // update FGATE state
+ if (counter_220_tc)
+ m_fgate = 0;
+ if (counter_224_tc)
+ m_fgate = 1;
+
+ // apply asynchronous clear to counters 234/236
+ if (counter_220_tc && m_phi1_20)
+ m_counter_236 = m_counter_234 = 0;
+
+ // derive glottal circuit output signals
+#if !TEMP_HACKS
+ UINT8 old_glottal_sync = m_glottal_sync;
+#endif
+ m_glottal_sync = (m_counter_234 == 0);
+ glottal_out = s_glottal_wave[m_counter_234];
+
+ //==============================================
+ //
+ // Transition circuit (patent figure 3a/3b)
+ //
+ //==============================================
+
+ // divide 1.25k clock by 2 (lower-left of 46)
+ UINT8 old_0625_clock = m_0625_clock;
+ if (_125k_rising)
+ m_0625_clock = !m_0625_clock;
+ UINT8 _0625_rising = (old_0625_clock ^ m_0625_clock) & m_0625_clock;
+
+ // update counter above
+ if (_0625_rising)
+ {
+ if (m_counter_46 == 0xf)
+ m_counter_46 = 0xd;
+ else
+ m_counter_46 = (m_counter_46 + 1) & 0xf;
+ }
+
+ // and then the latch to the right
+ if (a2_rising)
+ m_latch_46 = (BIT(m_counter_46, 1) << 0) |
+ (BIT(m_latch_46, 0) << 1) |
+ (m_0625_clock << 2) |
+ (BIT(m_latch_46, 2) << 3);
+
+#if TEMP_HACKS
+ m_latch_46 = 0xf;
+#endif
+
+ // determine the read/write signal
+ UINT8 ram_write = 0;
+ switch (a)
+ {
+ // write if not FF and low 2 bits of latch
+ // FF is the S/R flip-flop at 142 ANDed with !(/FA & /VA)
+ case 0: case 1: case 2: case 3: case 4:
+ if (!(m_srff_142 & !((m_fa == 0) & (m_va == 0))) && (m_latch_46 & 0x3) == 0x3)
+ ram_write = 1;
+ break;
+
+ case 5:
+ if ((m_latch_46 & 0xc) == 0xc && m_srff_112)
+ ram_write = 1;
+ break;
+
+ case 6:
+ if ((m_latch_46 & 0xc) == 0xc && m_srff_114)
+ ram_write = 1;
+ break;
+ }
+
+ // gate on the phi2 clock (OR gate @ 172)
+ ram_write &= m_phi2;
+
+ // write the transitioned values to RAM if requested
+ // (note we consolidate the serial addition and clocking steps here)
+ if (ram_write)
+ {
+ UINT8 old = (m_latch_168 << 4) | m_latch_170;
+ m_ram[a] = old - (old >> 3) + ((romdata & 0xf0) >> 3);
+ }
+
+ // latch some parameter values on rising edge of phi2
+ if (phi2_rising)
+ {
+ switch (a)
+ {
+ case 2:
+ m_fc = m_latch_168;
+ break;
+
+ case 5:
+ m_va = m_latch_168;
+ break;
+
+ case 6:
+ m_fa = m_latch_168;
+ break;
+ }
+ }
+
+ // latch remaining parameter values on rising edge of (phi2 & glottal sync)
+#if TEMP_HACKS
+ if (phi2_rising)
+#else
+ UINT8 old_phi2_glottal = (old_phi2 & old_glottal_sync);
+ UINT8 new_phi2_glottal = m_phi2 & m_glottal_sync;
+ if ((old_phi2_glottal ^ new_phi2_glottal) & new_phi2_glottal)
+#endif
+ switch (a)
+ {
+ case 0:
+ m_f1 = m_latch_168;
+ break;
+
+ case 1:
+ m_f2 = (m_latch_168 << 1) | (m_latch_170 >> 3);
+ break;
+
+ case 3:
+ m_f3 = m_latch_168;
+ break;
+
+ case 4:
+ m_f2q = m_latch_168;
+ break;
+ }
+
+ // latch value from RAM on rising edge of phi1
+ if (phi1_rising)
+ {
+ m_latch_168 = m_ram[a] >> 4;
+ m_latch_170 = m_ram[a] & 0xf;
+ }
+
+ //==============================================
+ //
+ // Noise generator circuit (patent figure 8)
+ //
+ //==============================================
+
+ // nose is clocked by the NOR of /FA and P1
+ UINT8 old_noise_clock = m_noise_clock;
+ m_noise_clock = !((m_fa == 0) | m_p1);
+ UINT8 noise_clock_rising = (old_noise_clock ^ m_noise_clock) & m_noise_clock;
+ UINT8 noise_clock_falling = (old_noise_clock ^ m_noise_clock) & old_noise_clock;
+
+ // falling edge clocks the shift register
+ if (noise_clock_falling)
+ {
+ // shift register 252 is actually 4 shift registers (2 4-bit, 2 5-bit)
+ // d1 and d3 are the 4-bit registers, d2 and d4 are the 5-bit registers
+ // XOR'ed input goes into d4, which shifts in to d2, then d3, then d1
+ // thus the full 18-bit value is effectively
+ //
+ // d4 = (m_shift_252 >> 0) & 0x1f;
+ // d2 = (m_shift_252 >> 5) & 0x1f;
+ // d3 = (m_shift_252 >> 10) & 0xf;
+ // d1 = (m_shift_252 >> 14) & 0xf;
+ //
+ // input at the low end is ((d1+4 ^ d2+5) ^ (d4+4 ^ d4+5)) ^ !(counter2 | counter3)
+ // output is tapped at d3+4
+
+ UINT32 old_shift = m_shift_252;
+ m_shift_252 <<= 1;
+ m_shift_252 |= ((BIT(old_shift, 17) ^ BIT(old_shift, 9)) ^ (BIT(old_shift, 3) ^ BIT(old_shift, 4))) ^
+ ((m_counter_250 & 0xc) == 0);
+ }
+
+ // rising edge clocks the counter
+ if (noise_clock_rising)
+ {
+ // counter is reset to 1 if terminal, otherwise it increments
+ if (m_counter_250 == 0xf)
+ m_counter_250 = 0x1;
+ else
+ m_counter_250 = (m_counter_250 + 1) & 0xf;
+ }
+
+ // compute final noise out signal
+ noise_out_digital = !(BIT(m_shift_252, 13) & (m_fgate | (m_va == 0)));
+ }
+
+ // TODO: cache the filters
+ // filter coefs
+ double k[3], a[4], b[4];
+
+ // base frequencies
+ double fc = m_master_clock_freq / 30.0; // Nominal is 20KHz
+ double fs = stream.sample_rate();
+
+ // useful temporaries
+ double rcp, rcq, rca;
+
+ // amplification stage
+ static const double va_caps[4] = { 27, 53, 107, 213 };
+ double va_out = glottal_out * bits_to_caps(m_va, 4, va_caps) / 400;
+
+ shift_hist(va_out, m_va_hist, 4);
+
+
+ // noise shaping
+ static const double fa_caps[4] = { 27, 53, 107, 213 };
+ rcp = bits_to_caps(m_fa, 4, fa_caps);
+
+ shift_hist(-noise_out_digital * 400*rcp/(358.0*100000*566*(fc*rcp*1e-12 + 1.0/100000 + 1.0/2000)), m_ni_hist, 4);
+
+ k[0] = 400/(fc*358);
+ k[1] = 400*400/(fc*358*566);
+ k[2] = 400*400/(fc*fc*358*358);
+
+ filter_s_to_z(k, fs, a, b);
+ double no_out = apply_filter(m_ni_hist, m_no_hist, a, b);
+ shift_hist(no_out, m_no_hist, 4);
+
+
+ // stage 1 filter
+
+ static const double s1_p_caps[4] = { 16.4, 33, 66, 130 };
+ rcp = 24 + bits_to_caps(m_f1, 4, s1_p_caps);
+ rcq = 20;
+
+ k[0] = 253/(fc*270);
+ k[1] = 1080*rcq/(fc*270*rcp);
+ k[2] = 1080*1080/(fc*fc*270*rcp);
+
+ filter_s_to_z(k, fs, a, b);
+ double s1_out = apply_filter(m_va_hist, m_s1_hist, a, b);
+ shift_hist(s1_out, m_s1_hist, 4);
+
+
+ // stage 2 filter, glottal half
+
+ static const double s2_p_caps[5] = { 14, 28, 56, 113, 226 };
+ static const double s2_q_caps[4] = { 23, 46, 93, 186 };
+ rcp = 46 + bits_to_caps(m_f2, 5, s2_p_caps);
+ rcq = 20 + bits_to_caps(m_f2q, 4, s2_q_caps);;
+
+ k[0] = 400/(fc*470);
+ k[1] = 620*rcq/(fc*470*rcp);
+ k[2] = 620*620/(fc*fc*470*rcp);
+
+ filter_s_to_z(k, fs, a, b);
+ double s2g_out = apply_filter(m_s1_hist, m_s2g_hist, a, b);
+ shift_hist(s2g_out, m_s2g_hist, 4);
+
+
+ // stage 2 filter, noise half (rcp and rcq kept from stage 2 glottal)
+
+ static const double s2_n_caps[5] = { 19, 38, 76, 152 };
+ rca = bits_to_caps(m_fc, 4, s2_n_caps);
+
+ shift_hist(-no_out*rcq*rca/(470*rcp), m_s2ni_hist, 4);
+
+ k[0] = 400/(fc*470);
+ k[1] = 620*rcq/(fc*470*rcp);
+ k[2] = 620*620/(fc*fc*470*rcp);
+
+ filter_s_to_z(k, fs, a, b);
+ double s2n_out = apply_filter(m_s2ni_hist, m_s2n_hist, a, b);
+ shift_hist(s2n_out, m_s2n_hist, 4);
+
+ // sum the stage 2 outputs
+ double s2_out = s2g_out + s2n_out;
+ shift_hist(s2_out, m_s2_hist, 4);
+
+
+ // stage 3 filter
+
+ static const double s3_p_caps[4] = { 21, 42, 84, 168 };
+ rcp = 76 + bits_to_caps(m_f3, 4, s3_p_caps);
+ rcq = 20;
+
+ k[0] = 0;
+ k[1] = 420*rcq/(fc*390*rcp);
+ k[2] = 420*420/(fc*fc*390*rcp);
+
+ filter_s_to_z(k, fs, a, b);
+ double s3_out = apply_filter(m_s2_hist, m_s3_hist, a, b);
+ shift_hist(s3_out, m_s3_hist, 4);
+
+
+ // stage 4 filter, noise injection
+
+ // The resulting non-amplifying filter is identical, so we
+ // inject instead of splitting
+
+ static const double s4_n_caps[4] = { 24, 48, 96, 192 };
+ rca = 115 + bits_to_caps(~m_fc, 4, s4_n_caps);
+
+ shift_hist(s3_out + no_out*470/rca, m_s4i_hist, 4);
+
+
+ // stage 4 filter
+
+ rcp = 30;
+ rcq = 20;
+
+ k[0] = 0;
+ k[1] = 338*rcq/(fc*470*rcp);
+ k[2] = 338*338/(fc*fc*470*rcp);
+
+ filter_s_to_z(k, fs, a, b);
+ double s4_out = apply_filter(m_s4i_hist, m_s4_hist, a, b);
+ shift_hist(s4_out, m_s4_hist, 4);
+
+
+ // TODO: apply closure circuit (undocumented)
+
+ // output the current result
+ *dest++ = INT16(s4_out * 4000);
+ }
+}
+
+
+
+//**************************************************************************
+// DEVICE INTERFACE
+//**************************************************************************
+
+//-------------------------------------------------
+// rom_region - return a pointer to the device's
+// internal ROM region
+//-------------------------------------------------
+
+const rom_entry *votrax_sc01_device::device_rom_region() const
+{
+ return ROM_NAME( votrax_sc01 );
+}
+
+
+//-------------------------------------------------
+// device_start - handle device startup
+//-------------------------------------------------
+
+void votrax_sc01_device::device_start()
+{
+ // initialize internal state
+ m_master_clock_freq = clock();
+ m_stream = stream_alloc(0, 1, m_master_clock_freq / 16);
+ m_phoneme_timer = timer_alloc();
+ m_rom = memregion("phoneme")->base();
+
+ // reset inputs
+ m_inflection = 0;
+ m_phoneme = 0x3f;
+
+ // reset outputs
+ m_request_cb.resolve_safe();
+ m_request_state = ASSERT_LINE;
+ m_internal_request = ASSERT_LINE;
+
+ // save inputs
+ save_item(NAME(m_inflection));
+ save_item(NAME(m_phoneme));
+
+ // save outputs
+ save_item(NAME(m_request_state));
+ save_item(NAME(m_internal_request));
+
+ // save timing circuit
+ save_item(NAME(m_master_clock_freq));
+ save_item(NAME(m_master_clock));
+ save_item(NAME(m_counter_34));
+ save_item(NAME(m_latch_70));
+ save_item(NAME(m_latch_72));
+ save_item(NAME(m_beta1));
+ save_item(NAME(m_p2));
+ save_item(NAME(m_p1));
+ save_item(NAME(m_phi2));
+ save_item(NAME(m_phi1));
+ save_item(NAME(m_subphoneme_period));
+ save_item(NAME(m_subphoneme_count));
+ save_item(NAME(m_clock_88));
+ save_item(NAME(m_latch_42));
+ save_item(NAME(m_counter_84));
+ save_item(NAME(m_latch_92));
+
+ // save low parameter clocking
+ save_item(NAME(m_srff_132));
+ save_item(NAME(m_srff_114));
+ save_item(NAME(m_srff_112));
+ save_item(NAME(m_srff_142));
+ save_item(NAME(m_latch_80));
+
+ // save glottal circuit
+ save_item(NAME(m_counter_220));
+ save_item(NAME(m_counter_222));
+ save_item(NAME(m_counter_224));
+ save_item(NAME(m_counter_234));
+ save_item(NAME(m_counter_236));
+ save_item(NAME(m_fgate));
+ save_item(NAME(m_glottal_sync));
+
+ // save transition circuit
+ save_item(NAME(m_0625_clock));
+ save_item(NAME(m_counter_46));
+ save_item(NAME(m_latch_46));
+ save_item(NAME(m_ram));
+ save_item(NAME(m_latch_168));
+ save_item(NAME(m_latch_170));
+ save_item(NAME(m_f1));
+ save_item(NAME(m_f2));
+ save_item(NAME(m_fc));
+ save_item(NAME(m_f3));
+ save_item(NAME(m_f2q));
+ save_item(NAME(m_va));
+ save_item(NAME(m_fa));
+
+ // save noise generator circuit
+ save_item(NAME(m_noise_clock));
+ save_item(NAME(m_shift_252));
+ save_item(NAME(m_counter_250));
+
+ // save filter histories
+ save_item(NAME(m_ni_hist));
+ save_item(NAME(m_no_hist));
+ save_item(NAME(m_va_hist));
+ save_item(NAME(m_s1_hist));
+ save_item(NAME(m_s2g_hist));
+ save_item(NAME(m_s2n_hist));
+ save_item(NAME(m_s2ni_hist));
+ save_item(NAME(m_s2_hist));
+ save_item(NAME(m_s3_hist));
+ save_item(NAME(m_s4i_hist));
+ save_item(NAME(m_s4_hist));
+}
+
+
+//-------------------------------------------------
+// device_reset - handle device reset
+//-------------------------------------------------
+
+void votrax_sc01_device::device_reset()
+{
+ // set the initial state
+ m_stream->update();
+
+ // reset inputs
+ m_phoneme = 0x3f;
+ m_request_cb(m_internal_request = m_request_state = ASSERT_LINE);
+
+ // reset timing circuit
+ m_master_clock = 0;
+ m_counter_34 = 0;
+ m_latch_70 = 0;
+ m_latch_72 = 0;
+ m_beta1 = 0;
+ m_p2 = 0;
+ m_p1 = 0;
+ m_phi2 = 0;
+ m_phi1 = 0;
+ m_subphoneme_period = 1000;
+ m_subphoneme_count = 0;
+ m_clock_88 = 0;
+ m_latch_42 = 0;
+ m_counter_84 = 0;
+ m_latch_92 = 0;
+
+ // reset low parameter clocking
+ m_srff_132 = 0;
+ m_srff_114 = 0;
+ m_srff_112 = 0;
+ m_srff_142 = 0;
+ m_latch_80 = 50;
+ update_subphoneme_clock_period();
+
+ // reset glottal circuit
+ m_counter_220 = 0;
+ m_counter_222 = 0;
+ m_counter_224 = 0;
+ m_counter_234 = 0;
+ m_counter_236 = 0;
+ m_fgate = 0;
+ m_glottal_sync = 0;
+
+ // reset transition circuit
+ m_0625_clock = 0;
+ m_counter_46 = 0;
+ m_latch_46 = 0;
+ memset(m_ram, 0, sizeof(m_ram));
+ m_latch_168 = 0;
+ m_latch_170 = 0;
+ m_f1 = 0;
+ m_f2 = 0;
+ m_fc = 0;
+ m_f3 = 0;
+ m_f2q = 0;
+ m_va = 0;
+ m_fa = 0;
+
+ // reset noise circuit
+ m_noise_clock = 0;
+ m_shift_252 = 0;
+ m_counter_250 = 0;
+
+ // reset filter histories
+ memset(m_ni_hist, 0, sizeof(m_ni_hist));
+ memset(m_no_hist, 0, sizeof(m_no_hist));
+ memset(m_va_hist, 0, sizeof(m_va_hist));
+ memset(m_s1_hist, 0, sizeof(m_s1_hist));
+ memset(m_s2g_hist, 0, sizeof(m_s2g_hist));
+ memset(m_s2n_hist, 0, sizeof(m_s2n_hist));
+ memset(m_s2ni_hist, 0, sizeof(m_s2ni_hist));
+ memset(m_s2_hist, 0, sizeof(m_s2_hist));
+ memset(m_s3_hist, 0, sizeof(m_s3_hist));
+ memset(m_s4i_hist, 0, sizeof(m_s4i_hist));
+ memset(m_s4_hist, 0, sizeof(m_s4_hist));
+}
+
+
+//-------------------------------------------------
+// device_clock_changed - handle dynamic clock
+// changes by altering our output frequency
+//-------------------------------------------------
+
+void votrax_sc01_device::device_clock_changed()
+{
+ // compute new frequency of the master clock, and update if changed
+ UINT32 newfreq = clock();
+ if (newfreq != m_master_clock_freq)
+ {
+ // if we have a stream
+ if (m_stream != NULL)
+ {
+ m_stream->update();
+ m_stream->set_sample_rate(newfreq / 16);
+ }
+
+ // determine how many clock ticks remained on the phoneme timer
+ UINT64 remaining = m_phoneme_timer->remaining().as_ticks(m_master_clock_freq);
+
+ // recompute the master clock
+ m_master_clock_freq = newfreq;
+
+ // adjust the phoneme timer to the same number of ticks based on the new frequency
+ if (remaining > 0)
+ m_phoneme_timer->adjust(attotime::from_ticks(remaining, newfreq));
+ }
+}
+
+
+//-------------------------------------------------
+// device_timer - handle device timer
+//-------------------------------------------------
+
+void votrax_sc01_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ // force a stream update
+ m_stream->update();
+
+ // if we're requesting more data, no need for timing
+ if (m_request_state == ASSERT_LINE)
+ return;
+
+ // if we're supposed to have fired, do it now
+ if (m_internal_request == ASSERT_LINE)
+ {
+osd_printf_debug("%s: REQUEST\n", timer.machine().time().as_string(3));
+ m_request_cb(m_request_state = ASSERT_LINE);
+ return;
+ }
+
+ // account for the rest of this subphoneme clock
+ UINT32 clocks_until_request = 0;
+ if (m_counter_84 != 0)
+ {
+ if (m_subphoneme_count < m_subphoneme_period)
+ clocks_until_request += m_subphoneme_period - m_subphoneme_count;
+ clocks_until_request += m_subphoneme_period * (m_counter_84 - 1);
+ }
+
+ // plus 1/2
+ clocks_until_request = MAX(clocks_until_request, (1 << P_CLOCK_BIT) / 2);
+ timer.adjust(attotime::from_ticks(clocks_until_request, m_master_clock_freq));
+}
diff --git a/src/devices/sound/votrax.h b/src/devices/sound/votrax.h
new file mode 100644
index 00000000000..152c1a51f99
--- /dev/null
+++ b/src/devices/sound/votrax.h
@@ -0,0 +1,164 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ votrax.h
+
+ Simple VOTRAX SC-01 simulator based on sample fragments.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __VOTRAX_H__
+#define __VOTRAX_H__
+
+#include "sound/samples.h"
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_VOTRAX_SC01_REQUEST_CB(_devcb) \
+ devcb = &votrax_sc01_device::set_request_callback(*device, DEVCB_##_devcb);
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> votrax_sc01_device
+
+class votrax_sc01_device : public device_t,
+ public device_sound_interface
+{
+public:
+ // construction/destruction
+ votrax_sc01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ template<class _Object> static devcb_base &set_request_callback(device_t &device, _Object object) { return downcast<votrax_sc01_device &>(device).m_request_cb.set_callback(object); }
+
+ // writers
+ DECLARE_WRITE8_MEMBER( write );
+ DECLARE_WRITE8_MEMBER( inflection_w );
+ DECLARE_READ_LINE_MEMBER( request ) { return m_request_state; }
+
+protected:
+ // device-level overrides
+ virtual const rom_entry *device_rom_region() const;
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_clock_changed();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // device_sound_interface overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ // internal helpers
+ void update_subphoneme_clock_period();
+ static double bits_to_caps(UINT32 value, int caps_count, const double *caps_values);
+ static void shift_hist(double val, double *hist_array, int hist_size);
+ static void filter_s_to_z(const double *k, double fs, double *a, double *b);
+ static double apply_filter(const double *x, const double *y, const double *a, const double *b);
+
+ // internal state
+ sound_stream * m_stream; // output stream
+ emu_timer * m_phoneme_timer; // phoneme timer
+ const UINT8 * m_rom; // pointer to our ROM
+
+ // inputs
+ UINT8 m_inflection; // 2-bit inflection value
+ UINT8 m_phoneme; // 6-bit phoneme value
+
+ // outputs
+ devcb_write_line m_request_cb; // callback for request
+ UINT8 m_request_state; // request as seen to the outside world
+ UINT8 m_internal_request; // request managed by stream timing
+
+ // timing circuit
+ UINT32 m_master_clock_freq; // frequency of the master clock
+ UINT8 m_master_clock; // master clock
+ UINT16 m_counter_34; // ripple counter @ 34
+ UINT8 m_latch_70; // 4-bit latch @ 70
+ UINT8 m_latch_72; // 4-bit latch @ 72
+ UINT8 m_beta1; // beta1 clock state
+ UINT8 m_p2; // P2 clock state
+ UINT8 m_p1; // P1 clock state
+ UINT8 m_phi2; // phi2 clock state
+ UINT8 m_phi1; // phi1 clock state
+ UINT8 m_phi2_20; // alternate phi2 clock state (20kHz)
+ UINT8 m_phi1_20; // alternate phi1 clock state (20kHz)
+ UINT32 m_subphoneme_period; // period of the subphoneme timer
+ UINT32 m_subphoneme_count; // number of ticks executed already
+ UINT8 m_clock_88; // subphoneme clock output @ 88
+ UINT8 m_latch_42; // D flip-flop @ 42
+ UINT8 m_counter_84; // 4-bit phoneme counter @ 84
+ UINT8 m_latch_92; // 2-bit latch @ 92
+
+ // low parameter clocking
+ UINT8 m_srff_132; // S/R flip-flop @ 132
+ UINT8 m_srff_114; // S/R flip-flop @ 114
+ UINT8 m_srff_112; // S/R flip-flop @ 112
+ UINT8 m_srff_142; // S/R flip-flop @ 142
+ UINT8 m_latch_80; // phoneme timing latch @ 80
+
+ // glottal circuit
+ UINT8 m_counter_220; // 4-bit counter @ 220
+ UINT8 m_counter_222; // 4-bit counter @ 222
+ UINT8 m_counter_224; // 4-bit counter @ 224
+ UINT8 m_counter_234; // 4-bit counter @ 234
+ UINT8 m_counter_236; // 4-bit counter @ 236
+ UINT8 m_fgate; // FGATE signal
+ UINT8 m_glottal_sync; // Glottal Sync signal
+
+ // transition circuit
+ UINT8 m_0625_clock; // state of 0.625kHz clock
+ UINT8 m_counter_46; // 4-bit counter in block @ 46
+ UINT8 m_latch_46; // 4-bit latch in block @ 46
+ UINT8 m_ram[8]; // RAM to hold parameters
+ UINT8 m_latch_168; // 4-bit latch @ 168
+ UINT8 m_latch_170; // 4-bit latch @ 170
+ UINT8 m_f1; // latched 4-bit F1 value
+ UINT8 m_f2; // latched 5-bit F2 value
+ UINT8 m_fc; // latched 4-bit FC value
+ UINT8 m_f3; // latched 4-bit F3 value
+ UINT8 m_f2q; // latched 4-bit F2Q value
+ UINT8 m_va; // latched 4-bit VA value
+ UINT8 m_fa; // latched 4-bit FA value
+
+ // noise generator circuit
+ UINT8 m_noise_clock; // clock input to noise generator
+ UINT32 m_shift_252; // shift register @ 252
+ UINT8 m_counter_250; // 4-bit counter @ 250
+
+ // stages outputs history
+ double m_ni_hist[4];
+ double m_no_hist[4];
+ double m_va_hist[4];
+ double m_s1_hist[4];
+ double m_s2g_hist[4];
+ double m_s2ni_hist[4];
+ double m_s2n_hist[4];
+ double m_s2_hist[4];
+ double m_s3_hist[4];
+ double m_s4i_hist[4];
+ double m_s4_hist[4];
+
+ // static tables
+ static const char *const s_phoneme_table[64];
+ static const double s_glottal_wave[16];
+};
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+extern const device_type VOTRAX_SC01;
+
+
+#endif /* __VOTRAX_H__ */
diff --git a/src/devices/sound/vrc6.c b/src/devices/sound/vrc6.c
new file mode 100644
index 00000000000..91775d932b6
--- /dev/null
+++ b/src/devices/sound/vrc6.c
@@ -0,0 +1,320 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+/***************************************************************************
+
+ vrc6.c
+ Konami VRC6 additional sound channels
+
+ Emulation by R. Belmont
+
+ References:
+ http://wiki.nesdev.com/w/index.php/VRC6_audio
+ http://nesdev.com/vrcvi.txt
+
+***************************************************************************/
+
+#include "emu.h"
+#include "vrc6.h"
+
+#define DISABLE_VRC6_SOUND // not ready yet
+
+// device type definition
+const device_type VRC6 = &device_creator<vrc6snd_device>;
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// vrc6snd_device - constructor
+//-------------------------------------------------
+
+vrc6snd_device::vrc6snd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, VRC6, "VRC6 sound", tag, owner, clock, "vrc6snd", __FILE__),
+ device_sound_interface(mconfig, *this)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void vrc6snd_device::device_start()
+{
+ m_stream = machine().sound().stream_alloc(*this, 0, 1, clock());
+
+ m_freqctrl = m_pulsectrl[0] = m_pulsectrl[1] = 0;
+ m_pulsefrql[0] = m_pulsefrql[1] = m_pulsefrqh[0] = m_pulsefrqh[1] = 0;
+ m_sawaccum = m_sawfrql = m_sawfrqh = m_sawclock = m_sawrate = 0;
+ m_ticks[0] = m_ticks[1] = m_ticks[2] = 0;
+ m_output[0] = m_output[1] = m_output[2] = 0;
+ m_pulseduty[0] = m_pulseduty[1] = 15;
+
+ save_item(NAME(m_freqctrl));
+ save_item(NAME(m_pulsectrl));
+ save_item(NAME(m_sawrate));
+ save_item(NAME(m_sawaccum));
+ save_item(NAME(m_pulsefrql));
+ save_item(NAME(m_pulsefrqh));
+ save_item(NAME(m_sawfrql));
+ save_item(NAME(m_sawfrqh));
+ save_item(NAME(m_ticks));
+ save_item(NAME(m_output));
+ save_item(NAME(m_pulseduty));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void vrc6snd_device::device_reset()
+{
+ m_stream->update();
+
+ m_freqctrl = m_pulsectrl[0] = m_pulsectrl[1] = 0;
+ m_pulsefrql[0] = m_pulsefrql[1] = 0;
+ m_sawaccum = m_sawfrql = m_sawclock = m_sawrate = 0;
+ m_ticks[0] = m_ticks[1] = m_ticks[2] = 0;
+ m_output[0] = m_output[1] = m_output[2] = 0;
+ m_pulseduty[0] = m_pulseduty[1] = 15;
+ m_pulsefrqh[0] = m_pulsefrqh[1] = m_sawfrqh = 0;
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void vrc6snd_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *out = outputs[0];
+ INT16 tmp;
+ int i;
+
+ // check global halt bit
+ if (m_freqctrl & 1)
+ {
+ return;
+ }
+
+ for (i = 0; i < samples; i++)
+ {
+ // update pulse1
+ if (m_pulsefrqh[0] & 0x80)
+ {
+ m_ticks[0]--;
+ if (m_ticks[0] == 0)
+ {
+ m_ticks[0] = m_pulsefrql[0] | (m_pulsefrqh[0] & 0xf)<<4;
+
+ m_pulseduty[0]--;
+ if (m_pulsectrl[0] & 0x80)
+ {
+ m_output[0] = m_pulsectrl[0] & 0xf;
+ }
+ else
+ {
+ if (m_pulseduty[0] <= ((m_pulsectrl[0]>>4) & 0x7))
+ {
+ m_output[0] = m_pulsectrl[0] & 0xf;
+ }
+ else
+ {
+ m_output[0] = 0;
+ }
+ }
+
+ if (m_pulseduty[0] == 0)
+ {
+ m_pulseduty[0] = 15;
+ }
+ }
+ }
+ else
+ {
+ m_output[0] = 0;
+ }
+
+ // update pulse2
+ if (m_pulsefrqh[1] & 0x80)
+ {
+ m_ticks[1]--;
+ if (m_ticks[1] == 0)
+ {
+ m_ticks[1] = m_pulsefrql[1] | (m_pulsefrqh[1] & 0xf)<<4;
+
+ m_pulseduty[1]--;
+ if (m_pulsectrl[1] & 0x80)
+ {
+ m_output[1] = m_pulsectrl[1] & 0xf;
+ }
+ else
+ {
+ if (m_pulseduty[1] <= ((m_pulsectrl[1]>>4) & 0x7))
+ {
+ m_output[1] = m_pulsectrl[1] & 0xf;
+ }
+ else
+ {
+ m_output[1] = 0;
+ }
+ }
+
+ if (m_pulseduty[1] == 0)
+ {
+ m_pulseduty[1] = 15;
+ }
+ }
+ }
+ else
+ {
+ m_output[1] = 0;
+ }
+
+ // update saw
+ if (m_sawfrqh & 0x80)
+ {
+ m_ticks[2]--;
+ if (m_ticks[2] == 0)
+ {
+ m_ticks[2] = m_sawfrql | (m_sawfrqh & 0xf)<<4;
+
+ // only update on even steps
+ if ((m_sawclock > 0) && (!(m_sawclock & 1)))
+ {
+ m_sawaccum += (m_sawrate & 0x3f);
+ m_output[2] = (m_sawaccum>>3);
+ }
+ m_sawclock++;
+
+ if (m_sawclock >= 14)
+ {
+ m_sawclock = m_sawaccum = 0;
+ m_output[2] = 0;
+ }
+ }
+ }
+ else
+ {
+ m_output[2] = 0;
+ }
+
+ // sum 2 4-bit pulses, 1 5-bit saw = unsigned 6 bit output
+ tmp = (INT16)(UINT8)(m_output[0] + m_output[1] + m_output[2]);
+ tmp <<= 8;
+
+ out[i] = tmp;
+ }
+}
+
+//---------------------------------------
+// write - write to the chip's registers
+//---------------------------------------
+
+WRITE8_MEMBER( vrc6snd_device::write )
+{
+ switch (offset >> 8)
+ {
+ case 0:
+ m_stream->update();
+ switch (offset & 3)
+ {
+ case 0:
+ m_pulsectrl[0] = data;
+ break;
+
+ case 1:
+ m_pulsefrql[0] = data;
+ if (!(m_pulsefrqh[1] & 0x80))
+ {
+ m_ticks[0] &= ~0xff;
+ m_ticks[0] |= m_pulsefrql[0];
+ }
+ break;
+
+ case 2:
+ #ifndef DISABLE_VRC6_SOUND
+ m_pulsefrqh[0] = data;
+ // if disabling channel, reset phase
+ if (!(data & 0x80))
+ {
+ m_pulseduty[0] = 15;
+ m_ticks[0] &= 0xff;
+ m_ticks[0] |= (m_pulsefrqh[0] & 0xf)<<4;
+ }
+ #endif
+ break;
+
+ case 3:
+ m_freqctrl = data;
+ break;
+ }
+ break;
+
+ case 1:
+ m_stream->update();
+ switch (offset & 3)
+ {
+ case 0:
+ m_pulsectrl[1] = data;
+ break;
+
+ case 1:
+ m_pulsefrql[1] = data;
+ if (!(m_pulsefrqh[1] & 0x80))
+ {
+ m_ticks[1] &= ~0xff;
+ m_ticks[1] |= m_pulsefrql[1];
+ }
+ break;
+
+ case 2:
+ #ifndef DISABLE_VRC6_SOUND
+ m_pulsefrqh[1] = data;
+ // if disabling channel, reset phase
+ if (!(data & 0x80))
+ {
+ m_pulseduty[1] = 15;
+ m_ticks[1] &= 0xff;
+ m_ticks[1] |= (m_pulsefrqh[1] & 0xf)<<4;
+ }
+ #endif
+ break;
+ }
+ break;
+
+ case 2:
+ m_stream->update();
+ switch (offset & 3)
+ {
+ case 0:
+ m_sawrate = data;
+ break;
+
+ case 1:
+ m_sawfrql = data;
+ if (!(m_sawfrqh & 0x80))
+ {
+ m_ticks[2] &= ~0xff;
+ m_ticks[2] |= m_sawfrql;
+ }
+ break;
+
+ case 2:
+ #ifndef DISABLE_VRC6_SOUND
+ m_sawfrqh = data;
+ // if disabling channel, reset phase
+ if (!(data & 0x80))
+ {
+ m_sawaccum = 0;
+ m_ticks[2] &= 0xff;
+ m_ticks[2] |= (m_sawfrqh & 0xf)<<4;
+ }
+ #endif
+ break;
+ }
+ break;
+ }
+
+}
diff --git a/src/devices/sound/vrc6.h b/src/devices/sound/vrc6.h
new file mode 100644
index 00000000000..db2cdffd542
--- /dev/null
+++ b/src/devices/sound/vrc6.h
@@ -0,0 +1,61 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+/***************************************************************************
+
+ vrc6.h
+ Konami VRC6 add-on sound
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __VRC6_H__
+#define __VRC6_H__
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_VRC6_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, VRC6, _clock)
+
+#define MCFG_VRC6_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, VRC6, _clock)
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> vrc6snd_device
+
+class vrc6snd_device : public device_t, public device_sound_interface
+{
+public:
+ // construction/destruction
+ vrc6snd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_WRITE8_MEMBER(write);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ UINT8 m_freqctrl, m_pulsectrl[2], m_sawrate;
+ UINT8 m_pulsefrql[2], m_pulsefrqh[2], m_pulseduty[2];
+ UINT8 m_sawfrql, m_sawfrqh, m_sawclock, m_sawaccum;
+ UINT16 m_ticks[3];
+ UINT8 m_output[3];
+
+ sound_stream *m_stream;
+};
+
+
+// device type definition
+extern const device_type VRC6;
+
+
+#endif /* __VRC6_H__ */
diff --git a/src/devices/sound/vrender0.c b/src/devices/sound/vrender0.c
new file mode 100644
index 00000000000..b13f4556525
--- /dev/null
+++ b/src/devices/sound/vrender0.c
@@ -0,0 +1,250 @@
+// license:BSD-3-Clause
+// copyright-holders:ElSemi
+#include "emu.h"
+#include "vrender0.h"
+
+/***********************************
+ VRENDER ZERO
+ AUDIO EMULATION
+************************************/
+/************
+MISSING:
+envelopes
+reverb
+interrupts
+*************/
+
+//Correct table thanks to Evoga
+//they left a ulaw<->linear conversion tool inside the roms
+static const unsigned short ULawTo16[]=
+{
+ 0x8000,0x8400,0x8800,0x8C00,0x9000,0x9400,0x9800,0x9C00,
+ 0xA000,0xA400,0xA800,0xAC00,0xB000,0xB400,0xB800,0xBC00,
+ 0x4000,0x4400,0x4800,0x4C00,0x5000,0x5400,0x5800,0x5C00,
+ 0x6000,0x6400,0x6800,0x6C00,0x7000,0x7400,0x7800,0x7C00,
+ 0xC000,0xC200,0xC400,0xC600,0xC800,0xCA00,0xCC00,0xCE00,
+ 0xD000,0xD200,0xD400,0xD600,0xD800,0xDA00,0xDC00,0xDE00,
+ 0x2000,0x2200,0x2400,0x2600,0x2800,0x2A00,0x2C00,0x2E00,
+ 0x3000,0x3200,0x3400,0x3600,0x3800,0x3A00,0x3C00,0x3E00,
+ 0xE000,0xE100,0xE200,0xE300,0xE400,0xE500,0xE600,0xE700,
+ 0xE800,0xE900,0xEA00,0xEB00,0xEC00,0xED00,0xEE00,0xEF00,
+ 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700,
+ 0x1800,0x1900,0x1A00,0x1B00,0x1C00,0x1D00,0x1E00,0x1F00,
+ 0xF000,0xF080,0xF100,0xF180,0xF200,0xF280,0xF300,0xF380,
+ 0xF400,0xF480,0xF500,0xF580,0xF600,0xF680,0xF700,0xF780,
+ 0x0800,0x0880,0x0900,0x0980,0x0A00,0x0A80,0x0B00,0x0B80,
+ 0x0C00,0x0C80,0x0D00,0x0D80,0x0E00,0x0E80,0x0F00,0x0F80,
+ 0xF800,0xF840,0xF880,0xF8C0,0xF900,0xF940,0xF980,0xF9C0,
+ 0xFA00,0xFA40,0xFA80,0xFAC0,0xFB00,0xFB40,0xFB80,0xFBC0,
+ 0x0400,0x0440,0x0480,0x04C0,0x0500,0x0540,0x0580,0x05C0,
+ 0x0600,0x0640,0x0680,0x06C0,0x0700,0x0740,0x0780,0x07C0,
+ 0xFC00,0xFC20,0xFC40,0xFC60,0xFC80,0xFCA0,0xFCC0,0xFCE0,
+ 0xFD00,0xFD20,0xFD40,0xFD60,0xFD80,0xFDA0,0xFDC0,0xFDE0,
+ 0x0200,0x0220,0x0240,0x0260,0x0280,0x02A0,0x02C0,0x02E0,
+ 0x0300,0x0320,0x0340,0x0360,0x0380,0x03A0,0x03C0,0x03E0,
+ 0xFE00,0xFE10,0xFE20,0xFE30,0xFE40,0xFE50,0xFE60,0xFE70,
+ 0xFE80,0xFE90,0xFEA0,0xFEB0,0xFEC0,0xFED0,0xFEE0,0xFEF0,
+ 0x0100,0x0110,0x0120,0x0130,0x0140,0x0150,0x0160,0x0170,
+ 0x0180,0x0190,0x01A0,0x01B0,0x01C0,0x01D0,0x01E0,0x01F0,
+ 0x0000,0x0008,0x0010,0x0018,0x0020,0x0028,0x0030,0x0038,
+ 0x0040,0x0048,0x0050,0x0058,0x0060,0x0068,0x0070,0x0078,
+ 0xFF80,0xFF88,0xFF90,0xFF98,0xFFA0,0xFFA8,0xFFB0,0xFFB8,
+ 0xFFC0,0xFFC8,0xFFD0,0xFFD8,0xFFE0,0xFFE8,0xFFF0,0xFFF8,
+};
+
+
+#define STATUS m_SOUNDREGS[0x404/4]
+#define CURSADDR(chan) (m_SOUNDREGS[(0x20/4)*chan+0x00])
+#define DSADDR(chan) ((m_SOUNDREGS[(0x20/4)*chan+0x08/4]>>0)&0xffff)
+#define LOOPBEGIN(chan) (m_SOUNDREGS[(0x20/4)*chan+0x0c/4]&0x3fffff)
+#define LOOPEND(chan) (m_SOUNDREGS[(0x20/4)*chan+0x10/4]&0x3fffff)
+#define ENVVOL(chan) (m_SOUNDREGS[(0x20/4)*chan+0x04/4]&0xffffff)
+
+/*
+#define GETSOUNDREG16(Chan,Offs) space.read_word(m_reg_base+0x20*Chan+Offs)
+#define GETSOUNDREG32(Chan,Offs) space.read_dword(m_reg_base+0x20*Chan+Offs)
+
+#define CURSADDR(chan) GETSOUNDREG32(chan,0x00)
+#define DSADDR(chan) GETSOUNDREG16(chan,0x08)
+#define LOOPBEGIN(chan) (GETSOUNDREG32(chan,0x0c)&0x3fffff)
+#define LOOPEND(chan) (GETSOUNDREG32(chan,0x10)&0x3fffff)
+#define ENVVOL(chan) (GETSOUNDREG32(chan,0x04)&0xffffff)
+*/
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+const device_type VRENDER0 = &device_creator<vrender0_device>;
+
+//-------------------------------------------------
+// vrender0_device - constructor
+//-------------------------------------------------
+
+vrender0_device::vrender0_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, VRENDER0, "VRender0", tag, owner, clock, "vrender0", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_TexBase(NULL),
+ m_FBBase(NULL),
+ m_stream(NULL),
+ m_reg_base(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void vrender0_device::device_start()
+{
+ memset(m_SOUNDREGS,0,sizeof(m_SOUNDREGS));
+
+ m_stream = stream_alloc(0, 2, 44100);
+
+ save_item(NAME(m_SOUNDREGS));
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests
+// for our sound stream
+//-------------------------------------------------
+
+void vrender0_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ VR0_RenderAudio(samples, outputs[0], outputs[1]);
+}
+
+
+
+READ32_MEMBER(vrender0_device::vr0_snd_read)
+{
+ return m_SOUNDREGS[offset];
+}
+
+
+WRITE32_MEMBER(vrender0_device::vr0_snd_write)
+{
+ if(offset==0x404/4)
+ {
+ data&=0xffff;
+ if(data&0x8000)
+ {
+ UINT32 c=data&0x1f;
+ STATUS|=1<<c;
+ CURSADDR(c)=0;
+ }
+ else
+ {
+ STATUS&=~(1<<(data&0x1f));
+ }
+ return;
+ }
+ else
+ {
+ COMBINE_DATA(&m_SOUNDREGS[offset]);
+ }
+}
+
+
+void vrender0_device::set_areas(UINT32 *texture, UINT32 *frame)
+{
+ m_TexBase=texture;
+ m_FBBase=frame;
+}
+
+
+void vrender0_device::VR0_RenderAudio(int nsamples, stream_sample_t *l, stream_sample_t *r)
+{
+ INT16 *SAMPLES;
+ UINT32 st=STATUS;
+ signed int lsample=0,rsample=0;
+ UINT32 CLK=(m_SOUNDREGS[0x600/4]>>0)&0xff;
+ UINT32 NCH=(m_SOUNDREGS[0x600/4]>>8)&0xff;
+ UINT32 CT1=(m_SOUNDREGS[0x600/4]>>16)&0xff;
+ UINT32 CT2=(m_SOUNDREGS[0x600/4]>>24)&0xff;
+ int div;
+ int s;
+
+
+ if(CT1&0x20)
+ SAMPLES=(INT16 *)m_TexBase;
+ else
+ SAMPLES=(INT16 *)m_FBBase;
+
+ if(CLK)
+ div=((30<<16)|0x8000)/(CLK+1);
+ else
+ div=1<<16;
+
+ for(s=0;s<nsamples;++s)
+ {
+ int i;
+ lsample=rsample=0;
+ for(i=0;i<=NCH;++i)
+ {
+ signed int sample;
+ UINT32 cur=CURSADDR(i);
+ UINT32 a=LOOPBEGIN(i)+(cur>>10);
+ UINT8 Mode=m_SOUNDREGS[(0x20/4)*i+0x8/4]>>24;
+ signed int LVOL=m_SOUNDREGS[(0x20/4)*i+0xc/4]>>24;
+ signed int RVOL=m_SOUNDREGS[(0x20/4)*i+0x10/4]>>24;
+
+ INT32 DSADD=(DSADDR(i)*div)>>16;
+
+ if(!(st&(1<<i)) || !(CT2&0x80))
+ continue;
+
+ if(Mode&0x10) //u-law
+ {
+ UINT16 s=SAMPLES[a];
+ if((cur&0x200))
+ s>>=8;
+ sample=(signed short)ULawTo16[s&0xff];
+ }
+ else
+ {
+ if(Mode&0x20) //8bit
+ {
+ UINT16 s=SAMPLES[a];
+ if((cur&0x200))
+ s>>=8;
+ sample=(signed short) (((signed char) (s&0xff))<<8);
+ }
+ else //16bit
+ {
+ sample=SAMPLES[a];
+ }
+ }
+
+ CURSADDR(i)+=DSADD;
+ if(a>=LOOPEND(i))
+ {
+ if(Mode&1) //Loop
+ CURSADDR(i)=0;//LOOPBEGIN(i)<<10;
+ else
+ {
+ STATUS&=~(1<<(i&0x1f));
+ break;
+ }
+ }
+// UINT32 v=(ENVVOL(i))>>8;
+// sample=(sample*v)>>16;
+ lsample+=(sample*LVOL)>>8;
+ rsample+=(sample*RVOL)>>8;
+ }
+ if(lsample>32767)
+ lsample=32767;
+ if(lsample<-32768)
+ lsample=-32768;
+ l[s]=lsample;
+ if(rsample>32767)
+ rsample=32767;
+ if(rsample<-32768)
+ rsample=-32768;
+ r[s]=rsample;
+ }
+}
diff --git a/src/devices/sound/vrender0.h b/src/devices/sound/vrender0.h
new file mode 100644
index 00000000000..9baaefac203
--- /dev/null
+++ b/src/devices/sound/vrender0.h
@@ -0,0 +1,63 @@
+// license:BSD-3-Clause
+// copyright-holders:ElSemi
+#pragma once
+
+#ifndef __VRENDER0_H__
+#define __VRENDER0_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_SOUND_VRENDER0_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, VRENDER0, _clock)
+#define MCFG_SOUND_VRENDER0_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, VRENDER0, _clock)
+
+#define MCFG_VR0_REGBASE(_base) \
+ vrender0_device::set_reg_base(*device, _base);
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+
+// ======================> vrender0_device
+
+class vrender0_device : public device_t,
+ public device_sound_interface
+{
+public:
+ vrender0_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~vrender0_device() { }
+
+ // static configuration
+ static void set_reg_base(device_t &device, int base) { downcast<vrender0_device &>(device).m_reg_base = base; }
+
+ DECLARE_READ32_MEMBER( vr0_snd_read );
+ DECLARE_WRITE32_MEMBER( vr0_snd_write );
+
+ void set_areas(UINT32 *texture, UINT32 *frame);
+
+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:
+ UINT32 *m_TexBase;
+ UINT32 *m_FBBase;
+ UINT32 m_SOUNDREGS[0x10000/4];
+ sound_stream *m_stream;
+ UINT32 m_reg_base;
+
+ void VR0_RenderAudio(int nsamples, stream_sample_t *l, stream_sample_t *r);
+};
+
+extern const device_type VRENDER0;
+
+
+#endif /* __VRENDER0_H__ */
diff --git a/src/devices/sound/wave.c b/src/devices/sound/wave.c
new file mode 100644
index 00000000000..76f9bdda8d6
--- /dev/null
+++ b/src/devices/sound/wave.c
@@ -0,0 +1,110 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/***************************************************************************
+
+ wave.c
+
+ Code that interfaces
+ Functions to handle loading, creation, recording and playback
+ of wave samples for IO_CASSETTE
+
+ 2010-06-19 - Found that since 0.132, the right channel is badly out of
+ sync on a mono system, causing bad sound. Added code to disable
+ the second channel on a mono system.
+
+
+****************************************************************************/
+
+#include "emu.h"
+#include "wave.h"
+
+#define ALWAYS_PLAY_SOUND 0
+
+
+
+void wave_device::static_set_cassette_tag(device_t &device, const char *cassette_tag)
+{
+ wave_device &wave = downcast<wave_device &>(device);
+ wave.m_cassette_tag = cassette_tag;
+}
+
+const device_type WAVE = &device_creator<wave_device>;
+
+wave_device::wave_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, WAVE, "Wave", tag, owner, clock, "wave", __FILE__),
+ device_sound_interface(mconfig, *this)
+{
+ m_cassette_tag = 0;
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void wave_device::device_config_complete()
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void wave_device::device_start()
+{
+ speaker_device_iterator spkiter(machine().root_device());
+ int speakers = spkiter.count();
+ if (speakers > 1)
+ machine().sound().stream_alloc(*this, 0, 2, machine().sample_rate());
+ else
+ machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate());
+ m_cass = machine().device<cassette_image_device>(m_cassette_tag);
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void wave_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ cassette_state state;
+ double time_index;
+ double duration;
+ stream_sample_t *left_buffer = outputs[0];
+ stream_sample_t *right_buffer = NULL;
+ int i;
+
+ speaker_device_iterator spkiter(m_cass->machine().root_device());
+ int speakers = spkiter.count();
+ if (speakers>1)
+ right_buffer = outputs[1];
+
+ state = m_cass->get_state();
+
+ state = (cassette_state)(state & (CASSETTE_MASK_UISTATE | CASSETTE_MASK_MOTOR | CASSETTE_MASK_SPEAKER));
+
+ if (m_cass->exists() && (ALWAYS_PLAY_SOUND || (state == (CASSETTE_PLAY | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED))))
+ {
+ cassette_image *cassette = m_cass->get_image();
+ time_index = m_cass->get_position();
+ duration = ((double) samples) / m_cass->machine().sample_rate();
+
+ cassette_get_samples(cassette, 0, time_index, duration, samples, 2, left_buffer, CASSETTE_WAVEFORM_16BIT);
+ if (speakers > 1)
+ cassette_get_samples(cassette, 1, time_index, duration, samples, 2, right_buffer, CASSETTE_WAVEFORM_16BIT);
+
+ for (i = samples - 1; i >= 0; i--)
+ {
+ left_buffer[i] = ((INT16 *) left_buffer)[i];
+ if (speakers > 1)
+ right_buffer[i] = ((INT16 *) right_buffer)[i];
+ }
+ }
+ else
+ {
+ memset(left_buffer, 0, sizeof(*left_buffer) * samples);
+ if (speakers > 1)
+ memset(right_buffer, 0, sizeof(*right_buffer) * samples);
+ }
+}
diff --git a/src/devices/sound/wave.h b/src/devices/sound/wave.h
new file mode 100644
index 00000000000..d4ff4865551
--- /dev/null
+++ b/src/devices/sound/wave.h
@@ -0,0 +1,47 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+#pragma once
+
+#ifndef __WAVE_H__
+#define __WAVE_H__
+
+#include "imagedev/cassette.h"
+
+
+/*****************************************************************************
+ * CassetteWave interface
+ *****************************************************************************/
+
+class wave_device : public device_t,
+ public device_sound_interface
+{
+public:
+ wave_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ static void static_set_cassette_tag(device_t &device, const char *cassette_tag);
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ 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:
+ const char *m_cassette_tag;
+ cassette_image_device *m_cass;
+};
+
+extern const device_type WAVE;
+
+
+#define WAVE_TAG "wave"
+#define WAVE2_TAG "wave2"
+
+
+#define MCFG_SOUND_WAVE_ADD(_tag, _cass_tag) \
+ MCFG_SOUND_ADD( _tag, WAVE, 0 ) \
+ wave_device::static_set_cassette_tag(*device, _cass_tag);
+
+#endif /* __WAVE_H__ */
diff --git a/src/devices/sound/wavwrite.c b/src/devices/sound/wavwrite.c
new file mode 100644
index 00000000000..baf5498f02c
--- /dev/null
+++ b/src/devices/sound/wavwrite.c
@@ -0,0 +1,189 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+#include "emu.h"
+#include "sound/wavwrite.h"
+
+struct wav_file
+{
+ FILE *file;
+ UINT32 total_offs;
+ UINT32 data_offs;
+};
+
+
+wav_file *wav_open(const char *filename, int sample_rate, int channels)
+{
+ wav_file *wav;
+ UINT32 bps, temp32;
+ UINT16 align, temp16;
+
+ /* allocate memory for the wav struct */
+ wav = (wav_file *) global_alloc(wav_file);
+ if (!wav)
+ return NULL;
+
+ /* create the file */
+ wav->file = fopen(filename, "wb");
+ if (!wav->file)
+ {
+ global_free(wav);
+ return NULL;
+ }
+
+ /* write the 'RIFF' header */
+ fwrite("RIFF", 1, 4, wav->file);
+
+ /* write the total size */
+ temp32 = 0;
+ wav->total_offs = ftell(wav->file);
+ fwrite(&temp32, 1, 4, wav->file);
+
+ /* write the 'WAVE' type */
+ fwrite("WAVE", 1, 4, wav->file);
+
+ /* write the 'fmt ' tag */
+ fwrite("fmt ", 1, 4, wav->file);
+
+ /* write the format length */
+ temp32 = LITTLE_ENDIANIZE_INT32(16);
+ fwrite(&temp32, 1, 4, wav->file);
+
+ /* write the format (PCM) */
+ temp16 = LITTLE_ENDIANIZE_INT16(1);
+ fwrite(&temp16, 1, 2, wav->file);
+
+ /* write the channels */
+ temp16 = LITTLE_ENDIANIZE_INT16(channels);
+ fwrite(&temp16, 1, 2, wav->file);
+
+ /* write the sample rate */
+ temp32 = LITTLE_ENDIANIZE_INT32(sample_rate);
+ fwrite(&temp32, 1, 4, wav->file);
+
+ /* write the bytes/second */
+ bps = sample_rate * 2 * channels;
+ temp32 = LITTLE_ENDIANIZE_INT32(bps);
+ fwrite(&temp32, 1, 4, wav->file);
+
+ /* write the block align */
+ align = 2 * channels;
+ temp16 = LITTLE_ENDIANIZE_INT16(align);
+ fwrite(&temp16, 1, 2, wav->file);
+
+ /* write the bits/sample */
+ temp16 = LITTLE_ENDIANIZE_INT16(16);
+ fwrite(&temp16, 1, 2, wav->file);
+
+ /* write the 'data' tag */
+ fwrite("data", 1, 4, wav->file);
+
+ /* write the data length */
+ temp32 = 0;
+ wav->data_offs = ftell(wav->file);
+ fwrite(&temp32, 1, 4, wav->file);
+
+ return wav;
+}
+
+
+void wav_close(wav_file *wav)
+{
+ UINT32 total;
+ UINT32 temp32;
+
+ if (!wav) return;
+
+ total = ftell(wav->file);
+
+ /* update the total file size */
+ fseek(wav->file, wav->total_offs, SEEK_SET);
+ temp32 = total - (wav->total_offs + 4);
+ temp32 = LITTLE_ENDIANIZE_INT32(temp32);
+ fwrite(&temp32, 1, 4, wav->file);
+
+ /* update the data size */
+ fseek(wav->file, wav->data_offs, SEEK_SET);
+ temp32 = total - (wav->data_offs + 4);
+ temp32 = LITTLE_ENDIANIZE_INT32(temp32);
+ fwrite(&temp32, 1, 4, wav->file);
+
+ fclose(wav->file);
+ global_free(wav);
+}
+
+
+void wav_add_data_16(wav_file *wav, INT16 *data, int samples)
+{
+ if (!wav) return;
+
+ /* just write and flush the data */
+ fwrite(data, 2, samples, wav->file);
+ fflush(wav->file);
+}
+
+
+void wav_add_data_32(wav_file *wav, INT32 *data, int samples, int shift)
+{
+ std::vector<INT16> temp;
+ int i;
+
+ if (!wav || !samples) return;
+
+ /* resize dynamic array */
+ temp.resize(samples);
+
+ /* clamp */
+ for (i = 0; i < samples; i++)
+ {
+ int val = data[i] >> shift;
+ temp[i] = (val < -32768) ? -32768 : (val > 32767) ? 32767 : val;
+ }
+
+ /* write and flush */
+ fwrite(&temp[0], 2, samples, wav->file);
+ fflush(wav->file);
+}
+
+
+void wav_add_data_16lr(wav_file *wav, INT16 *left, INT16 *right, int samples)
+{
+ std::vector<INT16> temp;
+ int i;
+
+ if (!wav || !samples) return;
+
+ /* resize dynamic array */
+ temp.resize(samples * 2);
+
+ /* interleave */
+ for (i = 0; i < samples * 2; i++)
+ temp[i] = (i & 1) ? right[i / 2] : left[i / 2];
+
+ /* write and flush */
+ fwrite(&temp[0], 4, samples, wav->file);
+ fflush(wav->file);
+}
+
+
+void wav_add_data_32lr(wav_file *wav, INT32 *left, INT32 *right, int samples, int shift)
+{
+ std::vector<INT16> temp;
+ int i;
+
+ if (!wav || !samples) return;
+
+ /* resize dynamic array */
+ temp.resize(samples);
+
+ /* interleave */
+ for (i = 0; i < samples * 2; i++)
+ {
+ int val = (i & 1) ? right[i / 2] : left[i / 2];
+ val >>= shift;
+ temp[i] = (val < -32768) ? -32768 : (val > 32767) ? 32767 : val;
+ }
+
+ /* write and flush */
+ fwrite(&temp[0], 4, samples, wav->file);
+ fflush(wav->file);
+}
diff --git a/src/devices/sound/wavwrite.h b/src/devices/sound/wavwrite.h
new file mode 100644
index 00000000000..376856fb16f
--- /dev/null
+++ b/src/devices/sound/wavwrite.h
@@ -0,0 +1,18 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+#pragma once
+
+#ifndef __WAVWRITE_H__
+#define __WAVWRITE_H__
+
+struct wav_file;
+
+wav_file *wav_open(const char *filename, int sample_rate, int channels);
+void wav_close(wav_file*wavptr);
+
+void wav_add_data_16(wav_file *wavptr, INT16 *data, int samples);
+void wav_add_data_32(wav_file *wavptr, INT32 *data, int samples, int shift);
+void wav_add_data_16lr(wav_file *wavptr, INT16 *left, INT16 *right, int samples);
+void wav_add_data_32lr(wav_file *wavptr, INT32 *left, INT32 *right, int samples, int shift);
+
+#endif /* __WAVWRITE_H__ */
diff --git a/src/devices/sound/x1_010.c b/src/devices/sound/x1_010.c
new file mode 100644
index 00000000000..5d8377569bb
--- /dev/null
+++ b/src/devices/sound/x1_010.c
@@ -0,0 +1,276 @@
+// license:BSD-3-Clause
+// copyright-holders:Luca Elia
+/***************************************************************************
+
+ -= Seta Hardware =-
+
+ driver by Luca Elia (l.elia@tin.it)
+
+ rewrite by Manbow-J(manbowj@hamal.freemail.ne.jp)
+
+ X1-010 Seta Custom Sound Chip (80 Pin PQFP)
+
+ Custom programmed Mitsubishi M60016 Gate Array, 3608 gates, 148 Max I/O ports
+
+ The X1-010 is 16 Voices sound generator, each channel gets it's
+ waveform from RAM (128 bytes per waveform, 8 bit unsigned data)
+ or sampling PCM(8bit unsigned data).
+
+Registers:
+ 8 registers per channel (mapped to the lower bytes of 16 words on the 68K)
+
+ Reg: Bits: Meaning:
+
+ 0 7--- ---- Frequency divider flag (only downtown seems to set this)
+ -654 3---
+ ---- -2-- PCM/Waveform repeat flag (0:Ones 1:Repeat) (*1)
+ ---- --1- Sound out select (0:PCM 1:Waveform)
+ ---- ---0 Key on / off
+
+ 1 7654 ---- PCM Volume 1 (L?)
+ ---- 3210 PCM Volume 2 (R?)
+ Waveform No.
+
+ 2 PCM Frequency
+ Waveform Pitch Lo
+
+ 3 Waveform Pitch Hi
+
+ 4 PCM Sample Start / 0x1000 [Start/End in bytes]
+ Waveform Envelope Time
+
+ 5 PCM Sample End 0x100 - (Sample End / 0x1000) [PCM ROM is Max 1MB?]
+ Waveform Envelope No.
+ 6 Reserved
+ 7 Reserved
+
+ offset 0x0000 - 0x0fff Wave form data
+ offset 0x1000 - 0x1fff Envelope data
+
+ *1 : when 0 is specified, hardware interrupt is caused(allways return soon)
+
+***************************************************************************/
+
+#include "emu.h"
+#include "x1_010.h"
+
+
+#define VERBOSE_SOUND 0
+#define VERBOSE_REGISTER_WRITE 0
+#define VERBOSE_REGISTER_READ 0
+
+#define LOG_SOUND(x) do { if (VERBOSE_SOUND) logerror x; } while (0)
+#define LOG_REGISTER_WRITE(x) do { if (VERBOSE_REGISTER_WRITE) logerror x; } while (0)
+#define LOG_REGISTER_READ(x) do { if (VERBOSE_REGISTER_READ) logerror x; } while (0)
+
+#define FREQ_BASE_BITS 8 // Frequency fixed decimal shift bits
+#define ENV_BASE_BITS 16 // wave form envelope fixed decimal shift bits
+#define VOL_BASE (2*32*256/30) // Volume base
+
+/* this structure defines the parameters for a channel */
+struct X1_010_CHANNEL {
+ unsigned char status;
+ unsigned char volume; // volume / wave form no.
+ unsigned char frequency; // frequency / pitch lo
+ unsigned char pitch_hi; // reserved / pitch hi
+ unsigned char start; // start address / envelope time
+ unsigned char end; // end address / envelope no.
+ unsigned char reserve[2];
+};
+
+
+/* mixer tables and internal buffers */
+//static short *mixer_buffer = NULL;
+
+
+const device_type X1_010 = &device_creator<x1_010_device>;
+
+x1_010_device::x1_010_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, X1_010, "X1-010", tag, owner, clock, "x1_010", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_region(*this, DEVICE_SELF),
+ m_rate(0),
+ m_adr(0),
+ m_stream(NULL),
+ m_sound_enable(0),
+ m_base_clock(0)
+{
+ memset(m_reg, 0, sizeof(m_reg));
+ memset(m_HI_WORD_BUF, 0, sizeof(m_HI_WORD_BUF));
+ memset(m_smp_offset, 0, sizeof(SETA_NUM_CHANNELS));
+ memset(m_env_offset, 0, sizeof(SETA_NUM_CHANNELS));
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void x1_010_device::device_start()
+{
+ int i;
+
+ m_base_clock = clock();
+ m_rate = clock() / 1024;
+
+ for( i = 0; i < SETA_NUM_CHANNELS; i++ ) {
+ m_smp_offset[i] = 0;
+ m_env_offset[i] = 0;
+ }
+ /* Print some more debug info */
+ LOG_SOUND(("masterclock = %d rate = %d\n", clock(), m_rate ));
+
+ /* get stream channels */
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, m_rate);
+
+ save_item(NAME(m_rate));
+ save_item(NAME(m_sound_enable));
+ save_item(NAME(m_reg));
+ save_item(NAME(m_HI_WORD_BUF));
+ save_item(NAME(m_smp_offset));
+ save_item(NAME(m_env_offset));
+ save_item(NAME(m_base_clock));
+}
+
+
+void x1_010_device::enable_w(int data)
+{
+ m_sound_enable = data;
+}
+
+/* Use these for 8 bit CPUs */
+
+
+READ8_MEMBER( x1_010_device::read )
+{
+ offset ^= m_adr;
+ return m_reg[offset];
+}
+
+WRITE8_MEMBER( x1_010_device::write )
+{
+ int channel, reg;
+ offset ^= m_adr;
+
+ channel = offset/sizeof(X1_010_CHANNEL);
+ reg = offset%sizeof(X1_010_CHANNEL);
+
+ if( channel < SETA_NUM_CHANNELS && reg == 0
+ && (m_reg[offset]&1) == 0 && (data&1) != 0 ) {
+ m_smp_offset[channel] = 0;
+ m_env_offset[channel] = 0;
+ }
+ LOG_REGISTER_WRITE(("%s: offset %6X : data %2X\n", machine().describe_context(), offset, data ));
+ m_reg[offset] = data;
+}
+
+
+/* Use these for 16 bit CPUs */
+
+READ16_MEMBER( x1_010_device::word_r )
+{
+ UINT16 ret;
+
+ ret = m_HI_WORD_BUF[offset]<<8;
+ ret += (read( space, offset )&0xff);
+ LOG_REGISTER_READ(( "%s: Read X1-010 Offset:%04X Data:%04X\n", machine().describe_context(), offset, ret ));
+ return ret;
+}
+
+WRITE16_MEMBER( x1_010_device::word_w )
+{
+ m_HI_WORD_BUF[offset] = (data>>8)&0xff;
+ write( space, offset, data&0xff );
+ LOG_REGISTER_WRITE(( "%s: Write X1-010 Offset:%04X Data:%04X\n", machine().describe_context(), offset, data ));
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void x1_010_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ X1_010_CHANNEL *reg;
+ int ch, i, volL, volR, freq, div;
+ register INT8 *start, *end, data;
+ register UINT8 *env;
+ register UINT32 smp_offs, smp_step, env_offs, env_step, delta;
+
+ // mixer buffer zero clear
+ memset( outputs[0], 0, samples*sizeof(*outputs[0]) );
+ memset( outputs[1], 0, samples*sizeof(*outputs[1]) );
+
+// if( m_sound_enable == 0 ) return;
+
+ for( ch = 0; ch < SETA_NUM_CHANNELS; ch++ ) {
+ reg = (X1_010_CHANNEL *)&(m_reg[ch*sizeof(X1_010_CHANNEL)]);
+ if( (reg->status&1) != 0 ) { // Key On
+ stream_sample_t *bufL = outputs[0];
+ stream_sample_t *bufR = outputs[1];
+ div = (reg->status&0x80) ? 1 : 0;
+ if( (reg->status&2) == 0 ) { // PCM sampling
+ start = m_region + reg->start*0x1000;
+ end = m_region + (0x100-reg->end)*0x1000;
+ volL = ((reg->volume>>4)&0xf)*VOL_BASE;
+ volR = ((reg->volume>>0)&0xf)*VOL_BASE;
+ smp_offs = m_smp_offset[ch];
+ freq = reg->frequency>>div;
+ // Meta Fox does write the frequency register, but this is a hack to make it "work" with the current setup
+ // This is broken for Arbalester (it writes 8), but that'll be fixed later.
+ if( freq == 0 ) freq = 4;
+ smp_step = (UINT32)((float)m_base_clock/8192.0f
+ *freq*(1<<FREQ_BASE_BITS)/(float)m_rate);
+ if( smp_offs == 0 ) {
+ LOG_SOUND(( "Play sample %p - %p, channel %X volume %d:%d freq %X step %X offset %X\n",
+ start, end, ch, volL, volR, freq, smp_step, smp_offs ));
+ }
+ for( i = 0; i < samples; i++ ) {
+ delta = smp_offs>>FREQ_BASE_BITS;
+ // sample ended?
+ if( start+delta >= end ) {
+ reg->status &= 0xfe; // Key off
+ break;
+ }
+ data = *(start+delta);
+ *bufL++ += (data*volL/256);
+ *bufR++ += (data*volR/256);
+ smp_offs += smp_step;
+ }
+ m_smp_offset[ch] = smp_offs;
+ } else { // Wave form
+ start = (INT8 *)&(m_reg[reg->volume*128+0x1000]);
+ smp_offs = m_smp_offset[ch];
+ freq = ((reg->pitch_hi<<8)+reg->frequency)>>div;
+ smp_step = (UINT32)((float)m_base_clock/128.0f/1024.0f/4.0f*freq*(1<<FREQ_BASE_BITS)/(float)m_rate);
+
+ env = (UINT8 *)&(m_reg[reg->end*128]);
+ env_offs = m_env_offset[ch];
+ env_step = (UINT32)((float)m_base_clock/128.0f/1024.0f/4.0f*reg->start*(1<<ENV_BASE_BITS)/(float)m_rate);
+ /* Print some more debug info */
+ if( smp_offs == 0 ) {
+ LOG_SOUND(( "Play waveform %X, channel %X volume %X freq %4X step %X offset %X\n",
+ reg->volume, ch, reg->end, freq, smp_step, smp_offs ));
+ }
+ for( i = 0; i < samples; i++ ) {
+ int vol;
+ delta = env_offs>>ENV_BASE_BITS;
+ // Envelope one shot mode
+ if( (reg->status&4) != 0 && delta >= 0x80 ) {
+ reg->status &= 0xfe; // Key off
+ break;
+ }
+ vol = *(env+(delta&0x7f));
+ volL = ((vol>>4)&0xf)*VOL_BASE;
+ volR = ((vol>>0)&0xf)*VOL_BASE;
+ data = *(start+((smp_offs>>FREQ_BASE_BITS)&0x7f));
+ *bufL++ += (data*volL/256);
+ *bufR++ += (data*volR/256);
+ smp_offs += smp_step;
+ env_offs += env_step;
+ }
+ m_smp_offset[ch] = smp_offs;
+ m_env_offset[ch] = env_offs;
+ }
+ }
+ }
+}
diff --git a/src/devices/sound/x1_010.h b/src/devices/sound/x1_010.h
new file mode 100644
index 00000000000..a5026124210
--- /dev/null
+++ b/src/devices/sound/x1_010.h
@@ -0,0 +1,58 @@
+// license:BSD-3-Clause
+// copyright-holders:Luca Elia
+#pragma once
+
+#ifndef __X1_010_H__
+#define __X1_010_H__
+
+#define SETA_NUM_CHANNELS 16
+
+class x1_010_device : public device_t,
+ public device_sound_interface
+{
+public:
+ x1_010_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~x1_010_device() {}
+
+ // static configuration
+ static void set_address(device_t &device, int addr) { downcast<x1_010_device &>(device).m_adr = addr; }
+
+ DECLARE_READ8_MEMBER ( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ DECLARE_READ16_MEMBER ( word_r );
+ DECLARE_WRITE16_MEMBER( word_w );
+
+ void enable_w(int data);
+
+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:
+ // internal state
+
+ /* Variables only used here */
+ required_region_ptr<INT8> m_region; // ROM
+ int m_rate; // Output sampling rate (Hz)
+ int m_adr; // address
+ sound_stream * m_stream; // Stream handle
+ int m_sound_enable; // sound output enable/disable
+ UINT8 m_reg[0x2000]; // X1-010 Register & wave form area
+ UINT8 m_HI_WORD_BUF[0x2000]; // X1-010 16bit access ram check avoidance work
+ UINT32 m_smp_offset[SETA_NUM_CHANNELS];
+ UINT32 m_env_offset[SETA_NUM_CHANNELS];
+
+ UINT32 m_base_clock;
+};
+
+extern const device_type X1_010;
+
+
+#define MCFG_X1_010_ADDRESS(_addr) \
+ x1_010_device::set_address(*device, _addr);
+
+
+#endif /* __X1_010_H__ */
diff --git a/src/devices/sound/ym2151.c b/src/devices/sound/ym2151.c
new file mode 100644
index 00000000000..804cbc5a6fd
--- /dev/null
+++ b/src/devices/sound/ym2151.c
@@ -0,0 +1,2500 @@
+// license:???
+// copyright-holders:Jarek Burczynski
+/*****************************************************************************
+*
+* Yamaha YM2151 driver (version 2.150 final beta)
+*
+******************************************************************************/
+
+#include "emu.h"
+#include "ym2151.h"
+
+
+/* undef this to not use MAME timer system */
+#define USE_MAME_TIMERS
+
+/*#define FM_EMU*/
+#ifdef FM_EMU
+ #ifdef USE_MAME_TIMERS
+ #undef USE_MAME_TIMERS
+ #endif
+#endif
+
+#define LOG_CYM_FILE 0
+static FILE * cymfile = NULL;
+
+
+/* struct describing a single operator */
+struct YM2151Operator
+{
+ UINT32 phase; /* accumulated operator phase */
+ UINT32 freq; /* operator frequency count */
+ INT32 dt1; /* current DT1 (detune 1 phase inc/decrement) value */
+ UINT32 mul; /* frequency count multiply */
+ UINT32 dt1_i; /* DT1 index * 32 */
+ UINT32 dt2; /* current DT2 (detune 2) value */
+
+ signed int *connect; /* operator output 'direction' */
+
+ /* only M1 (operator 0) is filled with this data: */
+ signed int *mem_connect; /* where to put the delayed sample (MEM) */
+ INT32 mem_value; /* delayed sample (MEM) value */
+
+ /* channel specific data; note: each operator number 0 contains channel specific data */
+ UINT32 fb_shift; /* feedback shift value for operators 0 in each channel */
+ INT32 fb_out_curr; /* operator feedback value (used only by operators 0) */
+ INT32 fb_out_prev; /* previous feedback value (used only by operators 0) */
+ UINT32 kc; /* channel KC (copied to all operators) */
+ UINT32 kc_i; /* just for speedup */
+ UINT32 pms; /* channel PMS */
+ UINT32 ams; /* channel AMS */
+ /* end of channel specific data */
+
+ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */
+ UINT32 state; /* Envelope state: 4-attack(AR) 3-decay(D1R) 2-sustain(D2R) 1-release(RR) 0-off */
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT32 tl; /* Total attenuation Level */
+ INT32 volume; /* current envelope attenuation level */
+ UINT8 eg_sh_d1r; /* (decay state) */
+ UINT8 eg_sel_d1r; /* (decay state) */
+ UINT32 d1l; /* envelope switches to sustain state after reaching this level */
+ UINT8 eg_sh_d2r; /* (sustain state) */
+ UINT8 eg_sel_d2r; /* (sustain state) */
+ UINT8 eg_sh_rr; /* (release state) */
+ UINT8 eg_sel_rr; /* (release state) */
+
+ UINT32 key; /* 0=last key was KEY OFF, 1=last key was KEY ON */
+
+ UINT32 ks; /* key scale */
+ UINT32 ar; /* attack rate */
+ UINT32 d1r; /* decay rate */
+ UINT32 d2r; /* sustain rate */
+ UINT32 rr; /* release rate */
+
+ UINT32 reserved0; /**/
+ UINT32 reserved1; /**/
+
+};
+
+
+struct YM2151
+{
+ signed int chanout[8];
+ signed int m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */
+ signed int mem; /* one sample delay memory */
+
+ YM2151Operator oper[32]; /* the 32 operators */
+
+ UINT32 pan[16]; /* channels output masks (0xffffffff = enable) */
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/64/3 */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 3 samples (on real chip) */
+
+ UINT32 lfo_phase; /* accumulated LFO phase (0 to 255) */
+ UINT32 lfo_timer; /* LFO timer */
+ UINT32 lfo_timer_add; /* step of lfo_timer */
+ UINT32 lfo_overflow; /* LFO generates new output when lfo_timer reaches this value */
+ UINT32 lfo_counter; /* LFO phase increment counter */
+ UINT32 lfo_counter_add; /* step of lfo_counter */
+ UINT8 lfo_wsel; /* LFO waveform (0-saw, 1-square, 2-triangle, 3-random noise) */
+ UINT8 amd; /* LFO Amplitude Modulation Depth */
+ INT8 pmd; /* LFO Phase Modulation Depth */
+ UINT32 lfa; /* LFO current AM output */
+ INT32 lfp; /* LFO current PM output */
+
+ UINT8 test; /* TEST register */
+ UINT8 ct; /* output control pins (bit1-CT2, bit0-CT1) */
+
+ UINT32 noise; /* noise enable/period register (bit 7 - noise enable, bits 4-0 - noise period */
+ UINT32 noise_rng; /* 17 bit noise shift register */
+ UINT32 noise_p; /* current noise 'phase'*/
+ UINT32 noise_f; /* current noise period */
+
+ UINT32 csm_req; /* CSM KEY ON / KEY OFF sequence request */
+
+ UINT32 irq_enable; /* IRQ enable for timer B (bit 3) and timer A (bit 2); bit 7 - CSM mode (keyon to all slots, everytime timer A overflows) */
+ UINT32 status; /* chip status (BUSY, IRQ Flags) */
+ UINT8 connect[8]; /* channels connections */
+
+#ifdef USE_MAME_TIMERS
+/* ASG 980324 -- added for tracking timers */
+ emu_timer *timer_A;
+ emu_timer *timer_B;
+ attotime timer_A_time[1024]; /* timer A times for MAME */
+ attotime timer_B_time[256]; /* timer B times for MAME */
+ int irqlinestate;
+#else
+ UINT8 tim_A; /* timer A enable (0-disabled) */
+ UINT8 tim_B; /* timer B enable (0-disabled) */
+ INT32 tim_A_val; /* current value of timer A */
+ INT32 tim_B_val; /* current value of timer B */
+ UINT32 tim_A_tab[1024]; /* timer A deltas */
+ UINT32 tim_B_tab[256]; /* timer B deltas */
+#endif
+ UINT32 timer_A_index; /* timer A index */
+ UINT32 timer_B_index; /* timer B index */
+ UINT32 timer_A_index_old; /* timer A previous index */
+ UINT32 timer_B_index_old; /* timer B previous index */
+
+ /* Frequency-deltas to get the closest frequency possible.
+ * There are 11 octaves because of DT2 (max 950 cents over base frequency)
+ * and LFO phase modulation (max 800 cents below AND over base frequency)
+ * Summary: octave explanation
+ * 0 note code - LFO PM
+ * 1 note code
+ * 2 note code
+ * 3 note code
+ * 4 note code
+ * 5 note code
+ * 6 note code
+ * 7 note code
+ * 8 note code
+ * 9 note code + DT2 + LFO PM
+ * 10 note code + DT2 + LFO PM
+ */
+ UINT32 freq[11*768]; /* 11 octaves, 768 'cents' per octave */
+
+ /* Frequency deltas for DT1. These deltas alter operator frequency
+ * after it has been taken from frequency-deltas table.
+ */
+ INT32 dt1_freq[8*32]; /* 8 DT1 levels, 32 KC values */
+
+ UINT32 noise_tab[32]; /* 17bit Noise Generator periods */
+
+ void (*irqhandler)(device_t *device, int irq); /* IRQ function handler */
+ void (*porthandler)(device_t *, offs_t, UINT8); /* port write function handler */
+
+ device_t *device;
+ unsigned int clock; /* chip clock in Hz (passed from 2151intf.c) */
+ unsigned int sampfreq; /* sampling frequency in Hz (passed from 2151intf.c) */
+};
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */
+#define LFO_SH 10 /* 22.10 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<<FREQ_SH)-1)
+
+#define ENV_BITS 10
+#define ENV_LEN (1<<ENV_BITS)
+#define ENV_STEP (128.0/ENV_LEN)
+
+#define MAX_ATT_INDEX (ENV_LEN-1) /* 1023 */
+#define MIN_ATT_INDEX (0) /* 0 */
+
+#define EG_ATT 4
+#define EG_DEC 3
+#define EG_SUS 2
+#define EG_REL 1
+#define EG_OFF 0
+
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
+
+
+#if (SAMPLE_BITS==16)
+ #define FINAL_SH (0)
+ #define MAXOUT (+32767)
+ #define MINOUT (-32768)
+#else
+ #define FINAL_SH (8)
+ #define MAXOUT (+127)
+ #define MINOUT (-128)
+#endif
+
+
+/* TL_TAB_LEN is calculated as:
+* 13 - sinus amplitude bits (Y axis)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (13*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+#define ENV_QUIET (TL_TAB_LEN>>3)
+
+/* sin waveform table in 'decibel' scale */
+static unsigned int sin_tab[SIN_LEN];
+
+
+/* translate from D1L to volume index (16 D1L levels) */
+static UINT32 d1l_tab[16];
+
+
+#define RATE_STEPS (8)
+static const UINT8 eg_inc[19*RATE_STEPS]={
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */
+/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */
+/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */
+/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */
+
+/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */
+/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */
+/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(17) in this table - it's directly in the code */
+static const UINT8 eg_rate_select[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */
+/* 32 dummy (infinite time) rates */
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+
+/* rates 00-11 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 12 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 13 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 14 */
+O(12),O(13),O(14),O(15),
+
+/* rate 15 */
+O(16),O(16),O(16),O(16),
+
+/* 32 dummy rates (same as 15 3) */
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16)
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/
+/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */
+/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */
+/* 32 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+
+/* rates 00-11 */
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 12 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 32 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0)
+
+};
+#undef O
+
+/* DT2 defines offset in cents from base note
+*
+* This table defines offset in frequency-deltas table.
+* User's Manual page 22
+*
+* Values below were calculated using formula: value = orig.val / 1.5625
+*
+* DT2=0 DT2=1 DT2=2 DT2=3
+* 0 600 781 950
+*/
+static const UINT32 dt2_tab[4] = { 0, 384, 500, 608 };
+
+/* DT1 defines offset in Hertz from base note
+* This table is converted while initialization...
+* Detune table shown in YM2151 User's Manual is wrong (verified on the real chip)
+*/
+
+static const UINT8 dt1_tab[4*32] = { /* 4*32 DT1 values */
+/* DT1=0 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+/* DT1=1 */
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
+
+/* DT1=2 */
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5,
+ 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16,
+
+/* DT1=3 */
+ 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
+ 8, 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22
+};
+
+static const UINT16 phaseinc_rom[768]={
+1299,1300,1301,1302,1303,1304,1305,1306,1308,1309,1310,1311,1313,1314,1315,1316,
+1318,1319,1320,1321,1322,1323,1324,1325,1327,1328,1329,1330,1332,1333,1334,1335,
+1337,1338,1339,1340,1341,1342,1343,1344,1346,1347,1348,1349,1351,1352,1353,1354,
+1356,1357,1358,1359,1361,1362,1363,1364,1366,1367,1368,1369,1371,1372,1373,1374,
+1376,1377,1378,1379,1381,1382,1383,1384,1386,1387,1388,1389,1391,1392,1393,1394,
+1396,1397,1398,1399,1401,1402,1403,1404,1406,1407,1408,1409,1411,1412,1413,1414,
+1416,1417,1418,1419,1421,1422,1423,1424,1426,1427,1429,1430,1431,1432,1434,1435,
+1437,1438,1439,1440,1442,1443,1444,1445,1447,1448,1449,1450,1452,1453,1454,1455,
+1458,1459,1460,1461,1463,1464,1465,1466,1468,1469,1471,1472,1473,1474,1476,1477,
+1479,1480,1481,1482,1484,1485,1486,1487,1489,1490,1492,1493,1494,1495,1497,1498,
+1501,1502,1503,1504,1506,1507,1509,1510,1512,1513,1514,1515,1517,1518,1520,1521,
+1523,1524,1525,1526,1528,1529,1531,1532,1534,1535,1536,1537,1539,1540,1542,1543,
+1545,1546,1547,1548,1550,1551,1553,1554,1556,1557,1558,1559,1561,1562,1564,1565,
+1567,1568,1569,1570,1572,1573,1575,1576,1578,1579,1580,1581,1583,1584,1586,1587,
+1590,1591,1592,1593,1595,1596,1598,1599,1601,1602,1604,1605,1607,1608,1609,1610,
+1613,1614,1615,1616,1618,1619,1621,1622,1624,1625,1627,1628,1630,1631,1632,1633,
+1637,1638,1639,1640,1642,1643,1645,1646,1648,1649,1651,1652,1654,1655,1656,1657,
+1660,1661,1663,1664,1666,1667,1669,1670,1672,1673,1675,1676,1678,1679,1681,1682,
+1685,1686,1688,1689,1691,1692,1694,1695,1697,1698,1700,1701,1703,1704,1706,1707,
+1709,1710,1712,1713,1715,1716,1718,1719,1721,1722,1724,1725,1727,1728,1730,1731,
+1734,1735,1737,1738,1740,1741,1743,1744,1746,1748,1749,1751,1752,1754,1755,1757,
+1759,1760,1762,1763,1765,1766,1768,1769,1771,1773,1774,1776,1777,1779,1780,1782,
+1785,1786,1788,1789,1791,1793,1794,1796,1798,1799,1801,1802,1804,1806,1807,1809,
+1811,1812,1814,1815,1817,1819,1820,1822,1824,1825,1827,1828,1830,1832,1833,1835,
+1837,1838,1840,1841,1843,1845,1846,1848,1850,1851,1853,1854,1856,1858,1859,1861,
+1864,1865,1867,1868,1870,1872,1873,1875,1877,1879,1880,1882,1884,1885,1887,1888,
+1891,1892,1894,1895,1897,1899,1900,1902,1904,1906,1907,1909,1911,1912,1914,1915,
+1918,1919,1921,1923,1925,1926,1928,1930,1932,1933,1935,1937,1939,1940,1942,1944,
+1946,1947,1949,1951,1953,1954,1956,1958,1960,1961,1963,1965,1967,1968,1970,1972,
+1975,1976,1978,1980,1982,1983,1985,1987,1989,1990,1992,1994,1996,1997,1999,2001,
+2003,2004,2006,2008,2010,2011,2013,2015,2017,2019,2021,2022,2024,2026,2028,2029,
+2032,2033,2035,2037,2039,2041,2043,2044,2047,2048,2050,2052,2054,2056,2058,2059,
+2062,2063,2065,2067,2069,2071,2073,2074,2077,2078,2080,2082,2084,2086,2088,2089,
+2092,2093,2095,2097,2099,2101,2103,2104,2107,2108,2110,2112,2114,2116,2118,2119,
+2122,2123,2125,2127,2129,2131,2133,2134,2137,2139,2141,2142,2145,2146,2148,2150,
+2153,2154,2156,2158,2160,2162,2164,2165,2168,2170,2172,2173,2176,2177,2179,2181,
+2185,2186,2188,2190,2192,2194,2196,2197,2200,2202,2204,2205,2208,2209,2211,2213,
+2216,2218,2220,2222,2223,2226,2227,2230,2232,2234,2236,2238,2239,2242,2243,2246,
+2249,2251,2253,2255,2256,2259,2260,2263,2265,2267,2269,2271,2272,2275,2276,2279,
+2281,2283,2285,2287,2288,2291,2292,2295,2297,2299,2301,2303,2304,2307,2308,2311,
+2315,2317,2319,2321,2322,2325,2326,2329,2331,2333,2335,2337,2338,2341,2342,2345,
+2348,2350,2352,2354,2355,2358,2359,2362,2364,2366,2368,2370,2371,2374,2375,2378,
+2382,2384,2386,2388,2389,2392,2393,2396,2398,2400,2402,2404,2407,2410,2411,2414,
+2417,2419,2421,2423,2424,2427,2428,2431,2433,2435,2437,2439,2442,2445,2446,2449,
+2452,2454,2456,2458,2459,2462,2463,2466,2468,2470,2472,2474,2477,2480,2481,2484,
+2488,2490,2492,2494,2495,2498,2499,2502,2504,2506,2508,2510,2513,2516,2517,2520,
+2524,2526,2528,2530,2531,2534,2535,2538,2540,2542,2544,2546,2549,2552,2553,2556,
+2561,2563,2565,2567,2568,2571,2572,2575,2577,2579,2581,2583,2586,2589,2590,2593
+};
+
+
+/*
+ Noise LFO waveform.
+
+ Here are just 256 samples out of much longer data.
+
+ It does NOT repeat every 256 samples on real chip and I wasnt able to find
+ the point where it repeats (even in strings as long as 131072 samples).
+
+ I only put it here because its better than nothing and perhaps
+ someone might be able to figure out the real algorithm.
+
+
+ Note that (due to the way the LFO output is calculated) it is quite
+ possible that two values: 0x80 and 0x00 might be wrong in this table.
+ To be exact:
+ some 0x80 could be 0x81 as well as some 0x00 could be 0x01.
+*/
+
+static const UINT8 lfo_noise_waveform[256] = {
+0xFF,0xEE,0xD3,0x80,0x58,0xDA,0x7F,0x94,0x9E,0xE3,0xFA,0x00,0x4D,0xFA,0xFF,0x6A,
+0x7A,0xDE,0x49,0xF6,0x00,0x33,0xBB,0x63,0x91,0x60,0x51,0xFF,0x00,0xD8,0x7F,0xDE,
+0xDC,0x73,0x21,0x85,0xB2,0x9C,0x5D,0x24,0xCD,0x91,0x9E,0x76,0x7F,0x20,0xFB,0xF3,
+0x00,0xA6,0x3E,0x42,0x27,0x69,0xAE,0x33,0x45,0x44,0x11,0x41,0x72,0x73,0xDF,0xA2,
+
+0x32,0xBD,0x7E,0xA8,0x13,0xEB,0xD3,0x15,0xDD,0xFB,0xC9,0x9D,0x61,0x2F,0xBE,0x9D,
+0x23,0x65,0x51,0x6A,0x84,0xF9,0xC9,0xD7,0x23,0xBF,0x65,0x19,0xDC,0x03,0xF3,0x24,
+0x33,0xB6,0x1E,0x57,0x5C,0xAC,0x25,0x89,0x4D,0xC5,0x9C,0x99,0x15,0x07,0xCF,0xBA,
+0xC5,0x9B,0x15,0x4D,0x8D,0x2A,0x1E,0x1F,0xEA,0x2B,0x2F,0x64,0xA9,0x50,0x3D,0xAB,
+
+0x50,0x77,0xE9,0xC0,0xAC,0x6D,0x3F,0xCA,0xCF,0x71,0x7D,0x80,0xA6,0xFD,0xFF,0xB5,
+0xBD,0x6F,0x24,0x7B,0x00,0x99,0x5D,0xB1,0x48,0xB0,0x28,0x7F,0x80,0xEC,0xBF,0x6F,
+0x6E,0x39,0x90,0x42,0xD9,0x4E,0x2E,0x12,0x66,0xC8,0xCF,0x3B,0x3F,0x10,0x7D,0x79,
+0x00,0xD3,0x1F,0x21,0x93,0x34,0xD7,0x19,0x22,0xA2,0x08,0x20,0xB9,0xB9,0xEF,0x51,
+
+0x99,0xDE,0xBF,0xD4,0x09,0x75,0xE9,0x8A,0xEE,0xFD,0xE4,0x4E,0x30,0x17,0xDF,0xCE,
+0x11,0xB2,0x28,0x35,0xC2,0x7C,0x64,0xEB,0x91,0x5F,0x32,0x0C,0x6E,0x00,0xF9,0x92,
+0x19,0xDB,0x8F,0xAB,0xAE,0xD6,0x12,0xC4,0x26,0x62,0xCE,0xCC,0x0A,0x03,0xE7,0xDD,
+0xE2,0x4D,0x8A,0xA6,0x46,0x95,0x0F,0x8F,0xF5,0x15,0x97,0x32,0xD4,0x28,0x1E,0x55
+};
+
+
+
+
+/* save output as raw 16-bit sample */
+/* #define SAVE_SAMPLE */
+/* #define SAVE_SEPARATE_CHANNELS */
+#if defined SAVE_SAMPLE || defined SAVE_SEPARATE_CHANNELS
+static FILE *sample[9];
+#endif
+
+
+
+
+static void init_tables(void)
+{
+ signed int i,x,n;
+ double o,m;
+
+ for (x=0; x<TL_RES_LEN; x++)
+ {
+ m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);
+ m = floor(m);
+
+ /* we never reach (1<<16) here due to the (x+1) */
+ /* result fits within 16 bits at maximum */
+
+ n = (int)m; /* 16 bits here */
+ n >>= 4; /* 12 bits here */
+ if (n&1) /* round to closest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 2; /* 13 bits here (as in real chip) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<13; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ #if 0
+ logerror("tl %04i", x*2);
+ for (i=0; i<13; i++)
+ logerror(", [%02i] %4i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ]);
+ logerror("\n");
+ #endif
+ }
+ /*logerror("TL_TAB_LEN = %i (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/
+ /*logerror("ENV_QUIET= %i\n",ENV_QUIET );*/
+
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* non-standard sinus */
+ m = sin( ((i*2)+1) * M_PI / SIN_LEN ); /* verified on the real chip */
+
+ /* we never reach zero here due to ((i*2)+1) */
+
+ if (m>0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to closest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+ /*logerror("sin [0x%4x]= %4i (tl_tab value=%8x)\n", i, sin_tab[i],tl_tab[sin_tab[i]]);*/
+ }
+
+
+ /* calculate d1l_tab table */
+ for (i=0; i<16; i++)
+ {
+ m = (i!=15 ? i : i+16) * (4.0/ENV_STEP); /* every 3 'dB' except for all bits = 1 = 45+48 'dB' */
+ d1l_tab[i] = m;
+ /*logerror("d1l_tab[%02x]=%08x\n",i,d1l_tab[i] );*/
+ }
+
+#ifdef SAVE_SAMPLE
+ sample[8]=fopen("sampsum.pcm","wb");
+#endif
+#ifdef SAVE_SEPARATE_CHANNELS
+ sample[0]=fopen("samp0.pcm","wb");
+ sample[1]=fopen("samp1.pcm","wb");
+ sample[2]=fopen("samp2.pcm","wb");
+ sample[3]=fopen("samp3.pcm","wb");
+ sample[4]=fopen("samp4.pcm","wb");
+ sample[5]=fopen("samp5.pcm","wb");
+ sample[6]=fopen("samp6.pcm","wb");
+ sample[7]=fopen("samp7.pcm","wb");
+#endif
+}
+
+
+static void init_chip_tables(YM2151 *chip)
+{
+ int i,j;
+ double mult,phaseinc,Hz;
+ double scaler;
+ attotime pom;
+
+ scaler = ( (double)chip->clock / 64.0 ) / ( (double)chip->sampfreq );
+ /*logerror("scaler = %20.15f\n", scaler);*/
+
+
+ /* this loop calculates Hertz values for notes from c-0 to b-7 */
+ /* including 64 'cents' (100/64 that is 1.5625 of real cent) per note */
+ /* i*100/64/1200 is equal to i/768 */
+
+ /* real chip works with 10 bits fixed point values (10.10) */
+ mult = (1<<(FREQ_SH-10)); /* -10 because phaseinc_rom table values are already in 10.10 format */
+
+ for (i=0; i<768; i++)
+ {
+ /* 3.4375 Hz is note A; C# is 4 semitones higher */
+ Hz = 1000;
+#if 0
+/* Hz is close, but not perfect */
+ //Hz = scaler * 3.4375 * pow (2, (i + 4 * 64 ) / 768.0 );
+ /* calculate phase increment */
+ phaseinc = (Hz*SIN_LEN) / (double)chip->sampfreq;
+#endif
+
+ phaseinc = phaseinc_rom[i]; /* real chip phase increment */
+ phaseinc *= scaler; /* adjust */
+
+
+ /* octave 2 - reference octave */
+ chip->freq[ 768+2*768+i ] = ((int)(phaseinc*mult)) & 0xffffffc0; /* adjust to X.10 fixed point */
+ /* octave 0 and octave 1 */
+ for (j=0; j<2; j++)
+ {
+ chip->freq[768 + j*768 + i] = (chip->freq[ 768+2*768+i ] >> (2-j) ) & 0xffffffc0; /* adjust to X.10 fixed point */
+ }
+ /* octave 3 to 7 */
+ for (j=3; j<8; j++)
+ {
+ chip->freq[768 + j*768 + i] = chip->freq[ 768+2*768+i ] << (j-2);
+ }
+
+ #if 0
+ pom = (double)chip->freq[ 768+2*768+i ] / ((double)(1<<FREQ_SH));
+ pom = pom * (double)chip->sampfreq / (double)SIN_LEN;
+ logerror("1freq[%4i][%08x]= real %20.15f Hz emul %20.15f Hz\n", i, chip->freq[ 768+2*768+i ], Hz, pom);
+ #endif
+ }
+
+ /* octave -1 (all equal to: oct 0, _KC_00_, _KF_00_) */
+ for (i=0; i<768; i++)
+ {
+ chip->freq[ 0*768 + i ] = chip->freq[1*768+0];
+ }
+
+ /* octave 8 and 9 (all equal to: oct 7, _KC_14_, _KF_63_) */
+ for (j=8; j<10; j++)
+ {
+ for (i=0; i<768; i++)
+ {
+ chip->freq[768+ j*768 + i ] = chip->freq[768 + 8*768 -1];
+ }
+ }
+
+#if 0
+ for (i=0; i<11*768; i++)
+ {
+ pom = (double)chip->freq[i] / ((double)(1<<FREQ_SH));
+ pom = pom * (double)chip->sampfreq / (double)SIN_LEN;
+ logerror("freq[%4i][%08x]= emul %20.15f Hz\n", i, chip->freq[i], pom);
+ }
+#endif
+
+ mult = (1<<FREQ_SH);
+ for (j=0; j<4; j++)
+ {
+ for (i=0; i<32; i++)
+ {
+ Hz = ( (double)dt1_tab[j*32+i] * ((double)chip->clock/64.0) ) / (double)(1<<20);
+
+ /*calculate phase increment*/
+ phaseinc = (Hz*SIN_LEN) / (double)chip->sampfreq;
+
+ /*positive and negative values*/
+ chip->dt1_freq[ (j+0)*32 + i ] = phaseinc * mult;
+ chip->dt1_freq[ (j+4)*32 + i ] = -chip->dt1_freq[ (j+0)*32 + i ];
+
+#if 0
+ {
+ int x = j*32 + i;
+ pom = (double)chip->dt1_freq[x] / mult;
+ pom = pom * (double)chip->sampfreq / (double)SIN_LEN;
+ logerror("DT1(%03i)[%02i %02i][%08x]= real %19.15f Hz emul %19.15f Hz\n",
+ x, j, i, chip->dt1_freq[x], Hz, pom);
+ }
+#endif
+ }
+ }
+
+
+ /* calculate timers' deltas */
+ /* User's Manual pages 15,16 */
+ mult = (1<<TIMER_SH);
+ for (i=0; i<1024; i++)
+ {
+ /* ASG 980324: changed to compute both tim_A_tab and timer_A_time */
+ pom= attotime::from_hz(chip->clock) * (64 * (1024 - i));
+ #ifdef USE_MAME_TIMERS
+ chip->timer_A_time[i] = pom;
+ #else
+ chip->tim_A_tab[i] = pom.as_double() * (double)chip->sampfreq * mult; /* number of samples that timer period takes (fixed point) */
+ #endif
+ }
+ for (i=0; i<256; i++)
+ {
+ /* ASG 980324: changed to compute both tim_B_tab and timer_B_time */
+ pom= attotime::from_hz(chip->clock) * (1024 * (256 - i));
+ #ifdef USE_MAME_TIMERS
+ chip->timer_B_time[i] = pom;
+ #else
+ chip->tim_B_tab[i] = pom.as_double() * (double)chip->sampfreq * mult; /* number of samples that timer period takes (fixed point) */
+ #endif
+ }
+
+ /* calculate noise periods table */
+ scaler = ( (double)chip->clock / 64.0 ) / ( (double)chip->sampfreq );
+ for (i=0; i<32; i++)
+ {
+ j = (i!=31 ? i : 30); /* rate 30 and 31 are the same */
+ j = 32-j;
+ j = (65536.0 / (double)(j*32.0)); /* number of samples per one shift of the shift register */
+ /*chip->noise_tab[i] = j * 64;*/ /* number of chip clock cycles per one shift */
+ chip->noise_tab[i] = j * 64 * scaler;
+ /*logerror("noise_tab[%02x]=%08x\n", i, chip->noise_tab[i]);*/
+ }
+}
+
+#define KEY_ON(op, key_set){ \
+ if (!(op)->key) \
+ { \
+ (op)->phase = 0; /* clear phase */ \
+ (op)->state = EG_ATT; /* KEY ON = attack */ \
+ (op)->volume += (~(op)->volume * \
+ (eg_inc[(op)->eg_sel_ar + ((PSG->eg_cnt>>(op)->eg_sh_ar)&7)]) \
+ ) >>4; \
+ if ((op)->volume <= MIN_ATT_INDEX) \
+ { \
+ (op)->volume = MIN_ATT_INDEX; \
+ (op)->state = EG_DEC; \
+ } \
+ } \
+ (op)->key |= key_set; \
+}
+
+#define KEY_OFF(op, key_clr){ \
+ if ((op)->key) \
+ { \
+ (op)->key &= key_clr; \
+ if (!(op)->key) \
+ { \
+ if ((op)->state>EG_REL) \
+ (op)->state = EG_REL;/* KEY OFF = release */\
+ } \
+ } \
+}
+
+INLINE void envelope_KONKOFF(YM2151 *PSG, YM2151Operator * op, int v)
+{
+ if (v&0x08) /* M1 */
+ KEY_ON (op+0, 1)
+ else
+ KEY_OFF(op+0,~1)
+
+ if (v&0x20) /* M2 */
+ KEY_ON (op+1, 1)
+ else
+ KEY_OFF(op+1,~1)
+
+ if (v&0x10) /* C1 */
+ KEY_ON (op+2, 1)
+ else
+ KEY_OFF(op+2,~1)
+
+ if (v&0x40) /* C2 */
+ KEY_ON (op+3, 1)
+ else
+ KEY_OFF(op+3,~1)
+}
+
+
+#ifdef USE_MAME_TIMERS
+
+static TIMER_CALLBACK( irqAon_callback )
+{
+ YM2151 *chip = (YM2151 *)ptr;
+ int oldstate = chip->irqlinestate;
+
+ chip->irqlinestate |= 1;
+
+ if (oldstate == 0 && chip->irqhandler) (*chip->irqhandler)(chip->device, 1);
+}
+
+static TIMER_CALLBACK( irqBon_callback )
+{
+ YM2151 *chip = (YM2151 *)ptr;
+ int oldstate = chip->irqlinestate;
+
+ chip->irqlinestate |= 2;
+
+ if (oldstate == 0 && chip->irqhandler) (*chip->irqhandler)(chip->device, 1);
+}
+
+static TIMER_CALLBACK( irqAoff_callback )
+{
+ YM2151 *chip = (YM2151 *)ptr;
+ int oldstate = chip->irqlinestate;
+
+ chip->irqlinestate &= ~1;
+
+ if (oldstate == 1 && chip->irqhandler) (*chip->irqhandler)(chip->device, 0);
+}
+
+static TIMER_CALLBACK( irqBoff_callback )
+{
+ YM2151 *chip = (YM2151 *)ptr;
+ int oldstate = chip->irqlinestate;
+
+ chip->irqlinestate &= ~2;
+
+ if (oldstate == 2 && chip->irqhandler) (*chip->irqhandler)(chip->device, 0);
+}
+
+static TIMER_CALLBACK( timer_callback_a )
+{
+ YM2151 *chip = (YM2151 *)ptr;
+ chip->timer_A->adjust(chip->timer_A_time[ chip->timer_A_index ]);
+ chip->timer_A_index_old = chip->timer_A_index;
+ if (chip->irq_enable & 0x04)
+ {
+ chip->status |= 1;
+ machine.scheduler().timer_set(attotime::zero, FUNC(irqAon_callback), 0, chip);
+ }
+ if (chip->irq_enable & 0x80)
+ chip->csm_req = 2; /* request KEY ON / KEY OFF sequence */
+}
+static TIMER_CALLBACK( timer_callback_b )
+{
+ YM2151 *chip = (YM2151 *)ptr;
+ chip->timer_B->adjust(chip->timer_B_time[ chip->timer_B_index ]);
+ chip->timer_B_index_old = chip->timer_B_index;
+ if (chip->irq_enable & 0x08)
+ {
+ chip->status |= 2;
+ machine.scheduler().timer_set(attotime::zero, FUNC(irqBon_callback), 0, chip);
+ }
+}
+#if 0
+static TIMER_CALLBACK( timer_callback_chip_busy )
+{
+ YM2151 *chip = (YM2151 *)ptr;
+ chip->status &= 0x7f; /* reset busy flag */
+}
+#endif
+#endif
+
+
+
+
+
+
+INLINE void set_connect(YM2151 *PSG, YM2151Operator *om1, int cha, int v)
+{
+ YM2151Operator *om2 = om1+1;
+ YM2151Operator *oc1 = om1+2;
+
+ /* set connect algorithm */
+
+ /* MEM is simply one sample delay */
+
+ switch( v&7 )
+ {
+ case 0:
+ /* M1---C1---MEM---M2---C2---OUT */
+ om1->connect = &PSG->c1;
+ oc1->connect = &PSG->mem;
+ om2->connect = &PSG->c2;
+ om1->mem_connect = &PSG->m2;
+ break;
+
+ case 1:
+ /* M1------+-MEM---M2---C2---OUT */
+ /* C1-+ */
+ om1->connect = &PSG->mem;
+ oc1->connect = &PSG->mem;
+ om2->connect = &PSG->c2;
+ om1->mem_connect = &PSG->m2;
+ break;
+
+ case 2:
+ /* M1-----------------+-C2---OUT */
+ /* C1---MEM---M2-+ */
+ om1->connect = &PSG->c2;
+ oc1->connect = &PSG->mem;
+ om2->connect = &PSG->c2;
+ om1->mem_connect = &PSG->m2;
+ break;
+
+ case 3:
+ /* M1---C1---MEM------+-C2---OUT */
+ /* M2-+ */
+ om1->connect = &PSG->c1;
+ oc1->connect = &PSG->mem;
+ om2->connect = &PSG->c2;
+ om1->mem_connect = &PSG->c2;
+ break;
+
+ case 4:
+ /* M1---C1-+-OUT */
+ /* M2---C2-+ */
+ /* MEM: not used */
+ om1->connect = &PSG->c1;
+ oc1->connect = &PSG->chanout[cha];
+ om2->connect = &PSG->c2;
+ om1->mem_connect = &PSG->mem; /* store it anywhere where it will not be used */
+ break;
+
+ case 5:
+ /* +----C1----+ */
+ /* M1-+-MEM---M2-+-OUT */
+ /* +----C2----+ */
+ om1->connect = 0; /* special mark */
+ oc1->connect = &PSG->chanout[cha];
+ om2->connect = &PSG->chanout[cha];
+ om1->mem_connect = &PSG->m2;
+ break;
+
+ case 6:
+ /* M1---C1-+ */
+ /* M2-+-OUT */
+ /* C2-+ */
+ /* MEM: not used */
+ om1->connect = &PSG->c1;
+ oc1->connect = &PSG->chanout[cha];
+ om2->connect = &PSG->chanout[cha];
+ om1->mem_connect = &PSG->mem; /* store it anywhere where it will not be used */
+ break;
+
+ case 7:
+ /* M1-+ */
+ /* C1-+-OUT */
+ /* M2-+ */
+ /* C2-+ */
+ /* MEM: not used*/
+ om1->connect = &PSG->chanout[cha];
+ oc1->connect = &PSG->chanout[cha];
+ om2->connect = &PSG->chanout[cha];
+ om1->mem_connect = &PSG->mem; /* store it anywhere where it will not be used */
+ break;
+ }
+}
+
+
+INLINE void refresh_EG(YM2151Operator * op)
+{
+ UINT32 kc;
+ UINT32 v;
+
+ kc = op->kc;
+
+ /* v = 32 + 2*RATE + RKS = max 126 */
+
+ v = kc >> op->ks;
+ if ((op->ar+v) < 32+62)
+ {
+ op->eg_sh_ar = eg_rate_shift [op->ar + v ];
+ op->eg_sel_ar = eg_rate_select[op->ar + v ];
+ }
+ else
+ {
+ op->eg_sh_ar = 0;
+ op->eg_sel_ar = 17*RATE_STEPS;
+ }
+ op->eg_sh_d1r = eg_rate_shift [op->d1r + v];
+ op->eg_sel_d1r= eg_rate_select[op->d1r + v];
+ op->eg_sh_d2r = eg_rate_shift [op->d2r + v];
+ op->eg_sel_d2r= eg_rate_select[op->d2r + v];
+ op->eg_sh_rr = eg_rate_shift [op->rr + v];
+ op->eg_sel_rr = eg_rate_select[op->rr + v];
+
+
+ op+=1;
+
+ v = kc >> op->ks;
+ if ((op->ar+v) < 32+62)
+ {
+ op->eg_sh_ar = eg_rate_shift [op->ar + v ];
+ op->eg_sel_ar = eg_rate_select[op->ar + v ];
+ }
+ else
+ {
+ op->eg_sh_ar = 0;
+ op->eg_sel_ar = 17*RATE_STEPS;
+ }
+ op->eg_sh_d1r = eg_rate_shift [op->d1r + v];
+ op->eg_sel_d1r= eg_rate_select[op->d1r + v];
+ op->eg_sh_d2r = eg_rate_shift [op->d2r + v];
+ op->eg_sel_d2r= eg_rate_select[op->d2r + v];
+ op->eg_sh_rr = eg_rate_shift [op->rr + v];
+ op->eg_sel_rr = eg_rate_select[op->rr + v];
+
+ op+=1;
+
+ v = kc >> op->ks;
+ if ((op->ar+v) < 32+62)
+ {
+ op->eg_sh_ar = eg_rate_shift [op->ar + v ];
+ op->eg_sel_ar = eg_rate_select[op->ar + v ];
+ }
+ else
+ {
+ op->eg_sh_ar = 0;
+ op->eg_sel_ar = 17*RATE_STEPS;
+ }
+ op->eg_sh_d1r = eg_rate_shift [op->d1r + v];
+ op->eg_sel_d1r= eg_rate_select[op->d1r + v];
+ op->eg_sh_d2r = eg_rate_shift [op->d2r + v];
+ op->eg_sel_d2r= eg_rate_select[op->d2r + v];
+ op->eg_sh_rr = eg_rate_shift [op->rr + v];
+ op->eg_sel_rr = eg_rate_select[op->rr + v];
+
+ op+=1;
+
+ v = kc >> op->ks;
+ if ((op->ar+v) < 32+62)
+ {
+ op->eg_sh_ar = eg_rate_shift [op->ar + v ];
+ op->eg_sel_ar = eg_rate_select[op->ar + v ];
+ }
+ else
+ {
+ op->eg_sh_ar = 0;
+ op->eg_sel_ar = 17*RATE_STEPS;
+ }
+ op->eg_sh_d1r = eg_rate_shift [op->d1r + v];
+ op->eg_sel_d1r= eg_rate_select[op->d1r + v];
+ op->eg_sh_d2r = eg_rate_shift [op->d2r + v];
+ op->eg_sel_d2r= eg_rate_select[op->d2r + v];
+ op->eg_sh_rr = eg_rate_shift [op->rr + v];
+ op->eg_sel_rr = eg_rate_select[op->rr + v];
+}
+
+
+/* write a register on YM2151 chip number 'n' */
+void ym2151_write_reg(void *_chip, int r, int v)
+{
+ YM2151 *chip = (YM2151 *)_chip;
+ YM2151Operator *op = &chip->oper[ (r&0x07)*4+((r&0x18)>>3) ];
+
+ /* adjust bus to 8 bits */
+ r &= 0xff;
+ v &= 0xff;
+
+#if 0
+ /* There is no info on what YM2151 really does when busy flag is set */
+ if ( chip->status & 0x80 ) return;
+ timer_set ( attotime::from_hz(chip->clock) * 64, chip, 0, timer_callback_chip_busy);
+ chip->status |= 0x80; /* set busy flag for 64 chip clock cycles */
+#endif
+
+ if (LOG_CYM_FILE && (cymfile) && (r!=0) )
+ {
+ fputc( (unsigned char)r, cymfile );
+ fputc( (unsigned char)v, cymfile );
+ }
+
+
+ switch(r & 0xe0)
+ {
+ case 0x00:
+ switch(r){
+ case 0x01: /* LFO reset(bit 1), Test Register (other bits) */
+ chip->test = v;
+ if (v&2) chip->lfo_phase = 0;
+ break;
+
+ case 0x08:
+ envelope_KONKOFF(chip, &chip->oper[ (v&7)*4 ], v );
+ break;
+
+ case 0x0f: /* noise mode enable, noise period */
+ chip->noise = v;
+ chip->noise_f = chip->noise_tab[ v & 0x1f ];
+ break;
+
+ case 0x10: /* timer A hi */
+ chip->timer_A_index = (chip->timer_A_index & 0x003) | (v<<2);
+ break;
+
+ case 0x11: /* timer A low */
+ chip->timer_A_index = (chip->timer_A_index & 0x3fc) | (v & 3);
+ break;
+
+ case 0x12: /* timer B */
+ chip->timer_B_index = v;
+ break;
+
+ case 0x14: /* CSM, irq flag reset, irq enable, timer start/stop */
+
+ chip->irq_enable = v; /* bit 3-timer B, bit 2-timer A, bit 7 - CSM */
+
+ if (v&0x10) /* reset timer A irq flag */
+ {
+#ifdef USE_MAME_TIMERS
+ chip->status &= ~1;
+ chip->device->machine().scheduler().timer_set(attotime::zero, FUNC(irqAoff_callback), 0, chip);
+#else
+ int oldstate = chip->status & 3;
+ chip->status &= ~1;
+ if ((oldstate==1) && (chip->irqhandler)) (*chip->irqhandler)(chip->device, 0);
+#endif
+ }
+
+ if (v&0x20) /* reset timer B irq flag */
+ {
+#ifdef USE_MAME_TIMERS
+ chip->status &= ~2;
+ chip->device->machine().scheduler().timer_set(attotime::zero, FUNC(irqBoff_callback), 0, chip);
+#else
+ int oldstate = chip->status & 3;
+ chip->status &= ~2;
+ if ((oldstate==2) && (chip->irqhandler)) (*chip->irqhandler)(chip->device, 0);
+#endif
+ }
+
+ if (v&0x02)
+ { /* load and start timer B */
+ #ifdef USE_MAME_TIMERS
+ /* ASG 980324: added a real timer */
+ /* start timer _only_ if it wasn't already started (it will reload time value next round) */
+ if (!chip->timer_B->enable(true))
+ {
+ chip->timer_B->adjust(chip->timer_B_time[ chip->timer_B_index ]);
+ chip->timer_B_index_old = chip->timer_B_index;
+ }
+ #else
+ if (!chip->tim_B)
+ {
+ chip->tim_B = 1;
+ chip->tim_B_val = chip->tim_B_tab[ chip->timer_B_index ];
+ }
+ #endif
+ }
+ else
+ { /* stop timer B */
+ #ifdef USE_MAME_TIMERS
+ /* ASG 980324: added a real timer */
+ chip->timer_B->enable(false);
+ #else
+ chip->tim_B = 0;
+ #endif
+ }
+
+ if (v&0x01)
+ { /* load and start timer A */
+ #ifdef USE_MAME_TIMERS
+ /* ASG 980324: added a real timer */
+ /* start timer _only_ if it wasn't already started (it will reload time value next round) */
+ if (!chip->timer_A->enable(true))
+ {
+ chip->timer_A->adjust(chip->timer_A_time[ chip->timer_A_index ]);
+ chip->timer_A_index_old = chip->timer_A_index;
+ }
+ #else
+ if (!chip->tim_A)
+ {
+ chip->tim_A = 1;
+ chip->tim_A_val = chip->tim_A_tab[ chip->timer_A_index ];
+ }
+ #endif
+ }
+ else
+ { /* stop timer A */
+ #ifdef USE_MAME_TIMERS
+ /* ASG 980324: added a real timer */
+ chip->timer_A->enable(false);
+ #else
+ chip->tim_A = 0;
+ #endif
+ }
+ break;
+
+ case 0x18: /* LFO frequency */
+ {
+ chip->lfo_overflow = ( 1 << ((15-(v>>4))+3) ) * (1<<LFO_SH);
+ chip->lfo_counter_add = 0x10 + (v & 0x0f);
+ }
+ break;
+
+ case 0x19: /* PMD (bit 7==1) or AMD (bit 7==0) */
+ if (v&0x80)
+ chip->pmd = v & 0x7f;
+ else
+ chip->amd = v & 0x7f;
+ break;
+
+ case 0x1b: /* CT2, CT1, LFO waveform */
+ chip->ct = v >> 6;
+ chip->lfo_wsel = v & 3;
+ if (chip->porthandler) (*chip->porthandler)(chip->device, 0 , chip->ct );
+ break;
+
+ default:
+ logerror("YM2151 Write %02x to undocumented register #%02x\n",v,r);
+ break;
+ }
+ break;
+
+ case 0x20:
+ op = &chip->oper[ (r&7) * 4 ];
+ switch(r & 0x18)
+ {
+ case 0x00: /* RL enable, Feedback, Connection */
+ op->fb_shift = ((v>>3)&7) ? ((v>>3)&7)+6:0;
+ chip->pan[ (r&7)*2 ] = (v & 0x40) ? ~0 : 0;
+ chip->pan[ (r&7)*2 +1 ] = (v & 0x80) ? ~0 : 0;
+ chip->connect[r&7] = v&7;
+ set_connect(chip, op, r&7, v&7);
+ break;
+
+ case 0x08: /* Key Code */
+ v &= 0x7f;
+ if (v != op->kc)
+ {
+ UINT32 kc, kc_channel;
+
+ kc_channel = (v - (v>>2))*64;
+ kc_channel += 768;
+ kc_channel |= (op->kc_i & 63);
+
+ (op+0)->kc = v;
+ (op+0)->kc_i = kc_channel;
+ (op+1)->kc = v;
+ (op+1)->kc_i = kc_channel;
+ (op+2)->kc = v;
+ (op+2)->kc_i = kc_channel;
+ (op+3)->kc = v;
+ (op+3)->kc_i = kc_channel;
+
+ kc = v>>2;
+
+ (op+0)->dt1 = chip->dt1_freq[ (op+0)->dt1_i + kc ];
+ (op+0)->freq = ( (chip->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1;
+
+ (op+1)->dt1 = chip->dt1_freq[ (op+1)->dt1_i + kc ];
+ (op+1)->freq = ( (chip->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1;
+
+ (op+2)->dt1 = chip->dt1_freq[ (op+2)->dt1_i + kc ];
+ (op+2)->freq = ( (chip->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1;
+
+ (op+3)->dt1 = chip->dt1_freq[ (op+3)->dt1_i + kc ];
+ (op+3)->freq = ( (chip->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1;
+
+ refresh_EG( op );
+ }
+ break;
+
+ case 0x10: /* Key Fraction */
+ v >>= 2;
+ if (v != (op->kc_i & 63))
+ {
+ UINT32 kc_channel;
+
+ kc_channel = v;
+ kc_channel |= (op->kc_i & ~63);
+
+ (op+0)->kc_i = kc_channel;
+ (op+1)->kc_i = kc_channel;
+ (op+2)->kc_i = kc_channel;
+ (op+3)->kc_i = kc_channel;
+
+ (op+0)->freq = ( (chip->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1;
+ (op+1)->freq = ( (chip->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1;
+ (op+2)->freq = ( (chip->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1;
+ (op+3)->freq = ( (chip->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1;
+ }
+ break;
+
+ case 0x18: /* PMS, AMS */
+ op->pms = (v>>4) & 7;
+ op->ams = (v & 3);
+ break;
+ }
+ break;
+
+ case 0x40: /* DT1, MUL */
+ {
+ UINT32 olddt1_i = op->dt1_i;
+ UINT32 oldmul = op->mul;
+
+ op->dt1_i = (v&0x70)<<1;
+ op->mul = (v&0x0f) ? (v&0x0f)<<1: 1;
+
+ if (olddt1_i != op->dt1_i)
+ op->dt1 = chip->dt1_freq[ op->dt1_i + (op->kc>>2) ];
+
+ if ( (olddt1_i != op->dt1_i) || (oldmul != op->mul) )
+ op->freq = ( (chip->freq[ op->kc_i + op->dt2 ] + op->dt1) * op->mul ) >> 1;
+ }
+ break;
+
+ case 0x60: /* TL */
+ op->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */
+ break;
+
+ case 0x80: /* KS, AR */
+ {
+ UINT32 oldks = op->ks;
+ UINT32 oldar = op->ar;
+
+ op->ks = 5-(v>>6);
+ op->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ if ( (op->ar != oldar) || (op->ks != oldks) )
+ {
+ if ((op->ar + (op->kc>>op->ks)) < 32+62)
+ {
+ op->eg_sh_ar = eg_rate_shift [op->ar + (op->kc>>op->ks) ];
+ op->eg_sel_ar = eg_rate_select[op->ar + (op->kc>>op->ks) ];
+ }
+ else
+ {
+ op->eg_sh_ar = 0;
+ op->eg_sel_ar = 17*RATE_STEPS;
+ }
+ }
+
+ if (op->ks != oldks)
+ {
+ op->eg_sh_d1r = eg_rate_shift [op->d1r + (op->kc>>op->ks) ];
+ op->eg_sel_d1r= eg_rate_select[op->d1r + (op->kc>>op->ks) ];
+ op->eg_sh_d2r = eg_rate_shift [op->d2r + (op->kc>>op->ks) ];
+ op->eg_sel_d2r= eg_rate_select[op->d2r + (op->kc>>op->ks) ];
+ op->eg_sh_rr = eg_rate_shift [op->rr + (op->kc>>op->ks) ];
+ op->eg_sel_rr = eg_rate_select[op->rr + (op->kc>>op->ks) ];
+ }
+ }
+ break;
+
+ case 0xa0: /* LFO AM enable, D1R */
+ op->AMmask = (v&0x80) ? ~0 : 0;
+ op->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+ op->eg_sh_d1r = eg_rate_shift [op->d1r + (op->kc>>op->ks) ];
+ op->eg_sel_d1r= eg_rate_select[op->d1r + (op->kc>>op->ks) ];
+ break;
+
+ case 0xc0: /* DT2, D2R */
+ {
+ UINT32 olddt2 = op->dt2;
+ op->dt2 = dt2_tab[ v>>6 ];
+ if (op->dt2 != olddt2)
+ op->freq = ( (chip->freq[ op->kc_i + op->dt2 ] + op->dt1) * op->mul ) >> 1;
+ }
+ op->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+ op->eg_sh_d2r = eg_rate_shift [op->d2r + (op->kc>>op->ks) ];
+ op->eg_sel_d2r= eg_rate_select[op->d2r + (op->kc>>op->ks) ];
+ break;
+
+ case 0xe0: /* D1L, RR */
+ op->d1l = d1l_tab[ v>>4 ];
+ op->rr = 34 + ((v&0x0f)<<2);
+ op->eg_sh_rr = eg_rate_shift [op->rr + (op->kc>>op->ks) ];
+ op->eg_sel_rr = eg_rate_select[op->rr + (op->kc>>op->ks) ];
+ break;
+ }
+}
+
+
+static TIMER_CALLBACK( cymfile_callback )
+{
+ if (cymfile)
+ fputc( (unsigned char)0, cymfile );
+}
+
+
+int ym2151_read_status( void *_chip )
+{
+ YM2151 *chip = (YM2151 *)_chip;
+ return chip->status;
+}
+
+
+
+//#ifdef USE_MAME_TIMERS
+#if 1 // disabled for now due to crashing with winalloc.c (ERROR_NOT_ENOUGH_MEMORY)
+/*
+* state save support for MAME
+*/
+void ym2151_postload(YM2151 *YM2151_chip)
+{
+ int j;
+
+ for (j=0; j<8; j++)
+ set_connect(YM2151_chip, &YM2151_chip->oper[j*4], j, YM2151_chip->connect[j]);
+}
+
+static void ym2151_state_save_register( YM2151 *chip, device_t *device )
+{
+ int j;
+
+ /* save all 32 operators of chip #i */
+ for (j=0; j<32; j++)
+ {
+ YM2151Operator *op;
+
+ op = &chip->oper[(j&7)*4+(j>>3)];
+
+ device->save_item(NAME(op->phase), j);
+ device->save_item(NAME(op->freq), j);
+ device->save_item(NAME(op->dt1), j);
+ device->save_item(NAME(op->mul), j);
+ device->save_item(NAME(op->dt1_i), j);
+ device->save_item(NAME(op->dt2), j);
+ /* operators connection is saved in chip data block */
+ device->save_item(NAME(op->mem_value), j);
+
+ device->save_item(NAME(op->fb_shift), j);
+ device->save_item(NAME(op->fb_out_curr), j);
+ device->save_item(NAME(op->fb_out_prev), j);
+ device->save_item(NAME(op->kc), j);
+ device->save_item(NAME(op->kc_i), j);
+ device->save_item(NAME(op->pms), j);
+ device->save_item(NAME(op->ams), j);
+ device->save_item(NAME(op->AMmask), j);
+
+ device->save_item(NAME(op->state), j);
+ device->save_item(NAME(op->eg_sh_ar), j);
+ device->save_item(NAME(op->eg_sel_ar), j);
+ device->save_item(NAME(op->tl), j);
+ device->save_item(NAME(op->volume), j);
+ device->save_item(NAME(op->eg_sh_d1r), j);
+ device->save_item(NAME(op->eg_sel_d1r), j);
+ device->save_item(NAME(op->d1l), j);
+ device->save_item(NAME(op->eg_sh_d2r), j);
+ device->save_item(NAME(op->eg_sel_d2r), j);
+ device->save_item(NAME(op->eg_sh_rr), j);
+ device->save_item(NAME(op->eg_sel_rr), j);
+
+ device->save_item(NAME(op->key), j);
+ device->save_item(NAME(op->ks), j);
+ device->save_item(NAME(op->ar), j);
+ device->save_item(NAME(op->d1r), j);
+ device->save_item(NAME(op->d2r), j);
+ device->save_item(NAME(op->rr), j);
+
+ device->save_item(NAME(op->reserved0), j);
+ device->save_item(NAME(op->reserved1), j);
+ }
+
+ device->save_item(NAME(chip->pan));
+
+ device->save_item(NAME(chip->eg_cnt));
+ device->save_item(NAME(chip->eg_timer));
+ device->save_item(NAME(chip->eg_timer_add));
+ device->save_item(NAME(chip->eg_timer_overflow));
+
+ device->save_item(NAME(chip->lfo_phase));
+ device->save_item(NAME(chip->lfo_timer));
+ device->save_item(NAME(chip->lfo_timer_add));
+ device->save_item(NAME(chip->lfo_overflow));
+ device->save_item(NAME(chip->lfo_counter));
+ device->save_item(NAME(chip->lfo_counter_add));
+ device->save_item(NAME(chip->lfo_wsel));
+ device->save_item(NAME(chip->amd));
+ device->save_item(NAME(chip->pmd));
+ device->save_item(NAME(chip->lfa));
+ device->save_item(NAME(chip->lfp));
+
+ device->save_item(NAME(chip->test));
+ device->save_item(NAME(chip->ct));
+
+ device->save_item(NAME(chip->noise));
+ device->save_item(NAME(chip->noise_rng));
+ device->save_item(NAME(chip->noise_p));
+ device->save_item(NAME(chip->noise_f));
+
+ device->save_item(NAME(chip->csm_req));
+ device->save_item(NAME(chip->irq_enable));
+ device->save_item(NAME(chip->status));
+
+ device->save_item(NAME(chip->timer_A_index));
+ device->save_item(NAME(chip->timer_B_index));
+ device->save_item(NAME(chip->timer_A_index_old));
+ device->save_item(NAME(chip->timer_B_index_old));
+
+#ifdef USE_MAME_TIMERS
+ device->save_item(NAME(chip->irqlinestate));
+#endif
+
+ device->save_item(NAME(chip->connect));
+
+ device->machine().save().register_postload(save_prepost_delegate(FUNC(ym2151_postload), chip));
+}
+#else
+void ym2151_postload(YM2151 *chip)
+{
+}
+
+static void ym2151_state_save_register( YM2151 *chip, device_t *device )
+{
+}
+#endif
+
+
+/*
+* Initialize YM2151 emulator(s).
+*
+* 'num' is the number of virtual YM2151's to allocate
+* 'clock' is the chip clock in Hz
+* 'rate' is sampling rate
+*/
+void * ym2151_init(device_t *device, int clock, int rate)
+{
+ YM2151 *PSG;
+
+ PSG = auto_alloc(device->machine(), YM2151);
+
+ memset(PSG, 0, sizeof(YM2151));
+
+ ym2151_state_save_register( PSG, device );
+
+ init_tables();
+
+ PSG->device = device;
+ PSG->clock = clock;
+ /*rate = clock/64;*/
+ PSG->sampfreq = rate ? rate : 44100; /* avoid division by 0 in init_chip_tables() */
+ PSG->irqhandler = NULL; /* interrupt handler */
+ PSG->porthandler = NULL; /* port write handler */
+ init_chip_tables( PSG );
+
+ PSG->lfo_timer_add = (1<<LFO_SH) * (clock/64.0) / PSG->sampfreq;
+
+ PSG->eg_timer_add = (1<<EG_SH) * (clock/64.0) / PSG->sampfreq;
+ PSG->eg_timer_overflow = ( 3 ) * (1<<EG_SH);
+ /*logerror("YM2151[init] eg_timer_add=%8x eg_timer_overflow=%8x\n", PSG->eg_timer_add, PSG->eg_timer_overflow);*/
+
+#ifdef USE_MAME_TIMERS
+/* this must be done _before_ a call to ym2151_reset_chip() */
+ PSG->timer_A = device->machine().scheduler().timer_alloc(FUNC(timer_callback_a), PSG);
+ PSG->timer_B = device->machine().scheduler().timer_alloc(FUNC(timer_callback_b), PSG);
+#else
+ PSG->tim_A = 0;
+ PSG->tim_B = 0;
+#endif
+ ym2151_reset_chip(PSG);
+ /*logerror("YM2151[init] clock=%i sampfreq=%i\n", PSG->clock, PSG->sampfreq);*/
+
+ if (LOG_CYM_FILE)
+ {
+ cymfile = fopen("2151_.cym","wb");
+ if (cymfile)
+ device->machine().scheduler().timer_pulse ( attotime::from_hz(110), FUNC(cymfile_callback)); /*110 Hz pulse timer*/
+ else
+ logerror("Could not create file 2151_.cym\n");
+ }
+
+ return PSG;
+}
+
+
+
+void ym2151_shutdown(void *_chip)
+{
+ YM2151 *chip = (YM2151 *)_chip;
+
+ auto_free (chip->device->machine(), chip);
+
+ if (cymfile)
+ fclose (cymfile);
+ cymfile = NULL;
+
+#ifdef SAVE_SAMPLE
+ fclose(sample[8]);
+#endif
+#ifdef SAVE_SEPARATE_CHANNELS
+ fclose(sample[0]);
+ fclose(sample[1]);
+ fclose(sample[2]);
+ fclose(sample[3]);
+ fclose(sample[4]);
+ fclose(sample[5]);
+ fclose(sample[6]);
+ fclose(sample[7]);
+#endif
+}
+
+
+
+/*
+* Reset chip number 'n'.
+*/
+void ym2151_reset_chip(void *_chip)
+{
+ int i;
+ YM2151 *chip = (YM2151 *)_chip;
+
+
+ /* initialize hardware registers */
+ for (i=0; i<32; i++)
+ {
+ memset(&chip->oper[i],'\0',sizeof(YM2151Operator));
+ chip->oper[i].volume = MAX_ATT_INDEX;
+ chip->oper[i].kc_i = 768; /* min kc_i value */
+ }
+
+ chip->eg_timer = 0;
+ chip->eg_cnt = 0;
+
+ chip->lfo_timer = 0;
+ chip->lfo_counter= 0;
+ chip->lfo_phase = 0;
+ chip->lfo_wsel = 0;
+ chip->pmd = 0;
+ chip->amd = 0;
+ chip->lfa = 0;
+ chip->lfp = 0;
+
+ chip->test= 0;
+
+ chip->irq_enable = 0;
+#ifdef USE_MAME_TIMERS
+ /* ASG 980324 -- reset the timers before writing to the registers */
+ chip->timer_A->enable(false);
+ chip->timer_B->enable(false);
+#else
+ chip->tim_A = 0;
+ chip->tim_B = 0;
+ chip->tim_A_val = 0;
+ chip->tim_B_val = 0;
+#endif
+ chip->timer_A_index = 0;
+ chip->timer_B_index = 0;
+ chip->timer_A_index_old = 0;
+ chip->timer_B_index_old = 0;
+
+ chip->noise = 0;
+ chip->noise_rng = 0;
+ chip->noise_p = 0;
+ chip->noise_f = chip->noise_tab[0];
+
+ chip->csm_req = 0;
+ chip->status = 0;
+
+ ym2151_write_reg(chip, 0x1b, 0); /* only because of CT1, CT2 output pins */
+ ym2151_write_reg(chip, 0x18, 0); /* set LFO frequency */
+ for (i=0x20; i<0x100; i++) /* set the operators */
+ {
+ ym2151_write_reg(chip, i, 0);
+ }
+}
+
+
+
+INLINE signed int op_calc(YM2151Operator * OP, unsigned int env, signed int pm)
+{
+ UINT32 p;
+
+
+ p = (env<<3) + sin_tab[ ( ((signed int)((OP->phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(YM2151Operator * OP, unsigned int env, signed int pm)
+{
+ UINT32 p;
+ INT32 i;
+
+
+ i = (OP->phase & ~FREQ_MASK) + pm;
+
+/*logerror("i=%08x (i>>16)&511=%8i phase=%i [pm=%08x] ",i, (i>>16)&511, OP->phase>>FREQ_SH, pm);*/
+
+ p = (env<<3) + sin_tab[ (i>>FREQ_SH) & SIN_MASK];
+
+/*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8) );*/
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+
+ return tl_tab[p];
+}
+
+
+
+#define volume_calc(OP) ((OP)->tl + ((UINT32)(OP)->volume) + (AM & (OP)->AMmask))
+
+INLINE void chan_calc(YM2151 *PSG, unsigned int chan)
+{
+ YM2151Operator *op;
+ unsigned int env;
+ UINT32 AM = 0;
+
+ PSG->m2 = PSG->c1 = PSG->c2 = PSG->mem = 0;
+ op = &PSG->oper[chan*4]; /* M1 */
+
+ *op->mem_connect = op->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */
+
+ if (op->ams)
+ AM = PSG->lfa << (op->ams-1);
+ env = volume_calc(op);
+ {
+ INT32 out = op->fb_out_prev + op->fb_out_curr;
+ op->fb_out_prev = op->fb_out_curr;
+
+ if (!op->connect)
+ /* algorithm 5 */
+ PSG->mem = PSG->c1 = PSG->c2 = op->fb_out_prev;
+ else
+ /* other algorithms */
+ *op->connect = op->fb_out_prev;
+
+ op->fb_out_curr = 0;
+ if (env < ENV_QUIET)
+ {
+ if (!op->fb_shift)
+ out=0;
+ op->fb_out_curr = op_calc1(op, env, (out<<op->fb_shift) );
+ }
+ }
+
+ env = volume_calc(op+1); /* M2 */
+ if (env < ENV_QUIET)
+ *(op+1)->connect += op_calc(op+1, env, PSG->m2);
+
+ env = volume_calc(op+2); /* C1 */
+ if (env < ENV_QUIET)
+ *(op+2)->connect += op_calc(op+2, env, PSG->c1);
+
+ env = volume_calc(op+3); /* C2 */
+ if (env < ENV_QUIET)
+ PSG->chanout[chan] += op_calc(op+3, env, PSG->c2);
+
+ /* M1 */
+ op->mem_value = PSG->mem;
+}
+
+INLINE void chan7_calc(YM2151 *PSG)
+{
+ YM2151Operator *op;
+ unsigned int env;
+ UINT32 AM = 0;
+
+ PSG->m2 = PSG->c1 = PSG->c2 = PSG->mem = 0;
+ op = &PSG->oper[7*4]; /* M1 */
+
+ *op->mem_connect = op->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */
+
+ if (op->ams)
+ AM = PSG->lfa << (op->ams-1);
+ env = volume_calc(op);
+ {
+ INT32 out = op->fb_out_prev + op->fb_out_curr;
+ op->fb_out_prev = op->fb_out_curr;
+
+ if (!op->connect)
+ /* algorithm 5 */
+ PSG->mem = PSG->c1 = PSG->c2 = op->fb_out_prev;
+ else
+ /* other algorithms */
+ *op->connect = op->fb_out_prev;
+
+ op->fb_out_curr = 0;
+ if (env < ENV_QUIET)
+ {
+ if (!op->fb_shift)
+ out=0;
+ op->fb_out_curr = op_calc1(op, env, (out<<op->fb_shift) );
+ }
+ }
+
+ env = volume_calc(op+1); /* M2 */
+ if (env < ENV_QUIET)
+ *(op+1)->connect += op_calc(op+1, env, PSG->m2);
+
+ env = volume_calc(op+2); /* C1 */
+ if (env < ENV_QUIET)
+ *(op+2)->connect += op_calc(op+2, env, PSG->c1);
+
+ env = volume_calc(op+3); /* C2 */
+ if (PSG->noise & 0x80)
+ {
+ UINT32 noiseout;
+
+ noiseout = 0;
+ if (env < 0x3ff)
+ noiseout = (env ^ 0x3ff) * 2; /* range of the YM2151 noise output is -2044 to 2040 */
+ PSG->chanout[7] += ((PSG->noise_rng&0x10000) ? noiseout: -noiseout); /* bit 16 -> output */
+ }
+ else
+ {
+ if (env < ENV_QUIET)
+ PSG->chanout[7] += op_calc(op+3, env, PSG->c2);
+ }
+ /* M1 */
+ op->mem_value = PSG->mem;
+}
+
+
+
+
+
+
+/*
+The 'rate' is calculated from following formula (example on decay rate):
+ rks = notecode after key scaling (a value from 0 to 31)
+ DR = value written to the chip register
+ rate = 2*DR + rks; (max rate = 2*31+31 = 93)
+Four MSBs of the 'rate' above are the 'main' rate (from 00 to 15)
+Two LSBs of the 'rate' above are the value 'x' (the shape type).
+(eg. '11 2' means that 'rate' is 11*4+2=46)
+
+NOTE: A 'sample' in the description below is actually 3 output samples,
+thats because the Envelope Generator clock is equal to internal_clock/3.
+
+Single '-' (minus) character in the diagrams below represents one sample
+on the output; this is for rates 11 x (11 0, 11 1, 11 2 and 11 3)
+
+these 'main' rates:
+00 x: single '-' = 2048 samples; (ie. level can change every 2048 samples)
+01 x: single '-' = 1024 samples;
+02 x: single '-' = 512 samples;
+03 x: single '-' = 256 samples;
+04 x: single '-' = 128 samples;
+05 x: single '-' = 64 samples;
+06 x: single '-' = 32 samples;
+07 x: single '-' = 16 samples;
+08 x: single '-' = 8 samples;
+09 x: single '-' = 4 samples;
+10 x: single '-' = 2 samples;
+11 x: single '-' = 1 sample; (ie. level can change every 1 sample)
+
+Shapes for rates 11 x look like this:
+rate: step:
+11 0 01234567
+
+level:
+0 --
+1 --
+2 --
+3 --
+
+rate: step:
+11 1 01234567
+
+level:
+0 --
+1 --
+2 -
+3 -
+4 --
+
+rate: step:
+11 2 01234567
+
+level:
+0 --
+1 -
+2 -
+3 --
+4 -
+5 -
+
+rate: step:
+11 3 01234567
+
+level:
+0 --
+1 -
+2 -
+3 -
+4 -
+5 -
+6 -
+
+
+For rates 12 x, 13 x, 14 x and 15 x output level changes on every
+sample - this means that the waveform looks like this: (but the level
+changes by different values on different steps)
+12 3 01234567
+
+0 -
+2 -
+4 -
+8 -
+10 -
+12 -
+14 -
+18 -
+20 -
+
+Notes about the timing:
+----------------------
+
+1. Synchronism
+
+Output level of each two (or more) voices running at the same 'main' rate
+(eg 11 0 and 11 1 in the diagram below) will always be changing in sync,
+even if there're started with some delay.
+
+Note that, in the diagram below, the decay phase in channel 0 starts at
+sample #2, while in channel 1 it starts at sample #6. Anyway, both channels
+will always change their levels at exactly the same (following) samples.
+
+(S - start point of this channel, A-attack phase, D-decay phase):
+
+step:
+01234567012345670123456
+
+channel 0:
+ --
+ | --
+ | -
+ | -
+ | --
+ | --
+| --
+| -
+| -
+| --
+AADDDDDDDDDDDDDDDD
+S
+
+01234567012345670123456
+channel 1:
+ -
+ | -
+ | --
+ | --
+ | --
+ | -
+ | -
+ | --
+ | --
+ | --
+ AADDDDDDDDDDDDDDDD
+ S
+01234567012345670123456
+
+
+2. Shifted (delayed) synchronism
+
+Output of each two (or more) voices running at different 'main' rate
+(9 1, 10 1 and 11 1 in the diagrams below) will always be changing
+in 'delayed-sync' (even if there're started with some delay as in "1.")
+
+Note that the shapes are delayed by exactly one sample per one 'main' rate
+increment. (Normally one would expect them to start at the same samples.)
+
+See diagram below (* - start point of the shape).
+
+cycle:
+0123456701234567012345670123456701234567012345670123456701234567
+
+rate 09 1
+*-------
+ --------
+ ----
+ ----
+ --------
+ *-------
+ | --------
+ | ----
+ | ----
+ | --------
+rate 10 1 |
+-- |
+ *--- |
+ ---- |
+ -- |
+ -- |
+ ---- |
+ *--- |
+ | ---- |
+ | -- | | <- one step (two samples) delay between 9 1 and 10 1
+ | -- | |
+ | ----|
+ | *---
+ | ----
+ | --
+ | --
+ | ----
+rate 11 1 |
+- |
+ -- |
+ *- |
+ -- |
+ - |
+ - |
+ -- |
+ *- |
+ -- |
+ - || <- one step (one sample) delay between 10 1 and 11 1
+ - ||
+ --|
+ *-
+ --
+ -
+ -
+ --
+ *-
+ --
+ -
+ -
+ --
+*/
+
+INLINE void advance_eg(YM2151 *PSG)
+{
+ YM2151Operator *op;
+ unsigned int i;
+
+
+
+ PSG->eg_timer += PSG->eg_timer_add;
+
+ while (PSG->eg_timer >= PSG->eg_timer_overflow)
+ {
+ PSG->eg_timer -= PSG->eg_timer_overflow;
+
+ PSG->eg_cnt++;
+
+ /* envelope generator */
+ op = &PSG->oper[0]; /* CH 0 M1 */
+ i = 32;
+ do
+ {
+ switch(op->state)
+ {
+ case EG_ATT: /* attack phase */
+ if ( !(PSG->eg_cnt & ((1<<op->eg_sh_ar)-1) ) )
+ {
+ op->volume += (~op->volume *
+ (eg_inc[op->eg_sel_ar + ((PSG->eg_cnt>>op->eg_sh_ar)&7)])
+ ) >>4;
+
+ if (op->volume <= MIN_ATT_INDEX)
+ {
+ op->volume = MIN_ATT_INDEX;
+ op->state = EG_DEC;
+ }
+
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ if ( !(PSG->eg_cnt & ((1<<op->eg_sh_d1r)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_d1r + ((PSG->eg_cnt>>op->eg_sh_d1r)&7)];
+
+ if ( op->volume >= op->d1l )
+ op->state = EG_SUS;
+
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+ if ( !(PSG->eg_cnt & ((1<<op->eg_sh_d2r)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_d2r + ((PSG->eg_cnt>>op->eg_sh_d2r)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ if ( !(PSG->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((PSG->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+
+ }
+ break;
+ }
+ op++;
+ i--;
+ }while (i);
+ }
+}
+
+
+INLINE void advance(YM2151 *PSG)
+{
+ YM2151Operator *op;
+ unsigned int i;
+ int a,p;
+
+ /* LFO */
+ if (PSG->test&2)
+ PSG->lfo_phase = 0;
+ else
+ {
+ PSG->lfo_timer += PSG->lfo_timer_add;
+ if (PSG->lfo_timer >= PSG->lfo_overflow)
+ {
+ PSG->lfo_timer -= PSG->lfo_overflow;
+ PSG->lfo_counter += PSG->lfo_counter_add;
+ PSG->lfo_phase += (PSG->lfo_counter>>4);
+ PSG->lfo_phase &= 255;
+ PSG->lfo_counter &= 15;
+ }
+ }
+
+ i = PSG->lfo_phase;
+ /* calculate LFO AM and PM waveform value (all verified on real chip, except for noise algorithm which is impossible to analyse)*/
+ switch (PSG->lfo_wsel)
+ {
+ case 0:
+ /* saw */
+ /* AM: 255 down to 0 */
+ /* PM: 0 to 127, -127 to 0 (at PMD=127: LFP = 0 to 126, -126 to 0) */
+ a = 255 - i;
+ if (i<128)
+ p = i;
+ else
+ p = i - 255;
+ break;
+ case 1:
+ /* square */
+ /* AM: 255, 0 */
+ /* PM: 128,-128 (LFP = exactly +PMD, -PMD) */
+ if (i<128)
+ {
+ a = 255;
+ p = 128;
+ }
+ else
+ {
+ a = 0;
+ p = -128;
+ }
+ break;
+ case 2:
+ /* triangle */
+ /* AM: 255 down to 1 step -2; 0 up to 254 step +2 */
+ /* PM: 0 to 126 step +2, 127 to 1 step -2, 0 to -126 step -2, -127 to -1 step +2*/
+ if (i<128)
+ a = 255 - (i*2);
+ else
+ a = (i*2) - 256;
+
+ if (i<64) /* i = 0..63 */
+ p = i*2; /* 0 to 126 step +2 */
+ else if (i<128) /* i = 64..127 */
+ p = 255 - i*2; /* 127 to 1 step -2 */
+ else if (i<192) /* i = 128..191 */
+ p = 256 - i*2; /* 0 to -126 step -2*/
+ else /* i = 192..255 */
+ p = i*2 - 511; /*-127 to -1 step +2*/
+ break;
+ case 3:
+ default: /*keep the compiler happy*/
+ /* random */
+ /* the real algorithm is unknown !!!
+ We just use a snapshot of data from real chip */
+
+ /* AM: range 0 to 255 */
+ /* PM: range -128 to 127 */
+
+ a = lfo_noise_waveform[i];
+ p = a-128;
+ break;
+ }
+ PSG->lfa = a * PSG->amd / 128;
+ PSG->lfp = p * PSG->pmd / 128;
+
+
+ /* The Noise Generator of the YM2151 is 17-bit shift register.
+ * Input to the bit16 is negated (bit0 XOR bit3) (EXNOR).
+ * Output of the register is negated (bit0 XOR bit3).
+ * Simply use bit16 as the noise output.
+ */
+ PSG->noise_p += PSG->noise_f;
+ i = (PSG->noise_p>>16); /* number of events (shifts of the shift register) */
+ PSG->noise_p &= 0xffff;
+ while (i)
+ {
+ UINT32 j;
+ j = ( (PSG->noise_rng ^ (PSG->noise_rng>>3) ) & 1) ^ 1;
+ PSG->noise_rng = (j<<16) | (PSG->noise_rng>>1);
+ i--;
+ }
+
+
+ /* phase generator */
+ op = &PSG->oper[0]; /* CH 0 M1 */
+ i = 8;
+ do
+ {
+ if (op->pms) /* only when phase modulation from LFO is enabled for this channel */
+ {
+ INT32 mod_ind = PSG->lfp; /* -128..+127 (8bits signed) */
+ if (op->pms < 6)
+ mod_ind >>= (6 - op->pms);
+ else
+ mod_ind <<= (op->pms - 5);
+
+ if (mod_ind)
+ {
+ UINT32 kc_channel = op->kc_i + mod_ind;
+ (op+0)->phase += ( (PSG->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1;
+ (op+1)->phase += ( (PSG->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1;
+ (op+2)->phase += ( (PSG->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1;
+ (op+3)->phase += ( (PSG->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1;
+ }
+ else /* phase modulation from LFO is equal to zero */
+ {
+ (op+0)->phase += (op+0)->freq;
+ (op+1)->phase += (op+1)->freq;
+ (op+2)->phase += (op+2)->freq;
+ (op+3)->phase += (op+3)->freq;
+ }
+ }
+ else /* phase modulation from LFO is disabled */
+ {
+ (op+0)->phase += (op+0)->freq;
+ (op+1)->phase += (op+1)->freq;
+ (op+2)->phase += (op+2)->freq;
+ (op+3)->phase += (op+3)->freq;
+ }
+
+ op+=4;
+ i--;
+ }while (i);
+
+
+ /* CSM is calculated *after* the phase generator calculations (verified on real chip)
+ * CSM keyon line seems to be ORed with the KO line inside of the chip.
+ * The result is that it only works when KO (register 0x08) is off, ie. 0
+ *
+ * Interesting effect is that when timer A is set to 1023, the KEY ON happens
+ * on every sample, so there is no KEY OFF at all - the result is that
+ * the sound played is the same as after normal KEY ON.
+ */
+
+ if (PSG->csm_req) /* CSM KEYON/KEYOFF seqeunce request */
+ {
+ if (PSG->csm_req==2) /* KEY ON */
+ {
+ op = &PSG->oper[0]; /* CH 0 M1 */
+ i = 32;
+ do
+ {
+ KEY_ON(op, 2);
+ op++;
+ i--;
+ }while (i);
+ PSG->csm_req = 1;
+ }
+ else /* KEY OFF */
+ {
+ op = &PSG->oper[0]; /* CH 0 M1 */
+ i = 32;
+ do
+ {
+ KEY_OFF(op,~2);
+ op++;
+ i--;
+ }while (i);
+ PSG->csm_req = 0;
+ }
+ }
+}
+
+#if 0
+INLINE signed int acc_calc(signed int value)
+{
+ if (value>=0)
+ {
+ if (value < 0x0200)
+ return (value & ~0);
+ if (value < 0x0400)
+ return (value & ~1);
+ if (value < 0x0800)
+ return (value & ~3);
+ if (value < 0x1000)
+ return (value & ~7);
+ if (value < 0x2000)
+ return (value & ~15);
+ if (value < 0x4000)
+ return (value & ~31);
+ return (value & ~63);
+ }
+ /*else value < 0*/
+ if (value > -0x0200)
+ return (~abs(value) & ~0);
+ if (value > -0x0400)
+ return (~abs(value) & ~1);
+ if (value > -0x0800)
+ return (~abs(value) & ~3);
+ if (value > -0x1000)
+ return (~abs(value) & ~7);
+ if (value > -0x2000)
+ return (~abs(value) & ~15);
+ if (value > -0x4000)
+ return (~abs(value) & ~31);
+ return (~abs(value) & ~63);
+}
+#endif
+
+/* first macro saves left and right channels to mono file */
+/* second macro saves left and right channels to stereo file */
+#if 0 /*MONO*/
+ #ifdef SAVE_SEPARATE_CHANNELS
+ #define SAVE_SINGLE_CHANNEL(j) \
+ { signed int pom= -(chanout[j] & PSG->pan[j*2]); \
+ if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \
+ fputc((unsigned short)pom&0xff,sample[j]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[j]); \
+ }
+ #else
+ #define SAVE_SINGLE_CHANNEL(j)
+ #endif
+#else /*STEREO*/
+ #ifdef SAVE_SEPARATE_CHANNELS
+ #define SAVE_SINGLE_CHANNEL(j) \
+ { signed int pom = -(chanout[j] & PSG->pan[j*2]); \
+ if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \
+ fputc((unsigned short)pom&0xff,sample[j]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[j]); \
+ pom = -(chanout[j] & PSG->pan[j*2+1]); \
+ if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \
+ fputc((unsigned short)pom&0xff,sample[j]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[j]); \
+ }
+ #else
+ #define SAVE_SINGLE_CHANNEL(j)
+ #endif
+#endif
+
+/* first macro saves left and right channels to mono file */
+/* second macro saves left and right channels to stereo file */
+#if 1 /*MONO*/
+ #ifdef SAVE_SAMPLE
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = outl; \
+ /*pom = acc_calc(pom);*/ \
+ /*fprintf(sample[8]," %i\n",pom);*/ \
+ fputc((unsigned short)pom&0xff,sample[8]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[8]); \
+ }
+ #else
+ #define SAVE_ALL_CHANNELS
+ #endif
+#else /*STEREO*/
+ #ifdef SAVE_SAMPLE
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = outl; \
+ fputc((unsigned short)pom&0xff,sample[8]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[8]); \
+ pom = outr; \
+ fputc((unsigned short)pom&0xff,sample[8]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[8]); \
+ }
+ #else
+ #define SAVE_ALL_CHANNELS
+ #endif
+#endif
+
+
+/* Generate samples for one of the YM2151's
+*
+* 'num' is the number of virtual YM2151
+* '**buffers' is table of pointers to the buffers: left and right
+* 'length' is the number of samples that should be generated
+*/
+void ym2151_update_one(void *chip, SAMP **buffers, int length)
+{
+ YM2151 *PSG = (YM2151 *)chip;
+ signed int *chanout = PSG->chanout;
+ int i;
+ signed int outl,outr;
+ SAMP *bufL, *bufR;
+
+ bufL = buffers[0];
+ bufR = buffers[1];
+
+#ifdef USE_MAME_TIMERS
+ /* ASG 980324 - handled by real timers now */
+#else
+ if (PSG->tim_B)
+ {
+ PSG->tim_B_val -= ( length << TIMER_SH );
+ if (PSG->tim_B_val<=0)
+ {
+ PSG->tim_B_val += PSG->tim_B_tab[ PSG->timer_B_index ];
+ if ( PSG->irq_enable & 0x08 )
+ {
+ int oldstate = PSG->status & 3;
+ PSG->status |= 2;
+ if ((!oldstate) && (PSG->irqhandler)) (*PSG->irqhandler)(chip->device, 1);
+ }
+ }
+ }
+#endif
+
+ for (i=0; i<length; i++)
+ {
+ advance_eg(PSG);
+
+ chanout[0] = 0;
+ chanout[1] = 0;
+ chanout[2] = 0;
+ chanout[3] = 0;
+ chanout[4] = 0;
+ chanout[5] = 0;
+ chanout[6] = 0;
+ chanout[7] = 0;
+
+ chan_calc(PSG, 0);
+ SAVE_SINGLE_CHANNEL(0)
+ chan_calc(PSG, 1);
+ SAVE_SINGLE_CHANNEL(1)
+ chan_calc(PSG, 2);
+ SAVE_SINGLE_CHANNEL(2)
+ chan_calc(PSG, 3);
+ SAVE_SINGLE_CHANNEL(3)
+ chan_calc(PSG, 4);
+ SAVE_SINGLE_CHANNEL(4)
+ chan_calc(PSG, 5);
+ SAVE_SINGLE_CHANNEL(5)
+ chan_calc(PSG, 6);
+ SAVE_SINGLE_CHANNEL(6)
+ chan7_calc(PSG);
+ SAVE_SINGLE_CHANNEL(7)
+
+ outl = chanout[0] & PSG->pan[0];
+ outr = chanout[0] & PSG->pan[1];
+ outl += (chanout[1] & PSG->pan[2]);
+ outr += (chanout[1] & PSG->pan[3]);
+ outl += (chanout[2] & PSG->pan[4]);
+ outr += (chanout[2] & PSG->pan[5]);
+ outl += (chanout[3] & PSG->pan[6]);
+ outr += (chanout[3] & PSG->pan[7]);
+ outl += (chanout[4] & PSG->pan[8]);
+ outr += (chanout[4] & PSG->pan[9]);
+ outl += (chanout[5] & PSG->pan[10]);
+ outr += (chanout[5] & PSG->pan[11]);
+ outl += (chanout[6] & PSG->pan[12]);
+ outr += (chanout[6] & PSG->pan[13]);
+ outl += (chanout[7] & PSG->pan[14]);
+ outr += (chanout[7] & PSG->pan[15]);
+
+ outl >>= FINAL_SH;
+ outr >>= FINAL_SH;
+ if (outl > MAXOUT) outl = MAXOUT;
+ else if (outl < MINOUT) outl = MINOUT;
+ if (outr > MAXOUT) outr = MAXOUT;
+ else if (outr < MINOUT) outr = MINOUT;
+ ((SAMP*)bufL)[i] = (SAMP)outl;
+ ((SAMP*)bufR)[i] = (SAMP)outr;
+
+ SAVE_ALL_CHANNELS
+
+#ifdef USE_MAME_TIMERS
+ /* ASG 980324 - handled by real timers now */
+#else
+ /* calculate timer A */
+ if (PSG->tim_A)
+ {
+ PSG->tim_A_val -= ( 1 << TIMER_SH );
+ if (PSG->tim_A_val <= 0)
+ {
+ PSG->tim_A_val += PSG->tim_A_tab[ PSG->timer_A_index ];
+ if (PSG->irq_enable & 0x04)
+ {
+ int oldstate = PSG->status & 3;
+ PSG->status |= 1;
+ if ((!oldstate) && (PSG->irqhandler)) (*PSG->irqhandler)(chip->device, 1);
+ }
+ if (PSG->irq_enable & 0x80)
+ PSG->csm_req = 2; /* request KEY ON / KEY OFF sequence */
+ }
+ }
+#endif
+ advance(PSG);
+ }
+}
+
+void ym2151_set_irq_handler(void *chip, void(*handler)(device_t *device, int irq))
+{
+ YM2151 *PSG = (YM2151 *)chip;
+ PSG->irqhandler = handler;
+}
+
+void ym2151_set_port_write_handler(void *chip, void (*handler)(device_t *, offs_t, UINT8))
+{
+ YM2151 *PSG = (YM2151 *)chip;
+ PSG->porthandler = handler;
+}
diff --git a/src/devices/sound/ym2151.h b/src/devices/sound/ym2151.h
new file mode 100644
index 00000000000..3d62ac278ac
--- /dev/null
+++ b/src/devices/sound/ym2151.h
@@ -0,0 +1,88 @@
+// license:???
+// copyright-holders:Jarek Burczynski
+/*
+** File: ym2151.h - header file for software implementation of YM2151
+** FM Operator Type-M(OPM)
+**
+** (c) 1997-2002 Jarek Burczynski (s0246@poczta.onet.pl, bujar@mame.net)
+** Some of the optimizing ideas by Tatsuyuki Satoh
+**
+** Version 2.150 final beta May, 11th 2002
+**
+**
+** I would like to thank following people for making this project possible:
+**
+** Beauty Planets - for making a lot of real YM2151 samples and providing
+** additional informations about the chip. Also for the time spent making
+** the samples and the speed of replying to my endless requests.
+**
+** Shigeharu Isoda - for general help, for taking time to scan his YM2151
+** Japanese Manual first of all, and answering MANY of my questions.
+**
+** Nao - for giving me some info about YM2151 and pointing me to Shigeharu.
+** Also for creating fmemu (which I still use to test the emulator).
+**
+** Aaron Giles and Chris Hardy - they made some samples of one of my favourite
+** arcade games so I could compare it to my emulator.
+**
+** Bryan McPhail and Tim (powerjaw) - for making some samples.
+**
+** Ishmair - for the datasheet and motivation.
+*/
+
+#pragma once
+
+#ifndef __YM2151_H__
+#define __YM2151_H__
+
+
+/* 16- and 8-bit samples (signed) are supported*/
+#define SAMPLE_BITS 16
+
+typedef stream_sample_t SAMP;
+/*
+#if (SAMPLE_BITS==16)
+ typedef INT16 SAMP;
+#endif
+#if (SAMPLE_BITS==8)
+ typedef signed char SAMP;
+#endif
+*/
+
+/*
+** Initialize YM2151 emulator(s).
+**
+** 'num' is the number of virtual YM2151's to allocate
+** 'clock' is the chip clock in Hz
+** 'rate' is sampling rate
+*/
+void *ym2151_init(device_t *device, int clock, int rate);
+
+/* shutdown the YM2151 emulators*/
+void ym2151_shutdown(void *chip);
+
+/* reset all chip registers for YM2151 number 'num'*/
+void ym2151_reset_chip(void *chip);
+
+/*
+** Generate samples for one of the YM2151's
+**
+** 'num' is the number of virtual YM2151
+** '**buffers' is table of pointers to the buffers: left and right
+** 'length' is the number of samples that should be generated
+*/
+void ym2151_update_one(void *chip, SAMP **buffers, int length);
+
+/* write 'v' to register 'r' on YM2151 chip number 'n'*/
+void ym2151_write_reg(void *chip, int r, int v);
+
+/* read status register on YM2151 chip number 'n'*/
+int ym2151_read_status(void *chip);
+
+/* set interrupt handler on YM2151 chip number 'n'*/
+void ym2151_set_irq_handler(void *chip, void (*handler)(device_t *device, int irq));
+
+/* set port write handler on YM2151 chip number 'n'*/
+void ym2151_set_port_write_handler(void *chip, void (*handler)(device_t *, offs_t, UINT8));
+
+#endif /*__YM2151_H__*/
diff --git a/src/devices/sound/ym2151.txt b/src/devices/sound/ym2151.txt
new file mode 100644
index 00000000000..a28893ae3ff
--- /dev/null
+++ b/src/devices/sound/ym2151.txt
@@ -0,0 +1,162 @@
+
+This is some very technical info I found during my experiments with real chip.
+I hope someone will find it interesting.
+
+
+ YM2151 TIMING DIAGRAM
+ +-------------------------------------------------------------------------------------------------------------------------------|---------
+Cycle no. | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 |13 |14 |15 |16 |17 |18 |19 |20 |21 |22 |23 |24 |25 |26 |27 |28 |29 |30 |31 | 0 | 1 |
+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---|---+---+-
+ | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _| _ _
+D/A clock |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_
+ | |
+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---|---+---+-
+ | _______________________________ |
+SH1 signal |_______________________________| |_______________________________________________________________|_________
+ | |
+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---|---+---+-
+ | _______________________________|
+SH2 signal |_______________________________________________________________________________________________| |_________
+ | |
+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---|---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---|---+---+-
+ | | |
+SO | x | x | x | D0| D1| D2| D3| D4| D5| D6| D7| D8| D9| S0| S1| S2| x | x | x | D0| D1| D2| D3| D4| D5| D6| D7| D8| D9| S0| S1| S2| x | x |
+(D/A data) | | |
+ | R I G H T C H A N N E L | L E F T C H A N N E L |
+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---|---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---|---+---+-
+READ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+INTERNAL CH| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 |
+DATA OP| C2| C2| C2| C2| C2| C2| C2| C2| M1| M1| M1| M1| M1| M1| M1| M1| M2| M2| M2| M2| M2| M2| M2| M2| C1| C1| C1| C1| C1| C1| C1| C1| C2| C2|
+ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+ operator #| 24| 25| 26| 27| 28| 29| 30| 31| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20| 21| 22| 23| 24| 25|
+ +---------------------------------------------------------------|---------------------------------------------------------------|---------
+
+
+ Note:
+ -----
+ The SO (D/A data) is sequentially output to the YM3012 (stereo) or YM3014 (mono).
+ Formula to calculate sample from SO data is:
+ N = S2(2^2) + S1(2^1) + S0
+ where S2 = S1 = S0 = 0 - not allowed
+ SAMPLE = (-1+D9 + D8(2^-1) + D7(2^-2) + D6(2^-3) + ... + D0(2^-9) + 2^-10) * 2^-N
+
+ Anyway, important is that SO data is delayed by one sample compared to READ INTERNAL DATA.
+ This is logical since chip has to sum all channels' outputs before it will send SO data
+ (this is what the ACC (accumulator) does).
+
+
+
+YM2151 Test register (0x01):
+
+ +----------+-----+------+-----+-----+------+------+------+-------+
+ | bit no. | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ +----------+-----+------+-----+-----+------+------+------+-------+
+ | hex val. | 80 | 40 | 20 | 10 | 08 | 04 | 02 | 01 |
+ +----------+-----+------+-----+-----+------+------+------+-------+
+ | |LS/MS| RIen | HEG | NEG | HPGL | ILFO | HLFO | u_clk |
+ +----------+-----+------+-----+-----+------+------+------+-------+
+
+
+bit 6 - RIen (Read Internal data Enable)
+
+ When set to 1, chip's internal data can be read via status register.
+
+ Simply set this bit and read data that will come out sequentially via the 8-bit
+ bus. Format of this data and the timing will be described later (diagram is above).
+
+
+
+bit 7 - LS/MS (Least Significant or Most Significant 8 bits of internal data)
+
+ When 0 - chip's internal data read via status register is lower 8 bits (LSB),
+ When 1 - chip's internal data is upper 8 bits (MSB) (plus something I haven't figured out).
+
+ In other words: in order to read 8 LSB bits set this bit to 0, in order to read
+ 8 MSB bits set this bit to 1.
+
+
+
+bit 5 - HEG (Halt Envelope Generator)
+
+ When 1 - Envelope Generator gets halted.
+
+ This means that *Phase* Generator data can be read out when:
+ HEG bit is set to 1 (EG gets halted), and data coming out via the status register
+ will be pure sinus wave at the _CURRENT_ EG output.
+ This means you need to know _EXACTLY_ when to set this bit.
+
+ Simple workaround is to do the following:
+ - TL (Total Level) of the operator to minimum dB value (0x00) - it is max volume,
+ - AR (Attack Rate) of the operator to maximum speed (shortest time) (0x1f),
+ - D1L (Decay Level) of the operator to max (93 dB) (0xf0),
+ - D1R (Decay Rate) of the operator to zero (infinite time) speed (0x00),
+ - set KEYON of that operator,
+ - wait a while (long enough so Attack phase of the EG can be done),
+ - set HEG bit in the test register to 1,
+ and its done, since EG will be halted generating 0 (zero) dB level for
+ that operator (exactly speaking EG will stay in D1R phase) and, since 0 dB from EG
+ means no change on the sinus data stored inside of the chip's internal ROM,
+ you can read this ROM data.
+
+ Careful reader will notice that it may not be needed to use HEG bit to read the data
+ (bacause EG will be at 0dB anyway), but using this bit, one can make samples
+ of pure PHASE generator innerworking - that is how I obtained phaseinc_rom[].
+
+
+
+bit 4 - NEG (Negate output data)
+
+ When 1 - output data sign will be simply inverted.
+
+ Worth noticing is that when this bit is 1 - also chip output data will be negated,
+ causing bad sound coming out of the speakers since the YM3012 (the D/A converter)
+ does not expect the negated data !!!
+
+
+
+bit 3 - HPGL (Halt Phase Generator AND LFO amplitude modulation)
+
+ When 1 - Phase Generator is halted. Also amplitude modulation
+ (from LFO to Envelope Generator) is halted.
+
+ You can use this bit to analyse Envelope Generator work.
+ As it was in the case of HEG bit you need to know _EXACTLY_ when to set this bit.
+ Unfortunately, there is no _simple_ way. You will need to synchronise on the chip
+ output signals to know when to set it.
+
+
+
+bit 2 - ILFO (Internal LFO related)
+
+ When 1 - LFO output (depends on selected waveform)
+
+ LFO outputs some internal signals...what are they ?
+ I did not test if this bit alters Phase Modulation in any way.
+
+ Also alters timer A somehow. At least it sounds like a restart.
+
+
+
+bit 1 - HLFO (Halt LFO)
+
+ When 1 - LFO gets HALTED (at maximum amplitude in case of AM).
+
+ On the 1 to 0 transition LFO will be RESET to startup of the waveform
+ (phase of the LFO _only_).
+
+
+
+bit 0 - u_clk (unknown, but probably internal clock related)
+
+ When 1 - Envelope Generator times are much shorter (faster envelopes).
+
+ I do not know if it alters Attacks parts of the envelope. I'm sure it
+ alters Decays times. Also timers are much (twice ?) faster than normally.
+ Perhaps this bit is disabling some internal clock divider.
+ FWIW, frequencies of the operators are NOT altered by setting this bit.
+
+
+Jarek Burczynski
+
+s0246@poczta.onet.pl
+bujar at mame dot net
diff --git a/src/devices/sound/ym2413.c b/src/devices/sound/ym2413.c
new file mode 100644
index 00000000000..631560912b0
--- /dev/null
+++ b/src/devices/sound/ym2413.c
@@ -0,0 +1,2176 @@
+// license:???
+// copyright-holders:Jarek Burczynski
+/*
+**
+** File: ym2413.c - software implementation of YM2413
+** FM sound generator type OPLL
+**
+** Copyright Jarek Burczynski
+**
+** Version 1.0
+**
+
+ Features as listed in LSI-212413A2 data sheet:
+ 1. FM Sound Generator for real sound creation.
+ 2. Two Selectable modes: 9 simultaneous sounds or 6 melody sounds plus 5 rhythm sounds
+ (different tones can be used together in either case).
+ 3. Built-in Instruments data (15 melody tones, 5 rhythm tones, "CAPTAIN and TELETEXT applicalbe tones).
+ 4. Built-in DA Converter.
+ 5. Built-in Quartz Oscillator.
+ 6. Built-in Vibrato Oscillator/AM Oscillator
+ 7. TTL Compatible Input.
+ 8. Si-Gate NMOS LSI
+ 9. A single 5V power source.
+
+to do:
+
+- make sure of the sinus amplitude bits
+
+- make sure of the EG resolution bits (looks like the biggest
+ modulation index generated by the modulator is 123, 124 = no modulation)
+- find proper algorithm for attack phase of EG
+
+- tune up instruments ROM
+
+- support sample replay in test mode (it is NOT as simple as setting bit 0
+ in register 0x0f and using register 0x10 for sample data).
+ Which games use this feature ?
+
+
+*/
+
+#include "emu.h"
+#include "ym2413.h"
+
+
+
+/* output final shift */
+#if (SAMPLE_BITS==16)
+ #define FINAL_SH (0)
+ #define MAXOUT (+32767)
+ #define MINOUT (-32768)
+#else
+ #define FINAL_SH (8)
+ #define MAXOUT (+127)
+ #define MINOUT (-128)
+#endif
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (EG timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+
+#define FREQ_MASK ((1<<FREQ_SH)-1)
+
+/* envelope output entries */
+#define ENV_BITS 10
+#define ENV_LEN (1<<ENV_BITS)
+#define ENV_STEP (128.0/ENV_LEN)
+
+#define MAX_ATT_INDEX ((1<<(ENV_BITS-2))-1) /*255*/
+#define MIN_ATT_INDEX (0)
+
+/* sinwave entries */
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
+
+
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* Envelope Generator phases */
+
+#define EG_DMP 5
+#define EG_ATT 4
+#define EG_DEC 3
+#define EG_SUS 2
+#define EG_REL 1
+#define EG_OFF 0
+
+
+/* save output as raw 16-bit sample */
+
+//#define SAVE_SAMPLE
+
+#ifdef SAVE_SAMPLE
+INLINE signed int acc_calc(signed int value)
+{
+ if (value>=0)
+ {
+ if (value < 0x0200)
+ return (value & ~0);
+ if (value < 0x0400)
+ return (value & ~1);
+ if (value < 0x0800)
+ return (value & ~3);
+ if (value < 0x1000)
+ return (value & ~7);
+ if (value < 0x2000)
+ return (value & ~15);
+ if (value < 0x4000)
+ return (value & ~31);
+ return (value & ~63);
+ }
+ /*else value < 0*/
+ if (value > -0x0200)
+ return (~abs(value) & ~0);
+ if (value > -0x0400)
+ return (~abs(value) & ~1);
+ if (value > -0x0800)
+ return (~abs(value) & ~3);
+ if (value > -0x1000)
+ return (~abs(value) & ~7);
+ if (value > -0x2000)
+ return (~abs(value) & ~15);
+ if (value > -0x4000)
+ return (~abs(value) & ~31);
+ return (~abs(value) & ~63);
+}
+
+
+static FILE *sample[1];
+ #if 0 /*save to MONO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = acc_calc(mo); \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #else /*save to STEREO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = mo; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ pom = ro; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #define SAVE_SEPARATE_CHANNEL(j) \
+ { signed int pom = outchan; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ pom = chip->instvol_r[j]>>4; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #endif
+#endif
+
+#define LOG_CYM_FILE 0
+static FILE * cymfile = NULL;
+
+
+
+
+struct OPLL_SLOT
+{
+ UINT32 ar; /* attack rate: AR<<2 */
+ UINT32 dr; /* decay rate: DR<<2 */
+ UINT32 rr; /* release rate:RR<<2 */
+ UINT8 KSR; /* key scale rate */
+ UINT8 ksl; /* keyscale level */
+ UINT8 ksr; /* key scale rate: kcode>>KSR */
+ UINT8 mul; /* multiple: mul_tab[ML] */
+
+ /* Phase Generator */
+ UINT32 phase; /* frequency counter */
+ UINT32 freq; /* frequency counter step */
+ UINT8 fb_shift; /* feedback shift value */
+ INT32 op1_out[2]; /* slot1 output for feedback */
+
+ /* Envelope Generator */
+ UINT8 eg_type; /* percussive/nonpercussive mode*/
+ UINT8 state; /* phase type */
+ UINT32 TL; /* total level: TL << 2 */
+ INT32 TLL; /* adjusted now TL */
+ INT32 volume; /* envelope counter */
+ UINT32 sl; /* sustain level: sl_tab[SL] */
+
+ UINT8 eg_sh_dp; /* (dump state) */
+ UINT8 eg_sel_dp; /* (dump state) */
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT8 eg_sh_dr; /* (decay state) */
+ UINT8 eg_sel_dr; /* (decay state) */
+ UINT8 eg_sh_rr; /* (release state for non-perc.)*/
+ UINT8 eg_sel_rr; /* (release state for non-perc.)*/
+ UINT8 eg_sh_rs; /* (release state for perc.mode)*/
+ UINT8 eg_sel_rs; /* (release state for perc.mode)*/
+
+ UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */
+
+ /* LFO */
+ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */
+ UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/
+
+ /* waveform select */
+ unsigned int wavetable;
+};
+
+struct OPLL_CH
+{
+ OPLL_SLOT SLOT[2];
+ /* phase generator state */
+ UINT32 block_fnum; /* block+fnum */
+ UINT32 fc; /* Freq. freqement base */
+ UINT32 ksl_base; /* KeyScaleLevel Base step */
+ UINT8 kcode; /* key code (for key scaling) */
+ UINT8 sus; /* sus on/off (release speed in percussive mode)*/
+};
+
+/* chip state */
+struct YM2413
+{
+ OPLL_CH P_CH[9]; /* OPLL chips have 9 channels*/
+ UINT8 instvol_r[9]; /* instrument/volume (or volume/volume in percussive mode)*/
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */
+
+ UINT8 rhythm; /* Rhythm mode */
+
+ /* LFO */
+ UINT32 LFO_AM;
+ INT32 LFO_PM;
+ UINT32 lfo_am_cnt;
+ UINT32 lfo_am_inc;
+ UINT32 lfo_pm_cnt;
+ UINT32 lfo_pm_inc;
+
+ UINT32 noise_rng; /* 23 bit noise shift register */
+ UINT32 noise_p; /* current noise 'phase' */
+ UINT32 noise_f; /* current noise period */
+
+
+/* instrument settings */
+/*
+ 0-user instrument
+ 1-15 - fixed instruments
+ 16 -bass drum settings
+ 17,18 - other percussion instruments
+*/
+ UINT8 inst_tab[19][8];
+
+ /* external event callback handlers */
+ OPLL_UPDATEHANDLER UpdateHandler; /* stream update handler */
+ void * UpdateParam; /* stream update parameter */
+
+ UINT32 fn_tab[1024]; /* fnumber->increment counter */
+
+ UINT8 address; /* address register */
+ UINT8 status; /* status flag */
+
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ device_t *device;
+
+ signed int output[2];
+ signed int outchan;
+
+};
+
+/* key scale level */
+/* table is 3dB/octave, DV converts this into 6dB/octave */
+/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */
+#define DV (0.1875/1.0)
+static const UINT32 ksl_tab[8*16]=
+{
+ /* OCT 0 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ /* OCT 1 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
+ 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
+ /* OCT 2 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
+ 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
+ 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
+ /* OCT 3 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
+ 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
+ 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
+ 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
+ /* OCT 4 */
+ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
+ 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
+ 9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
+ 10.875/DV,11.250/DV,11.625/DV,12.000/DV,
+ /* OCT 5 */
+ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
+ 9.000/DV,10.125/DV,10.875/DV,11.625/DV,
+ 12.000/DV,12.750/DV,13.125/DV,13.500/DV,
+ 13.875/DV,14.250/DV,14.625/DV,15.000/DV,
+ /* OCT 6 */
+ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
+ 12.000/DV,13.125/DV,13.875/DV,14.625/DV,
+ 15.000/DV,15.750/DV,16.125/DV,16.500/DV,
+ 16.875/DV,17.250/DV,17.625/DV,18.000/DV,
+ /* OCT 7 */
+ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
+ 15.000/DV,16.125/DV,16.875/DV,17.625/DV,
+ 18.000/DV,18.750/DV,19.125/DV,19.500/DV,
+ 19.875/DV,20.250/DV,20.625/DV,21.000/DV
+};
+#undef DV
+
+/* 0 / 1.5 / 3.0 / 6.0 dB/OCT, confirmed on a real YM2413 (the application manual is incorrect) */
+static const UINT32 ksl_shift[4] = { 31, 2, 1, 0 };
+
+
+/* sustain level table (3dB per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,45 (dB)*/
+#define SC(db) (UINT32) ( db * (1.0/ENV_STEP) )
+static const UINT32 sl_tab[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(15)
+};
+#undef SC
+
+
+#define RATE_STEPS (8)
+static const unsigned char eg_inc[15*RATE_STEPS]={
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */
+/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */
+/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(13) in this table - it's directly in the code */
+static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+
+/* rates 00-12 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 13 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 14 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 15 */
+O(12),O(12),O(12),O(12),
+
+/* 16 dummy rates (same as 15 3) */
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */
+/*shift 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0 */
+/*mask 8191, 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+/* rates 00-12 */
+O(13),O(13),O(13),O(13),
+O(12),O(12),O(12),O(12),
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 16 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+
+};
+#undef O
+
+
+/* multiple table */
+#define ML 2
+static const UINT8 mul_tab[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */
+ ML/2, 1*ML, 2*ML, 3*ML, 4*ML, 5*ML, 6*ML, 7*ML,
+ 8*ML, 9*ML,10*ML,10*ML,12*ML,12*ML,15*ML,15*ML
+};
+#undef ML
+
+/* TL_TAB_LEN is calculated as:
+* 11 - sinus amplitude bits (Y axis)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (11*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+#define ENV_QUIET (TL_TAB_LEN>>5)
+
+/* sin waveform table in 'decibel' scale */
+/* two waveforms on OPLL type chips */
+static unsigned int sin_tab[SIN_LEN * 2];
+
+
+/* LFO Amplitude Modulation table (verified on real YM3812)
+ 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples
+
+ Length: 210 elements.
+
+ Each of the elements has to be repeated
+ exactly 64 times (on 64 consecutive samples).
+ The whole table takes: 64 * 210 = 13440 samples.
+
+We use data>>1, until we find what it really is on real chip...
+
+*/
+
+#define LFO_AM_TAB_ELEMENTS 210
+
+static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = {
+0,0,0,0,0,0,0,
+1,1,1,1,
+2,2,2,2,
+3,3,3,3,
+4,4,4,4,
+5,5,5,5,
+6,6,6,6,
+7,7,7,7,
+8,8,8,8,
+9,9,9,9,
+10,10,10,10,
+11,11,11,11,
+12,12,12,12,
+13,13,13,13,
+14,14,14,14,
+15,15,15,15,
+16,16,16,16,
+17,17,17,17,
+18,18,18,18,
+19,19,19,19,
+20,20,20,20,
+21,21,21,21,
+22,22,22,22,
+23,23,23,23,
+24,24,24,24,
+25,25,25,25,
+26,26,26,
+25,25,25,25,
+24,24,24,24,
+23,23,23,23,
+22,22,22,22,
+21,21,21,21,
+20,20,20,20,
+19,19,19,19,
+18,18,18,18,
+17,17,17,17,
+16,16,16,16,
+15,15,15,15,
+14,14,14,14,
+13,13,13,13,
+12,12,12,12,
+11,11,11,11,
+10,10,10,10,
+9,9,9,9,
+8,8,8,8,
+7,7,7,7,
+6,6,6,6,
+5,5,5,5,
+4,4,4,4,
+3,3,3,3,
+2,2,2,2,
+1,1,1,1
+};
+
+/* LFO Phase Modulation table (verified on real YM2413) */
+static const INT8 lfo_pm_table[8*8] = {
+/* FNUM2/FNUM = 0 00xxxxxx (0x0000) */
+0, 0, 0, 0, 0, 0, 0, 0,
+
+/* FNUM2/FNUM = 0 01xxxxxx (0x0040) */
+1, 0, 0, 0,-1, 0, 0, 0,
+
+/* FNUM2/FNUM = 0 10xxxxxx (0x0080) */
+2, 1, 0,-1,-2,-1, 0, 1,
+
+/* FNUM2/FNUM = 0 11xxxxxx (0x00C0) */
+3, 1, 0,-1,-3,-1, 0, 1,
+
+/* FNUM2/FNUM = 1 00xxxxxx (0x0100) */
+4, 2, 0,-2,-4,-2, 0, 2,
+
+/* FNUM2/FNUM = 1 01xxxxxx (0x0140) */
+5, 2, 0,-2,-5,-2, 0, 2,
+
+/* FNUM2/FNUM = 1 10xxxxxx (0x0180) */
+6, 3, 0,-3,-6,-3, 0, 3,
+
+/* FNUM2/FNUM = 1 11xxxxxx (0x01C0) */
+7, 3, 0,-3,-7,-3, 0, 3,
+};
+
+
+
+
+
+
+/* This is not 100% perfect yet but very close */
+/*
+ - multi parameters are 100% correct (instruments and drums)
+ - LFO PM and AM enable are 100% correct
+ - waveform DC and DM select are 100% correct
+*/
+
+static const unsigned char table[19][8] = {
+/* MULT MULT modTL DcDmFb AR/DR AR/DR SL/RR SL/RR */
+/* 0 1 2 3 4 5 6 7 */
+ {0x49, 0x4c, 0x4c, 0x12, 0x00, 0x00, 0x00, 0x00 }, //0
+
+ {0x61, 0x61, 0x1e, 0x17, 0xf0, 0x78, 0x00, 0x17 }, //1
+ {0x13, 0x41, 0x1e, 0x0d, 0xd7, 0xf7, 0x13, 0x13 }, //2
+ {0x13, 0x01, 0x99, 0x04, 0xf2, 0xf4, 0x11, 0x23 }, //3
+ {0x21, 0x61, 0x1b, 0x07, 0xaf, 0x64, 0x40, 0x27 }, //4
+
+//{0x22, 0x21, 0x1e, 0x09, 0xf0, 0x76, 0x08, 0x28 }, //5
+ {0x22, 0x21, 0x1e, 0x06, 0xf0, 0x75, 0x08, 0x18 }, //5
+
+//{0x31, 0x22, 0x16, 0x09, 0x90, 0x7f, 0x00, 0x08 }, //6
+ {0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x13 }, //6
+
+ {0x21, 0x61, 0x1d, 0x07, 0x82, 0x80, 0x10, 0x17 }, //7
+ {0x23, 0x21, 0x2d, 0x16, 0xc0, 0x70, 0x07, 0x07 }, //8
+ {0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17 }, //9
+
+//{0x61, 0x61, 0x0c, 0x08, 0x85, 0xa0, 0x79, 0x07 }, //A
+ {0x61, 0x61, 0x0c, 0x18, 0x85, 0xf0, 0x70, 0x07 }, //A
+
+ {0x23, 0x01, 0x07, 0x11, 0xf0, 0xa4, 0x00, 0x22 }, //B
+ {0x97, 0xc1, 0x24, 0x07, 0xff, 0xf8, 0x22, 0x12 }, //C
+
+//{0x61, 0x10, 0x0c, 0x08, 0xf2, 0xc4, 0x40, 0xc8 }, //D
+ {0x61, 0x10, 0x0c, 0x05, 0xf2, 0xf4, 0x40, 0x44 }, //D
+
+ {0x01, 0x01, 0x55, 0x03, 0xf3, 0x92, 0xf3, 0xf3 }, //E
+ {0x61, 0x41, 0x89, 0x03, 0xf1, 0xf4, 0xf0, 0x13 }, //F
+
+/* drum instruments definitions */
+/* MULTI MULTI modTL xxx AR/DR AR/DR SL/RR SL/RR */
+/* 0 1 2 3 4 5 6 7 */
+ {0x01, 0x01, 0x16, 0x00, 0xfd, 0xf8, 0x2f, 0x6d },/* BD(multi verified, modTL verified, mod env - verified(close), carr. env verifed) */
+ {0x01, 0x01, 0x00, 0x00, 0xd8, 0xd8, 0xf9, 0xf8 },/* HH(multi verified), SD(multi not used) */
+ {0x05, 0x01, 0x00, 0x00, 0xf8, 0xba, 0x49, 0x55 },/* TOM(multi,env verified), TOP CYM(multi verified, env verified) */
+};
+
+/* lock level of common table */
+static int num_lock = 0;
+
+/* work table */
+#define SLOT7_1 (&chip->P_CH[7].SLOT[SLOT1])
+#define SLOT7_2 (&chip->P_CH[7].SLOT[SLOT2])
+#define SLOT8_1 (&chip->P_CH[8].SLOT[SLOT1])
+#define SLOT8_2 (&chip->P_CH[8].SLOT[SLOT2])
+
+
+INLINE int limit( int val, int max, int min )
+{
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+
+/* advance LFO to next sample */
+INLINE void advance_lfo(YM2413 *chip)
+{
+ /* LFO */
+ chip->lfo_am_cnt += chip->lfo_am_inc;
+ if (chip->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH) ) /* lfo_am_table is 210 elements long */
+ chip->lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH);
+
+ chip->LFO_AM = lfo_am_table[ chip->lfo_am_cnt >> LFO_SH ] >> 1;
+
+ chip->lfo_pm_cnt += chip->lfo_pm_inc;
+ chip->LFO_PM = (chip->lfo_pm_cnt>>LFO_SH) & 7;
+}
+
+/* advance to next sample */
+INLINE void advance(YM2413 *chip)
+{
+ OPLL_CH *CH;
+ OPLL_SLOT *op;
+ unsigned int i;
+
+ /* Envelope Generator */
+ chip->eg_timer += chip->eg_timer_add;
+
+ while (chip->eg_timer >= chip->eg_timer_overflow)
+ {
+ chip->eg_timer -= chip->eg_timer_overflow;
+
+ chip->eg_cnt++;
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &chip->P_CH[i/2];
+
+ op = &CH->SLOT[i&1];
+
+ switch(op->state)
+ {
+ case EG_DMP: /* dump phase */
+ /*dump phase is performed by both operators in each channel*/
+ /*when CARRIER envelope gets down to zero level,
+ ** phases in BOTH opearators are reset (at the same time ?)
+ */
+ if ( !(chip->eg_cnt & ((1<<op->eg_sh_dp)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_dp + ((chip->eg_cnt>>op->eg_sh_dp)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_ATT;
+ /* restart Phase Generator */
+ op->phase = 0;
+ }
+ }
+ break;
+
+ case EG_ATT: /* attack phase */
+ if ( !(chip->eg_cnt & ((1<<op->eg_sh_ar)-1) ) )
+ {
+ op->volume += (~op->volume *
+ (eg_inc[op->eg_sel_ar + ((chip->eg_cnt>>op->eg_sh_ar)&7)])
+ ) >>2;
+
+ if (op->volume <= MIN_ATT_INDEX)
+ {
+ op->volume = MIN_ATT_INDEX;
+ op->state = EG_DEC;
+ }
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ if ( !(chip->eg_cnt & ((1<<op->eg_sh_dr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_dr + ((chip->eg_cnt>>op->eg_sh_dr)&7)];
+
+ if ( op->volume >= op->sl )
+ op->state = EG_SUS;
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+ /* this is important behaviour:
+ one can change percusive/non-percussive modes on the fly and
+ the chip will remain in sustain phase - verified on real YM3812 */
+
+ if(op->eg_type) /* non-percussive mode (sustained tone) */
+ {
+ /* do nothing */
+ }
+ else /* percussive mode */
+ {
+ /* during sustain phase chip adds Release Rate (in percussive mode) */
+ if ( !(chip->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ op->volume = MAX_ATT_INDEX;
+ }
+ /* else do nothing in sustain phase */
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ /* exclude modulators in melody channels from performing anything in this mode*/
+ /* allowed are only carriers in melody mode and rhythm slots in rhythm mode */
+
+ /*This table shows which operators and on what conditions are allowed to perform EG_REL:
+ (a) - always perform EG_REL
+ (n) - never perform EG_REL
+ (r) - perform EG_REL in Rhythm mode ONLY
+ 0: 0 (n), 1 (a)
+ 1: 2 (n), 3 (a)
+ 2: 4 (n), 5 (a)
+ 3: 6 (n), 7 (a)
+ 4: 8 (n), 9 (a)
+ 5: 10(n), 11(a)
+ 6: 12(r), 13(a)
+ 7: 14(r), 15(a)
+ 8: 16(r), 17(a)
+ */
+ if ( (i&1) || ((chip->rhythm&0x20) && (i>=12)) )/* exclude modulators */
+ {
+ if(op->eg_type) /* non-percussive mode (sustained tone) */
+ /*this is correct: use RR when SUS = OFF*/
+ /*and use RS when SUS = ON*/
+ {
+ if (CH->sus)
+ {
+ if ( !(chip->eg_cnt & ((1<<op->eg_sh_rs)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rs + ((chip->eg_cnt>>op->eg_sh_rs)&7)];
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+ }
+ }
+ else
+ {
+ if ( !(chip->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)];
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+ }
+ }
+ }
+ else /* percussive mode */
+ {
+ if ( !(chip->eg_cnt & ((1<<op->eg_sh_rs)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rs + ((chip->eg_cnt>>op->eg_sh_rs)&7)];
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &chip->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Phase Generator */
+ if(op->vib)
+ {
+ UINT8 block;
+
+ unsigned int fnum_lfo = 8*((CH->block_fnum&0x01c0) >> 6);
+ unsigned int block_fnum = CH->block_fnum * 2;
+ signed int lfo_fn_table_index_offset = lfo_pm_table[chip->LFO_PM + fnum_lfo ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ block_fnum += lfo_fn_table_index_offset;
+ block = (block_fnum&0x1c00) >> 10;
+ op->phase += (chip->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ op->phase += op->freq;
+ }
+ }
+ else /* LFO phase modulation disabled for this operator */
+ {
+ op->phase += op->freq;
+ }
+ }
+
+ /* The Noise Generator of the YM3812 is 23-bit shift register.
+ * Period is equal to 2^23-2 samples.
+ * Register works at sampling frequency of the chip, so output
+ * can change on every sample.
+ *
+ * Output of the register and input to the bit 22 is:
+ * bit0 XOR bit14 XOR bit15 XOR bit22
+ *
+ * Simply use bit 22 as the noise output.
+ */
+
+ chip->noise_p += chip->noise_f;
+ i = chip->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */
+ chip->noise_p &= FREQ_MASK;
+ while (i)
+ {
+ /*
+ UINT32 j;
+ j = ( (chip->noise_rng) ^ (chip->noise_rng>>14) ^ (chip->noise_rng>>15) ^ (chip->noise_rng>>22) ) & 1;
+ chip->noise_rng = (j<<22) | (chip->noise_rng>>1);
+ */
+
+ /*
+ Instead of doing all the logic operations above, we
+ use a trick here (and use bit 0 as the noise output).
+ The difference is only that the noise bit changes one
+ step ahead. This doesn't matter since we don't know
+ what is real state of the noise_rng after the reset.
+ */
+
+ if (chip->noise_rng & 1) chip->noise_rng ^= 0x800302;
+ chip->noise_rng >>= 1;
+
+ i--;
+ }
+}
+
+
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+
+ p = (env<<5) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<17))) >> FREQ_SH ) & SIN_MASK) ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+ INT32 i;
+
+ i = (phase & ~FREQ_MASK) + pm;
+
+/*logerror("i=%08x (i>>16)&511=%8i phase=%i [pm=%08x] ",i, (i>>16)&511, phase>>FREQ_SH, pm);*/
+
+ p = (env<<5) + sin_tab[ wave_tab + ((i>>FREQ_SH) & SIN_MASK)];
+
+/*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8) );*/
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+
+#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (chip->LFO_AM & (OP)->AMmask))
+
+/* calculate output */
+INLINE void chan_calc( YM2413 *chip, OPLL_CH *CH )
+{
+ OPLL_SLOT *SLOT;
+ unsigned int env;
+ signed int out;
+ signed int phase_modulation; /* phase modulation input (SLOT 2) */
+
+
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env = volume_calc(SLOT);
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+ phase_modulation = SLOT->op1_out[0];
+
+ SLOT->op1_out[1] = 0;
+
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->fb_shift)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->phase, env, (out<<SLOT->fb_shift), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+
+ chip->outchan=0;
+
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ {
+ signed int outp = op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable);
+ chip->output[0] += outp;
+ chip->outchan = outp;
+ //chip->output[0] += op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable);
+ }
+}
+
+/*
+ operators used in the rhythm sounds generation process:
+
+ Envelope Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal
+ 6 / 0 12 50 70 90 f0 +
+ 6 / 1 15 53 73 93 f3 +
+ 7 / 0 13 51 71 91 f1 +
+ 7 / 1 16 54 74 94 f4 +
+ 8 / 0 14 52 72 92 f2 +
+ 8 / 1 17 55 75 95 f5 +
+
+ Phase Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number MULTIPLE Drum Hat Drum Tom Cymbal
+ 6 / 0 12 30 +
+ 6 / 1 15 33 +
+ 7 / 0 13 31 + + +
+ 7 / 1 16 34 ----- n o t u s e d -----
+ 8 / 0 14 32 +
+ 8 / 1 17 35 + +
+
+channel operator register number Bass High Snare Tom Top
+number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal
+ 6 12,15 B6 A6 +
+
+ 7 13,16 B7 A7 + + +
+
+ 8 14,17 B8 A8 + + +
+
+*/
+
+/* calculate rhythm */
+
+INLINE void rhythm_calc( YM2413 *chip, OPLL_CH *CH, unsigned int noise )
+{
+ OPLL_SLOT *SLOT;
+ signed int out;
+ unsigned int env;
+ signed int phase_modulation; /* phase modulation input (SLOT 2) */
+
+
+ /* Bass Drum (verified on real YM3812):
+ - depends on the channel 6 'connect' register:
+ when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out)
+ when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored
+ - output sample always is multiplied by 2
+ */
+
+
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env = volume_calc(SLOT);
+
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+
+ phase_modulation = SLOT->op1_out[0];
+
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->fb_shift)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->phase, env, (out<<SLOT->fb_shift), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ chip->output[1] += op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable) * 2;
+
+
+ /* Phase generation is based on: */
+ // HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases)
+ // SD (16) channel 7->slot 1
+ // TOM (14) channel 8->slot 1
+ // TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases)
+
+ /* Envelope generation based on: */
+ // HH channel 7->slot1
+ // SD channel 7->slot2
+ // TOM channel 8->slot1
+ // TOP channel 8->slot2
+
+
+ /* The following formulas can be well optimized.
+ I leave them in direct form for now (in case I've missed something).
+ */
+
+ /* High Hat (verified on real YM3812) */
+ env = volume_calc(SLOT7_1);
+ if( env < ENV_QUIET )
+ {
+ /* high hat phase generation:
+ phase = d0 or 234 (based on frequency only)
+ phase = 34 or 2d0 (based on noise)
+ */
+
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((SLOT7_1->phase>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((SLOT7_1->phase>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((SLOT7_1->phase>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0xd0; */
+ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */
+ UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((SLOT8_2->phase>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((SLOT8_2->phase>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e | bit5e);
+
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */
+ if (res2)
+ phase = (0x200|(0xd0>>2));
+
+
+ /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */
+ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */
+ if (phase&0x200)
+ {
+ if (noise)
+ phase = 0x200|0xd0;
+ }
+ else
+ /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */
+ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */
+ {
+ if (noise)
+ phase = 0xd0>>2;
+ }
+
+ chip->output[1] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_1->wavetable) * 2;
+ }
+
+ /* Snare Drum (verified on real YM3812) */
+ env = volume_calc(SLOT7_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit8 = ((SLOT7_1->phase>>FREQ_SH)>>8)&1;
+
+ /* when bit8 = 0 phase = 0x100; */
+ /* when bit8 = 1 phase = 0x200; */
+ UINT32 phase = bit8 ? 0x200 : 0x100;
+
+ /* Noise bit XOR'es phase by 0x100 */
+ /* when noisebit = 0 pass the phase from calculation above */
+ /* when noisebit = 1 phase ^= 0x100; */
+ /* in other words: phase ^= (noisebit<<8); */
+ if (noise)
+ phase ^= 0x100;
+
+ chip->output[1] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_2->wavetable) * 2;
+ }
+
+ /* Tom Tom (verified on real YM3812) */
+ env = volume_calc(SLOT8_1);
+ if( env < ENV_QUIET )
+ chip->output[1] += op_calc(SLOT8_1->phase, env, 0, SLOT8_1->wavetable) * 2;
+
+ /* Top Cymbal (verified on real YM2413) */
+ env = volume_calc(SLOT8_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((SLOT7_1->phase>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((SLOT7_1->phase>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((SLOT7_1->phase>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0x100; */
+ /* when res1 = 1 phase = 0x200 | 0x100; */
+ UINT32 phase = res1 ? 0x300 : 0x100;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((SLOT8_2->phase>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((SLOT8_2->phase>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e | bit5e);
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | 0x100; */
+ if (res2)
+ phase = 0x300;
+
+ chip->output[1] += op_calc(phase<<FREQ_SH, env, 0, SLOT8_2->wavetable) * 2;
+ }
+
+}
+
+
+/* generic table initialize */
+static int init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+
+ for (x=0; x<TL_RES_LEN; x++)
+ {
+ m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);
+ m = floor(m);
+
+ /* we never reach (1<<16) here due to the (x+1) */
+ /* result fits within 16 bits at maximum */
+
+ n = (int)m; /* 16 bits here */
+ n >>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<11; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ #if 0
+ logerror("tl %04i", x*2);
+ for (i=0; i<11; i++)
+ logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] );
+ logerror("\n");
+ #endif
+ }
+ /*logerror("ym2413.c: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/
+
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* non-standard sinus */
+ m = sin( ((i*2)+1) * M_PI / SIN_LEN ); /* checked against the real chip */
+
+ /* we never reach zero here due to ((i*2)+1) */
+
+ if (m>0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ /* waveform 0: standard sinus */
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+
+ /*logerror("ym2413.c: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/
+
+
+ /* waveform 1: __ __ */
+ /* / \____/ \____*/
+ /* output only first half of the sinus waveform (positive one) */
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[1*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[1*SIN_LEN+i] = sin_tab[i];
+
+ /*logerror("ym2413.c: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );*/
+ }
+#if 0
+ logerror("YM2413.C: ENV_QUIET= %08x (*32=%08x)\n", ENV_QUIET, ENV_QUIET*32 );
+ for (i=0; i<ENV_QUIET; i++)
+ {
+ logerror("tl_tb[%4x(%4i)]=%8x\n", i<<5, i, tl_tab[i<<5] );
+ }
+#endif
+#ifdef SAVE_SAMPLE
+ sample[0]=fopen("sampsum.pcm","wb");
+#endif
+
+ return 1;
+}
+
+static void OPLCloseTable( void )
+{
+#ifdef SAVE_SAMPLE
+ fclose(sample[0]);
+#endif
+}
+
+
+static void OPLL_init_save(YM2413 *chip, device_t *device)
+{
+ int chnum;
+
+ device->save_item(NAME(chip->instvol_r));
+ device->save_item(NAME(chip->eg_cnt));
+ device->save_item(NAME(chip->eg_timer));
+ device->save_item(NAME(chip->eg_timer_add));
+ device->save_item(NAME(chip->eg_timer_overflow));
+ device->save_item(NAME(chip->rhythm));
+ device->save_item(NAME(chip->lfo_am_cnt));
+ device->save_item(NAME(chip->lfo_am_inc));
+ device->save_item(NAME(chip->lfo_pm_cnt));
+ device->save_item(NAME(chip->lfo_pm_inc));
+ device->save_item(NAME(chip->noise_rng));
+ device->save_item(NAME(chip->noise_p));
+ device->save_item(NAME(chip->noise_f));
+ device->save_item(NAME(chip->inst_tab));
+ device->save_item(NAME(chip->address));
+ device->save_item(NAME(chip->status));
+
+ for (chnum = 0; chnum < ARRAY_LENGTH(chip->P_CH); chnum++)
+ {
+ OPLL_CH *ch = &chip->P_CH[chnum];
+ int slotnum;
+
+ device->save_item(NAME(ch->block_fnum), chnum);
+ device->save_item(NAME(ch->fc), chnum);
+ device->save_item(NAME(ch->ksl_base), chnum);
+ device->save_item(NAME(ch->kcode), chnum);
+ device->save_item(NAME(ch->sus), chnum);
+
+ for (slotnum = 0; slotnum < ARRAY_LENGTH(ch->SLOT); slotnum++)
+ {
+ OPLL_SLOT *sl = &ch->SLOT[slotnum];
+
+ device->save_item(NAME(sl->ar), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->dr), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->rr), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->KSR), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->ksl), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->ksr), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->mul), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->phase), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->freq), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->fb_shift), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->op1_out), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->eg_type), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->state), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->TL), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->TLL), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->volume), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->sl), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->eg_sh_dp), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->eg_sel_dp), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->eg_sh_ar), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->eg_sel_ar), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->eg_sh_dr), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->eg_sel_dr), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->eg_sh_rr), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->eg_sel_rr), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->eg_sh_rs), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->eg_sel_rs), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->key), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->AMmask), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->vib), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ device->save_item(NAME(sl->wavetable), chnum * ARRAY_LENGTH(ch->SLOT) + slotnum);
+ }
+ }
+}
+
+
+static void OPLL_initalize(YM2413 *chip, device_t *device)
+{
+ int i;
+
+ OPLL_init_save(chip, device);
+
+ /* frequency base */
+ chip->freqbase = (chip->rate) ? ((double)chip->clock / 72.0) / chip->rate : 0;
+#if 0
+ chip->rate = (double)chip->clock / 72.0;
+ chip->freqbase = 1.0;
+ logerror("freqbase=%f\n", chip->freqbase);
+#endif
+
+
+
+ /* make fnumber -> increment counter table */
+ for( i = 0 ; i < 1024; i++ )
+ {
+ /* OPLL (YM2413) phase increment counter = 18bit */
+
+ chip->fn_tab[i] = (UINT32)( (double)i * 64 * chip->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+#if 0
+ logerror("ym2413.c: fn_tab[%4i] = %08x (dec=%8i)\n",
+ i, chip->fn_tab[i]>>6, chip->fn_tab[i]>>6 );
+#endif
+ }
+
+#if 0
+ for( i=0 ; i < 16 ; i++ )
+ {
+ logerror("ym2413.c: sl_tab[%i] = %08x\n", i, sl_tab[i] );
+ }
+ for( i=0 ; i < 8 ; i++ )
+ {
+ int j;
+ logerror("ym2413.c: ksl_tab[oct=%2i] =",i);
+ for (j=0; j<16; j++)
+ {
+ logerror("%08x ", ksl_tab[i*16+j] );
+ }
+ logerror("\n");
+ }
+#endif
+
+
+ /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */
+ /* One entry from LFO_AM_TABLE lasts for 64 samples */
+ chip->lfo_am_inc = (1.0 / 64.0 ) * (1<<LFO_SH) * chip->freqbase;
+
+ /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */
+ chip->lfo_pm_inc = (1.0 / 1024.0) * (1<<LFO_SH) * chip->freqbase;
+
+ /*logerror ("chip->lfo_am_inc = %8x ; chip->lfo_pm_inc = %8x\n", chip->lfo_am_inc, chip->lfo_pm_inc);*/
+
+ /* Noise generator: a step takes 1 sample */
+ chip->noise_f = (1.0 / 1.0) * (1<<FREQ_SH) * chip->freqbase;
+ /*logerror("YM2413init noise_f=%8x\n", chip->noise_f);*/
+
+ chip->eg_timer_add = (1<<EG_SH) * chip->freqbase;
+ chip->eg_timer_overflow = ( 1 ) * (1<<EG_SH);
+ /*logerror("YM2413init eg_timer_add=%8x eg_timer_overflow=%8x\n", chip->eg_timer_add, chip->eg_timer_overflow);*/
+}
+
+INLINE void KEY_ON(OPLL_SLOT *SLOT, UINT32 key_set)
+{
+ if( !SLOT->key )
+ {
+ /* do NOT restart Phase Generator (verified on real YM2413)*/
+ /* phase -> Dump */
+ SLOT->state = EG_DMP;
+ }
+ SLOT->key |= key_set;
+}
+
+INLINE void KEY_OFF(OPLL_SLOT *SLOT, UINT32 key_clr)
+{
+ if( SLOT->key )
+ {
+ SLOT->key &= key_clr;
+
+ if( !SLOT->key )
+ {
+ /* phase -> Release */
+ if (SLOT->state>EG_REL)
+ SLOT->state = EG_REL;
+ }
+ }
+}
+
+/* update phase increment counter of operator (also update the EG rates if necessary) */
+INLINE void CALC_FCSLOT(OPLL_CH *CH,OPLL_SLOT *SLOT)
+{
+ int ksr;
+ UINT32 SLOT_rs;
+ UINT32 SLOT_dp;
+
+ /* (frequency) phase increment counter */
+ SLOT->freq = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+
+ }
+
+ if (CH->sus)
+ SLOT_rs = 16 + (5<<2);
+ else
+ SLOT_rs = 16 + (7<<2);
+
+ SLOT->eg_sh_rs = eg_rate_shift [SLOT_rs + SLOT->ksr ];
+ SLOT->eg_sel_rs = eg_rate_select[SLOT_rs + SLOT->ksr ];
+
+ SLOT_dp = 16 + (13<<2);
+ SLOT->eg_sh_dp = eg_rate_shift [SLOT_dp + SLOT->ksr ];
+ SLOT->eg_sel_dp = eg_rate_select[SLOT_dp + SLOT->ksr ];
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(YM2413 *chip,int slot,int v)
+{
+ OPLL_CH *CH = &chip->P_CH[slot/2];
+ OPLL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->mul = mul_tab[v&0x0f];
+ SLOT->KSR = (v&0x10) ? 0 : 2;
+ SLOT->eg_type = (v&0x20);
+ SLOT->vib = (v&0x40);
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;
+ CALC_FCSLOT(CH,SLOT);
+}
+
+/* set ksl, tl */
+INLINE void set_ksl_tl(YM2413 *chip,int chan,int v)
+{
+ OPLL_CH *CH = &chip->P_CH[chan];
+/* modulator */
+ OPLL_SLOT *SLOT = &CH->SLOT[SLOT1];
+
+ SLOT->ksl = ksl_shift[v >> 6];
+ SLOT->TL = (v&0x3f)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set ksl , waveforms, feedback */
+INLINE void set_ksl_wave_fb(YM2413 *chip,int chan,int v)
+{
+ OPLL_CH *CH = &chip->P_CH[chan];
+/* modulator */
+ OPLL_SLOT *SLOT = &CH->SLOT[SLOT1];
+ SLOT->wavetable = ((v&0x08)>>3)*SIN_LEN;
+ SLOT->fb_shift = (v&7) ? (v&7) + 8 : 0;
+
+/*carrier*/
+ SLOT = &CH->SLOT[SLOT2];
+
+ SLOT->ksl = ksl_shift[v >> 6];
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+
+ SLOT->wavetable = ((v&0x10)>>4)*SIN_LEN;
+}
+
+/* set attack rate & decay rate */
+INLINE void set_ar_dr(YM2413 *chip,int slot,int v)
+{
+ OPLL_CH *CH = &chip->P_CH[slot/2];
+ OPLL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0;
+
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+
+ SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(YM2413 *chip,int slot,int v)
+{
+ OPLL_CH *CH = &chip->P_CH[slot/2];
+ OPLL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->sl = sl_tab[ v>>4 ];
+
+ SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+}
+
+static void load_instrument(YM2413 *chip, UINT32 chan, UINT32 slot, UINT8* inst )
+{
+ set_mul (chip, slot, inst[0]);
+ set_mul (chip, slot+1, inst[1]);
+ set_ksl_tl (chip, chan, inst[2]);
+ set_ksl_wave_fb (chip, chan, inst[3]);
+ set_ar_dr (chip, slot, inst[4]);
+ set_ar_dr (chip, slot+1, inst[5]);
+ set_sl_rr (chip, slot, inst[6]);
+ set_sl_rr (chip, slot+1, inst[7]);
+}
+static void update_instrument_zero(YM2413 *chip, UINT8 r )
+{
+ UINT8* inst = &chip->inst_tab[0][0]; /* point to user instrument */
+ UINT32 chan;
+ UINT32 chan_max;
+
+ chan_max = 9;
+ if (chip->rhythm & 0x20)
+ chan_max=6;
+
+ switch(r)
+ {
+ case 0:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_mul (chip, chan*2, inst[0]);
+ }
+ }
+ break;
+ case 1:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_mul (chip, chan*2+1,inst[1]);
+ }
+ }
+ break;
+ case 2:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_ksl_tl (chip, chan, inst[2]);
+ }
+ }
+ break;
+ case 3:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_ksl_wave_fb (chip, chan, inst[3]);
+ }
+ }
+ break;
+ case 4:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_ar_dr (chip, chan*2, inst[4]);
+ }
+ }
+ break;
+ case 5:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_ar_dr (chip, chan*2+1,inst[5]);
+ }
+ }
+ break;
+ case 6:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_sl_rr (chip, chan*2, inst[6]);
+ }
+ }
+ break;
+ case 7:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_sl_rr (chip, chan*2+1,inst[7]);
+ }
+ }
+ break;
+ }
+}
+
+/* write a value v to register r on chip chip */
+static void OPLLWriteReg(YM2413 *chip, int r, int v)
+{
+ OPLL_CH *CH;
+ OPLL_SLOT *SLOT;
+ UINT8 *inst;
+ int chan;
+ int slot;
+
+ /* adjust bus to 8 bits */
+ r &= 0xff;
+ v &= 0xff;
+
+
+ if (LOG_CYM_FILE && (cymfile) && (r!=8) )
+ {
+ fputc( (unsigned char)r, cymfile );
+ fputc( (unsigned char)v, cymfile );
+ }
+
+
+ switch(r&0xf0)
+ {
+ case 0x00: /* 00-0f:control */
+ {
+ switch(r&0x0f)
+ {
+ case 0x00: /* AM/VIB/EGTYP/KSR/MULTI (modulator) */
+ case 0x01: /* AM/VIB/EGTYP/KSR/MULTI (carrier) */
+ case 0x02: /* Key Scale Level, Total Level (modulator) */
+ case 0x03: /* Key Scale Level, carrier waveform, modulator waveform, Feedback */
+ case 0x04: /* Attack, Decay (modulator) */
+ case 0x05: /* Attack, Decay (carrier) */
+ case 0x06: /* Sustain, Release (modulator) */
+ case 0x07: /* Sustain, Release (carrier) */
+ chip->inst_tab[0][r & 0x07] = v;
+ update_instrument_zero(chip,r&7);
+ break;
+
+ case 0x0e: /* x, x, r,bd,sd,tom,tc,hh */
+ {
+ if(v&0x20)
+ {
+ if ((chip->rhythm&0x20)==0)
+ /*rhythm off to on*/
+ {
+ logerror("YM2413: Rhythm mode enable\n");
+
+ /* Load instrument settings for channel seven(chan=6 since we're zero based). (Bass drum) */
+ chan = 6;
+ inst = &chip->inst_tab[16][0];
+ slot = chan*2;
+
+ load_instrument(chip, chan, slot, inst);
+
+ /* Load instrument settings for channel eight. (High hat and snare drum) */
+ chan = 7;
+ inst = &chip->inst_tab[17][0];
+ slot = chan*2;
+
+ load_instrument(chip, chan, slot, inst);
+
+ CH = &chip->P_CH[chan];
+ SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is HH */
+ SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+
+ /* Load instrument settings for channel nine. (Tom-tom and top cymbal) */
+ chan = 8;
+ inst = &chip->inst_tab[18][0];
+ slot = chan*2;
+
+ load_instrument(chip, chan, slot, inst);
+
+ CH = &chip->P_CH[chan];
+ SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is TOM */
+ SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+ /* BD key on/off */
+ if(v&0x10)
+ {
+ KEY_ON (&chip->P_CH[6].SLOT[SLOT1], 2);
+ KEY_ON (&chip->P_CH[6].SLOT[SLOT2], 2);
+ }
+ else
+ {
+ KEY_OFF(&chip->P_CH[6].SLOT[SLOT1],~2);
+ KEY_OFF(&chip->P_CH[6].SLOT[SLOT2],~2);
+ }
+ /* HH key on/off */
+ if(v&0x01) KEY_ON (&chip->P_CH[7].SLOT[SLOT1], 2);
+ else KEY_OFF(&chip->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key on/off */
+ if(v&0x08) KEY_ON (&chip->P_CH[7].SLOT[SLOT2], 2);
+ else KEY_OFF(&chip->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key on/off */
+ if(v&0x04) KEY_ON (&chip->P_CH[8].SLOT[SLOT1], 2);
+ else KEY_OFF(&chip->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY key on/off */
+ if(v&0x02) KEY_ON (&chip->P_CH[8].SLOT[SLOT2], 2);
+ else KEY_OFF(&chip->P_CH[8].SLOT[SLOT2],~2);
+ }
+ else
+ {
+ if (chip->rhythm&0x20)
+ /*rhythm on to off*/
+ {
+ logerror("YM2413: Rhythm mode disable\n");
+ /* Load instrument settings for channel seven(chan=6 since we're zero based).*/
+ chan = 6;
+ inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0];
+ slot = chan*2;
+
+ load_instrument(chip, chan, slot, inst);
+
+ /* Load instrument settings for channel eight.*/
+ chan = 7;
+ inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0];
+ slot = chan*2;
+
+ load_instrument(chip, chan, slot, inst);
+
+ /* Load instrument settings for channel nine.*/
+ chan = 8;
+ inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0];
+ slot = chan*2;
+
+ load_instrument(chip, chan, slot, inst);
+ }
+ /* BD key off */
+ KEY_OFF(&chip->P_CH[6].SLOT[SLOT1],~2);
+ KEY_OFF(&chip->P_CH[6].SLOT[SLOT2],~2);
+ /* HH key off */
+ KEY_OFF(&chip->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key off */
+ KEY_OFF(&chip->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key off */
+ KEY_OFF(&chip->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY off */
+ KEY_OFF(&chip->P_CH[8].SLOT[SLOT2],~2);
+ }
+ chip->rhythm = v&0x3f;
+ }
+ break;
+ }
+ }
+ break;
+
+ case 0x10:
+ case 0x20:
+ {
+ int block_fnum;
+
+ chan = r&0x0f;
+
+ if (chan >= 9)
+ chan -= 9; /* verified on real YM2413 */
+
+ CH = &chip->P_CH[chan];
+
+ if(r&0x10)
+ { /* 10-18: FNUM 0-7 */
+ block_fnum = (CH->block_fnum&0x0f00) | v;
+ }
+ else
+ { /* 20-28: suson, keyon, block, FNUM 8 */
+ block_fnum = ((v&0x0f)<<8) | (CH->block_fnum&0xff);
+
+ if(v&0x10)
+ {
+ KEY_ON (&CH->SLOT[SLOT1], 1);
+ KEY_ON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ KEY_OFF(&CH->SLOT[SLOT1],~1);
+ KEY_OFF(&CH->SLOT[SLOT2],~1);
+ }
+
+
+ if (CH->sus!=(v&0x20))
+ logerror("chan=%i sus=%2x\n",chan,v&0x20);
+
+ CH->sus = v & 0x20;
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ UINT8 block;
+
+ CH->block_fnum = block_fnum;
+
+ /* BLK 2,1,0 bits -> bits 3,2,1 of kcode, FNUM MSB -> kcode LSB */
+ CH->kcode = (block_fnum&0x0f00)>>8;
+
+ CH->ksl_base = ksl_tab[block_fnum>>5];
+
+ block_fnum = block_fnum * 2;
+ block = (block_fnum&0x1c00) >> 10;
+ CH->fc = chip->fn_tab[block_fnum&0x03ff] >> (7-block);
+
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ }
+ break;
+
+ case 0x30: /* inst 4 MSBs, VOL 4 LSBs */
+ {
+ UINT8 old_instvol;
+
+ chan = r&0x0f;
+
+ if (chan >= 9)
+ chan -= 9; /* verified on real YM2413 */
+
+ old_instvol = chip->instvol_r[chan];
+ chip->instvol_r[chan] = v; /* store for later use */
+
+ CH = &chip->P_CH[chan];
+ SLOT = &CH->SLOT[SLOT2]; /* carrier */
+ SLOT->TL = ((v&0x0f)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+
+
+ /*check whether we are in rhythm mode and handle instrument/volume register accordingly*/
+ if ((chan>=6) && (chip->rhythm&0x20))
+ {
+ /* we're in rhythm mode*/
+
+ if (chan>=7) /* only for channel 7 and 8 (channel 6 is handled in usual way)*/
+ {
+ SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is HH(chan=7) or TOM(chan=8) */
+ SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+ }
+ else
+ {
+ if ( (old_instvol&0xf0) == (v&0xf0) )
+ return;
+
+ inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0];
+ slot = chan*2;
+
+ load_instrument(chip, chan, slot, inst);
+
+ #if 0
+ logerror("YM2413: chan#%02i inst=%02i: (r=%2x, v=%2x)\n",chan,v>>4,r,v);
+ logerror(" 0:%2x 1:%2x\n",inst[0],inst[1]); logerror(" 2:%2x 3:%2x\n",inst[2],inst[3]);
+ logerror(" 4:%2x 5:%2x\n",inst[4],inst[5]); logerror(" 6:%2x 7:%2x\n",inst[6],inst[7]);
+ #endif
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static TIMER_CALLBACK( cymfile_callback )
+{
+ if (cymfile)
+ {
+ fputc( (unsigned char)8, cymfile );
+ }
+}
+
+/* lock/unlock for common table */
+static int OPLL_LockTable(device_t *device)
+{
+ num_lock++;
+ if(num_lock>1) return 0;
+
+ /* first time */
+
+ /* allocate total level table (128kb space) */
+ if( !init_tables() )
+ {
+ num_lock--;
+ return -1;
+ }
+
+ if (LOG_CYM_FILE)
+ {
+ cymfile = fopen("2413_.cym","wb");
+ if (cymfile)
+ device->machine().scheduler().timer_pulse ( attotime::from_hz(110), FUNC(cymfile_callback)); /*110 Hz pulse timer*/
+ else
+ logerror("Could not create file 2413_.cym\n");
+ }
+
+ return 0;
+}
+
+static void OPLL_UnLockTable(void)
+{
+ if(num_lock) num_lock--;
+ if(num_lock) return;
+
+ /* last time */
+
+ OPLCloseTable();
+
+ if (cymfile)
+ fclose (cymfile);
+ cymfile = NULL;
+}
+
+static void OPLLResetChip(YM2413 *chip)
+{
+ int c,s;
+ int i;
+
+ chip->eg_timer = 0;
+ chip->eg_cnt = 0;
+
+ chip->noise_rng = 1; /* noise shift register */
+
+
+ /* setup instruments table */
+ for (i=0; i<19; i++)
+ {
+ for (c=0; c<8; c++)
+ {
+ chip->inst_tab[i][c] = table[i][c];
+ }
+ }
+
+
+ /* reset with register write */
+ OPLLWriteReg(chip,0x0f,0); /*test reg*/
+ for(i = 0x3f ; i >= 0x10 ; i-- ) OPLLWriteReg(chip,i,0x00);
+
+ /* reset operator parameters */
+ for( c = 0 ; c < 9 ; c++ )
+ {
+ OPLL_CH *CH = &chip->P_CH[c];
+ for(s = 0 ; s < 2 ; s++ )
+ {
+ /* wave table */
+ CH->SLOT[s].wavetable = 0;
+ CH->SLOT[s].state = EG_OFF;
+ CH->SLOT[s].volume = MAX_ATT_INDEX;
+ }
+ }
+}
+
+/* Create one of virtual YM2413 */
+/* 'clock' is chip clock in Hz */
+/* 'rate' is sampling rate */
+static YM2413 *OPLLCreate(device_t *device, int clock, int rate)
+{
+ YM2413 *chip;
+
+ if (OPLL_LockTable(device) == -1) return NULL;
+
+ /* allocate memory block */
+ chip = auto_alloc_clear(device->machine(), YM2413);
+
+ chip->device = device;
+ chip->clock = clock;
+ chip->rate = rate;
+
+ /* init global tables */
+ OPLL_initalize(chip, device);
+
+ /* reset chip */
+ OPLLResetChip(chip);
+ return chip;
+}
+
+/* Destroy one of virtual YM3812 */
+static void OPLLDestroy(YM2413 *chip)
+{
+ OPLL_UnLockTable();
+ auto_free(chip->device->machine(), chip);
+}
+
+/* Option handlers */
+
+static void OPLLSetUpdateHandler(YM2413 *chip,OPLL_UPDATEHANDLER UpdateHandler,void * param)
+{
+ chip->UpdateHandler = UpdateHandler;
+ chip->UpdateParam = param;
+}
+
+/* YM3812 I/O interface */
+static void OPLLWrite(YM2413 *chip,int a,int v)
+{
+ if( !(a&1) )
+ { /* address port */
+ chip->address = v & 0xff;
+ }
+ else
+ { /* data port */
+ if(chip->UpdateHandler) chip->UpdateHandler(chip->UpdateParam,0);
+ OPLLWriteReg(chip,chip->address,v);
+ }
+}
+
+static unsigned char OPLLRead(YM2413 *chip,int a)
+{
+ if( !(a&1) )
+ {
+ /* status port */
+ return chip->status;
+ }
+ return 0xff;
+}
+
+
+
+
+
+void * ym2413_init(device_t *device, int clock, int rate)
+{
+ /* emulator create */
+ return OPLLCreate(device, clock, rate);
+}
+
+void ym2413_shutdown(void *chip)
+{
+ YM2413 *OPLL = (YM2413 *)chip;
+
+ /* emulator shutdown */
+ OPLLDestroy(OPLL);
+}
+
+void ym2413_reset_chip(void *chip)
+{
+ YM2413 *OPLL = (YM2413 *)chip;
+ OPLLResetChip(OPLL);
+}
+
+void ym2413_write(void *chip, int a, int v)
+{
+ YM2413 *OPLL = (YM2413 *)chip;
+ OPLLWrite(OPLL, a, v);
+}
+
+unsigned char ym2413_read(void *chip, int a)
+{
+ YM2413 *OPLL = (YM2413 *)chip;
+ return OPLLRead(OPLL, a) & 0x03 ;
+}
+
+void ym2413_set_update_handler(void *chip,OPLL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ YM2413 *OPLL = (YM2413 *)chip;
+ OPLLSetUpdateHandler(OPLL, UpdateHandler, param);
+}
+
+
+/*
+** Generate samples for one of the YM2413's
+**
+** 'which' is the virtual YM2413 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym2413_update_one(void *_chip, SAMP **buffers, int length)
+{
+ YM2413 *chip = (YM2413 *)_chip;
+ UINT8 rhythm = chip->rhythm&0x20;
+ SAMP *bufMO = buffers[0];
+ SAMP *bufRO = buffers[1];
+
+ int i;
+
+ for( i=0; i < length ; i++ )
+ {
+ int mo,ro;
+
+ chip->output[0] = 0;
+ chip->output[1] = 0;
+
+ advance_lfo(chip);
+
+ /* FM part */
+ chan_calc(chip, &chip->P_CH[0]);
+//SAVE_SEPARATE_CHANNEL(0);
+ chan_calc(chip, &chip->P_CH[1]);
+ chan_calc(chip, &chip->P_CH[2]);
+ chan_calc(chip, &chip->P_CH[3]);
+ chan_calc(chip, &chip->P_CH[4]);
+ chan_calc(chip, &chip->P_CH[5]);
+
+ if(!rhythm)
+ {
+ chan_calc(chip, &chip->P_CH[6]);
+ chan_calc(chip, &chip->P_CH[7]);
+ chan_calc(chip, &chip->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ rhythm_calc(chip, &chip->P_CH[0], (chip->noise_rng>>0)&1 );
+ }
+
+ mo = chip->output[0];
+ ro = chip->output[1];
+
+ mo >>= FINAL_SH;
+ ro >>= FINAL_SH;
+
+ /* limit check */
+ mo = limit( mo , MAXOUT, MINOUT );
+ ro = limit( ro , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ bufMO[i] = mo;
+ bufRO[i] = ro;
+
+ advance(chip);
+ }
+
+}
diff --git a/src/devices/sound/ym2413.h b/src/devices/sound/ym2413.h
new file mode 100644
index 00000000000..eb9441bec5a
--- /dev/null
+++ b/src/devices/sound/ym2413.h
@@ -0,0 +1,45 @@
+// license:???
+// copyright-holders:Jarek Burczynski
+#pragma once
+
+#ifndef __YM2413_H__
+#define __YM2413_H__
+
+/* select output bits size of output : 8 or 16 */
+#define SAMPLE_BITS 16
+
+/* compiler dependence */
+#ifndef __OSDCOMM_H__
+#define __OSDCOMM_H__
+typedef unsigned char UINT8; /* unsigned 8bit */
+typedef unsigned short UINT16; /* unsigned 16bit */
+typedef unsigned int UINT32; /* unsigned 32bit */
+typedef signed char INT8; /* signed 8bit */
+typedef signed short INT16; /* signed 16bit */
+typedef signed int INT32; /* signed 32bit */
+#endif
+
+typedef stream_sample_t SAMP;
+/*
+#if (SAMPLE_BITS==16)
+typedef INT16 SAMP;
+#endif
+#if (SAMPLE_BITS==8)
+typedef INT8 SAMP;
+#endif
+*/
+
+
+
+void *ym2413_init(device_t *device, int clock, int rate);
+void ym2413_shutdown(void *chip);
+void ym2413_reset_chip(void *chip);
+void ym2413_write(void *chip, int a, int v);
+unsigned char ym2413_read(void *chip, int a);
+void ym2413_update_one(void *chip, SAMP **buffers, int length);
+
+typedef void (*OPLL_UPDATEHANDLER)(void *param,int min_interval_us);
+
+void ym2413_set_update_handler(void *chip, OPLL_UPDATEHANDLER UpdateHandler, void *param);
+
+#endif /*__YM2413_H__*/
diff --git a/src/devices/sound/ymdeltat.c b/src/devices/sound/ymdeltat.c
new file mode 100644
index 00000000000..30600b2d02b
--- /dev/null
+++ b/src/devices/sound/ymdeltat.c
@@ -0,0 +1,650 @@
+// license:???
+// 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_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 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 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(YM_DELTAT *DELTAT)
+{
+ logerror("BRDY_callback reached (flag set) !\n");
+
+ /* set BRDY bit in status register */
+ 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);
+}
+#endif
+
+UINT8 YM_DELTAT_ADPCM_Read(YM_DELTAT *DELTAT)
+{
+ UINT8 v = 0;
+
+ /* external memory read */
+ if ( (DELTAT->portstate & 0xe0)==0x20 )
+ {
+ /* two dummy reads */
+ if (DELTAT->memread)
+ {
+ DELTAT->now_addr = DELTAT->start << 1;
+ DELTAT->memread--;
+ return 0;
+ }
+
+
+ if ( DELTAT->now_addr != (DELTAT->end<<1) )
+ {
+ v = DELTAT->memory[DELTAT->now_addr>>1];
+
+ /*logerror("YM Delta-T memory read $%08x, v=$%02x\n", DELTAT->now_addr >> 1, v);*/
+
+ DELTAT->now_addr+=2; /* two nibbles at a time */
+
+ /* reset BRDY bit in status register, which means we are reading the memory now */
+ if(DELTAT->status_reset_handler)
+ if(DELTAT->status_change_BRDY_bit)
+ (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->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(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
+ {
+ /* 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);
+ }
+ }
+
+ return v;
+}
+
+
+/* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */
+static const UINT8 dram_rightshift[4]={3,0,0,0};
+
+/* DELTA-T ADPCM write register */
+void YM_DELTAT_ADPCM_Write(YM_DELTAT *DELTAT,int r,int v)
+{
+ if(r>=0x10) return;
+ DELTAT->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(DELTAT->emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610)
+ {
+ v |= 0x20; /* YM2610 always uses external memory and doesn't even have memory flag bit. */
+ }
+
+ DELTAT->portstate = v & (0x80|0x40|0x20|0x10|0x01); /* start, rec, memory mode, repeat flag copy, reset(bit0) */
+
+ if( DELTAT->portstate&0x80 )/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */
+ {
+ /* set PCM BUSY bit */
+ DELTAT->PCM_BSY = 1;
+
+ /* start ADPCM */
+ DELTAT->now_step = 0;
+ DELTAT->acc = 0;
+ DELTAT->prev_acc = 0;
+ DELTAT->adpcml = 0;
+ DELTAT->adpcmd = YM_DELTAT_DELTA_DEF;
+ DELTAT->now_data = 0;
+
+ }
+
+ if( DELTAT->portstate&0x20 ) /* do we access external memory? */
+ {
+ DELTAT->now_addr = DELTAT->start << 1;
+ DELTAT->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(DELTAT->memory == 0)
+ {
+ logerror("YM Delta-T ADPCM rom not mapped\n");
+ DELTAT->portstate = 0x00;
+ DELTAT->PCM_BSY = 0;
+ }
+ else
+ {
+ if( DELTAT->end >= DELTAT->memory_size ) /* Check End in Range */
+ {
+ logerror("YM Delta-T ADPCM end out of range: $%08x\n", DELTAT->end);
+ DELTAT->end = DELTAT->memory_size - 1;
+ }
+ if( DELTAT->start >= DELTAT->memory_size ) /* Check Start in Range */
+ {
+ logerror("YM Delta-T ADPCM start out of range: $%08x\n", DELTAT->start);
+ DELTAT->portstate = 0x00;
+ DELTAT->PCM_BSY = 0;
+ }
+ }
+ }
+ else /* we access CPU memory (ADPCM data register $08) so we only reset now_addr here */
+ {
+ DELTAT->now_addr = 0;
+ }
+
+ if( DELTAT->portstate&0x01 )
+ {
+ DELTAT->portstate = 0x00;
+
+ /* clear PCM BUSY bit (in status register) */
+ DELTAT->PCM_BSY = 0;
+
+ /* set BRDY flag */
+ 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);
+ }
+ break;
+ case 0x01: /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */
+ /* handle emulation mode */
+ if(DELTAT->emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610)
+ {
+ v |= 0x01; /* YM2610 always uses ROM as an external memory and doesn't tave ROM/RAM memory flag bit. */
+ }
+
+ DELTAT->pan = &DELTAT->output_pointer[(v>>6)&0x03];
+ if ((DELTAT->control2 & 3) != (v & 3))
+ {
+ /*0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */
+ if (DELTAT->DRAMportshift != dram_rightshift[v&3])
+ {
+ DELTAT->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 */
+ DELTAT->start = (DELTAT->reg[0x3]*0x0100 | DELTAT->reg[0x2]) << (DELTAT->portshift - DELTAT->DRAMportshift);
+ DELTAT->end = (DELTAT->reg[0x5]*0x0100 | DELTAT->reg[0x4]) << (DELTAT->portshift - DELTAT->DRAMportshift);
+ DELTAT->end += (1 << (DELTAT->portshift-DELTAT->DRAMportshift) ) - 1;
+ DELTAT->limit = (DELTAT->reg[0xd]*0x0100 | DELTAT->reg[0xc]) << (DELTAT->portshift - DELTAT->DRAMportshift);
+ }
+ }
+ DELTAT->control2 = v;
+ break;
+ case 0x02: /* Start Address L */
+ case 0x03: /* Start Address H */
+ DELTAT->start = (DELTAT->reg[0x3]*0x0100 | DELTAT->reg[0x2]) << (DELTAT->portshift - DELTAT->DRAMportshift);
+ /*logerror("DELTAT start: 02=%2x 03=%2x addr=%8x\n",DELTAT->reg[0x2], DELTAT->reg[0x3],DELTAT->start );*/
+ break;
+ case 0x04: /* Stop Address L */
+ case 0x05: /* Stop Address H */
+ DELTAT->end = (DELTAT->reg[0x5]*0x0100 | DELTAT->reg[0x4]) << (DELTAT->portshift - DELTAT->DRAMportshift);
+ DELTAT->end += (1 << (DELTAT->portshift-DELTAT->DRAMportshift) ) - 1;
+ /*logerror("DELTAT end : 04=%2x 05=%2x addr=%8x\n",DELTAT->reg[0x4], DELTAT->reg[0x5],DELTAT->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 ( (DELTAT->portstate & 0xe0)==0x60 )
+ {
+ if (DELTAT->memread)
+ {
+ DELTAT->now_addr = DELTAT->start << 1;
+ DELTAT->memread = 0;
+ }
+
+ /*logerror("YM Delta-T memory write $%08x, v=$%02x\n", DELTAT->now_addr >> 1, v);*/
+
+ if ( DELTAT->now_addr != (DELTAT->end<<1) )
+ {
+ DELTAT->memory[DELTAT->now_addr>>1] = v;
+ DELTAT->now_addr+=2; /* two nibbles at a time */
+
+ /* reset BRDY bit in status register, which means we are processing the write */
+ if(DELTAT->status_reset_handler)
+ if(DELTAT->status_change_BRDY_bit)
+ (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->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(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
+ {
+ /* 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);
+ }
+
+ return;
+ }
+
+ /* ADPCM synthesis from CPU */
+ if ( (DELTAT->portstate & 0xe0)==0x80 )
+ {
+ DELTAT->CPU_data = v;
+
+ /* Reset BRDY bit in status register, which means we are full of data */
+ if(DELTAT->status_reset_handler)
+ if(DELTAT->status_change_BRDY_bit)
+ (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);
+ return;
+ }
+
+ break;
+ case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */
+ case 0x0a: /* DELTA-N H */
+ DELTAT->delta = (DELTAT->reg[0xa]*0x0100 | DELTAT->reg[0x9]);
+ DELTAT->step = (UINT32)( (double)(DELTAT->delta /* *(1<<(YM_DELTAT_SHIFT-16)) */ ) * (DELTAT->freqbase) );
+ /*logerror("DELTAT deltan:09=%2x 0a=%2x\n",DELTAT->reg[0x9], DELTAT->reg[0xa]);*/
+ break;
+ case 0x0b: /* Output level control (volume, linear) */
+ {
+ INT32 oldvol = DELTAT->volume;
+ DELTAT->volume = (v&0xff) * (DELTAT->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 )
+ {
+ DELTAT->adpcml = (int)((double)DELTAT->adpcml / (double)oldvol * (double)DELTAT->volume);
+ }
+ }
+ break;
+ case 0x0c: /* Limit Address L */
+ case 0x0d: /* Limit Address H */
+ DELTAT->limit = (DELTAT->reg[0xd]*0x0100 | DELTAT->reg[0xc]) << (DELTAT->portshift - DELTAT->DRAMportshift);
+ /*logerror("DELTAT limit: 0c=%2x 0d=%2x addr=%8x\n",DELTAT->reg[0xc], DELTAT->reg[0xd],DELTAT->limit );*/
+ break;
+ }
+}
+
+void YM_DELTAT_ADPCM_Reset(YM_DELTAT *DELTAT,int pan,int emulation_mode)
+{
+ DELTAT->now_addr = 0;
+ DELTAT->now_step = 0;
+ DELTAT->step = 0;
+ DELTAT->start = 0;
+ DELTAT->end = 0;
+ DELTAT->limit = ~0; /* this way YM2610 and Y8950 (both of which don't have limit address reg) will still work */
+ DELTAT->volume = 0;
+ DELTAT->pan = &DELTAT->output_pointer[pan];
+ DELTAT->acc = 0;
+ DELTAT->prev_acc = 0;
+ DELTAT->adpcmd = 127;
+ DELTAT->adpcml = 0;
+ DELTAT->emulation_mode = (UINT8)emulation_mode;
+ DELTAT->portstate = (emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) ? 0x20 : 0;
+ DELTAT->control2 = (emulation_mode == YM_DELTAT_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 */
+ DELTAT->DRAMportshift = dram_rightshift[DELTAT->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(DELTAT->status_set_handler)
+ if(DELTAT->status_change_BRDY_bit)
+ (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);
+}
+
+void YM_DELTAT_postload(YM_DELTAT *DELTAT,UINT8 *regs)
+{
+ int r;
+
+ /* to keep adpcml */
+ DELTAT->volume = 0;
+ /* update */
+ for(r=1;r<16;r++)
+ YM_DELTAT_ADPCM_Write(DELTAT,r,regs[r]);
+ DELTAT->reg[0] = regs[0];
+
+ /* current rom data */
+ if (DELTAT->memory)
+ DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1) );
+
+}
+void YM_DELTAT_savestate(device_t *device,YM_DELTAT *DELTAT)
+{
+#ifdef __SAVE_H__
+ 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; \
+}
+
+INLINE void YM_DELTAT_synthesis_from_external_memory(YM_DELTAT *DELTAT)
+{
+ UINT32 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;
+}
+
+
+
+INLINE void YM_DELTAT_synthesis_from_CPU_memory(YM_DELTAT *DELTAT)
+{
+ UINT32 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(YM_DELTAT *DELTAT)
+{
+/*
+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 ( (DELTAT->portstate & 0xe0)==0xa0 )
+ {
+ YM_DELTAT_synthesis_from_external_memory(DELTAT);
+ return;
+ }
+
+ if ( (DELTAT->portstate & 0xe0)==0x80 )
+ {
+ /* ADPCM synthesis from CPU-managed memory (from reg $08) */
+ YM_DELTAT_synthesis_from_CPU_memory(DELTAT); /* change output based on data in ADPCM data reg ($08) */
+ return;
+ }
+
+//todo: ADPCM analysis
+// if ( (DELTAT->portstate & 0xe0)==0xc0 )
+// if ( (DELTAT->portstate & 0xe0)==0xe0 )
+
+ return;
+}
diff --git a/src/devices/sound/ymdeltat.h b/src/devices/sound/ymdeltat.h
new file mode 100644
index 00000000000..28b5d535b32
--- /dev/null
+++ b/src/devices/sound/ymdeltat.h
@@ -0,0 +1,86 @@
+// license:???
+// copyright-holders:Jarek Burczynski
+#pragma once
+
+#ifndef __YMDELTAT_H__
+#define __YMDELTAT_H__
+
+#define YM_DELTAT_SHIFT (16)
+
+#define YM_DELTAT_EMULATION_MODE_NORMAL 0
+#define YM_DELTAT_EMULATION_MODE_YM2610 1
+
+
+typedef void (*STATUS_CHANGE_HANDLER)(void *chip, UINT8 status_bits);
+
+
+/* DELTA-T (adpcm type B) struct */
+struct YM_DELTAT { /* AT: rearranged and tigntened structure */
+ UINT8 *memory;
+ INT32 *output_pointer;/* pointer of output pointers */
+ INT32 *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 memory_size;
+ int output_range;
+ UINT32 now_addr; /* current address */
+ UINT32 now_step; /* currect step */
+ UINT32 step; /* step */
+ UINT32 start; /* start address */
+ UINT32 limit; /* limit address */
+ UINT32 end; /* end address */
+ UINT32 delta; /* delta scale */
+ INT32 volume; /* current volume */
+ INT32 acc; /* shift Measurement value*/
+ INT32 adpcmd; /* next Forecast */
+ INT32 adpcml; /* current value */
+ INT32 prev_acc; /* leveling value */
+ UINT8 now_data; /* current rom data */
+ UINT8 CPU_data; /* current data from reg 08 */
+ UINT8 portstate; /* port status */
+ UINT8 control2; /* control reg: SAMPLE, DA/AD, RAM TYPE (x8bit / x1bit), ROM/RAM */
+ UINT8 portshift; /* address bits shift-left:
+ ** 8 for YM2610,
+ ** 5 for Y8950 and YM2608 */
+
+ UINT8 DRAMportshift; /* address bits shift-right:
+ ** 0 for ROM and x8bit DRAMs,
+ ** 3 for x1 DRAMs */
+
+ UINT8 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 status_change_EOS_bit; /* 1 on End Of Sample (record/playback/cycle time of AD/DA converting has passed)*/
+ UINT8 status_change_BRDY_bit; /* 1 after recording 2 datas (2x4bits) or after reading/writing 1 data */
+ UINT8 status_change_ZERO_bit; /* 1 if silence lasts for more than 290 miliseconds 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 PCM_BSY; /* 1 when ADPCM is playing; Y8950/YM2608 only */
+
+ UINT8 reg[16]; /* adpcm registers */
+ UINT8 emulation_mode; /* which chip we're emulating */
+};
+
+/*void YM_DELTAT_BRDY_callback(YM_DELTAT *DELTAT);*/
+
+UINT8 YM_DELTAT_ADPCM_Read(YM_DELTAT *DELTAT);
+void YM_DELTAT_ADPCM_Write(YM_DELTAT *DELTAT,int r,int v);
+void YM_DELTAT_ADPCM_Reset(YM_DELTAT *DELTAT,int pan,int emulation_mode);
+void YM_DELTAT_ADPCM_CALC(YM_DELTAT *DELTAT);
+
+void YM_DELTAT_postload(YM_DELTAT *DELTAT,UINT8 *regs);
+void YM_DELTAT_savestate(device_t *device,YM_DELTAT *DELTAT);
+
+#endif /* __YMDELTAT_H__ */
diff --git a/src/devices/sound/ymf262.c b/src/devices/sound/ymf262.c
new file mode 100644
index 00000000000..0982ae266e8
--- /dev/null
+++ b/src/devices/sound/ymf262.c
@@ -0,0 +1,2715 @@
+// license:???
+// copyright-holders:Jarek Burczynski
+/*
+**
+** File: ymf262.c - software implementation of YMF262
+** FM sound generator type OPL3
+**
+** Copyright Jarek Burczynski
+**
+** Version 0.2
+**
+
+Revision History:
+
+03-03-2003: initial release
+ - thanks to Olivier Galibert and Chris Hardy for YMF262 and YAC512 chips
+ - thanks to Stiletto for the datasheets
+
+ Features as listed in 4MF262A6 data sheet:
+ 1. Registers are compatible with YM3812 (OPL2) FM sound source.
+ 2. Up to six sounds can be used as four-operator melody sounds for variety.
+ 3. 18 simultaneous melody sounds, or 15 melody sounds with 5 rhythm sounds (with two operators).
+ 4. 6 four-operator melody sounds and 6 two-operator melody sounds, or 6 four-operator melody
+ sounds, 3 two-operator melody sounds and 5 rhythm sounds (with four operators).
+ 5. 8 selectable waveforms.
+ 6. 4-channel sound output.
+ 7. YMF262 compabile DAC (YAC512) is available.
+ 8. LFO for vibrato and tremolo effedts.
+ 9. 2 programable timers.
+ 10. Shorter register access time compared with YM3812.
+ 11. 5V single supply silicon gate CMOS process.
+ 12. 24 Pin SOP Package (YMF262-M), 48 Pin SQFP Package (YMF262-S).
+
+
+differences between OPL2 and OPL3 not documented in Yamaha datahasheets:
+- sinus table is a little different: the negative part is off by one...
+
+- in order to enable selection of four different waveforms on OPL2
+ one must set bit 5 in register 0x01(test).
+ on OPL3 this bit is ignored and 4-waveform select works *always*.
+ (Don't confuse this with OPL3's 8-waveform select.)
+
+- Envelope Generator: all 15 x rates take zero time on OPL3
+ (on OPL2 15 0 and 15 1 rates take some time while 15 2 and 15 3 rates
+ take zero time)
+
+- channel calculations: output of operator 1 is in perfect sync with
+ output of operator 2 on OPL3; on OPL and OPL2 output of operator 1
+ is always delayed by one sample compared to output of operator 2
+
+
+differences between OPL2 and OPL3 shown in datasheets:
+- YMF262 does not support CSM mode
+
+
+*/
+
+#include "emu.h"
+#include "ymf262.h"
+
+
+
+/* output final shift */
+#if (OPL3_SAMPLE_BITS==16)
+ #define FINAL_SH (0)
+ #define MAXOUT (+32767)
+ #define MINOUT (-32768)
+#else
+ #define FINAL_SH (8)
+ #define MAXOUT (+127)
+ #define MINOUT (-128)
+#endif
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (EG timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<<FREQ_SH)-1)
+
+/* envelope output entries */
+#define ENV_BITS 10
+#define ENV_LEN (1<<ENV_BITS)
+#define ENV_STEP (128.0/ENV_LEN)
+
+#define MAX_ATT_INDEX ((1<<(ENV_BITS-1))-1) /*511*/
+#define MIN_ATT_INDEX (0)
+
+/* sinwave entries */
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
+
+
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* Envelope Generator phases */
+
+#define EG_ATT 4
+#define EG_DEC 3
+#define EG_SUS 2
+#define EG_REL 1
+#define EG_OFF 0
+
+
+/* save output as raw 16-bit sample */
+
+/*#define SAVE_SAMPLE*/
+
+#ifdef SAVE_SAMPLE
+static FILE *sample[1];
+ #if 1 /*save to MONO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = a; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #else /*save to STEREO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = a; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ pom = b; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #endif
+#endif
+
+#define LOG_CYM_FILE 0
+static FILE * cymfile = NULL;
+
+
+
+
+
+#define OPL3_TYPE_YMF262 (0) /* 36 operators, 8 waveforms */
+
+
+struct OPL3_SLOT
+{
+ UINT32 ar; /* attack rate: AR<<2 */
+ UINT32 dr; /* decay rate: DR<<2 */
+ UINT32 rr; /* release rate:RR<<2 */
+ UINT8 KSR; /* key scale rate */
+ UINT8 ksl; /* keyscale level */
+ UINT8 ksr; /* key scale rate: kcode>>KSR */
+ UINT8 mul; /* multiple: mul_tab[ML] */
+
+ /* Phase Generator */
+ UINT32 Cnt; /* frequency counter */
+ UINT32 Incr; /* frequency counter step */
+ UINT8 FB; /* feedback shift value */
+ INT32 *connect; /* slot output pointer */
+ INT32 op1_out[2]; /* slot1 output for feedback */
+ UINT8 CON; /* connection (algorithm) type */
+
+ /* Envelope Generator */
+ UINT8 eg_type; /* percussive/non-percussive mode */
+ UINT8 state; /* phase type */
+ UINT32 TL; /* total level: TL << 2 */
+ INT32 TLL; /* adjusted now TL */
+ INT32 volume; /* envelope counter */
+ UINT32 sl; /* sustain level: sl_tab[SL] */
+
+ UINT32 eg_m_ar; /* (attack state) */
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT32 eg_m_dr; /* (decay state) */
+ UINT8 eg_sh_dr; /* (decay state) */
+ UINT8 eg_sel_dr; /* (decay state) */
+ UINT32 eg_m_rr; /* (release state) */
+ UINT8 eg_sh_rr; /* (release state) */
+ UINT8 eg_sel_rr; /* (release state) */
+
+ UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */
+
+ /* LFO */
+ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */
+ UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/
+
+ /* waveform select */
+ UINT8 waveform_number;
+ unsigned int wavetable;
+
+//unsigned char reserved[128-84];//speedup: pump up the struct size to power of 2
+unsigned char reserved[128-100];//speedup: pump up the struct size to power of 2
+
+};
+
+struct OPL3_CH
+{
+ OPL3_SLOT SLOT[2];
+
+ UINT32 block_fnum; /* block+fnum */
+ UINT32 fc; /* Freq. Increment base */
+ UINT32 ksl_base; /* KeyScaleLevel Base step */
+ UINT8 kcode; /* key code (for key scaling) */
+
+ /*
+ there are 12 2-operator channels which can be combined in pairs
+ to form six 4-operator channel, they are:
+ 0 and 3,
+ 1 and 4,
+ 2 and 5,
+ 9 and 12,
+ 10 and 13,
+ 11 and 14
+ */
+ UINT8 extended; /* set to 1 if this channel forms up a 4op channel with another channel(only used by first of pair of channels, ie 0,1,2 and 9,10,11) */
+
+unsigned char reserved[512-272];//speedup:pump up the struct size to power of 2
+
+};
+
+/* OPL3 state */
+struct OPL3
+{
+ OPL3_CH P_CH[18]; /* OPL3 chips have 18 channels */
+
+ UINT32 pan[18*4]; /* channels output masks (0xffffffff = enable); 4 masks per one channel */
+ UINT32 pan_ctrl_value[18]; /* output control values 1 per one channel (1 value contains 4 masks) */
+
+ signed int chanout[18];
+ signed int phase_modulation; /* phase modulation input (SLOT 2) */
+ signed int phase_modulation2; /* phase modulation input (SLOT 3 in 4 operator channels) */
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/288 (288=8*36) */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */
+
+ UINT32 fn_tab[1024]; /* fnumber->increment counter */
+
+ /* LFO */
+ UINT32 LFO_AM;
+ INT32 LFO_PM;
+
+ UINT8 lfo_am_depth;
+ UINT8 lfo_pm_depth_range;
+ UINT32 lfo_am_cnt;
+ UINT32 lfo_am_inc;
+ UINT32 lfo_pm_cnt;
+ UINT32 lfo_pm_inc;
+
+ UINT32 noise_rng; /* 23 bit noise shift register */
+ UINT32 noise_p; /* current noise 'phase' */
+ UINT32 noise_f; /* current noise period */
+
+ UINT8 OPL3_mode; /* OPL3 extension enable flag */
+
+ UINT8 rhythm; /* Rhythm mode */
+
+ int T[2]; /* timer counters */
+ UINT8 st[2]; /* timer enable */
+
+ UINT32 address; /* address register */
+ UINT8 status; /* status flag */
+ UINT8 statusmask; /* status mask */
+
+ UINT8 nts; /* NTS (note select) */
+
+ /* external event callback handlers */
+ OPL3_TIMERHANDLER timer_handler;/* TIMER handler */
+ void *TimerParam; /* TIMER parameter */
+ OPL3_IRQHANDLER IRQHandler; /* IRQ handler */
+ void *IRQParam; /* IRQ parameter */
+ OPL3_UPDATEHANDLER UpdateHandler;/* stream update handler */
+ void *UpdateParam; /* stream update parameter */
+
+ UINT8 type; /* chip type */
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ attotime TimerBase; /* Timer base time (==sampling time)*/
+ device_t *device;
+};
+
+
+
+/* mapping of register number (offset) to slot number used by the emulator */
+static const int slot_array[32]=
+{
+ 0, 2, 4, 1, 3, 5,-1,-1,
+ 6, 8,10, 7, 9,11,-1,-1,
+ 12,14,16,13,15,17,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1
+};
+
+/* key scale level */
+/* table is 3dB/octave , DV converts this into 6dB/octave */
+/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */
+#define DV (0.1875/2.0)
+static const UINT32 ksl_tab[8*16]=
+{
+ /* OCT 0 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ /* OCT 1 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
+ 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
+ /* OCT 2 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
+ 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
+ 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
+ /* OCT 3 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
+ 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
+ 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
+ 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
+ /* OCT 4 */
+ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
+ 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
+ 9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
+ 10.875/DV,11.250/DV,11.625/DV,12.000/DV,
+ /* OCT 5 */
+ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
+ 9.000/DV,10.125/DV,10.875/DV,11.625/DV,
+ 12.000/DV,12.750/DV,13.125/DV,13.500/DV,
+ 13.875/DV,14.250/DV,14.625/DV,15.000/DV,
+ /* OCT 6 */
+ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
+ 12.000/DV,13.125/DV,13.875/DV,14.625/DV,
+ 15.000/DV,15.750/DV,16.125/DV,16.500/DV,
+ 16.875/DV,17.250/DV,17.625/DV,18.000/DV,
+ /* OCT 7 */
+ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
+ 15.000/DV,16.125/DV,16.875/DV,17.625/DV,
+ 18.000/DV,18.750/DV,19.125/DV,19.500/DV,
+ 19.875/DV,20.250/DV,20.625/DV,21.000/DV
+};
+#undef DV
+
+/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */
+static const UINT32 ksl_shift[4] = { 31, 1, 2, 0 };
+
+
+/* sustain level table (3dB per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) (UINT32) ( db * (2.0/ENV_STEP) )
+static const UINT32 sl_tab[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+
+#define RATE_STEPS (8)
+static const unsigned char eg_inc[15*RATE_STEPS]={
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 for decay */
+/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 for attack (zero time) */
+/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/* note that there is no O(13) in this table - it's directly in the code */
+static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+
+/* rates 00-12 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 13 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 14 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 15 */
+O(12),O(12),O(12),O(12),
+
+/* 16 dummy rates (same as 15 3) */
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */
+/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */
+/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+/* rates 00-12 */
+O(12),O(12),O(12),O(12),
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 16 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+
+};
+#undef O
+
+
+/* multiple table */
+#define ML 2
+static const UINT8 mul_tab[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */
+ ML/2, 1*ML, 2*ML, 3*ML, 4*ML, 5*ML, 6*ML, 7*ML,
+ 8*ML, 9*ML,10*ML,10*ML,12*ML,12*ML,15*ML,15*ML
+};
+#undef ML
+
+/* TL_TAB_LEN is calculated as:
+
+* (12+1)=13 - sinus amplitude bits (Y axis)
+* additional 1: to compensate for calculations of negative part of waveform
+* (if we don't add it then the greatest possible _negative_ value would be -2
+* and we really need -1 for waveform #7)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (13*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+#define ENV_QUIET (TL_TAB_LEN>>4)
+
+/* sin waveform table in 'decibel' scale */
+/* there are eight waveforms on OPL3 chips */
+static unsigned int sin_tab[SIN_LEN * 8];
+
+
+/* LFO Amplitude Modulation table (verified on real YM3812)
+ 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples
+
+ Length: 210 elements.
+
+ Each of the elements has to be repeated
+ exactly 64 times (on 64 consecutive samples).
+ The whole table takes: 64 * 210 = 13440 samples.
+
+ When AM = 1 data is used directly
+ When AM = 0 data is divided by 4 before being used (losing precision is important)
+*/
+
+#define LFO_AM_TAB_ELEMENTS 210
+
+static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = {
+0,0,0,0,0,0,0,
+1,1,1,1,
+2,2,2,2,
+3,3,3,3,
+4,4,4,4,
+5,5,5,5,
+6,6,6,6,
+7,7,7,7,
+8,8,8,8,
+9,9,9,9,
+10,10,10,10,
+11,11,11,11,
+12,12,12,12,
+13,13,13,13,
+14,14,14,14,
+15,15,15,15,
+16,16,16,16,
+17,17,17,17,
+18,18,18,18,
+19,19,19,19,
+20,20,20,20,
+21,21,21,21,
+22,22,22,22,
+23,23,23,23,
+24,24,24,24,
+25,25,25,25,
+26,26,26,
+25,25,25,25,
+24,24,24,24,
+23,23,23,23,
+22,22,22,22,
+21,21,21,21,
+20,20,20,20,
+19,19,19,19,
+18,18,18,18,
+17,17,17,17,
+16,16,16,16,
+15,15,15,15,
+14,14,14,14,
+13,13,13,13,
+12,12,12,12,
+11,11,11,11,
+10,10,10,10,
+9,9,9,9,
+8,8,8,8,
+7,7,7,7,
+6,6,6,6,
+5,5,5,5,
+4,4,4,4,
+3,3,3,3,
+2,2,2,2,
+1,1,1,1
+};
+
+/* LFO Phase Modulation table (verified on real YM3812) */
+static const INT8 lfo_pm_table[8*8*2] = {
+/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/
+};
+
+
+/* lock level of common table */
+static int num_lock = 0;
+
+/* work table */
+#define SLOT7_1 (&chip->P_CH[7].SLOT[SLOT1])
+#define SLOT7_2 (&chip->P_CH[7].SLOT[SLOT2])
+#define SLOT8_1 (&chip->P_CH[8].SLOT[SLOT1])
+#define SLOT8_2 (&chip->P_CH[8].SLOT[SLOT2])
+
+
+
+
+
+INLINE int limit( int val, int max, int min ) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+
+/* status set and IRQ handling */
+INLINE void OPL3_STATUS_SET(OPL3 *chip,int flag)
+{
+ /* set status flag masking out disabled IRQs */
+ chip->status |= (flag & chip->statusmask);
+ if(!(chip->status & 0x80))
+ {
+ if(chip->status & 0x7f)
+ { /* IRQ on */
+ chip->status |= 0x80;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(chip->IRQHandler) (chip->IRQHandler)(chip->IRQParam,1);
+ }
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void OPL3_STATUS_RESET(OPL3 *chip,int flag)
+{
+ /* reset status flag */
+ chip->status &= ~flag;
+ if(chip->status & 0x80)
+ {
+ if (!(chip->status & 0x7f))
+ {
+ chip->status &= 0x7f;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(chip->IRQHandler) (chip->IRQHandler)(chip->IRQParam,0);
+ }
+ }
+}
+
+/* IRQ mask set */
+INLINE void OPL3_STATUSMASK_SET(OPL3 *chip,int flag)
+{
+ chip->statusmask = flag;
+ /* IRQ handling check */
+ OPL3_STATUS_SET(chip,0);
+ OPL3_STATUS_RESET(chip,0);
+}
+
+
+/* advance LFO to next sample */
+INLINE void advance_lfo(OPL3 *chip)
+{
+ UINT8 tmp;
+
+ /* LFO */
+ chip->lfo_am_cnt += chip->lfo_am_inc;
+ if (chip->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH) ) /* lfo_am_table is 210 elements long */
+ chip->lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH);
+
+ tmp = lfo_am_table[ chip->lfo_am_cnt >> LFO_SH ];
+
+ if (chip->lfo_am_depth)
+ chip->LFO_AM = tmp;
+ else
+ chip->LFO_AM = tmp>>2;
+
+ chip->lfo_pm_cnt += chip->lfo_pm_inc;
+ chip->LFO_PM = ((chip->lfo_pm_cnt>>LFO_SH) & 7) | chip->lfo_pm_depth_range;
+}
+
+/* advance to next sample */
+INLINE void advance(OPL3 *chip)
+{
+ OPL3_CH *CH;
+ OPL3_SLOT *op;
+ int i;
+
+ chip->eg_timer += chip->eg_timer_add;
+
+ while (chip->eg_timer >= chip->eg_timer_overflow)
+ {
+ chip->eg_timer -= chip->eg_timer_overflow;
+
+ chip->eg_cnt++;
+
+ for (i=0; i<9*2*2; i++)
+ {
+ CH = &chip->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+#if 1
+ /* Envelope Generator */
+ switch(op->state)
+ {
+ case EG_ATT: /* attack phase */
+// if ( !(chip->eg_cnt & ((1<<op->eg_sh_ar)-1) ) )
+ if ( !(chip->eg_cnt & op->eg_m_ar) )
+ {
+ op->volume += (~op->volume *
+ (eg_inc[op->eg_sel_ar + ((chip->eg_cnt>>op->eg_sh_ar)&7)])
+ ) >>3;
+
+ if (op->volume <= MIN_ATT_INDEX)
+ {
+ op->volume = MIN_ATT_INDEX;
+ op->state = EG_DEC;
+ }
+
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+// if ( !(chip->eg_cnt & ((1<<op->eg_sh_dr)-1) ) )
+ if ( !(chip->eg_cnt & op->eg_m_dr) )
+ {
+ op->volume += eg_inc[op->eg_sel_dr + ((chip->eg_cnt>>op->eg_sh_dr)&7)];
+
+ if ( op->volume >= op->sl )
+ op->state = EG_SUS;
+
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+
+ /* this is important behaviour:
+ one can change percusive/non-percussive modes on the fly and
+ the chip will remain in sustain phase - verified on real YM3812 */
+
+ if(op->eg_type) /* non-percussive mode */
+ {
+ /* do nothing */
+ }
+ else /* percussive mode */
+ {
+ /* during sustain phase chip adds Release Rate (in percussive mode) */
+// if ( !(chip->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ if ( !(chip->eg_cnt & op->eg_m_rr) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ op->volume = MAX_ATT_INDEX;
+ }
+ /* else do nothing in sustain phase */
+ }
+ break;
+
+ case EG_REL: /* release phase */
+// if ( !(chip->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ if ( !(chip->eg_cnt & op->eg_m_rr) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+
+ }
+ break;
+
+ default:
+ break;
+ }
+#endif
+ }
+ }
+
+ for (i=0; i<9*2*2; i++)
+ {
+ CH = &chip->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Phase Generator */
+ if(op->vib)
+ {
+ UINT8 block;
+ unsigned int block_fnum = CH->block_fnum;
+
+ unsigned int fnum_lfo = (block_fnum&0x0380) >> 7;
+
+ signed int lfo_fn_table_index_offset = lfo_pm_table[chip->LFO_PM + 16*fnum_lfo ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ block_fnum += lfo_fn_table_index_offset;
+ block = (block_fnum&0x1c00) >> 10;
+ op->Cnt += (chip->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ op->Cnt += op->Incr;
+ }
+ }
+ else /* LFO phase modulation disabled for this operator */
+ {
+ op->Cnt += op->Incr;
+ }
+ }
+
+ /* The Noise Generator of the YM3812 is 23-bit shift register.
+ * Period is equal to 2^23-2 samples.
+ * Register works at sampling frequency of the chip, so output
+ * can change on every sample.
+ *
+ * Output of the register and input to the bit 22 is:
+ * bit0 XOR bit14 XOR bit15 XOR bit22
+ *
+ * Simply use bit 22 as the noise output.
+ */
+
+ chip->noise_p += chip->noise_f;
+ i = chip->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */
+ chip->noise_p &= FREQ_MASK;
+ while (i)
+ {
+ /*
+ UINT32 j;
+ j = ( (chip->noise_rng) ^ (chip->noise_rng>>14) ^ (chip->noise_rng>>15) ^ (chip->noise_rng>>22) ) & 1;
+ chip->noise_rng = (j<<22) | (chip->noise_rng>>1);
+ */
+
+ /*
+ Instead of doing all the logic operations above, we
+ use a trick here (and use bit 0 as the noise output).
+ The difference is only that the noise bit changes one
+ step ahead. This doesn't matter since we don't know
+ what is real state of the noise_rng after the reset.
+ */
+
+ if (chip->noise_rng & 1) chip->noise_rng ^= 0x800302;
+ chip->noise_rng >>= 1;
+
+ i--;
+ }
+}
+
+
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm))>>FREQ_SH) & SIN_MASK)];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+
+#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (chip->LFO_AM & (OP)->AMmask))
+
+/* calculate output of a standard 2 operator channel
+ (or 1st part of a 4-op channel) */
+INLINE void chan_calc( OPL3 *chip, OPL3_CH *CH )
+{
+ OPL3_SLOT *SLOT;
+ unsigned int env;
+ signed int out;
+
+ chip->phase_modulation = 0;
+ chip->phase_modulation2= 0;
+
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env = volume_calc(SLOT);
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
+ }
+ *SLOT->connect += SLOT->op1_out[1];
+//logerror("out0=%5i vol0=%4i ", SLOT->op1_out[1], env );
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable);
+
+//logerror("out1=%5i vol1=%4i\n", op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable), env );
+
+}
+
+/* calculate output of a 2nd part of 4-op channel */
+INLINE void chan_calc_ext( OPL3 *chip, OPL3_CH *CH )
+{
+ OPL3_SLOT *SLOT;
+ unsigned int env;
+
+ chip->phase_modulation = 0;
+
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation2, SLOT->wavetable );
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ *SLOT->connect += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable);
+
+}
+
+/*
+ operators used in the rhythm sounds generation process:
+
+ Envelope Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal
+ 6 / 0 12 50 70 90 f0 +
+ 6 / 1 15 53 73 93 f3 +
+ 7 / 0 13 51 71 91 f1 +
+ 7 / 1 16 54 74 94 f4 +
+ 8 / 0 14 52 72 92 f2 +
+ 8 / 1 17 55 75 95 f5 +
+
+ Phase Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number MULTIPLE Drum Hat Drum Tom Cymbal
+ 6 / 0 12 30 +
+ 6 / 1 15 33 +
+ 7 / 0 13 31 + + +
+ 7 / 1 16 34 ----- n o t u s e d -----
+ 8 / 0 14 32 +
+ 8 / 1 17 35 + +
+
+channel operator register number Bass High Snare Tom Top
+number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal
+ 6 12,15 B6 A6 +
+
+ 7 13,16 B7 A7 + + +
+
+ 8 14,17 B8 A8 + + +
+
+*/
+
+/* calculate rhythm */
+
+INLINE void chan_calc_rhythm( OPL3 *chip, OPL3_CH *CH, unsigned int noise )
+{
+ OPL3_SLOT *SLOT;
+ signed int *chanout = chip->chanout;
+ signed int out;
+ unsigned int env;
+
+
+ /* Bass Drum (verified on real YM3812):
+ - depends on the channel 6 'connect' register:
+ when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out)
+ when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored
+ - output sample always is multiplied by 2
+ */
+
+ chip->phase_modulation = 0;
+
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env = volume_calc(SLOT);
+
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+
+ if (!SLOT->CON)
+ chip->phase_modulation = SLOT->op1_out[0];
+ //else ignore output of operator 1
+
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ chanout[6] += op_calc(SLOT->Cnt, env, chip->phase_modulation, SLOT->wavetable) * 2;
+
+
+ /* Phase generation is based on: */
+ // HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases)
+ // SD (16) channel 7->slot 1
+ // TOM (14) channel 8->slot 1
+ // TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases)
+
+ /* Envelope generation based on: */
+ // HH channel 7->slot1
+ // SD channel 7->slot2
+ // TOM channel 8->slot1
+ // TOP channel 8->slot2
+
+
+ /* The following formulas can be well optimized.
+ I leave them in direct form for now (in case I've missed something).
+ */
+
+ /* High Hat (verified on real YM3812) */
+ env = volume_calc(SLOT7_1);
+ if( env < ENV_QUIET )
+ {
+ /* high hat phase generation:
+ phase = d0 or 234 (based on frequency only)
+ phase = 34 or 2d0 (based on noise)
+ */
+
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0xd0; */
+ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */
+ UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e ^ bit5e);
+
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */
+ if (res2)
+ phase = (0x200|(0xd0>>2));
+
+
+ /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */
+ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */
+ if (phase&0x200)
+ {
+ if (noise)
+ phase = 0x200|0xd0;
+ }
+ else
+ /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */
+ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */
+ {
+ if (noise)
+ phase = 0xd0>>2;
+ }
+
+ chanout[7] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_1->wavetable) * 2;
+ }
+
+ /* Snare Drum (verified on real YM3812) */
+ env = volume_calc(SLOT7_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1;
+
+ /* when bit8 = 0 phase = 0x100; */
+ /* when bit8 = 1 phase = 0x200; */
+ UINT32 phase = bit8 ? 0x200 : 0x100;
+
+ /* Noise bit XOR'es phase by 0x100 */
+ /* when noisebit = 0 pass the phase from calculation above */
+ /* when noisebit = 1 phase ^= 0x100; */
+ /* in other words: phase ^= (noisebit<<8); */
+ if (noise)
+ phase ^= 0x100;
+
+ chanout[7] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_2->wavetable) * 2;
+ }
+
+ /* Tom Tom (verified on real YM3812) */
+ env = volume_calc(SLOT8_1);
+ if( env < ENV_QUIET )
+ chanout[8] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2;
+
+ /* Top Cymbal (verified on real YM3812) */
+ env = volume_calc(SLOT8_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0x100; */
+ /* when res1 = 1 phase = 0x200 | 0x100; */
+ UINT32 phase = res1 ? 0x300 : 0x100;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e ^ bit5e);
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | 0x100; */
+ if (res2)
+ phase = 0x300;
+
+ chanout[8] += op_calc(phase<<FREQ_SH, env, 0, SLOT8_2->wavetable) * 2;
+ }
+
+}
+
+
+/* generic table initialize */
+static int init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+
+ for (x=0; x<TL_RES_LEN; x++)
+ {
+ m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);
+ m = floor(m);
+
+ /* we never reach (1<<16) here due to the (x+1) */
+ /* result fits within 16 bits at maximum */
+
+ n = (int)m; /* 16 bits here */
+ n >>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 1; /* 12 bits here (as in real chip) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = ~tl_tab[ x*2 + 0 ]; /* this *is* different from OPL2 (verified on real YMF262) */
+
+ for (i=1; i<13; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = ~tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; /* this *is* different from OPL2 (verified on real YMF262) */
+ }
+ #if 0
+ logerror("tl %04i", x*2);
+ for (i=0; i<13; i++)
+ logerror(", [%02i] %5i", i*2, tl_tab[ x*2 +0 + i*2*TL_RES_LEN ] ); /* positive */
+ logerror("\n");
+
+ logerror("tl %04i", x*2);
+ for (i=0; i<13; i++)
+ logerror(", [%02i] %5i", i*2, tl_tab[ x*2 +1 + i*2*TL_RES_LEN ] ); /* negative */
+ logerror("\n");
+ #endif
+ }
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* non-standard sinus */
+ m = sin( ((i*2)+1) * M_PI / SIN_LEN ); /* checked against the real chip */
+
+ /* we never reach zero here due to ((i*2)+1) */
+
+ if (m>0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+
+ /*logerror("YMF262.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/
+ }
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* these 'pictures' represent _two_ cycles */
+ /* waveform 1: __ __ */
+ /* / \____/ \____*/
+ /* output only first half of the sinus waveform (positive one) */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[1*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[1*SIN_LEN+i] = sin_tab[i];
+
+ /* waveform 2: __ __ __ __ */
+ /* / \/ \/ \/ \*/
+ /* abs(sin) */
+
+ sin_tab[2*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>1) ];
+
+ /* waveform 3: _ _ _ _ */
+ /* / |_/ |_/ |_/ |_*/
+ /* abs(output only first quarter of the sinus waveform) */
+
+ if (i & (1<<(SIN_BITS-2)) )
+ sin_tab[3*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)];
+
+ /* waveform 4: */
+ /* /\ ____/\ ____*/
+ /* \/ \/ */
+ /* output whole sinus waveform in half the cycle(step=2) and output 0 on the other half of cycle */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[4*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[4*SIN_LEN+i] = sin_tab[i*2];
+
+ /* waveform 5: */
+ /* /\/\____/\/\____*/
+ /* */
+ /* output abs(whole sinus) waveform in half the cycle(step=2) and output 0 on the other half of cycle */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[5*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[5*SIN_LEN+i] = sin_tab[(i*2) & (SIN_MASK>>1) ];
+
+ /* waveform 6: ____ ____ */
+ /* */
+ /* ____ ____*/
+ /* output maximum in half the cycle and output minimum on the other half of cycle */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[6*SIN_LEN+i] = 1; /* negative */
+ else
+ sin_tab[6*SIN_LEN+i] = 0; /* positive */
+
+ /* waveform 7: */
+ /* |\____ |\____ */
+ /* \| \|*/
+ /* output sawtooth waveform */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ x = ((SIN_LEN-1)-i)*16 + 1; /* negative: from 8177 to 1 */
+ else
+ x = i*16; /*positive: from 0 to 8176 */
+
+ if (x > TL_TAB_LEN)
+ x = TL_TAB_LEN; /* clip to the allowed range */
+
+ sin_tab[7*SIN_LEN+i] = x;
+
+ //logerror("YMF262.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );
+ //logerror("YMF262.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] );
+ //logerror("YMF262.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );
+ //logerror("YMF262.C: sin4[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[4*SIN_LEN+i], tl_tab[sin_tab[4*SIN_LEN+i]] );
+ //logerror("YMF262.C: sin5[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[5*SIN_LEN+i], tl_tab[sin_tab[5*SIN_LEN+i]] );
+ //logerror("YMF262.C: sin6[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[6*SIN_LEN+i], tl_tab[sin_tab[6*SIN_LEN+i]] );
+ //logerror("YMF262.C: sin7[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[7*SIN_LEN+i], tl_tab[sin_tab[7*SIN_LEN+i]] );
+ }
+ /*logerror("YMF262.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/
+
+#ifdef SAVE_SAMPLE
+ sample[0]=fopen("sampsum.pcm","wb");
+#endif
+
+ return 1;
+}
+
+static void OPLCloseTable( void )
+{
+#ifdef SAVE_SAMPLE
+ fclose(sample[0]);
+#endif
+}
+
+
+
+static void OPL3_initalize(OPL3 *chip)
+{
+ int i;
+
+ /* frequency base */
+ chip->freqbase = (chip->rate) ? ((double)chip->clock / (8.0*36)) / chip->rate : 0;
+#if 0
+ chip->rate = (double)chip->clock / (8.0*36);
+ chip->freqbase = 1.0;
+#endif
+
+ /* logerror("YMF262: freqbase=%f\n", chip->freqbase); */
+
+ /* Timer base time */
+ chip->TimerBase = attotime::from_hz(chip->clock) * (8*36);
+
+ /* make fnumber -> increment counter table */
+ for( i=0 ; i < 1024 ; i++ )
+ {
+ /* opn phase increment counter = 20bit */
+ chip->fn_tab[i] = (UINT32)( (double)i * 64 * chip->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+#if 0
+ logerror("YMF262.C: fn_tab[%4i] = %08x (dec=%8i)\n",
+ i, chip->fn_tab[i]>>6, chip->fn_tab[i]>>6 );
+#endif
+ }
+
+#if 0
+ for( i=0 ; i < 16 ; i++ )
+ {
+ logerror("YMF262.C: sl_tab[%i] = %08x\n",
+ i, sl_tab[i] );
+ }
+ for( i=0 ; i < 8 ; i++ )
+ {
+ int j;
+ logerror("YMF262.C: ksl_tab[oct=%2i] =",i);
+ for (j=0; j<16; j++)
+ {
+ logerror("%08x ", ksl_tab[i*16+j] );
+ }
+ logerror("\n");
+ }
+#endif
+
+
+ /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */
+ /* One entry from LFO_AM_TABLE lasts for 64 samples */
+ chip->lfo_am_inc = (1.0 / 64.0 ) * (1<<LFO_SH) * chip->freqbase;
+
+ /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */
+ chip->lfo_pm_inc = (1.0 / 1024.0) * (1<<LFO_SH) * chip->freqbase;
+
+ /*logerror ("chip->lfo_am_inc = %8x ; chip->lfo_pm_inc = %8x\n", chip->lfo_am_inc, chip->lfo_pm_inc);*/
+
+ /* Noise generator: a step takes 1 sample */
+ chip->noise_f = (1.0 / 1.0) * (1<<FREQ_SH) * chip->freqbase;
+
+ chip->eg_timer_add = (1<<EG_SH) * chip->freqbase;
+ chip->eg_timer_overflow = ( 1 ) * (1<<EG_SH);
+ /*logerror("YMF262init eg_timer_add=%8x eg_timer_overflow=%8x\n", chip->eg_timer_add, chip->eg_timer_overflow);*/
+
+}
+
+INLINE void FM_KEYON(OPL3_SLOT *SLOT, UINT32 key_set)
+{
+ if( !SLOT->key )
+ {
+ /* restart Phase Generator */
+ SLOT->Cnt = 0;
+ /* phase -> Attack */
+ SLOT->state = EG_ATT;
+ }
+ SLOT->key |= key_set;
+}
+
+INLINE void FM_KEYOFF(OPL3_SLOT *SLOT, UINT32 key_clr)
+{
+ if( SLOT->key )
+ {
+ SLOT->key &= key_clr;
+
+ if( !SLOT->key )
+ {
+ /* phase -> Release */
+ if (SLOT->state>EG_REL)
+ SLOT->state = EG_REL;
+ }
+ }
+}
+
+/* update phase increment counter of operator (also update the EG rates if necessary) */
+INLINE void CALC_FCSLOT(OPL3_CH *CH,OPL3_SLOT *SLOT)
+{
+ int ksr;
+
+ /* (frequency) phase increment counter */
+ SLOT->Incr = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT->ar + SLOT->ksr) < 16+60)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_m_ar = (1<<SLOT->eg_sh_ar)-1;
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_m_ar = (1<<SLOT->eg_sh_ar)-1;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_m_dr = (1<<SLOT->eg_sh_dr)-1;
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_m_rr = (1<<SLOT->eg_sh_rr)-1;
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+ }
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(OPL3 *chip,int slot,int v)
+{
+ OPL3_CH *CH = &chip->P_CH[slot/2];
+ OPL3_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->mul = mul_tab[v&0x0f];
+ SLOT->KSR = (v&0x10) ? 0 : 2;
+ SLOT->eg_type = (v&0x20);
+ SLOT->vib = (v&0x40);
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;
+
+ if (chip->OPL3_mode & 1)
+ {
+ int chan_no = slot/2;
+
+ /* in OPL3 mode */
+ //DO THIS:
+ //if this is one of the slots of 1st channel forming up a 4-op channel
+ //do normal operation
+ //else normal 2 operator function
+ //OR THIS:
+ //if this is one of the slots of 2nd channel forming up a 4-op channel
+ //update it using channel data of 1st channel of a pair
+ //else normal 2 operator function
+ switch(chan_no)
+ {
+ case 0: case 1: case 2:
+ case 9: case 10: case 11:
+ if (CH->extended)
+ {
+ /* normal */
+ CALC_FCSLOT(CH,SLOT);
+ }
+ else
+ {
+ /* normal */
+ CALC_FCSLOT(CH,SLOT);
+ }
+ break;
+ case 3: case 4: case 5:
+ case 12: case 13: case 14:
+ if ((CH-3)->extended)
+ {
+ /* update this SLOT using frequency data for 1st channel of a pair */
+ CALC_FCSLOT(CH-3,SLOT);
+ }
+ else
+ {
+ /* normal */
+ CALC_FCSLOT(CH,SLOT);
+ }
+ break;
+ default:
+ /* normal */
+ CALC_FCSLOT(CH,SLOT);
+ break;
+ }
+ }
+ else
+ {
+ /* in OPL2 mode */
+ CALC_FCSLOT(CH,SLOT);
+ }
+}
+
+/* set ksl & tl */
+INLINE void set_ksl_tl(OPL3 *chip,int slot,int v)
+{
+ OPL3_CH *CH = &chip->P_CH[slot/2];
+ OPL3_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->ksl = ksl_shift[v >> 6];
+ SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */
+
+ if (chip->OPL3_mode & 1)
+ {
+ int chan_no = slot/2;
+
+ /* in OPL3 mode */
+ //DO THIS:
+ //if this is one of the slots of 1st channel forming up a 4-op channel
+ //do normal operation
+ //else normal 2 operator function
+ //OR THIS:
+ //if this is one of the slots of 2nd channel forming up a 4-op channel
+ //update it using channel data of 1st channel of a pair
+ //else normal 2 operator function
+ switch(chan_no)
+ {
+ case 0: case 1: case 2:
+ case 9: case 10: case 11:
+ if (CH->extended)
+ {
+ /* normal */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+ else
+ {
+ /* normal */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+ break;
+ case 3: case 4: case 5:
+ case 12: case 13: case 14:
+ if ((CH-3)->extended)
+ {
+ /* update this SLOT using frequency data for 1st channel of a pair */
+ SLOT->TLL = SLOT->TL + ((CH-3)->ksl_base>>SLOT->ksl);
+ }
+ else
+ {
+ /* normal */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+ break;
+ default:
+ /* normal */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ break;
+ }
+ }
+ else
+ {
+ /* in OPL2 mode */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+
+}
+
+/* set attack rate & decay rate */
+INLINE void set_ar_dr(OPL3 *chip,int slot,int v)
+{
+ OPL3_CH *CH = &chip->P_CH[slot/2];
+ OPL3_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0;
+
+ if ((SLOT->ar + SLOT->ksr) < 16+60) /* verified on real YMF262 - all 15 x rates take "zero" time */
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_m_ar = (1<<SLOT->eg_sh_ar)-1;
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_m_ar = (1<<SLOT->eg_sh_ar)-1;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+
+ SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_m_dr = (1<<SLOT->eg_sh_dr)-1;
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(OPL3 *chip,int slot,int v)
+{
+ OPL3_CH *CH = &chip->P_CH[slot/2];
+ OPL3_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->sl = sl_tab[ v>>4 ];
+
+ SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_m_rr = (1<<SLOT->eg_sh_rr)-1;
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+}
+
+
+static void update_channels(OPL3 *chip, OPL3_CH *CH)
+{
+ /* update channel passed as a parameter and a channel at CH+=3; */
+ if (CH->extended)
+ { /* we've just switched to combined 4 operator mode */
+
+ }
+ else
+ { /* we've just switched to normal 2 operator mode */
+
+ }
+
+}
+
+/* write a value v to register r on OPL chip */
+static void OPL3WriteReg(OPL3 *chip, int r, int v)
+{
+ OPL3_CH *CH;
+ signed int *chanout = chip->chanout;
+ unsigned int ch_offset = 0;
+ int slot;
+ int block_fnum;
+
+
+
+ if (LOG_CYM_FILE && (cymfile) && ((r&255)!=0) && (r!=255) )
+ {
+ if (r>0xff)
+ fputc( (unsigned char)0xff, cymfile );/*mark writes to second register set*/
+
+ fputc( (unsigned char)r&0xff, cymfile );
+ fputc( (unsigned char)v, cymfile );
+ }
+
+ if(r&0x100)
+ {
+ switch(r)
+ {
+ case 0x101: /* test register */
+ return;
+
+ case 0x104: /* 6 channels enable */
+ {
+ UINT8 prev;
+
+ CH = &chip->P_CH[0]; /* channel 0 */
+ prev = CH->extended;
+ CH->extended = (v>>0) & 1;
+ if(prev != CH->extended)
+ update_channels(chip, CH);
+ CH++; /* channel 1 */
+ prev = CH->extended;
+ CH->extended = (v>>1) & 1;
+ if(prev != CH->extended)
+ update_channels(chip, CH);
+ CH++; /* channel 2 */
+ prev = CH->extended;
+ CH->extended = (v>>2) & 1;
+ if(prev != CH->extended)
+ update_channels(chip, CH);
+
+
+ CH = &chip->P_CH[9]; /* channel 9 */
+ prev = CH->extended;
+ CH->extended = (v>>3) & 1;
+ if(prev != CH->extended)
+ update_channels(chip, CH);
+ CH++; /* channel 10 */
+ prev = CH->extended;
+ CH->extended = (v>>4) & 1;
+ if(prev != CH->extended)
+ update_channels(chip, CH);
+ CH++; /* channel 11 */
+ prev = CH->extended;
+ CH->extended = (v>>5) & 1;
+ if(prev != CH->extended)
+ update_channels(chip, CH);
+
+ }
+ return;
+
+ case 0x105: /* OPL3 extensions enable register */
+
+ chip->OPL3_mode = v&0x01; /* OPL3 mode when bit0=1 otherwise it is OPL2 mode */
+
+ /* following behaviour was tested on real YMF262,
+ switching OPL3/OPL2 modes on the fly:
+ - does not change the waveform previously selected (unless when ....)
+ - does not update CH.A, CH.B, CH.C and CH.D output selectors (registers c0-c8) (unless when ....)
+ - does not disable channels 9-17 on OPL3->OPL2 switch
+ - does not switch 4 operator channels back to 2 operator channels
+ */
+
+ return;
+
+ default:
+ if (r < 0x120)
+ logerror("YMF262: write to unknown register (set#2): %03x value=%02x\n",r,v);
+ break;
+ }
+
+ ch_offset = 9; /* register page #2 starts from channel 9 (counting from 0) */
+ }
+
+ /* adjust bus to 8 bits */
+ r &= 0xff;
+ v &= 0xff;
+
+
+ switch(r&0xe0)
+ {
+ case 0x00: /* 00-1f:control */
+ switch(r&0x1f)
+ {
+ case 0x01: /* test register */
+ break;
+ case 0x02: /* Timer 1 */
+ chip->T[0] = (256-v)*4;
+ break;
+ case 0x03: /* Timer 2 */
+ chip->T[1] = (256-v)*16;
+ break;
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v&0x80)
+ { /* IRQ flags clear */
+ OPL3_STATUS_RESET(chip,0x60);
+ }
+ else
+ { /* set IRQ mask ,timer enable */
+ UINT8 st1 = v & 1;
+ UINT8 st2 = (v>>1) & 1;
+
+ /* IRQRST,T1MSK,t2MSK,x,x,x,ST2,ST1 */
+ OPL3_STATUS_RESET(chip, v & 0x60);
+ OPL3_STATUSMASK_SET(chip, (~v) & 0x60 );
+
+ /* timer 2 */
+ if(chip->st[1] != st2)
+ {
+ attotime period = st2 ? chip->TimerBase * chip->T[1] : attotime::zero;
+ chip->st[1] = st2;
+ if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,1,period);
+ }
+ /* timer 1 */
+ if(chip->st[0] != st1)
+ {
+ attotime period = st1 ? chip->TimerBase * chip->T[0] : attotime::zero;
+ chip->st[0] = st1;
+ if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,0,period);
+ }
+ }
+ break;
+ case 0x08: /* x,NTS,x,x, x,x,x,x */
+ chip->nts = v;
+ break;
+
+ default:
+ logerror("YMF262: write to unknown register: %02x value=%02x\n",r,v);
+ break;
+ }
+ break;
+ case 0x20: /* am ON, vib ON, ksr, eg_type, mul */
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_mul(chip, slot + ch_offset*2, v);
+ break;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ksl_tl(chip, slot + ch_offset*2, v);
+ break;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ar_dr(chip, slot + ch_offset*2, v);
+ break;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_sl_rr(chip, slot + ch_offset*2, v);
+ break;
+ case 0xa0:
+ if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */
+ {
+ if (ch_offset != 0) /* 0xbd register is present in set #1 only */
+ return;
+
+ chip->lfo_am_depth = v & 0x80;
+ chip->lfo_pm_depth_range = (v&0x40) ? 8 : 0;
+
+ chip->rhythm = v&0x3f;
+
+ if(chip->rhythm&0x20)
+ {
+ /* BD key on/off */
+ if(v&0x10)
+ {
+ FM_KEYON (&chip->P_CH[6].SLOT[SLOT1], 2);
+ FM_KEYON (&chip->P_CH[6].SLOT[SLOT2], 2);
+ }
+ else
+ {
+ FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT1],~2);
+ FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT2],~2);
+ }
+ /* HH key on/off */
+ if(v&0x01) FM_KEYON (&chip->P_CH[7].SLOT[SLOT1], 2);
+ else FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key on/off */
+ if(v&0x08) FM_KEYON (&chip->P_CH[7].SLOT[SLOT2], 2);
+ else FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key on/off */
+ if(v&0x04) FM_KEYON (&chip->P_CH[8].SLOT[SLOT1], 2);
+ else FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY key on/off */
+ if(v&0x02) FM_KEYON (&chip->P_CH[8].SLOT[SLOT2], 2);
+ else FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT2],~2);
+ }
+ else
+ {
+ /* BD key off */
+ FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT1],~2);
+ FM_KEYOFF(&chip->P_CH[6].SLOT[SLOT2],~2);
+ /* HH key off */
+ FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key off */
+ FM_KEYOFF(&chip->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key off */
+ FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY off */
+ FM_KEYOFF(&chip->P_CH[8].SLOT[SLOT2],~2);
+ }
+ return;
+ }
+
+ /* keyon,block,fnum */
+ if( (r&0x0f) > 8) return;
+ CH = &chip->P_CH[(r&0x0f) + ch_offset];
+
+ if(!(r&0x10))
+ { /* a0-a8 */
+ block_fnum = (CH->block_fnum&0x1f00) | v;
+ }
+ else
+ { /* b0-b8 */
+ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+
+ if (chip->OPL3_mode & 1)
+ {
+ int chan_no = (r&0x0f) + ch_offset;
+
+ /* in OPL3 mode */
+ //DO THIS:
+ //if this is 1st channel forming up a 4-op channel
+ //ALSO keyon/off slots of 2nd channel forming up 4-op channel
+ //else normal 2 operator function keyon/off
+ //OR THIS:
+ //if this is 2nd channel forming up 4-op channel just do nothing
+ //else normal 2 operator function keyon/off
+ switch(chan_no)
+ {
+ case 0: case 1: case 2:
+ case 9: case 10: case 11:
+ if (CH->extended)
+ {
+ //if this is 1st channel forming up a 4-op channel
+ //ALSO keyon/off slots of 2nd channel forming up 4-op channel
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ FM_KEYON (&(CH+3)->SLOT[SLOT1], 1);
+ FM_KEYON (&(CH+3)->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ FM_KEYOFF(&(CH+3)->SLOT[SLOT1],~1);
+ FM_KEYOFF(&(CH+3)->SLOT[SLOT2],~1);
+ }
+ }
+ else
+ {
+ //else normal 2 operator function keyon/off
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ }
+ }
+ break;
+
+ case 3: case 4: case 5:
+ case 12: case 13: case 14:
+ if ((CH-3)->extended)
+ {
+ //if this is 2nd channel forming up 4-op channel just do nothing
+ }
+ else
+ {
+ //else normal 2 operator function keyon/off
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ }
+ }
+ break;
+
+ default:
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ }
+ break;
+ }
+ }
+ else
+ {
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ }
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ UINT8 block = block_fnum >> 10;
+
+ CH->block_fnum = block_fnum;
+
+ CH->ksl_base = ksl_tab[block_fnum>>6];
+ CH->fc = chip->fn_tab[block_fnum&0x03ff] >> (7-block);
+
+ /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */
+ CH->kcode = (CH->block_fnum&0x1c00)>>9;
+
+ /* the info below is actually opposite to what is stated in the Manuals (verifed on real YMF262) */
+ /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */
+ /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */
+ if (chip->nts&0x40)
+ CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */
+ else
+ CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */
+
+ if (chip->OPL3_mode & 1)
+ {
+ int chan_no = (r&0x0f) + ch_offset;
+ /* in OPL3 mode */
+ //DO THIS:
+ //if this is 1st channel forming up a 4-op channel
+ //ALSO update slots of 2nd channel forming up 4-op channel
+ //else normal 2 operator function keyon/off
+ //OR THIS:
+ //if this is 2nd channel forming up 4-op channel just do nothing
+ //else normal 2 operator function keyon/off
+ switch(chan_no)
+ {
+ case 0: case 1: case 2:
+ case 9: case 10: case 11:
+ if (CH->extended)
+ {
+ //if this is 1st channel forming up a 4-op channel
+ //ALSO update slots of 2nd channel forming up 4-op channel
+
+ /* refresh Total Level in FOUR SLOTs of this channel and channel+3 using data from THIS channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+ (CH+3)->SLOT[SLOT1].TLL = (CH+3)->SLOT[SLOT1].TL + (CH->ksl_base>>(CH+3)->SLOT[SLOT1].ksl);
+ (CH+3)->SLOT[SLOT2].TLL = (CH+3)->SLOT[SLOT2].TL + (CH->ksl_base>>(CH+3)->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in FOUR SLOTs of this channel and channel+3 using data from THIS channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ CALC_FCSLOT(CH,&(CH+3)->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&(CH+3)->SLOT[SLOT2]);
+ }
+ else
+ {
+ //else normal 2 operator function
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ break;
+
+ case 3: case 4: case 5:
+ case 12: case 13: case 14:
+ if ((CH-3)->extended)
+ {
+ //if this is 2nd channel forming up 4-op channel just do nothing
+ }
+ else
+ {
+ //else normal 2 operator function
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ break;
+
+ default:
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ break;
+ }
+ }
+ else
+ {
+ /* in OPL2 mode */
+
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ }
+ break;
+
+ case 0xc0:
+ /* CH.D, CH.C, CH.B, CH.A, FB(3bits), C */
+ if( (r&0xf) > 8) return;
+
+ CH = &chip->P_CH[(r&0xf) + ch_offset];
+
+ if( chip->OPL3_mode & 1 )
+ {
+ int base = ((r&0xf) + ch_offset) * 4;
+
+ /* OPL3 mode */
+ chip->pan[ base ] = (v & 0x10) ? ~0 : 0; /* ch.A */
+ chip->pan[ base +1 ] = (v & 0x20) ? ~0 : 0; /* ch.B */
+ chip->pan[ base +2 ] = (v & 0x40) ? ~0 : 0; /* ch.C */
+ chip->pan[ base +3 ] = (v & 0x80) ? ~0 : 0; /* ch.D */
+ }
+ else
+ {
+ int base = ((r&0xf) + ch_offset) * 4;
+
+ /* OPL2 mode - always enabled */
+ chip->pan[ base ] = ~0; /* ch.A */
+ chip->pan[ base +1 ] = ~0; /* ch.B */
+ chip->pan[ base +2 ] = ~0; /* ch.C */
+ chip->pan[ base +3 ] = ~0; /* ch.D */
+ }
+
+ chip->pan_ctrl_value[ (r&0xf) + ch_offset ] = v; /* store control value for OPL3/OPL2 mode switching on the fly */
+
+ CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0;
+ CH->SLOT[SLOT1].CON = v&1;
+
+ if( chip->OPL3_mode & 1 )
+ {
+ int chan_no = (r&0x0f) + ch_offset;
+
+ switch(chan_no)
+ {
+ case 0: case 1: case 2:
+ case 9: case 10: case 11:
+ if (CH->extended)
+ {
+ UINT8 conn = (CH->SLOT[SLOT1].CON<<1) | ((CH+3)->SLOT[SLOT1].CON<<0);
+ switch(conn)
+ {
+ case 0:
+ /* 1 -> 2 -> 3 -> 4 - out */
+
+ CH->SLOT[SLOT1].connect = &chip->phase_modulation;
+ CH->SLOT[SLOT2].connect = &chip->phase_modulation2;
+ (CH+3)->SLOT[SLOT1].connect = &chip->phase_modulation;
+ (CH+3)->SLOT[SLOT2].connect = &chanout[ chan_no + 3 ];
+ break;
+ case 1:
+ /* 1 -> 2 -\
+ 3 -> 4 -+- out */
+
+ CH->SLOT[SLOT1].connect = &chip->phase_modulation;
+ CH->SLOT[SLOT2].connect = &chanout[ chan_no ];
+ (CH+3)->SLOT[SLOT1].connect = &chip->phase_modulation;
+ (CH+3)->SLOT[SLOT2].connect = &chanout[ chan_no + 3 ];
+ break;
+ case 2:
+ /* 1 -----------\
+ 2 -> 3 -> 4 -+- out */
+
+ CH->SLOT[SLOT1].connect = &chanout[ chan_no ];
+ CH->SLOT[SLOT2].connect = &chip->phase_modulation2;
+ (CH+3)->SLOT[SLOT1].connect = &chip->phase_modulation;
+ (CH+3)->SLOT[SLOT2].connect = &chanout[ chan_no + 3 ];
+ break;
+ case 3:
+ /* 1 ------\
+ 2 -> 3 -+- out
+ 4 ------/ */
+ CH->SLOT[SLOT1].connect = &chanout[ chan_no ];
+ CH->SLOT[SLOT2].connect = &chip->phase_modulation2;
+ (CH+3)->SLOT[SLOT1].connect = &chanout[ chan_no + 3 ];
+ (CH+3)->SLOT[SLOT2].connect = &chanout[ chan_no + 3 ];
+ break;
+ }
+ }
+ else
+ {
+ /* 2 operators mode */
+ CH->SLOT[SLOT1].connect = CH->SLOT[SLOT1].CON ? &chanout[(r&0xf)+ch_offset] : &chip->phase_modulation;
+ CH->SLOT[SLOT2].connect = &chanout[(r&0xf)+ch_offset];
+ }
+ break;
+
+ case 3: case 4: case 5:
+ case 12: case 13: case 14:
+ if ((CH-3)->extended)
+ {
+ UINT8 conn = ((CH-3)->SLOT[SLOT1].CON<<1) | (CH->SLOT[SLOT1].CON<<0);
+ switch(conn)
+ {
+ case 0:
+ /* 1 -> 2 -> 3 -> 4 - out */
+
+ (CH-3)->SLOT[SLOT1].connect = &chip->phase_modulation;
+ (CH-3)->SLOT[SLOT2].connect = &chip->phase_modulation2;
+ CH->SLOT[SLOT1].connect = &chip->phase_modulation;
+ CH->SLOT[SLOT2].connect = &chanout[ chan_no ];
+ break;
+ case 1:
+ /* 1 -> 2 -\
+ 3 -> 4 -+- out */
+
+ (CH-3)->SLOT[SLOT1].connect = &chip->phase_modulation;
+ (CH-3)->SLOT[SLOT2].connect = &chanout[ chan_no - 3 ];
+ CH->SLOT[SLOT1].connect = &chip->phase_modulation;
+ CH->SLOT[SLOT2].connect = &chanout[ chan_no ];
+ break;
+ case 2:
+ /* 1 -----------\
+ 2 -> 3 -> 4 -+- out */
+
+ (CH-3)->SLOT[SLOT1].connect = &chanout[ chan_no - 3 ];
+ (CH-3)->SLOT[SLOT2].connect = &chip->phase_modulation2;
+ CH->SLOT[SLOT1].connect = &chip->phase_modulation;
+ CH->SLOT[SLOT2].connect = &chanout[ chan_no ];
+ break;
+ case 3:
+ /* 1 ------\
+ 2 -> 3 -+- out
+ 4 ------/ */
+ (CH-3)->SLOT[SLOT1].connect = &chanout[ chan_no - 3 ];
+ (CH-3)->SLOT[SLOT2].connect = &chip->phase_modulation2;
+ CH->SLOT[SLOT1].connect = &chanout[ chan_no ];
+ CH->SLOT[SLOT2].connect = &chanout[ chan_no ];
+ break;
+ }
+ }
+ else
+ {
+ /* 2 operators mode */
+ CH->SLOT[SLOT1].connect = CH->SLOT[SLOT1].CON ? &chanout[(r&0xf)+ch_offset] : &chip->phase_modulation;
+ CH->SLOT[SLOT2].connect = &chanout[(r&0xf)+ch_offset];
+ }
+ break;
+
+ default:
+ /* 2 operators mode */
+ CH->SLOT[SLOT1].connect = CH->SLOT[SLOT1].CON ? &chanout[(r&0xf)+ch_offset] : &chip->phase_modulation;
+ CH->SLOT[SLOT2].connect = &chanout[(r&0xf)+ch_offset];
+ break;
+ }
+ }
+ else
+ {
+ /* OPL2 mode - always 2 operators mode */
+ CH->SLOT[SLOT1].connect = CH->SLOT[SLOT1].CON ? &chanout[(r&0xf)+ch_offset] : &chip->phase_modulation;
+ CH->SLOT[SLOT2].connect = &chanout[(r&0xf)+ch_offset];
+ }
+ break;
+
+ case 0xe0: /* waveform select */
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+
+ slot += ch_offset*2;
+
+ CH = &chip->P_CH[slot/2];
+
+
+ /* store 3-bit value written regardless of current OPL2 or OPL3 mode... (verified on real YMF262) */
+ v &= 7;
+ CH->SLOT[slot&1].waveform_number = v;
+
+ /* ... but select only waveforms 0-3 in OPL2 mode */
+ if( !(chip->OPL3_mode & 1) )
+ {
+ v &= 3; /* we're in OPL2 mode */
+ }
+ CH->SLOT[slot&1].wavetable = v * SIN_LEN;
+ break;
+ }
+}
+
+static TIMER_CALLBACK( cymfile_callback )
+{
+ if (cymfile)
+ {
+ fputc( (unsigned char)0, cymfile );
+ }
+}
+
+/* lock/unlock for common table */
+static int OPL3_LockTable(device_t *device)
+{
+ num_lock++;
+ if(num_lock>1) return 0;
+
+ /* first time */
+
+ if( !init_tables() )
+ {
+ num_lock--;
+ return -1;
+ }
+
+ if (LOG_CYM_FILE)
+ {
+ cymfile = fopen("ymf262_.cym","wb");
+ if (cymfile)
+ device->machine().scheduler().timer_pulse ( attotime::from_hz(110), FUNC(cymfile_callback)); /*110 Hz pulse timer*/
+ else
+ logerror("Could not create ymf262_.cym file\n");
+ }
+
+ return 0;
+}
+
+static void OPL3_UnLockTable(void)
+{
+ if(num_lock) num_lock--;
+ if(num_lock) return;
+
+ /* last time */
+ OPLCloseTable();
+
+ if (LOG_CYM_FILE)
+ fclose (cymfile);
+ cymfile = NULL;
+}
+
+static void OPL3ResetChip(OPL3 *chip)
+{
+ int c,s;
+
+ chip->eg_timer = 0;
+ chip->eg_cnt = 0;
+
+ chip->noise_rng = 1; /* noise shift register */
+ chip->nts = 0; /* note split */
+ OPL3_STATUS_RESET(chip,0x60);
+
+ /* reset with register write */
+ OPL3WriteReg(chip,0x01,0); /* test register */
+ OPL3WriteReg(chip,0x02,0); /* Timer1 */
+ OPL3WriteReg(chip,0x03,0); /* Timer2 */
+ OPL3WriteReg(chip,0x04,0); /* IRQ mask clear */
+
+
+//FIX IT registers 101, 104 and 105
+
+
+//FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers)
+ for(c = 0xff ; c >= 0x20 ; c-- )
+ OPL3WriteReg(chip,c,0);
+//FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers)
+ for(c = 0x1ff ; c >= 0x120 ; c-- )
+ OPL3WriteReg(chip,c,0);
+
+
+
+ /* reset operator parameters */
+ for( c = 0 ; c < 9*2 ; c++ )
+ {
+ OPL3_CH *CH = &chip->P_CH[c];
+ for(s = 0 ; s < 2 ; s++ )
+ {
+ CH->SLOT[s].state = EG_OFF;
+ CH->SLOT[s].volume = MAX_ATT_INDEX;
+ }
+ }
+}
+
+/* Create one of virtual YMF262 */
+/* 'clock' is chip clock in Hz */
+/* 'rate' is sampling rate */
+static OPL3 *OPL3Create(device_t *device, int clock, int rate, int type)
+{
+ OPL3 *chip;
+
+ if (OPL3_LockTable(device) == -1) return NULL;
+
+ /* allocate memory block */
+ chip = auto_alloc_clear(device->machine(), OPL3);
+
+ chip->device = device;
+ chip->type = type;
+ chip->clock = clock;
+ chip->rate = rate;
+
+ /* init global tables */
+ OPL3_initalize(chip);
+
+ /* reset chip */
+ OPL3ResetChip(chip);
+ return chip;
+}
+
+/* Destroy one of virtual YMF262 */
+static void OPL3Destroy(OPL3 *chip)
+{
+ OPL3_UnLockTable();
+ auto_free(chip->device->machine(), chip);
+}
+
+
+/* Optional handlers */
+
+static void OPL3SetTimerHandler(OPL3 *chip,OPL3_TIMERHANDLER timer_handler,void *param)
+{
+ chip->timer_handler = timer_handler;
+ chip->TimerParam = param;
+}
+static void OPL3SetIRQHandler(OPL3 *chip,OPL3_IRQHANDLER IRQHandler,void *param)
+{
+ chip->IRQHandler = IRQHandler;
+ chip->IRQParam = param;
+}
+static void OPL3SetUpdateHandler(OPL3 *chip,OPL3_UPDATEHANDLER UpdateHandler,void *param)
+{
+ chip->UpdateHandler = UpdateHandler;
+ chip->UpdateParam = param;
+}
+
+/* YMF262 I/O interface */
+static int OPL3Write(OPL3 *chip, int a, int v)
+{
+ /* data bus is 8 bits */
+ v &= 0xff;
+
+ switch(a&3)
+ {
+ case 0: /* address port 0 (register set #1) */
+ chip->address = v;
+ break;
+
+ case 1: /* data port - ignore A1 */
+ case 3: /* data port - ignore A1 */
+ if(chip->UpdateHandler) chip->UpdateHandler(chip->UpdateParam,0);
+ OPL3WriteReg(chip,chip->address,v);
+ break;
+
+ case 2: /* address port 1 (register set #2) */
+
+ /* verified on real YMF262:
+ in OPL3 mode:
+ address line A1 is stored during *address* write and ignored during *data* write.
+
+ in OPL2 mode:
+ register set#2 writes go to register set#1 (ignoring A1)
+ verified on registers from set#2: 0x01, 0x04, 0x20-0xef
+ The only exception is register 0x05.
+ */
+ if( chip->OPL3_mode & 1 )
+ {
+ /* OPL3 mode */
+ chip->address = v | 0x100;
+ }
+ else
+ {
+ /* in OPL2 mode the only accessible in set #2 is register 0x05 */
+ if( v==5 )
+ chip->address = v | 0x100;
+ else
+ chip->address = v; /* verified range: 0x01, 0x04, 0x20-0xef(set #2 becomes set #1 in opl2 mode) */
+ }
+ break;
+ }
+
+ return chip->status>>7;
+}
+
+static unsigned char OPL3Read(OPL3 *chip,int a)
+{
+ if( a==0 )
+ {
+ /* status port */
+ return chip->status;
+ }
+
+ return 0x00; /* verified on real YMF262 */
+}
+
+
+
+static int OPL3TimerOver(OPL3 *chip,int c)
+{
+ if( c )
+ { /* Timer B */
+ OPL3_STATUS_SET(chip,0x20);
+ }
+ else
+ { /* Timer A */
+ OPL3_STATUS_SET(chip,0x40);
+ }
+ /* reload timer */
+ if (chip->timer_handler) (chip->timer_handler)(chip->TimerParam,c,chip->TimerBase * chip->T[c]);
+ return chip->status>>7;
+}
+
+
+
+
+void * ymf262_init(device_t *device, int clock, int rate)
+{
+ return OPL3Create(device,clock,rate,OPL3_TYPE_YMF262);
+}
+
+void ymf262_shutdown(void *chip)
+{
+ OPL3Destroy((OPL3 *)chip);
+}
+void ymf262_reset_chip(void *chip)
+{
+ OPL3ResetChip((OPL3 *)chip);
+}
+
+int ymf262_write(void *chip, int a, int v)
+{
+ return OPL3Write((OPL3 *)chip, a, v);
+}
+
+unsigned char ymf262_read(void *chip, int a)
+{
+ /* Note on status register: */
+
+ /* YM3526(OPL) and YM3812(OPL2) return bit2 and bit1 in HIGH state */
+
+ /* YMF262(OPL3) always returns bit2 and bit1 in LOW state */
+ /* which can be used to identify the chip */
+
+ /* YMF278(OPL4) returns bit2 in LOW and bit1 in HIGH state ??? info from manual - not verified */
+
+ return OPL3Read((OPL3 *)chip, a);
+}
+int ymf262_timer_over(void *chip, int c)
+{
+ return OPL3TimerOver((OPL3 *)chip, c);
+}
+
+void ymf262_set_timer_handler(void *chip, OPL3_TIMERHANDLER timer_handler, void *param)
+{
+ OPL3SetTimerHandler((OPL3 *)chip, timer_handler, param);
+}
+void ymf262_set_irq_handler(void *chip,OPL3_IRQHANDLER IRQHandler,void *param)
+{
+ OPL3SetIRQHandler((OPL3 *)chip, IRQHandler, param);
+}
+void ymf262_set_update_handler(void *chip,OPL3_UPDATEHANDLER UpdateHandler,void *param)
+{
+ OPL3SetUpdateHandler((OPL3 *)chip, UpdateHandler, param);
+}
+
+
+/*
+** Generate samples for one of the YMF262's
+**
+** 'which' is the virtual YMF262 number
+** '**buffers' is table of 4 pointers to the buffers: CH.A, CH.B, CH.C and CH.D
+** 'length' is the number of samples that should be generated
+*/
+void ymf262_update_one(void *_chip, OPL3SAMPLE **buffers, int length)
+{
+ int i;
+ OPL3 *chip = (OPL3 *)_chip;
+ signed int *chanout = chip->chanout;
+ UINT8 rhythm = chip->rhythm&0x20;
+
+ OPL3SAMPLE *ch_a = buffers[0];
+ OPL3SAMPLE *ch_b = buffers[1];
+ OPL3SAMPLE *ch_c = buffers[2];
+ OPL3SAMPLE *ch_d = buffers[3];
+
+ for( i=0; i < length ; i++ )
+ {
+ int a,b,c,d;
+
+
+ advance_lfo(chip);
+
+ /* clear channel outputs */
+ memset(chip->chanout, 0, sizeof(chip->chanout));
+
+#if 1
+ /* register set #1 */
+ chan_calc(chip, &chip->P_CH[0]); /* extended 4op ch#0 part 1 or 2op ch#0 */
+ if (chip->P_CH[0].extended)
+ chan_calc_ext(chip, &chip->P_CH[3]); /* extended 4op ch#0 part 2 */
+ else
+ chan_calc(chip, &chip->P_CH[3]); /* standard 2op ch#3 */
+
+
+ chan_calc(chip, &chip->P_CH[1]); /* extended 4op ch#1 part 1 or 2op ch#1 */
+ if (chip->P_CH[1].extended)
+ chan_calc_ext(chip, &chip->P_CH[4]); /* extended 4op ch#1 part 2 */
+ else
+ chan_calc(chip, &chip->P_CH[4]); /* standard 2op ch#4 */
+
+
+ chan_calc(chip, &chip->P_CH[2]); /* extended 4op ch#2 part 1 or 2op ch#2 */
+ if (chip->P_CH[2].extended)
+ chan_calc_ext(chip, &chip->P_CH[5]); /* extended 4op ch#2 part 2 */
+ else
+ chan_calc(chip, &chip->P_CH[5]); /* standard 2op ch#5 */
+
+
+ if(!rhythm)
+ {
+ chan_calc(chip, &chip->P_CH[6]);
+ chan_calc(chip, &chip->P_CH[7]);
+ chan_calc(chip, &chip->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ chan_calc_rhythm(chip, &chip->P_CH[0], (chip->noise_rng>>0)&1 );
+ }
+
+ /* register set #2 */
+ chan_calc(chip, &chip->P_CH[ 9]);
+ if (chip->P_CH[9].extended)
+ chan_calc_ext(chip, &chip->P_CH[12]);
+ else
+ chan_calc(chip, &chip->P_CH[12]);
+
+
+ chan_calc(chip, &chip->P_CH[10]);
+ if (chip->P_CH[10].extended)
+ chan_calc_ext(chip, &chip->P_CH[13]);
+ else
+ chan_calc(chip, &chip->P_CH[13]);
+
+
+ chan_calc(chip, &chip->P_CH[11]);
+ if (chip->P_CH[11].extended)
+ chan_calc_ext(chip, &chip->P_CH[14]);
+ else
+ chan_calc(chip, &chip->P_CH[14]);
+
+
+ /* channels 15,16,17 are fixed 2-operator channels only */
+ chan_calc(chip, &chip->P_CH[15]);
+ chan_calc(chip, &chip->P_CH[16]);
+ chan_calc(chip, &chip->P_CH[17]);
+#endif
+
+ /* accumulator register set #1 */
+ a = chanout[0] & chip->pan[0];
+ b = chanout[0] & chip->pan[1];
+ c = chanout[0] & chip->pan[2];
+ d = chanout[0] & chip->pan[3];
+#if 1
+ a += chanout[1] & chip->pan[4];
+ b += chanout[1] & chip->pan[5];
+ c += chanout[1] & chip->pan[6];
+ d += chanout[1] & chip->pan[7];
+ a += chanout[2] & chip->pan[8];
+ b += chanout[2] & chip->pan[9];
+ c += chanout[2] & chip->pan[10];
+ d += chanout[2] & chip->pan[11];
+
+ a += chanout[3] & chip->pan[12];
+ b += chanout[3] & chip->pan[13];
+ c += chanout[3] & chip->pan[14];
+ d += chanout[3] & chip->pan[15];
+ a += chanout[4] & chip->pan[16];
+ b += chanout[4] & chip->pan[17];
+ c += chanout[4] & chip->pan[18];
+ d += chanout[4] & chip->pan[19];
+ a += chanout[5] & chip->pan[20];
+ b += chanout[5] & chip->pan[21];
+ c += chanout[5] & chip->pan[22];
+ d += chanout[5] & chip->pan[23];
+
+ a += chanout[6] & chip->pan[24];
+ b += chanout[6] & chip->pan[25];
+ c += chanout[6] & chip->pan[26];
+ d += chanout[6] & chip->pan[27];
+ a += chanout[7] & chip->pan[28];
+ b += chanout[7] & chip->pan[29];
+ c += chanout[7] & chip->pan[30];
+ d += chanout[7] & chip->pan[31];
+ a += chanout[8] & chip->pan[32];
+ b += chanout[8] & chip->pan[33];
+ c += chanout[8] & chip->pan[34];
+ d += chanout[8] & chip->pan[35];
+
+ /* accumulator register set #2 */
+ a += chanout[9] & chip->pan[36];
+ b += chanout[9] & chip->pan[37];
+ c += chanout[9] & chip->pan[38];
+ d += chanout[9] & chip->pan[39];
+ a += chanout[10] & chip->pan[40];
+ b += chanout[10] & chip->pan[41];
+ c += chanout[10] & chip->pan[42];
+ d += chanout[10] & chip->pan[43];
+ a += chanout[11] & chip->pan[44];
+ b += chanout[11] & chip->pan[45];
+ c += chanout[11] & chip->pan[46];
+ d += chanout[11] & chip->pan[47];
+
+ a += chanout[12] & chip->pan[48];
+ b += chanout[12] & chip->pan[49];
+ c += chanout[12] & chip->pan[50];
+ d += chanout[12] & chip->pan[51];
+ a += chanout[13] & chip->pan[52];
+ b += chanout[13] & chip->pan[53];
+ c += chanout[13] & chip->pan[54];
+ d += chanout[13] & chip->pan[55];
+ a += chanout[14] & chip->pan[56];
+ b += chanout[14] & chip->pan[57];
+ c += chanout[14] & chip->pan[58];
+ d += chanout[14] & chip->pan[59];
+
+ a += chanout[15] & chip->pan[60];
+ b += chanout[15] & chip->pan[61];
+ c += chanout[15] & chip->pan[62];
+ d += chanout[15] & chip->pan[63];
+ a += chanout[16] & chip->pan[64];
+ b += chanout[16] & chip->pan[65];
+ c += chanout[16] & chip->pan[66];
+ d += chanout[16] & chip->pan[67];
+ a += chanout[17] & chip->pan[68];
+ b += chanout[17] & chip->pan[69];
+ c += chanout[17] & chip->pan[70];
+ d += chanout[17] & chip->pan[71];
+#endif
+ a >>= FINAL_SH;
+ b >>= FINAL_SH;
+ c >>= FINAL_SH;
+ d >>= FINAL_SH;
+
+ /* limit check */
+ a = limit( a , MAXOUT, MINOUT );
+ b = limit( b , MAXOUT, MINOUT );
+ c = limit( c , MAXOUT, MINOUT );
+ d = limit( d , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ ch_a[i] = a;
+ ch_b[i] = b;
+ ch_c[i] = c;
+ ch_d[i] = d;
+
+ advance(chip);
+ }
+
+}
diff --git a/src/devices/sound/ymf262.h b/src/devices/sound/ymf262.h
new file mode 100644
index 00000000000..904b4f259c5
--- /dev/null
+++ b/src/devices/sound/ymf262.h
@@ -0,0 +1,50 @@
+// license:???
+// copyright-holders:Jarek Burczynski
+#pragma once
+
+#ifndef __YMF262_H__
+#define __YMF262_H__
+
+/* select number of output bits: 8 or 16 */
+#define OPL3_SAMPLE_BITS 16
+
+/* compiler dependence */
+#ifndef __OSDCOMM_H__
+#define __OSDCOMM_H__
+typedef unsigned char UINT8; /* unsigned 8bit */
+typedef unsigned short UINT16; /* unsigned 16bit */
+typedef unsigned int UINT32; /* unsigned 32bit */
+typedef signed char INT8; /* signed 8bit */
+typedef signed short INT16; /* signed 16bit */
+typedef signed int INT32; /* signed 32bit */
+#endif
+
+typedef stream_sample_t OPL3SAMPLE;
+/*
+#if (OPL3_SAMPLE_BITS==16)
+typedef INT16 OPL3SAMPLE;
+#endif
+#if (OPL3_SAMPLE_BITS==8)
+typedef INT8 OPL3SAMPLE;
+#endif
+*/
+
+typedef void (*OPL3_TIMERHANDLER)(void *param,int timer,const attotime &period);
+typedef void (*OPL3_IRQHANDLER)(void *param,int irq);
+typedef void (*OPL3_UPDATEHANDLER)(void *param,int min_interval_us);
+
+
+void *ymf262_init(device_t *device, int clock, int rate);
+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, void *param);
+void ymf262_set_irq_handler(void *chip, OPL3_IRQHANDLER IRQHandler, void *param);
+void ymf262_set_update_handler(void *chip, OPL3_UPDATEHANDLER UpdateHandler, void *param);
+
+
+#endif /* __YMF262_H__ */
diff --git a/src/devices/sound/ymf271.c b/src/devices/sound/ymf271.c
new file mode 100644
index 00000000000..c10420af8a4
--- /dev/null
+++ b/src/devices/sound/ymf271.c
@@ -0,0 +1,1777 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert, R. Belmont, hap
+/*
+ Yamaha YMF271-F "OPX" emulator v0.1
+ By R. Belmont.
+ Based in part on YMF278B emulator by R. Belmont and O. Galibert.
+ 12June04 update by Toshiaki Nijiura
+ Copyright R. Belmont.
+
+ TODO:
+ - A/L bit (alternate loop)
+ - EN and EXT Out bits
+ - Src B and Src NOTE bits
+ - statusreg Busy and End bits
+ - timer register 0x11
+ - ch2/ch3 (4 speakers)
+ - PFM (FM using external PCM waveform)
+ - detune (should be same as on other Yamaha chips)
+ - Acc On bit (some sound effects in viprp1?). The documentation says
+ "determines if slot output is accumulated(1), or output directly(0)"
+ - Is memory handling 100% correct? At the moment, seibuspi.c is the only
+ hardware currently emulated that uses external handlers.
+*/
+
+#include "emu.h"
+#include "ymf271.h"
+
+#define STD_CLOCK (16934400)
+
+#define MAXOUT (+32767)
+#define MINOUT (-32768)
+
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define LFO_LENGTH 256
+#define LFO_SHIFT 8
+#define PLFO_MAX (+1.0)
+#define PLFO_MIN (-1.0)
+#define ALFO_MAX (+65536)
+#define ALFO_MIN (0)
+
+#define ENV_ATTACK 0
+#define ENV_DECAY1 1
+#define ENV_DECAY2 2
+#define ENV_RELEASE 3
+
+#define OP_INPUT_FEEDBACK -1
+#define OP_INPUT_NONE -2
+
+#define ENV_VOLUME_SHIFT 16
+
+#define INF -1.0
+
+static const double ARTime[64] =
+{
+ INF, INF, INF, INF, 6188.12, 4980.68, 4144.76, 3541.04,
+ 3094.06, 2490.34, 2072.38, 1770.52, 1547.03, 1245.17, 1036.19, 885.26,
+ 773.51, 622.59, 518.10, 441.63, 386.76, 311.29, 259.05, 221.32,
+ 193.38, 155.65, 129.52, 110.66, 96.69, 77.82, 64.76, 55.33,
+ 48.34, 38.91, 32.38, 27.66, 24.17, 19.46, 16.19, 13.83,
+ 12.09, 9.73, 8.10, 6.92, 6.04, 4.86, 4.05, 3.46,
+ 3.02, 2.47, 2.14, 1.88, 1.70, 1.38, 1.16, 1.02,
+ 0.88, 0.70, 0.57, 0.48, 0.43, 0.43, 0.43, 0.07
+};
+
+static const double DCTime[64] =
+{
+ INF, INF, INF, INF, 93599.64, 74837.91, 62392.02, 53475.56,
+ 46799.82, 37418.96, 31196.01, 26737.78, 23399.91, 18709.48, 15598.00, 13368.89,
+ 11699.95, 9354.74, 7799.00, 6684.44, 5849.98, 4677.37, 3899.50, 3342.22,
+ 2924.99, 2338.68, 1949.75, 1671.11, 1462.49, 1169.34, 974.88, 835.56,
+ 731.25, 584.67, 487.44, 417.78, 365.62, 292.34, 243.72, 208.89,
+ 182.81, 146.17, 121.86, 104.44, 91.41, 73.08, 60.93, 52.22,
+ 45.69, 36.55, 33.85, 26.09, 22.83, 18.28, 15.22, 13.03,
+ 11.41, 9.12, 7.60, 6.51, 5.69, 5.69, 5.69, 5.69
+};
+
+/* Notes about the LFO Frequency Table below:
+
+ There are 2 known errors in the LFO table listed in the original manual.
+
+ Both 201 & 202 are listed as 3.74490. 202 has been computed/corrected to 3.91513
+ 232 was listed as 13.35547 but has been replaced with the correct value of 14.35547.
+
+ Corrections are computed values based on formulas by Olivier Galibert & Nicola Salmoria listed below:
+
+LFO period seems easy to compute:
+
+Olivier Galibert's version Nicola Salmoria's version
+
+int lfo_period(int entry) or int calc_lfo_period(int entry)
+{ {
+ int ma, ex; entry = 256 - entry;
+ entry = 256-entry;
+ ma = entry & 15; if (entry < 16)
+ {
+ ex = entry >> 4; return (entry & 0x0f) << 7;
+ if(ex) }
+ return (ma | 16) << (ex+6); else
+ else {
+ return ma << 7; int shift = 6 + (entry >> 4);
+} return (0x10 + (entry & 0x0f)) << shift;
+ }
+lfo_freq = 44100 / lfo_period }
+
+*/
+
+static const double LFO_frequency_table[256] =
+{
+ 0.00066, 0.00068, 0.00070, 0.00073, 0.00075, 0.00078, 0.00081, 0.00084,
+ 0.00088, 0.00091, 0.00096, 0.00100, 0.00105, 0.00111, 0.00117, 0.00124,
+ 0.00131, 0.00136, 0.00140, 0.00145, 0.00150, 0.00156, 0.00162, 0.00168,
+ 0.00175, 0.00183, 0.00191, 0.00200, 0.00210, 0.00221, 0.00234, 0.00247,
+ 0.00263, 0.00271, 0.00280, 0.00290, 0.00300, 0.00312, 0.00324, 0.00336,
+ 0.00350, 0.00366, 0.00382, 0.00401, 0.00421, 0.00443, 0.00467, 0.00495,
+ 0.00526, 0.00543, 0.00561, 0.00580, 0.00601, 0.00623, 0.00647, 0.00673,
+ 0.00701, 0.00731, 0.00765, 0.00801, 0.00841, 0.00885, 0.00935, 0.00990,
+ 0.01051, 0.01085, 0.01122, 0.01160, 0.01202, 0.01246, 0.01294, 0.01346,
+ 0.01402, 0.01463, 0.01529, 0.01602, 0.01682, 0.01771, 0.01869, 0.01979,
+ 0.02103, 0.02171, 0.02243, 0.02320, 0.02403, 0.02492, 0.02588, 0.02692,
+ 0.02804, 0.02926, 0.03059, 0.03204, 0.03365, 0.03542, 0.03738, 0.03958,
+ 0.04206, 0.04341, 0.04486, 0.04641, 0.04807, 0.04985, 0.05176, 0.05383,
+ 0.05608, 0.05851, 0.06117, 0.06409, 0.06729, 0.07083, 0.07477, 0.07917,
+ 0.08411, 0.08683, 0.08972, 0.09282, 0.09613, 0.09969, 0.10353, 0.10767,
+ 0.11215, 0.11703, 0.12235, 0.12817, 0.13458, 0.14167, 0.14954, 0.15833,
+ 0.16823, 0.17365, 0.17944, 0.18563, 0.19226, 0.19938, 0.20705, 0.21533,
+ 0.22430, 0.23406, 0.24470, 0.25635, 0.26917, 0.28333, 0.29907, 0.31666,
+ 0.33646, 0.34731, 0.35889, 0.37126, 0.38452, 0.39876, 0.41410, 0.43066,
+ 0.44861, 0.46811, 0.48939, 0.51270, 0.53833, 0.56666, 0.59814, 0.63333,
+ 0.67291, 0.69462, 0.71777, 0.74252, 0.76904, 0.79753, 0.82820, 0.86133,
+ 0.89722, 0.93623, 0.97878, 1.02539, 1.07666, 1.13333, 1.19629, 1.26666,
+ 1.34583, 1.38924, 1.43555, 1.48505, 1.53809, 1.59509, 1.65640, 1.72266,
+ 1.79443, 1.87245, 1.95756, 2.05078, 2.15332, 2.26665, 2.39258, 2.53332,
+ 2.69165, 2.77848, 2.87109, 2.97010, 3.07617, 3.19010, 3.31280, 3.44531,
+ 3.58887, 3.74490, 3.91513, 4.10156, 4.30664, 4.53331, 4.78516, 5.06664,
+ 5.38330, 5.55696, 5.74219, 5.94019, 6.15234, 6.38021, 6.62560, 6.89062,
+ 7.17773, 7.48981, 7.83026, 8.20312, 8.61328, 9.06661, 9.57031, 10.13327,
+ 10.76660, 11.11391, 11.48438, 11.88039, 12.30469, 12.76042, 13.25120, 13.78125,
+ 14.35547, 14.97962, 15.66051, 16.40625, 17.22656, 18.13322, 19.14062, 20.26654,
+ 21.53320, 22.96875, 24.60938, 26.50240, 28.71094, 31.32102, 34.45312, 38.28125,
+ 43.06641, 49.21875, 57.42188, 68.90625, 86.13281, 114.84375, 172.26562, 344.53125
+};
+
+static const int RKS_Table[32][8] =
+{
+ { 0, 0, 0, 0, 0, 2, 4, 8 },
+ { 0, 0, 0, 0, 1, 3, 5, 9 },
+ { 0, 0, 0, 1, 2, 4, 6, 10 },
+ { 0, 0, 0, 1, 3, 5, 7, 11 },
+ { 0, 0, 1, 2, 4, 6, 8, 12 },
+ { 0, 0, 1, 2, 5, 7, 9, 13 },
+ { 0, 0, 1, 3, 6, 8, 10, 14 },
+ { 0, 0, 1, 3, 7, 9, 11, 15 },
+ { 0, 1, 2, 4, 8, 10, 12, 16 },
+ { 0, 1, 2, 4, 9, 11, 13, 17 },
+ { 0, 1, 2, 5, 10, 12, 14, 18 },
+ { 0, 1, 2, 5, 11, 13, 15, 19 },
+ { 0, 1, 3, 6, 12, 14, 16, 20 },
+ { 0, 1, 3, 6, 13, 15, 17, 21 },
+ { 0, 1, 3, 7, 14, 16, 18, 22 },
+ { 0, 1, 3, 7, 15, 17, 19, 23 },
+ { 0, 2, 4, 8, 16, 18, 20, 24 },
+ { 0, 2, 4, 8, 17, 19, 21, 25 },
+ { 0, 2, 4, 9, 18, 20, 22, 26 },
+ { 0, 2, 4, 9, 19, 21, 23, 27 },
+ { 0, 2, 5, 10, 20, 22, 24, 28 },
+ { 0, 2, 5, 10, 21, 23, 25, 29 },
+ { 0, 2, 5, 11, 22, 24, 26, 30 },
+ { 0, 2, 5, 11, 23, 25, 27, 31 },
+ { 0, 3, 6, 12, 24, 26, 28, 31 },
+ { 0, 3, 6, 12, 25, 27, 29, 31 },
+ { 0, 3, 6, 13, 26, 28, 30, 31 },
+ { 0, 3, 6, 13, 27, 29, 31, 31 },
+ { 0, 3, 7, 14, 28, 30, 31, 31 },
+ { 0, 3, 7, 14, 29, 31, 31, 31 },
+ { 0, 3, 7, 15, 30, 31, 31, 31 },
+ { 0, 3, 7, 15, 31, 31, 31, 31 },
+};
+
+static const double multiple_table[16] = { 0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+
+static const double pow_table[16] = { 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 0.5, 1, 2, 4, 8, 16, 32, 64 };
+
+static const double fs_frequency[4] = { 1.0/1.0, 1.0/2.0, 1.0/4.0, 1.0/8.0 };
+
+static const double channel_attenuation_table[16] =
+{
+ 0.0, 2.5, 6.0, 8.5, 12.0, 14.5, 18.1, 20.6, 24.1, 26.6, 30.1, 32.6, 36.1, 96.1, 96.1, 96.1
+};
+
+static const int modulation_level[8] = { 16, 8, 4, 2, 1, 32, 64, 128 };
+
+// feedback_level * 16
+static const int feedback_level[8] = { 0, 1, 2, 4, 8, 16, 32, 64 };
+
+// slot mapping assists
+static const int fm_tab[16] = { 0, 1, 2, -1, 3, 4, 5, -1, 6, 7, 8, -1, 9, 10, 11, -1 };
+static const int pcm_tab[16] = { 0, 4, 8, -1, 12, 16, 20, -1, 24, 28, 32, -1, 36, 40, 44, -1 };
+
+
+/*****************************************************************************/
+
+void ymf271_device::calculate_step(YMF271Slot *slot)
+{
+ double st;
+
+ if (slot->waveform == 7)
+ {
+ // external waveform (PCM)
+ st = (double)(2 * (slot->fns | 2048)) * pow_table[slot->block] * fs_frequency[slot->fs];
+ st = st * multiple_table[slot->multiple];
+
+ // LFO phase modulation
+ st *= slot->lfo_phasemod;
+
+ st /= (double)(524288/65536); // pre-multiply with 65536
+
+ slot->step = (UINT32)st;
+ }
+ else
+ {
+ // internal waveform (FM)
+ st = (double)(2 * slot->fns) * pow_table[slot->block];
+ st = st * multiple_table[slot->multiple] * (double)(SIN_LEN);
+
+ // LFO phase modulation
+ st *= slot->lfo_phasemod;
+
+ st /= (double)(536870912/65536); // pre-multiply with 65536
+
+ slot->step = (UINT32)st;
+ }
+}
+
+inline bool ymf271_device::check_envelope_end(YMF271Slot *slot)
+{
+ if (slot->volume <= 0)
+ {
+ slot->active = 0;
+ slot->volume = 0;
+ return true;
+ }
+ return false;
+}
+
+void ymf271_device::update_envelope(YMF271Slot *slot)
+{
+ switch (slot->env_state)
+ {
+ case ENV_ATTACK:
+ {
+ slot->volume += slot->env_attack_step;
+
+ if (slot->volume >= (255 << ENV_VOLUME_SHIFT))
+ {
+ slot->volume = (255 << ENV_VOLUME_SHIFT);
+ slot->env_state = ENV_DECAY1;
+ }
+ break;
+ }
+
+ case ENV_DECAY1:
+ {
+ int decay_level = 255 - (slot->decay1lvl << 4);
+ slot->volume -= slot->env_decay1_step;
+
+ if (!check_envelope_end(slot) && (slot->volume >> ENV_VOLUME_SHIFT) <= decay_level)
+ {
+ slot->env_state = ENV_DECAY2;
+ }
+ break;
+ }
+
+ case ENV_DECAY2:
+ {
+ slot->volume -= slot->env_decay2_step;
+ check_envelope_end(slot);
+ break;
+ }
+
+ case ENV_RELEASE:
+ {
+ slot->volume -= slot->env_release_step;
+ check_envelope_end(slot);
+ break;
+ }
+ }
+}
+
+inline int ymf271_device::get_keyscaled_rate(int rate, int keycode, int keyscale)
+{
+ int newrate = rate + RKS_Table[keycode][keyscale];
+
+ if (newrate > 63)
+ {
+ newrate = 63;
+ }
+ if (newrate < 0)
+ {
+ newrate = 0;
+ }
+ return newrate;
+}
+
+inline int ymf271_device::get_internal_keycode(int block, int fns)
+{
+ int n43;
+ if (fns < 0x780)
+ {
+ n43 = 0;
+ }
+ else if (fns < 0x900)
+ {
+ n43 = 1;
+ }
+ else if (fns < 0xa80)
+ {
+ n43 = 2;
+ }
+ else
+ {
+ n43 = 3;
+ }
+
+ return ((block & 7) * 4) + n43;
+}
+
+inline int ymf271_device::get_external_keycode(int block, int fns)
+{
+ int n43;
+ if (fns < 0x100)
+ {
+ n43 = 0;
+ }
+ else if (fns < 0x300)
+ {
+ n43 = 1;
+ }
+ else if (fns < 0x500)
+ {
+ n43 = 2;
+ }
+ else
+ {
+ n43 = 3;
+ }
+
+ return ((block & 7) * 4) + n43;
+}
+
+void ymf271_device::init_envelope(YMF271Slot *slot)
+{
+ int keycode, rate;
+ int decay_level = 255 - (slot->decay1lvl << 4);
+
+ if (slot->waveform != 7)
+ {
+ keycode = get_internal_keycode(slot->block, slot->fns);
+ }
+ else
+ {
+ keycode = get_external_keycode(slot->block, slot->fns & 0x7ff);
+ /* keycode = (keycode + slot->srcb * 4 + slot->srcnote) / 2; */ // not sure
+ }
+
+ // init attack state
+ rate = get_keyscaled_rate(slot->ar * 2, keycode, slot->keyscale);
+ slot->env_attack_step = (rate < 4) ? 0 : (int)(((double)(255-0) / m_lut_ar[rate]) * 65536.0);
+
+ // init decay1 state
+ rate = get_keyscaled_rate(slot->decay1rate * 2, keycode, slot->keyscale);
+ slot->env_decay1_step = (rate < 4) ? 0 : (int)(((double)(255-decay_level) / m_lut_dc[rate]) * 65536.0);
+
+ // init decay2 state
+ rate = get_keyscaled_rate(slot->decay2rate * 2, keycode, slot->keyscale);
+ slot->env_decay2_step = (rate < 4) ? 0 : (int)(((double)(255-0) / m_lut_dc[rate]) * 65536.0);
+
+ // init release state
+ rate = get_keyscaled_rate(slot->relrate * 4, keycode, slot->keyscale);
+ slot->env_release_step = (rate < 4) ? 0 : (int)(((double)(255-0) / m_lut_ar[rate]) * 65536.0);
+
+ slot->volume = (255-160) << ENV_VOLUME_SHIFT; // -60db
+ slot->env_state = ENV_ATTACK;
+}
+
+void ymf271_device::init_lfo(YMF271Slot *slot)
+{
+ slot->lfo_phase = 0;
+ slot->lfo_amplitude = 0;
+ slot->lfo_phasemod = 0;
+
+ slot->lfo_step = (int)((((double)LFO_LENGTH * m_lut_lfo[slot->lfoFreq]) / 44100.0) * 256.0);
+}
+
+void ymf271_device::update_lfo(YMF271Slot *slot)
+{
+ slot->lfo_phase += slot->lfo_step;
+
+ slot->lfo_amplitude = m_lut_alfo[slot->lfowave][(slot->lfo_phase >> LFO_SHIFT) & (LFO_LENGTH-1)];
+ slot->lfo_phasemod = m_lut_plfo[slot->lfowave][slot->pms][(slot->lfo_phase >> LFO_SHIFT) & (LFO_LENGTH-1)];
+
+ calculate_step(slot);
+}
+
+INT64 ymf271_device::calculate_slot_volume(YMF271Slot *slot)
+{
+ INT64 volume;
+ INT64 env_volume;
+ INT64 lfo_volume = 65536;
+
+ switch (slot->ams)
+ {
+ case 0: lfo_volume = 65536; break; // 0dB
+ case 1: lfo_volume = 65536 - ((slot->lfo_amplitude * 33124) >> 16); break; // 5.90625dB
+ case 2: lfo_volume = 65536 - ((slot->lfo_amplitude * 16742) >> 16); break; // 11.8125dB
+ case 3: lfo_volume = 65536 - ((slot->lfo_amplitude * 4277) >> 16); break; // 23.625dB
+ }
+
+ env_volume = (m_lut_env_volume[255 - (slot->volume >> ENV_VOLUME_SHIFT)] * lfo_volume) >> 16;
+
+ volume = (env_volume * m_lut_total_level[slot->tl]) >> 16;
+
+ return volume;
+}
+
+void ymf271_device::update_pcm(int slotnum, INT32 *mixp, int length)
+{
+ int i;
+ INT64 final_volume;
+ INT16 sample;
+ INT64 ch0_vol, ch1_vol; //, ch2_vol, ch3_vol;
+
+ YMF271Slot *slot = &m_slots[slotnum];
+
+ if (!slot->active)
+ {
+ return;
+ }
+
+ if (slot->waveform != 7)
+ {
+ fatalerror("Waveform %d in update_pcm!!!\n", slot->waveform);
+ }
+
+ for (i = 0; i < length; i++)
+ {
+ // loop
+ if ((slot->stepptr>>16) > slot->endaddr)
+ {
+ slot->stepptr = slot->stepptr - ((UINT64)slot->endaddr<<16) + ((UINT64)slot->loopaddr<<16);
+ if ((slot->stepptr>>16) > slot->endaddr)
+ {
+ // overflow
+ slot->stepptr &= 0xffff;
+ slot->stepptr |= ((UINT64)slot->loopaddr<<16);
+ if ((slot->stepptr>>16) > slot->endaddr)
+ {
+ // still overflow? (triggers in rdft2, rarely)
+ slot->stepptr &= 0xffff;
+ slot->stepptr |= ((UINT64)slot->endaddr<<16);
+ }
+ }
+ }
+
+ if (slot->bits == 8)
+ {
+ // 8bit
+ sample = ymf271_read_memory(slot->startaddr + (slot->stepptr>>16))<<8;
+ }
+ else
+ {
+ // 12bit
+ if (slot->stepptr & 0x10000)
+ sample = ymf271_read_memory(slot->startaddr + (slot->stepptr>>17)*3 + 2)<<8 | ((ymf271_read_memory(slot->startaddr + (slot->stepptr>>17)*3 + 1) << 4) & 0xf0);
+ else
+ sample = ymf271_read_memory(slot->startaddr + (slot->stepptr>>17)*3)<<8 | (ymf271_read_memory(slot->startaddr + (slot->stepptr>>17)*3 + 1) & 0xf0);
+ }
+
+ update_envelope(slot);
+ update_lfo(slot);
+
+ final_volume = calculate_slot_volume(slot);
+
+ ch0_vol = (final_volume * m_lut_attenuation[slot->ch0_level]) >> 16;
+ ch1_vol = (final_volume * m_lut_attenuation[slot->ch1_level]) >> 16;
+// ch2_vol = (final_volume * m_lut_attenuation[slot->ch2_level]) >> 16;
+// ch3_vol = (final_volume * m_lut_attenuation[slot->ch3_level]) >> 16;
+
+ if (ch0_vol > 65536) ch0_vol = 65536;
+ if (ch1_vol > 65536) ch1_vol = 65536;
+
+ *mixp++ += (sample * ch0_vol) >> 16;
+ *mixp++ += (sample * ch1_vol) >> 16;
+
+ // go to next step
+ slot->stepptr += slot->step;
+ }
+}
+
+// calculates the output of one FM operator
+INT64 ymf271_device::calculate_op(int slotnum, INT64 inp)
+{
+ YMF271Slot *slot = &m_slots[slotnum];
+ INT64 env, slot_output, slot_input = 0;
+
+ update_envelope(slot);
+ update_lfo(slot);
+ env = calculate_slot_volume(slot);
+
+ if (inp == OP_INPUT_FEEDBACK)
+ {
+ // from own feedback
+ slot_input = (slot->feedback_modulation0 + slot->feedback_modulation1) / 2;
+ slot->feedback_modulation0 = slot->feedback_modulation1;
+ }
+ else if (inp != OP_INPUT_NONE)
+ {
+ // from previous slot output
+ slot_input = ((inp << (SIN_BITS-2)) * modulation_level[slot->feedback]);
+ }
+
+ slot_output = m_lut_waves[slot->waveform][((slot->stepptr + slot_input) >> 16) & SIN_MASK];
+ slot_output = (slot_output * env) >> 16;
+ slot->stepptr += slot->step;
+
+ return slot_output;
+}
+
+void ymf271_device::set_feedback(int slotnum, INT64 inp)
+{
+ YMF271Slot *slot = &m_slots[slotnum];
+ slot->feedback_modulation1 = (((inp << (SIN_BITS-2)) * feedback_level[slot->feedback]) / 16);
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void ymf271_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int i, j;
+ int op;
+ INT32 *mixp;
+
+ memset(m_mix_buffer, 0, sizeof(m_mix_buffer[0])*samples*2);
+
+ for (j = 0; j < 12; j++)
+ {
+ YMF271Group *slot_group = &m_groups[j];
+ mixp = m_mix_buffer;
+
+ if (slot_group->pfm && slot_group->sync != 3)
+ {
+ popmessage("ymf271 PFM, contact MAMEdev");
+ logerror("ymf271 Group %d: PFM, Sync = %d, Waveform Slot1 = %d, Slot2 = %d, Slot3 = %d, Slot4 = %d\n",
+ j, slot_group->sync, m_slots[j+0].waveform, m_slots[j+12].waveform, m_slots[j+24].waveform, m_slots[j+36].waveform);
+ }
+
+ switch (slot_group->sync)
+ {
+ // 4 operator FM
+ case 0:
+ {
+ int slot1 = j + (0*12);
+ int slot2 = j + (1*12);
+ int slot3 = j + (2*12);
+ int slot4 = j + (3*12);
+ mixp = m_mix_buffer;
+
+ if (m_slots[slot1].active)
+ {
+ for (i = 0; i < samples; i++)
+ {
+ INT64 output1 = 0, output2 = 0, output3 = 0, output4 = 0;
+ INT64 phase_mod1 = 0, phase_mod2 = 0, phase_mod3 = 0;
+ switch (m_slots[slot1].algorithm)
+ {
+ // <--------|
+ // +--[S1]--|--+--[S3]--+--[S2]--+--[S4]-->
+ case 0:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ phase_mod3 = calculate_op(slot3, phase_mod1);
+ phase_mod2 = calculate_op(slot2, phase_mod3);
+ output4 = calculate_op(slot4, phase_mod2);
+ break;
+
+ // <-----------------|
+ // +--[S1]--+--[S3]--|--+--[S2]--+--[S4]-->
+ case 1:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ phase_mod3 = calculate_op(slot3, phase_mod1);
+ set_feedback(slot1, phase_mod3);
+ phase_mod2 = calculate_op(slot2, phase_mod3);
+ output4 = calculate_op(slot4, phase_mod2);
+ break;
+
+ // <--------|
+ // +--[S1]--|
+ // |
+ // --[S3]--+--[S2]--+--[S4]-->
+ case 2:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ phase_mod3 = calculate_op(slot3, OP_INPUT_NONE);
+ phase_mod2 = calculate_op(slot2, (phase_mod1 + phase_mod3) / 1);
+ output4 = calculate_op(slot4, phase_mod2);
+ break;
+
+ // <--------|
+ // +--[S1]--|
+ // |
+ // --[S3]--+--[S2]--+--[S4]-->
+ case 3:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ phase_mod3 = calculate_op(slot3, OP_INPUT_NONE);
+ phase_mod2 = calculate_op(slot2, phase_mod3);
+ output4 = calculate_op(slot4, (phase_mod1 + phase_mod2) / 1);
+ break;
+
+ // --[S2]--|
+ // <--------| |
+ // +--[S1]--|--+--[S3]--+--[S4]-->
+ case 4:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ phase_mod3 = calculate_op(slot3, phase_mod1);
+ phase_mod2 = calculate_op(slot2, OP_INPUT_NONE);
+ output4 = calculate_op(slot4, (phase_mod3 + phase_mod2) / 1);
+ break;
+
+ // --[S2]-----|
+ // <-----------------| |
+ // +--[S1]--+--[S3]--|--+--[S4]-->
+ case 5:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ phase_mod3 = calculate_op(slot3, phase_mod1);
+ set_feedback(slot1, phase_mod3);
+ phase_mod2 = calculate_op(slot2, OP_INPUT_NONE);
+ output4 = calculate_op(slot4, (phase_mod3 + phase_mod2) / 1);
+ break;
+
+ // --[S2]-----+--[S4]--|
+ // |
+ // <--------| |
+ // +--[S1]--|--+--[S3]--+-->
+ case 6:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output3 = calculate_op(slot3, phase_mod1);
+ phase_mod2 = calculate_op(slot2, OP_INPUT_NONE);
+ output4 = calculate_op(slot4, phase_mod2);
+ break;
+
+ // --[S2]--+--[S4]-----|
+ // |
+ // <-----------------| |
+ // +--[S1]--+--[S3]--|--+-->
+ case 7:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ phase_mod3 = calculate_op(slot3, phase_mod1);
+ set_feedback(slot1, phase_mod3);
+ output3 = phase_mod3;
+ phase_mod2 = calculate_op(slot2, OP_INPUT_NONE);
+ output4 = calculate_op(slot4, phase_mod2);
+ break;
+
+ // --[S3]--+--[S2]--+--[S4]--|
+ // |
+ // <--------| |
+ // +--[S1]--|-----------------+-->
+ case 8:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output1 = phase_mod1;
+ phase_mod3 = calculate_op(slot3, OP_INPUT_NONE);
+ phase_mod2 = calculate_op(slot2, phase_mod3);
+ output4 = calculate_op(slot4, phase_mod2);
+ break;
+
+ // <--------|
+ // +--[S1]--|
+ // |
+ // --[S3]--| |
+ // --[S2]--+--[S4]--+-->
+ case 9:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output1 = phase_mod1;
+ phase_mod3 = calculate_op(slot3, OP_INPUT_NONE);
+ phase_mod2 = calculate_op(slot2, OP_INPUT_NONE);
+ output4 = calculate_op(slot4, (phase_mod3 + phase_mod2) / 1);
+ break;
+
+ // --[S4]--|
+ // --[S2]--|
+ // <--------| |
+ // +--[S1]--|--+--[S3]--+-->
+ case 10:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output3 = calculate_op(slot3, phase_mod1);
+ output2 = calculate_op(slot2, OP_INPUT_NONE);
+ output4 = calculate_op(slot4, OP_INPUT_NONE);
+ break;
+
+ // --[S4]-----|
+ // --[S2]-----|
+ // <-----------------| |
+ // +--[S1]--+--[S3]--|--+-->
+ case 11:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ phase_mod3 = calculate_op(slot3, phase_mod1);
+ set_feedback(slot1, phase_mod3);
+ output3 = phase_mod3;
+ output2 = calculate_op(slot2, OP_INPUT_NONE);
+ output4 = calculate_op(slot4, OP_INPUT_NONE);
+ break;
+
+ // |--+--[S4]--|
+ // <--------| |--+--[S3]--|
+ // +--[S1]--|--|--+--[S2]--+-->
+ case 12:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output3 = calculate_op(slot3, phase_mod1);
+ output2 = calculate_op(slot2, phase_mod1);
+ output4 = calculate_op(slot4, phase_mod1);
+ break;
+
+ // --[S3]--+--[S2]--|
+ // |
+ // --[S4]-----------|
+ // <--------| |
+ // +--[S1]--|--------+-->
+ case 13:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output1 = phase_mod1;
+ phase_mod3 = calculate_op(slot3, OP_INPUT_NONE);
+ output2 = calculate_op(slot2, phase_mod3);
+ output4 = calculate_op(slot4, OP_INPUT_NONE);
+ break;
+
+ // --[S2]-----+--[S4]--|
+ // |
+ // <--------| +--[S3]--|
+ // +--[S1]--|--|--------+-->
+ case 14:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output1 = phase_mod1;
+ output3 = calculate_op(slot3, phase_mod1);
+ phase_mod2 = calculate_op(slot2, OP_INPUT_NONE);
+ output4 = calculate_op(slot4, phase_mod2);
+ break;
+
+ // --[S4]-----|
+ // --[S2]-----|
+ // --[S3]-----|
+ // <--------| |
+ // +--[S1]--|--+-->
+ case 15:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output1 = phase_mod1;
+ output3 = calculate_op(slot3, OP_INPUT_NONE);
+ output2 = calculate_op(slot2, OP_INPUT_NONE);
+ output4 = calculate_op(slot4, OP_INPUT_NONE);
+ break;
+ }
+
+ *mixp++ += ((output1 * m_lut_attenuation[m_slots[slot1].ch0_level]) +
+ (output2 * m_lut_attenuation[m_slots[slot2].ch0_level]) +
+ (output3 * m_lut_attenuation[m_slots[slot3].ch0_level]) +
+ (output4 * m_lut_attenuation[m_slots[slot4].ch0_level])) >> 16;
+ *mixp++ += ((output1 * m_lut_attenuation[m_slots[slot1].ch1_level]) +
+ (output2 * m_lut_attenuation[m_slots[slot2].ch1_level]) +
+ (output3 * m_lut_attenuation[m_slots[slot3].ch1_level]) +
+ (output4 * m_lut_attenuation[m_slots[slot4].ch1_level])) >> 16;
+ }
+ }
+ break;
+ }
+
+ // 2x 2 operator FM
+ case 1:
+ {
+ for (op = 0; op < 2; op++)
+ {
+ int slot1 = j + ((op + 0) * 12);
+ int slot3 = j + ((op + 2) * 12);
+
+ mixp = m_mix_buffer;
+ if (m_slots[slot1].active)
+ {
+ for (i = 0; i < samples; i++)
+ {
+ INT64 output1 = 0, output3 = 0;
+ INT64 phase_mod1, phase_mod3 = 0;
+ switch (m_slots[slot1].algorithm & 3)
+ {
+ // <--------|
+ // +--[S1]--|--+--[S3]-->
+ case 0:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output3 = calculate_op(slot3, phase_mod1);
+ break;
+
+ // <-----------------|
+ // +--[S1]--+--[S3]--|-->
+ case 1:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ phase_mod3 = calculate_op(slot3, phase_mod1);
+ set_feedback(slot1, phase_mod3);
+ output3 = phase_mod3;
+ break;
+
+ // --[S3]-----|
+ // <--------| |
+ // +--[S1]--|--+-->
+ case 2:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output1 = phase_mod1;
+ output3 = calculate_op(slot3, OP_INPUT_NONE);
+ break;
+ //
+ // <--------| +--[S3]--|
+ // +--[S1]--|--|--------+-->
+ case 3:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output1 = phase_mod1;
+ output3 = calculate_op(slot3, phase_mod1);
+ break;
+ }
+
+ *mixp++ += ((output1 * m_lut_attenuation[m_slots[slot1].ch0_level]) +
+ (output3 * m_lut_attenuation[m_slots[slot3].ch0_level])) >> 16;
+ *mixp++ += ((output1 * m_lut_attenuation[m_slots[slot1].ch1_level]) +
+ (output3 * m_lut_attenuation[m_slots[slot3].ch1_level])) >> 16;
+ }
+ }
+ }
+ break;
+ }
+
+ // 3 operator FM + PCM
+ case 2:
+ {
+ int slot1 = j + (0*12);
+ int slot2 = j + (1*12);
+ int slot3 = j + (2*12);
+ mixp = m_mix_buffer;
+
+ if (m_slots[slot1].active)
+ {
+ for (i = 0; i < samples; i++)
+ {
+ INT64 output1 = 0, output2 = 0, output3 = 0;
+ INT64 phase_mod1 = 0, phase_mod3 = 0;
+ switch (m_slots[slot1].algorithm & 7)
+ {
+ // <--------|
+ // +--[S1]--|--+--[S3]--+--[S2]-->
+ case 0:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ phase_mod3 = calculate_op(slot3, phase_mod1);
+ output2 = calculate_op(slot2, phase_mod3);
+ break;
+
+ // <-----------------|
+ // +--[S1]--+--[S3]--|--+--[S2]-->
+ case 1:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ phase_mod3 = calculate_op(slot3, phase_mod1);
+ set_feedback(slot1, phase_mod3);
+ output2 = calculate_op(slot2, phase_mod3);
+ break;
+
+ // --[S3]-----|
+ // <--------| |
+ // +--[S1]--|--+--[S2]-->
+ case 2:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ phase_mod3 = calculate_op(slot3, OP_INPUT_NONE);
+ output2 = calculate_op(slot2, (phase_mod1 + phase_mod3) / 1);
+ break;
+
+ // --[S3]--+--[S2]--|
+ // <--------| |
+ // +--[S1]--|--------+-->
+ case 3:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output1 = phase_mod1;
+ phase_mod3 = calculate_op(slot3, OP_INPUT_NONE);
+ output2 = calculate_op(slot2, phase_mod3);
+ break;
+
+ // --[S2]--|
+ // <--------| |
+ // +--[S1]--|--+--[S3]--+-->
+ case 4:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output3 = calculate_op(slot3, phase_mod1);
+ output2 = calculate_op(slot2, OP_INPUT_NONE);
+ break;
+
+ // --[S2]--|
+ // <-----------------| |
+ // +--[S1]--+--[S3]--|--+-->
+ case 5:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ phase_mod3 = calculate_op(slot3, phase_mod1);
+ set_feedback(slot1, phase_mod3);
+ output3 = phase_mod3;
+ output2 = calculate_op(slot2, OP_INPUT_NONE);
+ break;
+
+ // --[S2]-----|
+ // --[S3]-----|
+ // <--------| |
+ // +--[S1]--|--+-->
+ case 6:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output1 = phase_mod1;
+ output3 = calculate_op(slot3, OP_INPUT_NONE);
+ output2 = calculate_op(slot2, OP_INPUT_NONE);
+ break;
+
+ // --[S2]--|
+ // <--------| +--[S3]--|
+ // +--[S1]--|--|--------+-->
+ case 7:
+ phase_mod1 = calculate_op(slot1, OP_INPUT_FEEDBACK);
+ set_feedback(slot1, phase_mod1);
+ output1 = phase_mod1;
+ output3 = calculate_op(slot3, phase_mod1);
+ output2 = calculate_op(slot2, OP_INPUT_NONE);
+ break;
+ }
+
+ *mixp++ += ((output1 * m_lut_attenuation[m_slots[slot1].ch0_level]) +
+ (output2 * m_lut_attenuation[m_slots[slot2].ch0_level]) +
+ (output3 * m_lut_attenuation[m_slots[slot3].ch0_level])) >> 16;
+ *mixp++ += ((output1 * m_lut_attenuation[m_slots[slot1].ch1_level]) +
+ (output2 * m_lut_attenuation[m_slots[slot2].ch1_level]) +
+ (output3 * m_lut_attenuation[m_slots[slot3].ch1_level])) >> 16;
+ }
+ }
+
+ mixp = m_mix_buffer;
+ update_pcm(j + (3*12), mixp, samples);
+ break;
+ }
+
+ // PCM
+ case 3:
+ {
+ update_pcm(j + (0*12), mixp, samples);
+ update_pcm(j + (1*12), mixp, samples);
+ update_pcm(j + (2*12), mixp, samples);
+ update_pcm(j + (3*12), mixp, samples);
+ break;
+ }
+ }
+ }
+
+ mixp = m_mix_buffer;
+ for (i = 0; i < samples; i++)
+ {
+ outputs[0][i] = (*mixp++)>>2;
+ outputs[1][i] = (*mixp++)>>2;
+ }
+}
+
+void ymf271_device::write_register(int slotnum, int reg, UINT8 data)
+{
+ YMF271Slot *slot = &m_slots[slotnum];
+
+ switch (reg)
+ {
+ case 0x0:
+ slot->ext_en = (data & 0x80) ? 1 : 0;
+ slot->ext_out = (data>>3)&0xf;
+
+ if (data & 1)
+ {
+ // key on
+ slot->step = 0;
+ slot->stepptr = 0;
+
+ slot->active = 1;
+
+ calculate_step(slot);
+ init_envelope(slot);
+ init_lfo(slot);
+ slot->feedback_modulation0 = 0;
+ slot->feedback_modulation1 = 0;
+ }
+ else
+ {
+ if (slot->active)
+ {
+ slot->env_state = ENV_RELEASE;
+ }
+ }
+ break;
+
+ case 0x1:
+ slot->lfoFreq = data;
+ break;
+
+ case 0x2:
+ slot->lfowave = data & 3;
+ slot->pms = (data >> 3) & 0x7;
+ slot->ams = (data >> 6) & 0x3;
+ break;
+
+ case 0x3:
+ slot->multiple = data & 0xf;
+ slot->detune = (data >> 4) & 0x7;
+ break;
+
+ case 0x4:
+ slot->tl = data & 0x7f;
+ break;
+
+ case 0x5:
+ slot->ar = data & 0x1f;
+ slot->keyscale = (data >> 5) & 0x7;
+ break;
+
+ case 0x6:
+ slot->decay1rate = data & 0x1f;
+ break;
+
+ case 0x7:
+ slot->decay2rate = data & 0x1f;
+ break;
+
+ case 0x8:
+ slot->relrate = data & 0xf;
+ slot->decay1lvl = (data >> 4) & 0xf;
+ break;
+
+ case 0x9:
+ // write frequency and block here
+ slot->fns = (slot->fns_hi << 8 & 0x0f00) | data;
+ slot->block = slot->fns_hi >> 4 & 0xf;
+ break;
+
+ case 0xa:
+ slot->fns_hi = data;
+ break;
+
+ case 0xb:
+ slot->waveform = data & 0x7;
+ slot->feedback = (data >> 4) & 0x7;
+ slot->accon = (data & 0x80) ? 1 : 0;
+ break;
+
+ case 0xc:
+ slot->algorithm = data & 0xf;
+ break;
+
+ case 0xd:
+ slot->ch0_level = data >> 4;
+ slot->ch1_level = data & 0xf;
+ break;
+
+ case 0xe:
+ slot->ch2_level = data >> 4;
+ slot->ch3_level = data & 0xf;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void ymf271_device::ymf271_write_fm(int bank, UINT8 address, UINT8 data)
+{
+ int groupnum = fm_tab[address & 0xf];
+ if (groupnum == -1)
+ {
+ logerror("ymf271_write_fm invalid group %02X %02X\n", address, data);
+ return;
+ }
+
+ int reg = (address >> 4) & 0xf;
+
+ // check if the register is a synchronized register
+ int sync_reg = 0;
+ switch (reg)
+ {
+ case 0:
+ case 9:
+ case 10:
+ case 12:
+ case 13:
+ case 14:
+ sync_reg = 1;
+ break;
+
+ default:
+ break;
+ }
+
+ // check if the slot is key on slot for synchronizing
+ int sync_mode = 0;
+ switch (m_groups[groupnum].sync)
+ {
+ // 4 slot mode
+ case 0:
+ if (bank == 0)
+ sync_mode = 1;
+ break;
+
+ // 2x 2 slot mode
+ case 1:
+ if (bank == 0 || bank == 1)
+ sync_mode = 1;
+ break;
+
+ // 3 slot + 1 slot mode
+ case 2:
+ if (bank == 0)
+ sync_mode = 1;
+ break;
+
+ default:
+ break;
+ }
+
+ // key-on slot & synced register
+ if (sync_mode && sync_reg)
+ {
+ switch (m_groups[groupnum].sync)
+ {
+ // 4 slot mode
+ case 0:
+ write_register((12 * 0) + groupnum, reg, data);
+ write_register((12 * 1) + groupnum, reg, data);
+ write_register((12 * 2) + groupnum, reg, data);
+ write_register((12 * 3) + groupnum, reg, data);
+ break;
+
+ // 2x 2 slot mode
+ case 1:
+ if (bank == 0)
+ {
+ // Slot 1 - Slot 3
+ write_register((12 * 0) + groupnum, reg, data);
+ write_register((12 * 2) + groupnum, reg, data);
+ }
+ else
+ {
+ // Slot 2 - Slot 4
+ write_register((12 * 1) + groupnum, reg, data);
+ write_register((12 * 3) + groupnum, reg, data);
+ }
+ break;
+
+ // 3 slot + 1 slot mode (1 slot is handled normally)
+ case 2:
+ write_register((12 * 0) + groupnum, reg, data);
+ write_register((12 * 1) + groupnum, reg, data);
+ write_register((12 * 2) + groupnum, reg, data);
+ break;
+ }
+ }
+ else
+ {
+ // write register normally
+ write_register((12 * bank) + groupnum, reg, data);
+ }
+}
+
+void ymf271_device::ymf271_write_pcm(UINT8 address, UINT8 data)
+{
+ int slotnum = pcm_tab[address & 0xf];
+ if (slotnum == -1)
+ {
+ logerror("ymf271_write_pcm invalid slot %02X %02X\n", address, data);
+ return;
+ }
+ YMF271Slot *slot = &m_slots[slotnum];
+
+ switch (address >> 4 & 0xf)
+ {
+ case 0x0:
+ slot->startaddr &= ~0xff;
+ slot->startaddr |= data;
+ break;
+
+ case 0x1:
+ slot->startaddr &= ~0xff00;
+ slot->startaddr |= data<<8;
+ break;
+
+ case 0x2:
+ slot->startaddr &= ~0xff0000;
+ slot->startaddr |= (data & 0x7f)<<16;
+ slot->altloop = (data & 0x80) ? 1 : 0;
+ if (slot->altloop)
+ popmessage("ymf271 A/L, contact MAMEdev");
+ break;
+
+ case 0x3:
+ slot->endaddr &= ~0xff;
+ slot->endaddr |= data;
+ break;
+
+ case 0x4:
+ slot->endaddr &= ~0xff00;
+ slot->endaddr |= data<<8;
+ break;
+
+ case 0x5:
+ slot->endaddr &= ~0xff0000;
+ slot->endaddr |= (data & 0x7f)<<16;
+ break;
+
+ case 0x6:
+ slot->loopaddr &= ~0xff;
+ slot->loopaddr |= data;
+ break;
+
+ case 0x7:
+ slot->loopaddr &= ~0xff00;
+ slot->loopaddr |= data<<8;
+ break;
+
+ case 0x8:
+ slot->loopaddr &= ~0xff0000;
+ slot->loopaddr |= (data & 0x7f)<<16;
+ break;
+
+ case 0x9:
+ slot->fs = data & 0x3;
+ slot->bits = (data & 0x4) ? 12 : 8;
+ slot->srcnote = (data >> 3) & 0x3;
+ slot->srcb = (data >> 5) & 0x7;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void ymf271_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case 0:
+ m_status |= 1;
+
+ // assert IRQ
+ if (m_enable & 4)
+ {
+ m_irqstate |= 1;
+
+ if (!m_irq_handler.isnull())
+ m_irq_handler(1);
+ }
+
+ // reload timer
+ m_timA->adjust(attotime::from_hz(m_clock) * (384 * 4 * (256 - m_timerA)), 0);
+ break;
+
+ case 1:
+ m_status |= 2;
+
+ // assert IRQ
+ if (m_enable & 8)
+ {
+ m_irqstate |= 2;
+
+ if (!m_irq_handler.isnull())
+ m_irq_handler(1);
+ }
+
+ // reload timer
+ m_timB->adjust(attotime::from_hz(m_clock) * (384 * 16 * (256 - m_timerB)), 0);
+ break;
+
+ default:
+ assert_always(FALSE, "Unknown id in ymf271_device::device_timer");
+ break;
+ }
+}
+
+UINT8 ymf271_device::ymf271_read_memory(UINT32 offset)
+{
+ if (m_ext_read_handler.isnull())
+ {
+ if (offset < m_mem_size)
+ return m_mem_base[offset];
+
+ /* 8MB chip limit (shouldn't happen) */
+ else if (offset > 0x7fffff)
+ return ymf271_read_memory(offset & 0x7fffff);
+
+ else
+ return 0;
+ }
+ else
+ return m_ext_read_handler(offset);
+}
+
+void ymf271_device::ymf271_write_timer(UINT8 address, UINT8 data)
+{
+ if ((address & 0xf0) == 0)
+ {
+ int groupnum = fm_tab[address & 0xf];
+ if (groupnum == -1)
+ {
+ logerror("ymf271_write_timer invalid group %02X %02X\n", address, data);
+ return;
+ }
+ YMF271Group *group = &m_groups[groupnum];
+
+ group->sync = data & 0x3;
+ group->pfm = data >> 7;
+ }
+ else
+ {
+ switch (address)
+ {
+ case 0x10:
+ m_timerA = data;
+ break;
+
+ case 0x11:
+ // According to Yamaha's documentation, this sets timer A upper 2 bits
+ // (it says timer A is 10 bits). But, PCB audio recordings proves
+ // otherwise: it doesn't affect timer A frequency. (see ms32.c tetrisp)
+ // Does this register have another function regarding timer A/B?
+ break;
+
+ case 0x12:
+ m_timerB = data;
+ break;
+
+ case 0x13:
+ // timer A load
+ if (~m_enable & data & 1)
+ {
+ attotime period = attotime::from_hz(m_clock) * (384 * 4 * (256 - m_timerA));
+ m_timA->adjust((data & 1) ? period : attotime::never, 0);
+ }
+
+ // timer B load
+ if (~m_enable & data & 2)
+ {
+ attotime period = attotime::from_hz(m_clock) * (384 * 16 * (256 - m_timerB));
+ m_timB->adjust((data & 2) ? period : attotime::never, 0);
+ }
+
+ // timer A reset
+ if (data & 0x10)
+ {
+ m_irqstate &= ~1;
+ m_status &= ~1;
+
+ if (!m_irq_handler.isnull() && ~m_irqstate & 2)
+ m_irq_handler(0);
+ }
+
+ // timer B reset
+ if (data & 0x20)
+ {
+ m_irqstate &= ~2;
+ m_status &= ~2;
+
+ if (!m_irq_handler.isnull() && ~m_irqstate & 1)
+ m_irq_handler(0);
+ }
+
+ m_enable = data;
+ break;
+
+ case 0x14:
+ m_ext_address &= ~0xff;
+ m_ext_address |= data;
+ break;
+
+ case 0x15:
+ m_ext_address &= ~0xff00;
+ m_ext_address |= data << 8;
+ break;
+
+ case 0x16:
+ m_ext_address &= ~0xff0000;
+ m_ext_address |= (data & 0x7f) << 16;
+ m_ext_rw = (data & 0x80) ? 1 : 0;
+ break;
+
+ case 0x17:
+ m_ext_address = (m_ext_address + 1) & 0x7fffff;
+ if (!m_ext_rw && !m_ext_write_handler.isnull())
+ m_ext_write_handler(m_ext_address, data);
+ break;
+
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ // test
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+WRITE8_MEMBER( ymf271_device::write )
+{
+ m_stream->update();
+
+ m_regs_main[offset & 0xf] = data;
+
+ switch (offset & 0xf)
+ {
+ case 0x0:
+ case 0x2:
+ case 0x4:
+ case 0x6:
+ case 0x8:
+ case 0xc:
+ // address regs
+ break;
+
+ case 0x1:
+ ymf271_write_fm(0, m_regs_main[0x0], data);
+ break;
+
+ case 0x3:
+ ymf271_write_fm(1, m_regs_main[0x2], data);
+ break;
+
+ case 0x5:
+ ymf271_write_fm(2, m_regs_main[0x4], data);
+ break;
+
+ case 0x7:
+ ymf271_write_fm(3, m_regs_main[0x6], data);
+ break;
+
+ case 0x9:
+ ymf271_write_pcm(m_regs_main[0x8], data);
+ break;
+
+ case 0xd:
+ ymf271_write_timer(m_regs_main[0xc], data);
+ break;
+
+ default:
+ break;
+ }
+}
+
+READ8_MEMBER( ymf271_device::read )
+{
+ switch (offset & 0xf)
+ {
+ case 0x0:
+ return m_status;
+
+ case 0x1:
+ // statusreg 2
+ return 0;
+
+ case 0x2:
+ {
+ if (!m_ext_rw)
+ return 0xff;
+
+ UINT8 ret = m_ext_readlatch;
+ m_ext_address = (m_ext_address + 1) & 0x7fffff;
+ m_ext_readlatch = ymf271_read_memory(m_ext_address);
+ return ret;
+ }
+
+ default:
+ break;
+ }
+
+ return 0xff;
+}
+
+void ymf271_device::init_tables()
+{
+ int i, j;
+
+ for (i = 0; i < 8; i++)
+ m_lut_waves[i] = auto_alloc_array(machine(), INT16, SIN_LEN);
+
+ for (i = 0; i < 4*8; i++)
+ m_lut_plfo[i>>3][i&7] = auto_alloc_array(machine(), double, LFO_LENGTH);
+
+ for (i = 0; i < 4; i++)
+ m_lut_alfo[i] = auto_alloc_array(machine(), int, LFO_LENGTH);
+
+ for (i = 0; i < SIN_LEN; i++)
+ {
+ double m = sin( ((i*2)+1) * M_PI / SIN_LEN );
+ double m2 = sin( ((i*4)+1) * M_PI / SIN_LEN );
+
+ // Waveform 0: sin(wt) (0 <= wt <= 2PI)
+ m_lut_waves[0][i] = (INT16)(m * MAXOUT);
+
+ // Waveform 1: sin?(wt) (0 <= wt <= PI) -sin?(wt) (PI <= wt <= 2PI)
+ m_lut_waves[1][i] = (i < (SIN_LEN/2)) ? (INT16)((m * m) * MAXOUT) : (INT16)((m * m) * MINOUT);
+
+ // Waveform 2: sin(wt) (0 <= wt <= PI) -sin(wt) (PI <= wt <= 2PI)
+ m_lut_waves[2][i] = (i < (SIN_LEN/2)) ? (INT16)(m * MAXOUT) : (INT16)(-m * MAXOUT);
+
+ // Waveform 3: sin(wt) (0 <= wt <= PI) 0
+ m_lut_waves[3][i] = (i < (SIN_LEN/2)) ? (INT16)(m * MAXOUT) : 0;
+
+ // Waveform 4: sin(2wt) (0 <= wt <= PI) 0
+ m_lut_waves[4][i] = (i < (SIN_LEN/2)) ? (INT16)(m2 * MAXOUT) : 0;
+
+ // Waveform 5: |sin(2wt)| (0 <= wt <= PI) 0
+ m_lut_waves[5][i] = (i < (SIN_LEN/2)) ? (INT16)(fabs(m2) * MAXOUT) : 0;
+
+ // Waveform 6: 1 (0 <= wt <= 2PI)
+ m_lut_waves[6][i] = (INT16)(1 * MAXOUT);
+
+ m_lut_waves[7][i] = 0;
+ }
+
+ for (i = 0; i < LFO_LENGTH; i++)
+ {
+ int tri_wave;
+ double ftri_wave, fsaw_wave;
+ double plfo[4];
+
+ // LFO phase modulation
+ plfo[0] = 0;
+
+ fsaw_wave = ((i % (LFO_LENGTH/2)) * PLFO_MAX) / (double)((LFO_LENGTH/2)-1);
+ plfo[1] = (i < (LFO_LENGTH/2)) ? fsaw_wave : fsaw_wave - PLFO_MAX;
+
+ plfo[2] = (i < (LFO_LENGTH/2)) ? PLFO_MAX : PLFO_MIN;
+
+ ftri_wave = ((i % (LFO_LENGTH/4)) * PLFO_MAX) / (double)(LFO_LENGTH/4);
+ switch (i / (LFO_LENGTH/4))
+ {
+ case 0: plfo[3] = ftri_wave; break;
+ case 1: plfo[3] = PLFO_MAX - ftri_wave; break;
+ case 2: plfo[3] = 0 - ftri_wave; break;
+ case 3: plfo[3] = 0 - (PLFO_MAX - ftri_wave); break;
+ default: plfo[3] = 0; assert(0); break;
+ }
+
+ for (j = 0; j < 4; j++)
+ {
+ m_lut_plfo[j][0][i] = pow(2.0, 0.0);
+ m_lut_plfo[j][1][i] = pow(2.0, (3.378 * plfo[j]) / 1200.0);
+ m_lut_plfo[j][2][i] = pow(2.0, (5.0646 * plfo[j]) / 1200.0);
+ m_lut_plfo[j][3][i] = pow(2.0, (6.7495 * plfo[j]) / 1200.0);
+ m_lut_plfo[j][4][i] = pow(2.0, (10.1143 * plfo[j]) / 1200.0);
+ m_lut_plfo[j][5][i] = pow(2.0, (20.1699 * plfo[j]) / 1200.0);
+ m_lut_plfo[j][6][i] = pow(2.0, (40.1076 * plfo[j]) / 1200.0);
+ m_lut_plfo[j][7][i] = pow(2.0, (79.307 * plfo[j]) / 1200.0);
+ }
+
+ // LFO amplitude modulation
+ m_lut_alfo[0][i] = 0;
+
+ m_lut_alfo[1][i] = ALFO_MAX - ((i * ALFO_MAX) / LFO_LENGTH);
+
+ m_lut_alfo[2][i] = (i < (LFO_LENGTH/2)) ? ALFO_MAX : ALFO_MIN;
+
+ tri_wave = ((i % (LFO_LENGTH/2)) * ALFO_MAX) / (LFO_LENGTH/2);
+ m_lut_alfo[3][i] = (i < (LFO_LENGTH/2)) ? ALFO_MAX-tri_wave : tri_wave;
+ }
+
+ for (i = 0; i < 256; i++)
+ {
+ m_lut_env_volume[i] = (int)(65536.0 / pow(10.0, ((double)i / (256.0 / 96.0)) / 20.0));
+ }
+
+ for (i = 0; i < 16; i++)
+ {
+ m_lut_attenuation[i] = (int)(65536.0 / pow(10.0, channel_attenuation_table[i] / 20.0));
+ }
+ for (i = 0; i < 128; i++)
+ {
+ double db = 0.75 * (double)i;
+ m_lut_total_level[i] = (int)(65536.0 / pow(10.0, db / 20.0));
+ }
+
+ // timing may use a non-standard XTAL
+ double clock_correction = (double)(STD_CLOCK) / (double)(m_clock);
+ for (i = 0; i < 256; i++)
+ {
+ m_lut_lfo[i] = LFO_frequency_table[i] * clock_correction;
+ }
+
+ for (i = 0; i < 64; i++)
+ {
+ // attack/release rate in number of samples
+ m_lut_ar[i] = (ARTime[i] * clock_correction * 44100.0) / 1000.0;
+ }
+ for (i = 0; i < 64; i++)
+ {
+ // decay rate in number of samples
+ m_lut_dc[i] = (DCTime[i] * clock_correction * 44100.0) / 1000.0;
+ }
+}
+
+void ymf271_device::init_state()
+{
+ int i;
+
+ for (i = 0; i < ARRAY_LENGTH(m_slots); i++)
+ {
+ save_item(NAME(m_slots[i].ext_en), i);
+ save_item(NAME(m_slots[i].ext_out), i);
+ save_item(NAME(m_slots[i].lfoFreq), i);
+ save_item(NAME(m_slots[i].pms), i);
+ save_item(NAME(m_slots[i].ams), i);
+ save_item(NAME(m_slots[i].detune), i);
+ save_item(NAME(m_slots[i].multiple), i);
+ save_item(NAME(m_slots[i].tl), i);
+ save_item(NAME(m_slots[i].keyscale), i);
+ save_item(NAME(m_slots[i].ar), i);
+ save_item(NAME(m_slots[i].decay1rate), i);
+ save_item(NAME(m_slots[i].decay2rate), i);
+ save_item(NAME(m_slots[i].decay1lvl), i);
+ save_item(NAME(m_slots[i].relrate), i);
+ save_item(NAME(m_slots[i].block), i);
+ save_item(NAME(m_slots[i].fns_hi), i);
+ save_item(NAME(m_slots[i].fns), i);
+ save_item(NAME(m_slots[i].feedback), i);
+ save_item(NAME(m_slots[i].waveform), i);
+ save_item(NAME(m_slots[i].accon), i);
+ save_item(NAME(m_slots[i].algorithm), i);
+ save_item(NAME(m_slots[i].ch0_level), i);
+ save_item(NAME(m_slots[i].ch1_level), i);
+ save_item(NAME(m_slots[i].ch2_level), i);
+ save_item(NAME(m_slots[i].ch3_level), i);
+ save_item(NAME(m_slots[i].startaddr), i);
+ save_item(NAME(m_slots[i].loopaddr), i);
+ save_item(NAME(m_slots[i].endaddr), i);
+ save_item(NAME(m_slots[i].altloop), i);
+ save_item(NAME(m_slots[i].fs), i);
+ save_item(NAME(m_slots[i].srcnote), i);
+ save_item(NAME(m_slots[i].srcb), i);
+ save_item(NAME(m_slots[i].step), i);
+ save_item(NAME(m_slots[i].stepptr), i);
+ save_item(NAME(m_slots[i].active), i);
+ save_item(NAME(m_slots[i].bits), i);
+ save_item(NAME(m_slots[i].volume), i);
+ save_item(NAME(m_slots[i].env_state), i);
+ save_item(NAME(m_slots[i].env_attack_step), i);
+ save_item(NAME(m_slots[i].env_decay1_step), i);
+ save_item(NAME(m_slots[i].env_decay2_step), i);
+ save_item(NAME(m_slots[i].env_release_step), i);
+ save_item(NAME(m_slots[i].feedback_modulation0), i);
+ save_item(NAME(m_slots[i].feedback_modulation1), i);
+ save_item(NAME(m_slots[i].lfo_phase), i);
+ save_item(NAME(m_slots[i].lfo_step), i);
+ save_item(NAME(m_slots[i].lfo_amplitude), i);
+ }
+
+ for (i = 0; i < ARRAY_LENGTH(m_groups); i++)
+ {
+ save_item(NAME(m_groups[i].sync), i);
+ save_item(NAME(m_groups[i].pfm), i);
+ }
+
+ save_item(NAME(m_regs_main));
+ save_item(NAME(m_timerA));
+ save_item(NAME(m_timerB));
+ save_item(NAME(m_irqstate));
+ save_item(NAME(m_status));
+ save_item(NAME(m_enable));
+ save_item(NAME(m_ext_address));
+ save_item(NAME(m_ext_rw));
+ save_item(NAME(m_ext_readlatch));
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ymf271_device::device_start()
+{
+ m_clock = clock();
+
+ m_timA = timer_alloc(0);
+ m_timB = timer_alloc(1);
+
+ m_mem_base = region()->base();
+ m_mem_size = region()->bytes();
+ m_irq_handler.resolve();
+
+ m_ext_read_handler.resolve();
+ m_ext_write_handler.resolve();
+
+ init_tables();
+ init_state();
+
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, clock()/384);
+ m_mix_buffer = auto_alloc_array(machine(), INT32, 44100*2);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ymf271_device::device_reset()
+{
+ for (int i = 0; i < 48; i++)
+ {
+ m_slots[i].active = 0;
+ m_slots[i].volume = 0;
+ }
+
+ // reset timers and IRQ
+ m_timA->reset();
+ m_timB->reset();
+
+ m_irqstate = 0;
+ m_status = 0;
+ m_enable = 0;
+
+ if (!m_irq_handler.isnull())
+ m_irq_handler(0);
+}
+
+const device_type YMF271 = &device_creator<ymf271_device>;
+
+ymf271_device::ymf271_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, YMF271, "YMF271", tag, owner, clock, "ymf271", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_timerA(0),
+ m_timerB(0),
+ m_irqstate(0),
+ m_status(0),
+ m_enable(0),
+ m_ext_address(0),
+ m_ext_rw(0),
+ m_ext_readlatch(0),
+ m_irq_handler(*this),
+ m_ext_read_handler(*this),
+ m_ext_write_handler(*this)
+{
+ memset(m_slots, 0, sizeof(m_slots));
+ memset(m_groups, 0, sizeof(m_groups));
+ memset(m_regs_main, 0, sizeof(m_regs_main));
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void ymf271_device::device_config_complete()
+{
+}
diff --git a/src/devices/sound/ymf271.h b/src/devices/sound/ymf271.h
new file mode 100644
index 00000000000..185a8fda4eb
--- /dev/null
+++ b/src/devices/sound/ymf271.h
@@ -0,0 +1,166 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert, R. Belmont, hap
+#pragma once
+
+#ifndef __YMF271_H__
+#define __YMF271_H__
+
+#include "emu.h"
+
+#define MCFG_YMF271_IRQ_HANDLER(_devcb) \
+ devcb = &ymf271_device::set_irq_handler(*device, DEVCB_##_devcb);
+
+#define MCFG_YMF271_EXT_READ_HANDLER(_devcb) \
+ devcb = &ymf271_device::set_ext_read_handler(*device, DEVCB_##_devcb);
+
+#define MCFG_YMF271_EXT_WRITE_HANDLER(_devcb) \
+ devcb = &ymf271_device::set_ext_write_handler(*device, DEVCB_##_devcb);
+
+class ymf271_device : public device_t,
+ public device_sound_interface
+{
+public:
+ ymf271_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<ymf271_device &>(device).m_irq_handler.set_callback(object); }
+ template<class _Object> static devcb_base &set_ext_read_handler(device_t &device, _Object object) { return downcast<ymf271_device &>(device).m_ext_read_handler.set_callback(object); }
+ template<class _Object> static devcb_base &set_ext_write_handler(device_t &device, _Object object) { return downcast<ymf271_device &>(device).m_ext_write_handler.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+private:
+ struct YMF271Slot
+ {
+ UINT8 ext_en;
+ UINT8 ext_out;
+ UINT8 lfoFreq;
+ UINT8 lfowave;
+ UINT8 pms, ams;
+ UINT8 detune;
+ UINT8 multiple;
+ UINT8 tl;
+ UINT8 keyscale;
+ UINT8 ar;
+ UINT8 decay1rate, decay2rate;
+ UINT8 decay1lvl;
+ UINT8 relrate;
+ UINT8 block;
+ UINT8 fns_hi;
+ UINT32 fns;
+ UINT8 feedback;
+ UINT8 waveform;
+ UINT8 accon;
+ UINT8 algorithm;
+ UINT8 ch0_level, ch1_level, ch2_level, ch3_level;
+
+ UINT32 startaddr;
+ UINT32 loopaddr;
+ UINT32 endaddr;
+ UINT8 altloop;
+ UINT8 fs;
+ UINT8 srcnote, srcb;
+
+ UINT32 step;
+ UINT64 stepptr;
+
+ UINT8 active;
+ UINT8 bits;
+
+ // envelope generator
+ INT32 volume;
+ INT32 env_state;
+ INT32 env_attack_step; // volume increase step in attack state
+ INT32 env_decay1_step;
+ INT32 env_decay2_step;
+ INT32 env_release_step;
+
+ INT64 feedback_modulation0;
+ INT64 feedback_modulation1;
+
+ int lfo_phase, lfo_step;
+ int lfo_amplitude;
+ double lfo_phasemod;
+ };
+
+ struct YMF271Group
+ {
+ UINT8 sync, pfm;
+ };
+
+ void init_state();
+ void init_tables();
+ void calculate_step(YMF271Slot *slot);
+ void update_envelope(YMF271Slot *slot);
+ void init_envelope(YMF271Slot *slot);
+ void init_lfo(YMF271Slot *slot);
+ void update_lfo(YMF271Slot *slot);
+ INT64 calculate_slot_volume(YMF271Slot *slot);
+ void update_pcm(int slotnum, INT32 *mixp, int length);
+ INT64 calculate_op(int slotnum, INT64 inp);
+ void set_feedback(int slotnum, INT64 inp);
+ void write_register(int slotnum, int reg, UINT8 data);
+ void ymf271_write_fm(int bank, UINT8 address, UINT8 data);
+ void ymf271_write_pcm(UINT8 address, UINT8 data);
+ void ymf271_write_timer(UINT8 address, UINT8 data);
+ UINT8 ymf271_read_memory(UINT32 offset);
+
+ inline int get_keyscaled_rate(int rate, int keycode, int keyscale);
+ inline int get_internal_keycode(int block, int fns);
+ inline int get_external_keycode(int block, int fns);
+ inline bool check_envelope_end(YMF271Slot *slot);
+
+ // lookup tables
+ INT16 *m_lut_waves[8];
+ double *m_lut_plfo[4][8];
+ int *m_lut_alfo[4];
+ double m_lut_ar[64];
+ double m_lut_dc[64];
+ double m_lut_lfo[256];
+ int m_lut_attenuation[16];
+ int m_lut_total_level[128];
+ int m_lut_env_volume[256];
+
+ // internal state
+ YMF271Slot m_slots[48];
+ YMF271Group m_groups[12];
+
+ UINT8 m_regs_main[0x10];
+
+ UINT32 m_timerA;
+ UINT32 m_timerB;
+ UINT8 m_irqstate;
+ UINT8 m_status;
+ UINT8 m_enable;
+
+ UINT32 m_ext_address;
+ UINT8 m_ext_rw;
+ UINT8 m_ext_readlatch;
+
+ UINT8 *m_mem_base;
+ UINT32 m_mem_size;
+ UINT32 m_clock;
+
+ emu_timer *m_timA, *m_timB;
+ sound_stream *m_stream;
+ INT32 *m_mix_buffer;
+
+ devcb_write_line m_irq_handler;
+ devcb_read8 m_ext_read_handler;
+ devcb_write8 m_ext_write_handler;
+};
+
+extern const device_type YMF271;
+
+
+#endif /* __YMF271_H__ */
diff --git a/src/devices/sound/ymf278b.c b/src/devices/sound/ymf278b.c
new file mode 100644
index 00000000000..60097767655
--- /dev/null
+++ b/src/devices/sound/ymf278b.c
@@ -0,0 +1,1051 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont, Olivier Galibert, hap
+/*
+
+ YMF278B FM + Wave table Synthesizer (OPL4)
+
+ Timer and PCM YMF278B. The FM will be shared with the ymf262, eventually.
+
+ This chip roughly splits the difference between the Sega 315-5560 MultiPCM
+ (Multi32, Model 1/2) and YMF 292-F SCSP (later Model 2, STV, Saturn, Model 3).
+
+ Features as listed in LSI-4MF2782 data sheet:
+ FM Synthesis (same as YMF262)
+ 1. Sound generation mode
+ Two-operater mode
+ Generates eighteen voices or fifteen voices plus five rhythm sounds simultaneously
+ Four-operator mode
+ Generates six voices in four-operator mode plus six voices in two-operator mode simultaneously,
+ or generates six voices in four-operator mode plus three voices in two-operator mode plus five
+ rhythm sounds simultaneously
+ 2. Eight selectable waveforms
+ 3. Stereo output
+ Wave Table Synthesis
+ 1. Generates twenty-four voices simultaneously
+ 2. 44.1kHz sampling rate for output sound data
+ 3. Selectable from 8-bit, 12-bit and 16-bit word lengths for wave data
+ 4. Stereo output (16-stage panpot for each voice)
+ Wave Data
+ 1. Accepts 32M bit external memory at maximum
+ 2. Up to 512 wave tables
+ 3. External ROM or SRAM can be connected. With SRAM connected, the CPU can download wave data
+ 4. Outputs chip select signals for 1Mbit, 4Mbit, 8Mbit or 16Mbit memory
+ 5. Can be directly connected to the Yamaha YRW801 (Wave data ROM)
+ Features of YRW801 as listed in LSI 4RW801A2
+ Built-in wave data of tones which comply with GM system Level 1
+ Melody tone ....... 128 tones
+ Percussion tone ... 47 tones
+ 16Mbit capacity (2,097,152word x 8)
+
+ By R. Belmont and O. Galibert.
+
+
+ TODO:
+ - accurate timing of envelopes
+ - LFO (vibrato, tremolo)
+ - integrate YMF262 mixing (used by Fuuki games, not used by Psikyo and Metro games)
+*/
+
+#include "emu.h"
+#include "ymf278b.h"
+#include "ymf262.h"
+
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+// default address map
+static ADDRESS_MAP_START( ymf278b, AS_0, 8, ymf278b_device )
+ AM_RANGE(0x000000, 0x3fffff) AM_ROM
+ADDRESS_MAP_END
+
+
+/**************************************************************************/
+
+int ymf278b_device::compute_rate(YMF278BSlot *slot, int val)
+{
+ int res, oct;
+
+ if(val == 0)
+ return 0;
+ if(val == 15)
+ return 63;
+ if(slot->RC != 15)
+ {
+ oct = slot->octave;
+ if (oct & 8)
+ oct |= -8;
+
+ res = (oct+slot->RC)*2 + (slot->F_NUMBER & 0x200 ? 1 : 0) + val*4;
+ }
+ else
+ res = val * 4;
+ if(res < 0)
+ res = 0;
+ else if(res > 63)
+ res = 63;
+
+ return res;
+}
+
+UINT32 ymf278b_device::compute_decay_env_vol_step(YMF278BSlot *slot, int val)
+{
+ int rate;
+ UINT32 res;
+
+ // rate override with damping/pseudo reverb
+ if (slot->DAMP)
+ rate = 56; // approximate, datasheet says it's slightly curved though
+ else if (slot->preverb && slot->env_vol > ((6*8)<<23))
+ {
+ // pseudo reverb starts at -18dB (6 in voltab)
+ slot->env_preverb = 1;
+ rate = 5;
+ }
+ else
+ rate = compute_rate(slot, val);
+
+ if (rate < 4)
+ res = 0;
+ else
+ res = (256U<<23) / m_lut_dr[rate];
+
+ return res;
+}
+
+void ymf278b_device::compute_freq_step(YMF278BSlot *slot)
+{
+ UINT32 step;
+ int oct;
+
+ oct = slot->octave;
+ if(oct & 8)
+ oct |= -8;
+
+ step = (slot->F_NUMBER | 1024) << (oct + 8);
+ slot->step = step >> 3;
+}
+
+void ymf278b_device::compute_envelope(YMF278BSlot *slot)
+{
+ switch (slot->env_step)
+ {
+ // Attack
+ case 0:
+ {
+ // Attack
+ int rate = compute_rate(slot, slot->AR);
+ slot->env_vol = 256U<<23;
+ slot->env_vol_lim = (256U<<23) - 1;
+
+ if (rate==63)
+ {
+ // immediate
+ LOG(("YMF278B: Attack skipped - "));
+ slot->env_vol = 0;
+ slot->env_step++;
+ compute_envelope(slot);
+ }
+ else if (rate<4)
+ {
+ slot->env_vol_step = 0;
+ }
+ else
+ {
+ // NOTE: attack rate is linear here, but datasheet shows a smooth curve
+ LOG(("YMF278B: Attack, val = %d, rate = %d, delay = %g\n", slot->AR, rate, m_lut_ar[rate]*1000.0));
+ slot->env_vol_step = ~((256U<<23) / m_lut_ar[rate]);
+ }
+
+ break;
+ }
+
+ // Decay 1
+ case 1:
+ if(slot->DL)
+ {
+ LOG(("YMF278B: Decay step 1, dl=%d, val = %d rate = %d, delay = %g, PRVB = %d, DAMP = %d\n", slot->DL, slot->D1R, compute_rate(slot, slot->D1R), m_lut_dr[compute_rate(slot, slot->D1R)]*1000.0, slot->preverb, slot->DAMP));
+ slot->env_vol_step = compute_decay_env_vol_step(slot, slot->D1R);
+ slot->env_vol_lim = (slot->DL*8)<<23;
+ }
+ else
+ {
+ LOG(("YMF278B: Decay 1 skipped - "));
+ slot->env_step++;
+ compute_envelope(slot);
+ }
+
+ break;
+
+ // Decay 2
+ case 2:
+ LOG(("YMF278B: Decay step 2, val = %d, rate = %d, delay = %g, , PRVB = %d, DAMP = %d, current vol = %d\n", slot->D2R, compute_rate(slot, slot->D2R), m_lut_dr[compute_rate(slot, slot->D2R)]*1000.0, slot->preverb, slot->DAMP, slot->env_vol >> 23));
+ slot->env_vol_step = compute_decay_env_vol_step(slot, slot->D2R);
+ slot->env_vol_lim = 256U<<23;
+ break;
+
+ // Decay 2 reached -96dB
+ case 3:
+ LOG(("YMF278B: Voice cleared because of decay 2\n"));
+ slot->env_vol = 256U<<23;
+ slot->env_vol_step = 0;
+ slot->env_vol_lim = 0;
+ slot->active = 0;
+ break;
+
+ // Release
+ case 4:
+ LOG(("YMF278B: Release, val = %d, rate = %d, delay = %g, PRVB = %d, DAMP = %d\n", slot->RR, compute_rate(slot, slot->RR), m_lut_dr[compute_rate(slot, slot->RR)]*1000.0, slot->preverb, slot->DAMP));
+ slot->env_vol_step = compute_decay_env_vol_step(slot, slot->RR);
+ slot->env_vol_lim = 256U<<23;
+ break;
+
+ // Release reached -96dB
+ case 5:
+ LOG(("YMF278B: Release ends\n"));
+ slot->env_vol = 256U<<23;
+ slot->env_vol_step = 0;
+ slot->env_vol_lim = 0;
+ slot->active = 0;
+ break;
+
+ default: break;
+ }
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void ymf278b_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int i, j;
+ YMF278BSlot *slot = NULL;
+ INT16 sample = 0;
+ INT32 *mixp;
+ INT32 vl, vr;
+
+ if (&stream == m_stream_ymf262)
+ {
+ ymf262_update_one(m_ymf262, outputs, samples);
+ return;
+ }
+
+ memset(m_mix_buffer, 0, sizeof(m_mix_buffer[0])*samples*2);
+
+ for (i = 0; i < 24; i++)
+ {
+ slot = &m_slots[i];
+
+ if (slot->active)
+ {
+ mixp = m_mix_buffer;
+
+ for (j = 0; j < samples; j++)
+ {
+ if (slot->stepptr >= slot->endaddr)
+ {
+ slot->stepptr = slot->stepptr - slot->endaddr + slot->loopaddr;
+
+ // NOTE: loop overflow is still possible here if (slot->stepptr >= slot->endaddr)
+ // This glitch may be (ab)used to your advantage to create pseudorandom noise.
+ }
+
+ switch (slot->bits)
+ {
+ // 8 bit
+ case 0:
+ sample = m_direct->read_byte(slot->startaddr + (slot->stepptr>>16))<<8;
+ break;
+
+ // 12 bit
+ case 1:
+ if (slot->stepptr & 0x10000)
+ sample = m_direct->read_byte(slot->startaddr + (slot->stepptr>>17)*3+2)<<8 |
+ (m_direct->read_byte(slot->startaddr + (slot->stepptr>>17)*3+1) << 4 & 0xf0);
+ else
+ sample = m_direct->read_byte(slot->startaddr + (slot->stepptr>>17)*3)<<8 |
+ (m_direct->read_byte(slot->startaddr + (slot->stepptr>>17)*3+1) & 0xf0);
+ break;
+
+ // 16 bit
+ case 2:
+ sample = m_direct->read_byte(slot->startaddr + ((slot->stepptr>>16)*2))<<8 |
+ m_direct->read_byte(slot->startaddr + ((slot->stepptr>>16)*2)+1);
+ break;
+
+ // ?? bit, effect is unknown, datasheet says it's prohibited
+ case 3:
+ sample = 0;
+ break;
+ }
+
+ *mixp++ += (sample * m_volume[slot->TL+m_pan_left [slot->pan]+(slot->env_vol>>23)])>>17;
+ *mixp++ += (sample * m_volume[slot->TL+m_pan_right[slot->pan]+(slot->env_vol>>23)])>>17;
+
+ // update frequency
+ slot->stepptr += slot->step;
+
+ // update envelope
+ slot->env_vol += slot->env_vol_step;
+ if (((INT32)(slot->env_vol - slot->env_vol_lim)) >= 0)
+ {
+ slot->env_step++;
+ compute_envelope(slot);
+ }
+ else if (slot->preverb && !slot->env_preverb && slot->env_step && slot->env_vol > ((6*8)<<23))
+ compute_envelope(slot);
+ }
+ }
+ }
+
+ mixp = m_mix_buffer;
+ vl = m_mix_level[m_pcm_l];
+ vr = m_mix_level[m_pcm_r];
+ for (i = 0; i < samples; i++)
+ {
+ outputs[0][i] = (*mixp++ * vl) >> 16;
+ outputs[1][i] = (*mixp++ * vr) >> 16;
+ }
+}
+
+void ymf278b_device::irq_check()
+{
+ int prev_line = m_irq_line;
+ m_irq_line = m_current_irq ? 1 : 0;
+ if (m_irq_line != prev_line && !m_irq_handler.isnull())
+ m_irq_handler(m_irq_line);
+}
+
+enum
+{
+ TIMER_A = 0,
+ TIMER_B,
+ TIMER_BUSY_CLEAR,
+ TIMER_LD_CLEAR
+};
+
+void ymf278b_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch(id)
+ {
+ case TIMER_A:
+ if(!(m_enable & 0x40))
+ {
+ m_current_irq |= 0x40;
+ irq_check();
+ }
+ break;
+
+ case TIMER_B:
+ if(!(m_enable & 0x20))
+ {
+ m_current_irq |= 0x20;
+ irq_check();
+ }
+ break;
+
+ case TIMER_BUSY_CLEAR:
+ m_status_busy = 0;
+ break;
+
+ case TIMER_LD_CLEAR:
+ m_status_ld = 0;
+ break;
+ }
+}
+
+
+/**************************************************************************/
+
+void ymf278b_device::A_w(UINT8 reg, UINT8 data)
+{
+ // FM register array 0 (compatible with YMF262)
+ switch(reg)
+ {
+ // LSI TEST
+ case 0x00:
+ case 0x01:
+ break;
+
+ // timer a count
+ case 0x02:
+ if (data != m_timer_a_count)
+ {
+ m_timer_a_count = data;
+
+ // change period, ~80.8us * t
+ if (m_enable & 1)
+ m_timer_a->adjust(m_timer_a->remaining(), 0, m_timer_base * (256-data) * 4);
+ }
+ break;
+
+ // timer b count
+ case 0x03:
+ if (data != m_timer_b_count)
+ {
+ m_timer_b_count = data;
+
+ // change period, ~323.1us * t
+ if (m_enable & 2)
+ m_timer_b->adjust(m_timer_b->remaining(), 0, m_timer_base * (256-data) * 16);
+ }
+ break;
+
+ // timer control
+ case 0x04:
+ if(data & 0x80)
+ m_current_irq = 0;
+ else
+ {
+ // reset timers
+ if((m_enable ^ data) & 1)
+ {
+ attotime period = (data & 1) ? m_timer_base * (256-m_timer_a_count) * 4 : attotime::never;
+ m_timer_a->adjust(period, 0, period);
+ }
+ if((m_enable ^ data) & 2)
+ {
+ attotime period = (data & 2) ? m_timer_base * (256-m_timer_b_count) * 16 : attotime::never;
+ m_timer_b->adjust(period, 0, period);
+ }
+
+ m_enable = data;
+ m_current_irq &= ~data;
+ }
+ irq_check();
+ break;
+
+ default:
+ logerror("YMF278B: Port A write %02x, %02x\n", reg, data);
+ break;
+ }
+}
+
+void ymf278b_device::B_w(UINT8 reg, UINT8 data)
+{
+ // FM register array 1 (compatible with YMF262)
+ switch(reg)
+ {
+ // LSI TEST
+ case 0x00:
+ case 0x01:
+ break;
+
+ // expansion register (NEW2/NEW)
+ case 0x05:
+ m_exp = data;
+ break;
+
+ default:
+ logerror("YMF278B: Port B write %02x, %02x\n", reg, data);
+ break;
+ }
+}
+
+void ymf278b_device::retrigger_note(YMF278BSlot *slot)
+{
+ // activate channel
+ if (slot->octave != 8)
+ slot->active = 1;
+
+ // reset sample pos and go to attack stage
+ slot->stepptr = 0;
+ slot->env_step = 0;
+ slot->env_preverb = 0;
+
+ compute_freq_step(slot);
+ compute_envelope(slot);
+}
+
+void ymf278b_device::C_w(UINT8 reg, UINT8 data)
+{
+ // Handle slot registers specifically
+ if (reg >= 0x08 && reg <= 0xf7)
+ {
+ YMF278BSlot *slot = NULL;
+ int snum;
+ snum = (reg-8) % 24;
+ slot = &m_slots[snum];
+ switch((reg-8) / 24)
+ {
+ case 0:
+ {
+ attotime period;
+ UINT32 offset;
+ UINT8 p[12];
+ int i;
+
+ slot->wave &= 0x100;
+ slot->wave |= data;
+
+ // load wavetable header
+ if(slot->wave < 384 || !m_wavetblhdr)
+ offset = slot->wave * 12;
+ else
+ offset = m_wavetblhdr*0x80000 + (slot->wave - 384) * 12;
+ for (i = 0; i < 12; i++)
+ p[i] = m_direct->read_byte(offset+i);
+
+ slot->bits = (p[0]&0xc0)>>6;
+ slot->startaddr = (p[2] | (p[1]<<8) | ((p[0]&0x3f)<<16));
+ slot->loopaddr = (p[4]<<16) | (p[3]<<24);
+ slot->endaddr = (p[6]<<16) | (p[5]<<24);
+ slot->endaddr -= 0x00010000U;
+ slot->endaddr ^= 0xffff0000U;
+
+ // copy internal registers data
+ for (i = 7; i < 12; i++)
+ C_w(8 + snum + (i-2) * 24, p[i]);
+
+ // status register LD bit is on for approx 300us
+ m_status_ld = 1;
+ period = attotime::from_usec(300);
+ if (m_clock != YMF278B_STD_CLOCK)
+ period = (period * m_clock) / YMF278B_STD_CLOCK;
+ m_timer_ld->adjust(period);
+
+ // retrigger if key is on
+ if (slot->KEY_ON)
+ retrigger_note(slot);
+ else if (slot->active)
+ {
+ // deactivate channel
+ slot->env_step = 5;
+ compute_envelope(slot);
+ }
+
+ break;
+ }
+
+ case 1:
+ slot->wave &= 0xff;
+ slot->wave |= ((data&0x1)<<8);
+ slot->F_NUMBER &= 0x380;
+ slot->F_NUMBER |= (data>>1);
+ if (slot->active && (data ^ m_pcmregs[reg]) & 0xfe)
+ {
+ compute_freq_step(slot);
+ compute_envelope(slot);
+ }
+ break;
+
+ case 2:
+ slot->F_NUMBER &= 0x07f;
+ slot->F_NUMBER |= ((data&0x07)<<7);
+ slot->preverb = (data&0x8)>>3;
+ slot->octave = (data&0xf0)>>4;
+ if (data != m_pcmregs[reg])
+ {
+ // channel goes off if octave is set to -8 (datasheet says it's prohibited)
+ // (it is ok if this activates the channel while it was off: compute_envelope will reset it again if needed)
+ slot->active = (slot->octave != 8);
+
+ if (slot->active)
+ {
+ slot->env_preverb = 0;
+ compute_freq_step(slot);
+ compute_envelope(slot);
+ }
+ }
+ break;
+
+ case 3:
+ slot->TL = data>>1;
+ slot->LD = data&0x1;
+ break;
+
+ case 4:
+ slot->CH = (data&0x10)>>4;
+ // CH bit note: output to DO1 pin (1) or DO2 pin (0), this may
+ // silence the channel depending on how it's wired up on the PCB.
+ // For now, it's always enabled.
+ // (bit 5 (LFO reset) is also not hooked up yet)
+
+ slot->pan = data&0xf;
+ slot->DAMP = (data&0x40)>>6;
+ if (data & 0x80)
+ {
+ // don't retrigger if key was already on
+ if (slot->KEY_ON)
+ {
+ if ((data ^ m_pcmregs[reg]) & 0x40)
+ compute_envelope(slot);
+
+ break;
+ }
+
+ retrigger_note(slot);
+ }
+ else if (slot->active)
+ {
+ // release
+ slot->env_step = 4;
+ compute_envelope(slot);
+ }
+ slot->KEY_ON = (data&0x80)>>7;
+ break;
+
+ case 5:
+ // LFO and vibrato level, not hooked up yet
+ slot->LFO = (data>>3)&0x7;
+ slot->VIB = data&0x7;
+ break;
+
+ case 6:
+ slot->AR = data>>4;
+ slot->D1R = data&0xf;
+ if (slot->active && data != m_pcmregs[reg])
+ compute_envelope(slot);
+ break;
+
+ case 7:
+ slot->DL = data>>4;
+ slot->D2R = data&0xf;
+ if (slot->active && data != m_pcmregs[reg])
+ compute_envelope(slot);
+ break;
+
+ case 8:
+ slot->RC = data>>4;
+ slot->RR = data&0xf;
+ if (slot->active && data != m_pcmregs[reg])
+ compute_envelope(slot);
+ break;
+
+ case 9:
+ // tremolo level, not hooked up yet
+ slot->AM = data & 0x7;
+ break;
+ }
+ }
+ else
+ {
+ // All non-slot registers
+ switch (reg)
+ {
+ // LSI TEST
+ case 0x00:
+ case 0x01:
+ break;
+
+ case 0x02:
+ m_wavetblhdr = (data>>2)&0x7;
+ m_memmode = data&3;
+ break;
+
+ case 0x03:
+ data &= 0x3f; // !
+ break;
+ case 0x04:
+ break;
+ case 0x05:
+ // set memory address
+ m_memadr = m_pcmregs[3] << 16 | m_pcmregs[4] << 8 | data;
+ break;
+
+ case 0x06:
+ // memory data
+ m_addrspace[0]->write_byte(m_memadr, data);
+ m_memadr = (m_memadr + 1) & 0x3fffff;
+ break;
+
+ case 0x07:
+ break; // unused
+
+ case 0xf8:
+ m_fm_l = data & 0x7;
+ m_fm_r = (data>>3)&0x7;
+ break;
+
+ case 0xf9:
+ m_pcm_l = data & 0x7;
+ m_pcm_r = (data>>3)&0x7;
+ break;
+
+ default:
+ logerror("YMF278B: Port C write %02x, %02x\n", reg, data);
+ break;
+ }
+ }
+
+ m_pcmregs[reg] = data;
+}
+
+void ymf278b_device::timer_busy_start(int is_pcm)
+{
+ // status register BUSY bit is on for 56(FM) or 88(PCM) cycles
+ m_status_busy = 1;
+ m_timer_busy->adjust(attotime::from_hz(m_clock / (is_pcm ? 88 : 56)));
+}
+
+WRITE8_MEMBER( ymf278b_device::write )
+{
+ switch (offset)
+ {
+ case 0:
+ case 2:
+ timer_busy_start(0);
+ m_port_AB = data;
+ m_lastport = offset>>1 & 1;
+ ymf262_write(m_ymf262, offset, data);
+ break;
+
+ case 1:
+ case 3:
+ timer_busy_start(0);
+ if (m_lastport) B_w(m_port_AB, data);
+ else A_w(m_port_AB, data);
+ m_last_fm_data = data;
+ ymf262_write(m_ymf262, offset, data);
+ break;
+
+ case 4:
+ timer_busy_start(1);
+ m_port_C = data;
+ break;
+
+ case 5:
+ // PCM regs are only accessible if NEW2 is set
+ if (~m_exp & 2)
+ break;
+
+ m_stream->update();
+
+ timer_busy_start(1);
+ C_w(m_port_C, data);
+ break;
+
+ default:
+ logerror("%s: unexpected write at offset %X to ymf278b = %02X\n", machine().describe_context(), offset, data);
+ break;
+ }
+}
+
+
+READ8_MEMBER( ymf278b_device::read )
+{
+ UINT8 ret = 0;
+
+ switch (offset)
+ {
+ // status register
+ case 0:
+ {
+ // bits 0 and 1 are only valid if NEW2 is set
+ UINT8 newbits = 0;
+ if (m_exp & 2)
+ newbits = (m_status_ld << 1) | m_status_busy;
+
+ ret = newbits | m_current_irq | (m_irq_line ? 0x80 : 0x00);
+ break;
+ }
+
+ // FM regs can be read too (on contrary to what the datasheet says)
+ case 1:
+ case 3:
+ // but they're not implemented here yet
+ // This may be incorrect, but it makes the mbwave moonsound detection in msx drivers pass.
+ ret = m_last_fm_data;
+ break;
+
+ // PCM regs
+ case 5:
+ // only accessible if NEW2 is set
+ if (~m_exp & 2)
+ break;
+
+ switch (m_port_C)
+ {
+ // special cases
+ case 2:
+ ret = (m_pcmregs[m_port_C] & 0x1f) | 0x20; // device ID in upper bits
+ break;
+ case 6:
+ ret = m_direct->read_byte(m_memadr);
+ m_memadr = (m_memadr + 1) & 0x3fffff;
+ break;
+
+ default:
+ ret = m_pcmregs[m_port_C];
+ break;
+ }
+ break;
+
+ default:
+ logerror("%s: unexpected read at offset %X from ymf278b\n", machine().describe_context(), offset);
+ break;
+ }
+
+ return ret;
+}
+
+
+/**************************************************************************/
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ymf278b_device::device_reset()
+{
+ int i;
+
+ // clear registers
+ for (i = 0; i <= 4; i++)
+ A_w(i, 0);
+ B_w(5, 0);
+ for (i = 0; i < 8; i++)
+ C_w(i, 0);
+ for (i = 0xff; i >= 8; i--)
+ C_w(i, 0);
+ C_w(0xf8, 0x1b);
+
+ m_port_AB = m_port_C = 0;
+ m_lastport = 0;
+ m_memadr = 0;
+
+ // init/silence channels
+ for (i = 0; i < 24 ; i++)
+ {
+ YMF278BSlot *slot = &m_slots[i];
+
+ slot->LFO = 0;
+ slot->VIB = 0;
+ slot->AR = 0;
+ slot->D1R = 0;
+ slot->DL = 0;
+ slot->D2R = 0;
+ slot->RC = 0;
+ slot->RR = 0;
+ slot->AM = 0;
+
+ slot->startaddr = 0;
+ slot->loopaddr = 0;
+ slot->endaddr = 0;
+
+ slot->env_step = 5;
+ compute_envelope(slot);
+ }
+
+ m_timer_a->reset();
+ m_timer_b->reset();
+ m_timer_busy->reset(); m_status_busy = 0;
+ m_timer_ld->reset(); m_status_ld = 0;
+
+ m_irq_line = 0;
+ m_current_irq = 0;
+ if (!m_irq_handler.isnull())
+ m_irq_handler(0);
+
+ ymf262_reset_chip(m_ymf262);
+}
+
+void ymf278b_device::device_stop()
+{
+ ymf262_shutdown(m_ymf262);
+ m_ymf262 = NULL;
+}
+
+void ymf278b_device::precompute_rate_tables()
+{
+ int i;
+
+ // decay rate
+ for (i = 0; i < 64; i++)
+ {
+ if (i <= 3)
+ m_lut_dr[i] = 0;
+ else if (i >= 60)
+ m_lut_dr[i] = 15 << 4;
+ else
+ m_lut_dr[i] = (15 << (21 - i / 4)) / (4 + i % 4);
+ }
+
+ // attack rate (manual shows curve instead of linear though, so this is not entirely accurate)
+ for (i = 0; i < 64; i++)
+ {
+ if (i <= 3 || i == 63)
+ m_lut_ar[i] = 0;
+ else if (i >= 60)
+ m_lut_ar[i] = 17;
+ else
+ m_lut_ar[i] = (67 << (15 - i / 4)) / (4 + i % 4);
+ }
+}
+
+void ymf278b_device::register_save_state()
+{
+ int i;
+
+ save_item(NAME(m_pcmregs));
+ save_item(NAME(m_wavetblhdr));
+ save_item(NAME(m_memmode));
+ save_item(NAME(m_memadr));
+ save_item(NAME(m_status_busy));
+ save_item(NAME(m_status_ld));
+ save_item(NAME(m_exp));
+ save_item(NAME(m_fm_l));
+ save_item(NAME(m_fm_r));
+ save_item(NAME(m_pcm_l));
+ save_item(NAME(m_pcm_r));
+ save_item(NAME(m_timer_a_count));
+ save_item(NAME(m_timer_b_count));
+ save_item(NAME(m_enable));
+ save_item(NAME(m_current_irq));
+ save_item(NAME(m_irq_line));
+ save_item(NAME(m_port_AB));
+ save_item(NAME(m_port_C));
+ save_item(NAME(m_lastport));
+ save_item(NAME(m_last_fm_data));
+
+ for (i = 0; i < 24; ++i)
+ {
+ save_item(NAME(m_slots[i].wave), i);
+ save_item(NAME(m_slots[i].F_NUMBER), i);
+ save_item(NAME(m_slots[i].octave), i);
+ save_item(NAME(m_slots[i].preverb), i);
+ save_item(NAME(m_slots[i].DAMP), i);
+ save_item(NAME(m_slots[i].CH), i);
+ save_item(NAME(m_slots[i].LD), i);
+ save_item(NAME(m_slots[i].TL), i);
+ save_item(NAME(m_slots[i].pan), i);
+ save_item(NAME(m_slots[i].LFO), i);
+ save_item(NAME(m_slots[i].VIB), i);
+ save_item(NAME(m_slots[i].AM), i);
+
+ save_item(NAME(m_slots[i].AR), i);
+ save_item(NAME(m_slots[i].D1R), i);
+ save_item(NAME(m_slots[i].DL), i);
+ save_item(NAME(m_slots[i].D2R), i);
+ save_item(NAME(m_slots[i].RC), i);
+ save_item(NAME(m_slots[i].RR), i);
+
+ save_item(NAME(m_slots[i].step), i);
+ save_item(NAME(m_slots[i].stepptr), i);
+
+ save_item(NAME(m_slots[i].active), i);
+ save_item(NAME(m_slots[i].KEY_ON), i);
+ save_item(NAME(m_slots[i].bits), i);
+ save_item(NAME(m_slots[i].startaddr), i);
+ save_item(NAME(m_slots[i].loopaddr), i);
+ save_item(NAME(m_slots[i].endaddr), i);
+
+ save_item(NAME(m_slots[i].env_step), i);
+ save_item(NAME(m_slots[i].env_vol), i);
+ save_item(NAME(m_slots[i].env_vol_step), i);
+ save_item(NAME(m_slots[i].env_vol_lim), i);
+ save_item(NAME(m_slots[i].env_preverb), i);
+ }
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+static void ymf278b_ymf262_irq_handler(void *param,int irq)
+{
+}
+
+
+static void ymf278b_ymf262_timer_handler(void *param, int c, const attotime &period)
+{
+}
+
+static void ymf278b_ymf262_update_request(void *param, int interval)
+{
+ ymf278b_device *ymf278b = (ymf278b_device *) param;
+ ymf278b->ymf262_update_request();
+}
+
+
+void ymf278b_device::ymf262_update_request()
+{
+ m_stream_ymf262->update();
+}
+
+
+void ymf278b_device::device_start()
+{
+ int i;
+
+ m_direct = &space().direct();
+ m_clock = clock();
+ m_irq_handler.resolve();
+
+ m_timer_base = attotime::from_hz(m_clock) * (19*36);
+ m_timer_a = timer_alloc(TIMER_A);
+ m_timer_b = timer_alloc(TIMER_B);
+ m_timer_busy = timer_alloc(TIMER_BUSY_CLEAR);
+ m_timer_ld = timer_alloc(TIMER_LD_CLEAR);
+
+ for (i = 0; i < 24; i++)
+ {
+ m_slots[i].num = i;
+ }
+
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, clock()/768);
+ m_mix_buffer = auto_alloc_array(machine(), INT32, 44100*2);
+
+ // rate tables
+ precompute_rate_tables();
+
+ // Volume table, 1 = -0.375dB, 8 = -3dB, 256 = -96dB
+ for(i = 0; i < 256; i++)
+ m_volume[i] = 65536*pow(2.0, (-0.375/6)*i);
+ for(i = 256; i < 256*4; i++)
+ m_volume[i] = 0;
+
+ // Pan values, units are -3dB, i.e. 8.
+ for(i = 0; i < 16; i++)
+ {
+ m_pan_left[i] = i < 7 ? i*8 : i < 9 ? 256 : 0;
+ m_pan_right[i] = i < 8 ? 0 : i < 10 ? 256 : (16-i)*8;
+ }
+
+ // Mixing levels, units are -3dB, and add some margin to avoid clipping
+ for(i=0; i<7; i++)
+ m_mix_level[i] = m_volume[8*i+13];
+ m_mix_level[7] = 0;
+
+ // Register state for saving
+ register_save_state();
+
+ // YMF262 related
+
+ /* stream system initialize */
+ int ymf262_clock = clock() / (19/8.0);
+ m_ymf262 = ymf262_init(this, ymf262_clock, ymf262_clock / 288);
+ assert_always(m_ymf262 != NULL, "Error creating YMF262 chip");
+
+ m_stream_ymf262 = machine().sound().stream_alloc(*this, 0, 4, ymf262_clock / 288);
+
+ /* YMF262 setup */
+ ymf262_set_timer_handler (m_ymf262, ymf278b_ymf262_timer_handler, this);
+ ymf262_set_irq_handler (m_ymf262, ymf278b_ymf262_irq_handler, this);
+ ymf262_set_update_handler(m_ymf262, ymf278b_ymf262_update_request, this);
+}
+
+
+const device_type YMF278B = &device_creator<ymf278b_device>;
+
+ymf278b_device::ymf278b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, YMF278B, "YMF278B", tag, owner, clock, "ymf278b", __FILE__),
+ device_sound_interface(mconfig, *this),
+ device_memory_interface(mconfig, *this),
+ m_space_config("samples", ENDIANNESS_BIG, 8, 22, 0, NULL),
+ m_irq_handler(*this),
+ m_last_fm_data(0)
+{
+ m_address_map[0] = *ADDRESS_MAP_NAME(ymf278b);
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void ymf278b_device::device_config_complete()
+{
+}
diff --git a/src/devices/sound/ymf278b.h b/src/devices/sound/ymf278b.h
new file mode 100644
index 00000000000..096e2e3d3c4
--- /dev/null
+++ b/src/devices/sound/ymf278b.h
@@ -0,0 +1,147 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont, Olivier Galibert, hap
+#pragma once
+
+#ifndef __YMF278B_H__
+#define __YMF278B_H__
+
+#include "emu.h"
+
+#define YMF278B_STD_CLOCK (33868800) /* standard clock for OPL4 */
+
+#define MCFG_YMF278B_IRQ_HANDLER(_devcb) \
+ devcb = &ymf278b_device::set_irq_handler(*device, DEVCB_##_devcb);
+
+class ymf278b_device : public device_t,
+ public device_sound_interface,
+ public device_memory_interface
+{
+public:
+ ymf278b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<ymf278b_device &>(device).m_irq_handler.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+ void ymf262_update_request();
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+ virtual void device_reset();
+ virtual void device_stop();
+
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // device_memory_interface overrides
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const { return (spacenum == AS_0) ? &m_space_config : NULL; }
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ struct YMF278BSlot
+ {
+ INT16 wave; /* wavetable number */
+ INT16 F_NUMBER; /* frequency */
+ INT8 octave; /* octave */
+ INT8 preverb; /* pseudo-reverb */
+ INT8 DAMP; /* damping */
+ INT8 CH; /* output channel */
+ INT8 LD; /* level direct */
+ INT8 TL; /* total level */
+ INT8 pan; /* panpot */
+ INT8 LFO; /* LFO */
+ INT8 VIB; /* vibrato */
+ INT8 AM; /* tremolo */
+
+ INT8 AR; /* attack rate */
+ INT8 D1R; /* decay 1 rate */
+ INT8 DL; /* decay level */
+ INT8 D2R; /* decay 2 rate */
+ INT8 RC; /* rate correction */
+ INT8 RR; /* release rate */
+
+ UINT32 step; /* fixed-point frequency step */
+ UINT64 stepptr; /* fixed-point pointer into the sample */
+
+ INT8 active; /* channel is playing */
+ INT8 KEY_ON; /* slot keyed on */
+ INT8 bits; /* width of the samples */
+ UINT32 startaddr;
+ UINT32 loopaddr;
+ UINT32 endaddr;
+
+ int env_step;
+ UINT32 env_vol;
+ UINT32 env_vol_step;
+ UINT32 env_vol_lim;
+ INT8 env_preverb;
+
+ int num; /* slot number (for debug only) */
+ };
+
+ int compute_rate(YMF278BSlot *slot, int val);
+ UINT32 compute_decay_env_vol_step(YMF278BSlot *slot, int val);
+ void compute_freq_step(YMF278BSlot *slot);
+ void compute_envelope(YMF278BSlot *slot);
+ void irq_check();
+ void A_w(UINT8 reg, UINT8 data);
+ void B_w(UINT8 reg, UINT8 data);
+ void retrigger_note(YMF278BSlot *slot);
+ void C_w(UINT8 reg, UINT8 data);
+ void timer_busy_start(int is_pcm);
+ void precompute_rate_tables();
+ void register_save_state();
+
+ // internal state
+ UINT8 m_pcmregs[256];
+ YMF278BSlot m_slots[24];
+ INT8 m_wavetblhdr;
+ INT8 m_memmode;
+ INT32 m_memadr;
+
+ UINT8 m_status_busy, m_status_ld;
+ emu_timer *m_timer_busy;
+ emu_timer *m_timer_ld;
+ UINT8 m_exp;
+
+ INT32 m_fm_l, m_fm_r;
+ INT32 m_pcm_l, m_pcm_r;
+
+ attotime m_timer_base;
+ UINT8 m_timer_a_count, m_timer_b_count;
+ UINT8 m_enable, m_current_irq;
+ int m_irq_line;
+
+ UINT8 m_port_C, m_port_AB, m_lastport;
+
+ // precomputed tables
+ UINT32 m_lut_ar[64]; // attack rate
+ UINT32 m_lut_dr[64]; // decay rate
+ INT32 m_volume[256*4]; // precalculated attenuation values with some margin for envelope and pan levels
+ int m_pan_left[16],m_pan_right[16]; // pan volume offsets
+ INT32 m_mix_level[8];
+
+ emu_timer *m_timer_a, *m_timer_b;
+ int m_clock;
+
+ sound_stream * m_stream;
+ INT32 *m_mix_buffer;
+ direct_read_data * m_direct;
+ const address_space_config m_space_config;
+ devcb_write_line m_irq_handler;
+ UINT8 m_last_fm_data;
+
+ // ymf262
+ void *m_ymf262;
+ sound_stream * m_stream_ymf262;
+};
+
+extern const device_type YMF278B;
+
+
+#endif /* __YMF278B_H__ */
diff --git a/src/devices/sound/ymz280b.c b/src/devices/sound/ymz280b.c
new file mode 100644
index 00000000000..8a1f323bbdd
--- /dev/null
+++ b/src/devices/sound/ymz280b.c
@@ -0,0 +1,968 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/*
+
+ Yamaha YMZ280B driver
+ by Aaron Giles
+
+ YMZ280B 8-Channel PCMD8 PCM/ADPCM Decoder
+
+ Features as listed in LSI-4MZ280B3 data sheet:
+ Voice data stored in external memory can be played back simultaneously for up to eight voices
+ Voice data format can be selected from 4-bit ADPCM, 8-bit PCM and 16-bit PCM
+ Control of voice data external memory
+ Up to 16M bytes of ROM or SRAM (x 8 bits, access time 150ms max) can be connected
+ Continuous access is possible
+ Loop playback between selective addresses is possible
+ Voice data playback frequency control
+ 4-bit ADPCM ................ 0.172 to 44.1kHz in 256 steps
+ 8-bit PCM, 16-bit PCM ...... 0.172 to 88.2kHz in 512 steps
+ 256 steps total level and 16 steps panpot can be set
+ Voice signal is output in stereo 16-bit 2's complement MSB-first format
+
+ TODO:
+ - Is memory handling 100% correct? At the moment, Konami firebeat.c is the only
+ hardware currently emulated that uses external handlers.
+ It also happens to be the only one using 16-bit PCM.
+
+ Some other drivers (eg. bishi.c, bfm_sc4/5.c) also use ROM readback.
+
+*/
+
+
+#include "emu.h"
+#include "ymz280b.h"
+
+
+#define MAX_SAMPLE_CHUNK 10000
+
+#define FRAC_BITS 14
+#define FRAC_ONE (1 << FRAC_BITS)
+#define FRAC_MASK (FRAC_ONE - 1)
+
+#define INTERNAL_BUFFER_SIZE (1 << 15)
+#define INTERNAL_SAMPLE_RATE (m_master_clock * 2.0)
+
+#if MAKE_WAVS
+#include "wavwrite.h"
+#endif
+
+
+
+/* step size index shift table */
+static const int index_scale[8] = { 0x0e6, 0x0e6, 0x0e6, 0x0e6, 0x133, 0x199, 0x200, 0x266 };
+
+/* lookup table for the precomputed difference */
+static int diff_lookup[16];
+
+
+UINT8 ymz280b_device::ymz280b_read_memory(UINT32 offset)
+{
+ if (m_ext_read_handler.isnull())
+ {
+ if (offset < m_mem_size)
+ return m_mem_base[offset];
+
+ /* 16MB chip limit (shouldn't happen) */
+ else if (offset > 0xffffff)
+ return m_mem_base[offset & 0xffffff];
+
+ else
+ return 0;
+ }
+ else
+ return m_ext_read_handler(offset);
+}
+
+
+void ymz280b_device::update_irq_state()
+{
+ int irq_bits = m_status_register & m_irq_mask;
+
+ /* always off if the enable is off */
+ if (!m_irq_enable)
+ irq_bits = 0;
+
+ /* update the state if changed */
+ if (irq_bits && !m_irq_state)
+ {
+ m_irq_state = 1;
+ if (!m_irq_handler.isnull())
+ m_irq_handler(1);
+ else logerror("YMZ280B: IRQ generated, but no callback specified!\n");
+ }
+ else if (!irq_bits && m_irq_state)
+ {
+ m_irq_state = 0;
+ if (!m_irq_handler.isnull())
+ m_irq_handler(0);
+ else logerror("YMZ280B: IRQ generated, but no callback specified!\n");
+ }
+}
+
+
+void ymz280b_device::update_step(struct YMZ280BVoice *voice)
+{
+ double frequency;
+
+ /* compute the frequency */
+ if (voice->mode == 1)
+ frequency = m_master_clock * (double)((voice->fnum & 0x0ff) + 1) * (1.0 / 256.0);
+ else
+ frequency = m_master_clock * (double)((voice->fnum & 0x1ff) + 1) * (1.0 / 256.0);
+ voice->output_step = (UINT32)(frequency * (double)FRAC_ONE / INTERNAL_SAMPLE_RATE);
+}
+
+
+void ymz280b_device::update_volumes(struct YMZ280BVoice *voice)
+{
+ if (voice->pan == 8)
+ {
+ voice->output_left = voice->level;
+ voice->output_right = voice->level;
+ }
+ else if (voice->pan < 8)
+ {
+ voice->output_left = voice->level;
+
+ /* pan 1 is hard-left, what's pan 0? for now assume same as pan 1 */
+ voice->output_right = (voice->pan == 0) ? 0 : voice->level * (voice->pan - 1) / 7;
+ }
+ else
+ {
+ voice->output_left = voice->level * (15 - voice->pan) / 7;
+ voice->output_right = voice->level;
+ }
+}
+
+
+void ymz280b_device::device_post_load()
+{
+ for (int j = 0; j < 8; j++)
+ {
+ struct YMZ280BVoice *voice = &m_voice[j];
+ update_step(voice);
+ if(voice->irq_schedule)
+ voice->timer->adjust(attotime::zero);
+ }
+}
+
+
+void ymz280b_device::update_irq_state_timer_common(int voicenum)
+{
+ struct YMZ280BVoice *voice = &m_voice[voicenum];
+
+ if(!voice->irq_schedule) return;
+
+ voice->playing = 0;
+ m_status_register |= 1 << voicenum;
+ update_irq_state();
+ voice->irq_schedule = 0;
+}
+
+/**********************************************************************************************
+
+ compute_tables -- compute the difference tables
+
+***********************************************************************************************/
+
+static void compute_tables(void)
+{
+ /* loop over all nibbles and compute the difference */
+ for (int nib = 0; nib < 16; nib++)
+ {
+ int value = (nib & 0x07) * 2 + 1;
+ diff_lookup[nib] = (nib & 0x08) ? -value : value;
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ generate_adpcm -- general ADPCM decoding routine
+
+***********************************************************************************************/
+
+int ymz280b_device::generate_adpcm(struct YMZ280BVoice *voice, INT16 *buffer, int samples)
+{
+ int position = voice->position;
+ int signal = voice->signal;
+ int step = voice->step;
+ int val;
+
+ /* two cases: first cases is non-looping */
+ if (!voice->looping)
+ {
+ /* loop while we still have samples to generate */
+ while (samples)
+ {
+ /* compute the new amplitude and update the current step */
+ val = ymz280b_read_memory(position / 2) >> ((~position & 1) << 2);
+ signal += (step * diff_lookup[val & 15]) / 8;
+
+ /* clamp to the maximum */
+ if (signal > 32767)
+ signal = 32767;
+ else if (signal < -32768)
+ signal = -32768;
+
+ /* adjust the step size and clamp */
+ step = (step * index_scale[val & 7]) >> 8;
+ if (step > 0x6000)
+ step = 0x6000;
+ else if (step < 0x7f)
+ step = 0x7f;
+
+ /* output to the buffer, scaling by the volume */
+ *buffer++ = signal;
+ samples--;
+
+ /* next! */
+ position++;
+ if (position >= voice->stop)
+ {
+ voice->ended = true;
+ break;
+ }
+ }
+ }
+
+ /* second case: looping */
+ else
+ {
+ /* loop while we still have samples to generate */
+ while (samples)
+ {
+ /* compute the new amplitude and update the current step */
+ val = ymz280b_read_memory(position / 2) >> ((~position & 1) << 2);
+ signal += (step * diff_lookup[val & 15]) / 8;
+
+ /* clamp to the maximum */
+ if (signal > 32767)
+ signal = 32767;
+ else if (signal < -32768)
+ signal = -32768;
+
+ /* adjust the step size and clamp */
+ step = (step * index_scale[val & 7]) >> 8;
+ if (step > 0x6000)
+ step = 0x6000;
+ else if (step < 0x7f)
+ step = 0x7f;
+
+ /* output to the buffer, scaling by the volume */
+ *buffer++ = signal;
+ samples--;
+
+ /* next! */
+ position++;
+ if (position == voice->loop_start && voice->loop_count == 0)
+ {
+ voice->loop_signal = signal;
+ voice->loop_step = step;
+ }
+ if (position >= voice->loop_end)
+ {
+ if (voice->keyon)
+ {
+ position = voice->loop_start;
+ signal = voice->loop_signal;
+ step = voice->loop_step;
+ voice->loop_count++;
+ }
+ }
+ if (position >= voice->stop)
+ {
+ voice->ended = true;
+ break;
+ }
+ }
+ }
+
+ /* update the parameters */
+ voice->position = position;
+ voice->signal = signal;
+ voice->step = step;
+
+ return samples;
+}
+
+
+
+/**********************************************************************************************
+
+ generate_pcm8 -- general 8-bit PCM decoding routine
+
+***********************************************************************************************/
+
+int ymz280b_device::generate_pcm8(struct YMZ280BVoice *voice, INT16 *buffer, int samples)
+{
+ int position = voice->position;
+ int val;
+
+ /* two cases: first cases is non-looping */
+ if (!voice->looping)
+ {
+ /* loop while we still have samples to generate */
+ while (samples)
+ {
+ /* fetch the current value */
+ val = ymz280b_read_memory(position / 2);
+
+ /* output to the buffer, scaling by the volume */
+ *buffer++ = (INT8)val * 256;
+ samples--;
+
+ /* next! */
+ position += 2;
+ if (position >= voice->stop)
+ {
+ voice->ended = true;
+ break;
+ }
+ }
+ }
+
+ /* second case: looping */
+ else
+ {
+ /* loop while we still have samples to generate */
+ while (samples)
+ {
+ /* fetch the current value */
+ val = ymz280b_read_memory(position / 2);
+
+ /* output to the buffer, scaling by the volume */
+ *buffer++ = (INT8)val * 256;
+ samples--;
+
+ /* next! */
+ position += 2;
+ if (position >= voice->loop_end)
+ {
+ if (voice->keyon)
+ position = voice->loop_start;
+ }
+ if (position >= voice->stop)
+ {
+ voice->ended = true;
+ break;
+ }
+ }
+ }
+
+ /* update the parameters */
+ voice->position = position;
+
+ return samples;
+}
+
+
+
+/**********************************************************************************************
+
+ generate_pcm16 -- general 16-bit PCM decoding routine
+
+***********************************************************************************************/
+
+int ymz280b_device::generate_pcm16(struct YMZ280BVoice *voice, INT16 *buffer, int samples)
+{
+ int position = voice->position;
+ int val;
+
+ /* two cases: first cases is non-looping */
+ if (!voice->looping)
+ {
+ /* loop while we still have samples to generate */
+ while (samples)
+ {
+ /* fetch the current value */
+ val = (INT16)((ymz280b_read_memory(position / 2 + 1) << 8) + ymz280b_read_memory(position / 2 + 0));
+
+ /* output to the buffer, scaling by the volume */
+ *buffer++ = val;
+ samples--;
+
+ /* next! */
+ position += 4;
+ if (position >= voice->stop)
+ {
+ voice->ended = true;
+ break;
+ }
+ }
+ }
+
+ /* second case: looping */
+ else
+ {
+ /* loop while we still have samples to generate */
+ while (samples)
+ {
+ /* fetch the current value */
+ val = (INT16)((ymz280b_read_memory(position / 2 + 1) << 8) + ymz280b_read_memory(position / 2 + 0));
+
+ /* output to the buffer, scaling by the volume */
+ *buffer++ = val;
+ samples--;
+
+ /* next! */
+ position += 4;
+ if (position >= voice->loop_end)
+ {
+ if (voice->keyon)
+ position = voice->loop_start;
+ }
+ if (position >= voice->stop)
+ {
+ voice->ended = true;
+ break;
+ }
+ }
+ }
+
+ /* update the parameters */
+ voice->position = position;
+
+ return samples;
+}
+
+
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void ymz280b_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *lacc = outputs[0];
+ stream_sample_t *racc = outputs[1];
+ int v;
+
+ /* clear out the accumulator */
+ memset(lacc, 0, samples * sizeof(lacc[0]));
+ memset(racc, 0, samples * sizeof(racc[0]));
+
+ /* loop over voices */
+ for (v = 0; v < 8; v++)
+ {
+ struct YMZ280BVoice *voice = &m_voice[v];
+ INT16 prev = voice->last_sample;
+ INT16 curr = voice->curr_sample;
+ INT16 *curr_data = m_scratch;
+ INT32 *ldest = lacc;
+ INT32 *rdest = racc;
+ UINT32 new_samples, samples_left;
+ UINT32 final_pos;
+ int remaining = samples;
+ int lvol = voice->output_left;
+ int rvol = voice->output_right;
+
+ /* quick out if we're not playing and we're at 0 */
+ if (!voice->playing && curr == 0 && prev == 0)
+ {
+ /* make sure next sound plays immediately */
+ voice->output_pos = FRAC_ONE;
+
+ continue;
+ }
+
+ /* finish off the current sample */
+ /* interpolate */
+ while (remaining > 0 && voice->output_pos < FRAC_ONE)
+ {
+ int interp_sample = (((INT32)prev * (FRAC_ONE - voice->output_pos)) + ((INT32)curr * voice->output_pos)) >> FRAC_BITS;
+ *ldest++ += interp_sample * lvol;
+ *rdest++ += interp_sample * rvol;
+ voice->output_pos += voice->output_step;
+ remaining--;
+ }
+
+ /* if we're over, continue; otherwise, we're done */
+ if (voice->output_pos >= FRAC_ONE)
+ voice->output_pos -= FRAC_ONE;
+ else
+ continue;
+
+ /* compute how many new samples we need */
+ final_pos = voice->output_pos + remaining * voice->output_step;
+ new_samples = (final_pos + FRAC_ONE) >> FRAC_BITS;
+ if (new_samples > MAX_SAMPLE_CHUNK)
+ new_samples = MAX_SAMPLE_CHUNK;
+ samples_left = new_samples;
+
+ /* generate them into our buffer */
+ switch (voice->playing << 7 | voice->mode)
+ {
+ case 0x81: samples_left = generate_adpcm(voice, m_scratch, new_samples); break;
+ case 0x82: samples_left = generate_pcm8(voice, m_scratch, new_samples); break;
+ case 0x83: samples_left = generate_pcm16(voice, m_scratch, new_samples); break;
+ default: samples_left = 0; memset(m_scratch, 0, new_samples * sizeof(m_scratch[0])); break;
+ }
+
+ if (samples_left || voice->ended)
+ {
+ voice->ended = false;
+
+ /* if there are leftovers, ramp back to 0 */
+ int base = new_samples - samples_left;
+ int i, t = (base == 0) ? curr : m_scratch[base - 1];
+ for (i = 0; i < samples_left; i++)
+ {
+ if (t < 0) t = -((-t * 15) >> 4);
+ else if (t > 0) t = (t * 15) >> 4;
+ m_scratch[base + i] = t;
+ }
+
+ /* if we hit the end and IRQs are enabled, signal it */
+ if (base != 0)
+ {
+ voice->playing = 0;
+
+ /* set update_irq_state_timer. IRQ is signaled on next CPU execution. */
+ voice->timer->adjust(attotime::zero);
+ voice->irq_schedule = 1;
+ }
+ }
+
+ /* advance forward one sample */
+ prev = curr;
+ curr = *curr_data++;
+
+ /* then sample-rate convert with linear interpolation */
+ while (remaining > 0)
+ {
+ /* interpolate */
+ while (remaining > 0 && voice->output_pos < FRAC_ONE)
+ {
+ int interp_sample = (((INT32)prev * (FRAC_ONE - voice->output_pos)) + ((INT32)curr * voice->output_pos)) >> FRAC_BITS;
+ *ldest++ += interp_sample * lvol;
+ *rdest++ += interp_sample * rvol;
+ voice->output_pos += voice->output_step;
+ remaining--;
+ }
+
+ /* if we're over, grab the next samples */
+ if (voice->output_pos >= FRAC_ONE)
+ {
+ voice->output_pos -= FRAC_ONE;
+ prev = curr;
+ curr = *curr_data++;
+ }
+ }
+
+ /* remember the last samples */
+ voice->last_sample = prev;
+ voice->curr_sample = curr;
+ }
+
+ for (v = 0; v < samples; v++)
+ {
+ outputs[0][v] /= 256;
+ outputs[1][v] /= 256;
+ }
+}
+
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ymz280b_device::device_start()
+{
+ m_ext_read_handler.resolve();
+ m_ext_write_handler.resolve();
+
+ /* compute ADPCM tables */
+ compute_tables();
+
+ /* initialize the rest of the structure */
+ m_master_clock = (double)clock() / 384.0;
+ m_mem_base = region()->base();
+ m_mem_size = region()->bytes();
+ m_irq_handler.resolve();
+
+ for (int i = 0; i < 8; i++)
+ {
+ m_voice[i].timer = timer_alloc(i);
+ }
+
+ /* create the stream */
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, INTERNAL_SAMPLE_RATE);
+
+ /* allocate memory */
+ assert(MAX_SAMPLE_CHUNK < 0x10000);
+ m_scratch = auto_alloc_array(machine(), INT16, MAX_SAMPLE_CHUNK);
+
+ /* state save */
+ save_item(NAME(m_current_register));
+ save_item(NAME(m_status_register));
+ save_item(NAME(m_irq_state));
+ save_item(NAME(m_irq_mask));
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_keyon_enable));
+ save_item(NAME(m_ext_mem_enable));
+ save_item(NAME(m_ext_mem_address));
+ save_item(NAME(m_ext_readlatch));
+ save_item(NAME(m_ext_mem_address_hi));
+ save_item(NAME(m_ext_mem_address_mid));
+ for (int j = 0; j < 8; j++)
+ {
+ save_item(NAME(m_voice[j].playing), j);
+ save_item(NAME(m_voice[j].ended), j);
+ save_item(NAME(m_voice[j].keyon), j);
+ save_item(NAME(m_voice[j].looping), j);
+ save_item(NAME(m_voice[j].mode), j);
+ save_item(NAME(m_voice[j].fnum), j);
+ save_item(NAME(m_voice[j].level), j);
+ save_item(NAME(m_voice[j].pan), j);
+ save_item(NAME(m_voice[j].start), j);
+ save_item(NAME(m_voice[j].stop), j);
+ save_item(NAME(m_voice[j].loop_start), j);
+ save_item(NAME(m_voice[j].loop_end), j);
+ save_item(NAME(m_voice[j].position), j);
+ save_item(NAME(m_voice[j].signal), j);
+ save_item(NAME(m_voice[j].step), j);
+ save_item(NAME(m_voice[j].loop_signal), j);
+ save_item(NAME(m_voice[j].loop_step), j);
+ save_item(NAME(m_voice[j].loop_count), j);
+ save_item(NAME(m_voice[j].output_left), j);
+ save_item(NAME(m_voice[j].output_right), j);
+ save_item(NAME(m_voice[j].output_pos), j);
+ save_item(NAME(m_voice[j].last_sample), j);
+ save_item(NAME(m_voice[j].curr_sample), j);
+ save_item(NAME(m_voice[j].irq_schedule), j);
+ }
+
+#if MAKE_WAVS
+ m_wavresample = wav_open("resamp.wav", INTERNAL_SAMPLE_RATE, 2);
+#endif
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ymz280b_device::device_reset()
+{
+ /* initial clear registers */
+ for (int i = 0xff; i >= 0; i--)
+ {
+ m_current_register = i;
+ write_to_register(0);
+ }
+
+ m_current_register = 0;
+ m_status_register = 0;
+ m_ext_mem_address = 0;
+
+ /* clear other voice parameters */
+ for (int i = 0; i < 8; i++)
+ {
+ struct YMZ280BVoice *voice = &m_voice[i];
+
+ voice->curr_sample = 0;
+ voice->last_sample = 0;
+ voice->output_pos = FRAC_ONE;
+ voice->playing = 0;
+ }
+}
+
+
+void ymz280b_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ if (id < 8)
+ update_irq_state_timer_common( id );
+ else
+ assert_always(FALSE, "Unknown id in ymz280b_device::device_timer");
+}
+
+
+/**********************************************************************************************
+
+ write_to_register -- handle a write to the current register
+
+***********************************************************************************************/
+
+void ymz280b_device::write_to_register(int data)
+{
+ struct YMZ280BVoice *voice;
+ int i;
+
+ /* lower registers follow a pattern */
+ if (m_current_register < 0x80)
+ {
+ voice = &m_voice[(m_current_register >> 2) & 7];
+
+ switch (m_current_register & 0xe3)
+ {
+ case 0x00: /* pitch low 8 bits */
+ voice->fnum = (voice->fnum & 0x100) | (data & 0xff);
+ update_step(voice);
+ break;
+
+ case 0x01: /* pitch upper 1 bit, loop, key on, mode */
+ voice->fnum = (voice->fnum & 0xff) | ((data & 0x01) << 8);
+ voice->looping = (data & 0x10) >> 4;
+ if ((data & 0x60) == 0) data &= 0x7f; /* ignore mode setting and set to same state as KON=0 */
+ else voice->mode = (data & 0x60) >> 5;
+ if (!voice->keyon && (data & 0x80) && m_keyon_enable)
+ {
+ voice->playing = 1;
+ voice->position = voice->start;
+ voice->signal = voice->loop_signal = 0;
+ voice->step = voice->loop_step = 0x7f;
+ voice->loop_count = 0;
+
+ /* if update_irq_state_timer is set, cancel it. */
+ voice->irq_schedule = 0;
+ }
+ else if (voice->keyon && !(data & 0x80))
+ {
+ voice->playing = 0;
+
+ /* if update_irq_state_timer is set, cancel it. */
+ voice->irq_schedule = 0;
+ }
+ voice->keyon = (data & 0x80) >> 7;
+ update_step(voice);
+ break;
+
+ case 0x02: /* total level */
+ voice->level = data;
+ update_volumes(voice);
+ break;
+
+ case 0x03: /* pan */
+ voice->pan = data & 0x0f;
+ update_volumes(voice);
+ break;
+
+ case 0x20: /* start address high */
+ voice->start = (voice->start & (0x00ffff << 1)) | (data << 17);
+ break;
+
+ case 0x21: /* loop start address high */
+ voice->loop_start = (voice->loop_start & (0x00ffff << 1)) | (data << 17);
+ break;
+
+ case 0x22: /* loop end address high */
+ voice->loop_end = (voice->loop_end & (0x00ffff << 1)) | (data << 17);
+ break;
+
+ case 0x23: /* stop address high */
+ voice->stop = (voice->stop & (0x00ffff << 1)) | (data << 17);
+ break;
+
+ case 0x40: /* start address middle */
+ voice->start = (voice->start & (0xff00ff << 1)) | (data << 9);
+ break;
+
+ case 0x41: /* loop start address middle */
+ voice->loop_start = (voice->loop_start & (0xff00ff << 1)) | (data << 9);
+ break;
+
+ case 0x42: /* loop end address middle */
+ voice->loop_end = (voice->loop_end & (0xff00ff << 1)) | (data << 9);
+ break;
+
+ case 0x43: /* stop address middle */
+ voice->stop = (voice->stop & (0xff00ff << 1)) | (data << 9);
+ break;
+
+ case 0x60: /* start address low */
+ voice->start = (voice->start & (0xffff00 << 1)) | (data << 1);
+ break;
+
+ case 0x61: /* loop start address low */
+ voice->loop_start = (voice->loop_start & (0xffff00 << 1)) | (data << 1);
+ break;
+
+ case 0x62: /* loop end address low */
+ voice->loop_end = (voice->loop_end & (0xffff00 << 1)) | (data << 1);
+ break;
+
+ case 0x63: /* stop address low */
+ voice->stop = (voice->stop & (0xffff00 << 1)) | (data << 1);
+ break;
+
+ default:
+ logerror("YMZ280B: unknown register write %02X = %02X\n", m_current_register, data);
+ break;
+ }
+ }
+
+ /* upper registers are special */
+ else
+ {
+ switch (m_current_register)
+ {
+ /* DSP related (not implemented yet) */
+ case 0x80: // d0-2: DSP Rch, d3: enable Rch (0: yes, 1: no), d4-6: DSP Lch, d7: enable Lch (0: yes, 1: no)
+ case 0x81: // d0: enable control of $82 (0: yes, 1: no)
+ case 0x82: // DSP data
+ logerror("YMZ280B: DSP register write %02X = %02X\n", m_current_register, data);
+ break;
+
+ case 0x84: /* ROM readback / RAM write (high) */
+ m_ext_mem_address_hi = data << 16;
+ break;
+
+ case 0x85: /* ROM readback / RAM write (middle) */
+ m_ext_mem_address_mid = data << 8;
+ break;
+
+ case 0x86: /* ROM readback / RAM write (low) -> update latch */
+ m_ext_mem_address = m_ext_mem_address_hi | m_ext_mem_address_mid | data;
+ if (m_ext_mem_enable)
+ m_ext_readlatch = ymz280b_read_memory(m_ext_mem_address);
+ break;
+
+ case 0x87: /* RAM write */
+ if (m_ext_mem_enable)
+ {
+ if (!m_ext_write_handler.isnull())
+ m_ext_write_handler(m_ext_mem_address, data);
+ else
+ logerror("YMZ280B attempted RAM write to %X\n", m_ext_mem_address);
+ m_ext_mem_address = (m_ext_mem_address + 1) & 0xffffff;
+ }
+ break;
+
+ case 0xfe: /* IRQ mask */
+ m_irq_mask = data;
+ update_irq_state();
+ break;
+
+ case 0xff: /* IRQ enable, test, etc */
+ m_ext_mem_enable = (data & 0x40) >> 6;
+ m_irq_enable = (data & 0x10) >> 4;
+ update_irq_state();
+
+ if (m_keyon_enable && !(data & 0x80))
+ {
+ for (i = 0; i < 8; i++)
+ {
+ m_voice[i].playing = 0;
+
+ /* if update_irq_state_timer is set, cancel it. */
+ m_voice[i].irq_schedule = 0;
+ }
+ }
+ else if (!m_keyon_enable && (data & 0x80))
+ {
+ for (i = 0; i < 8; i++)
+ {
+ if (m_voice[i].keyon && m_voice[i].looping)
+ m_voice[i].playing = 1;
+ }
+ }
+ m_keyon_enable = (data & 0x80) >> 7;
+ break;
+
+ default:
+ logerror("YMZ280B: unknown register write %02X = %02X\n", m_current_register, data);
+ break;
+ }
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ compute_status -- determine the status bits
+
+***********************************************************************************************/
+
+int ymz280b_device::compute_status()
+{
+ UINT8 result;
+
+ /* force an update */
+ m_stream->update();
+
+ result = m_status_register;
+
+ /* clear the IRQ state */
+ m_status_register = 0;
+ update_irq_state();
+
+ return result;
+}
+
+
+
+/**********************************************************************************************
+
+ ymz280b_r/ymz280b_w -- handle external accesses
+
+***********************************************************************************************/
+
+READ8_MEMBER( ymz280b_device::read )
+{
+ if ((offset & 1) == 0)
+ {
+ if (!m_ext_mem_enable)
+ return 0xff;
+
+ /* read from external memory */
+ UINT8 ret = m_ext_readlatch;
+ m_ext_readlatch = ymz280b_read_memory(m_ext_mem_address);
+ m_ext_mem_address = (m_ext_mem_address + 1) & 0xffffff;
+ return ret;
+ }
+ else
+ return compute_status();
+}
+
+
+WRITE8_MEMBER( ymz280b_device::write )
+{
+ if ((offset & 1) == 0)
+ m_current_register = data;
+ else
+ {
+ /* force an update */
+ m_stream->update();
+
+ write_to_register(data);
+ }
+}
+
+
+const device_type YMZ280B = &device_creator<ymz280b_device>;
+
+ymz280b_device::ymz280b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, YMZ280B, "YMZ280B", tag, owner, clock, "ymz280b", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_current_register(0),
+ m_status_register(0),
+ m_irq_state(0),
+ m_irq_mask(0),
+ m_irq_enable(0),
+ m_keyon_enable(0),
+ m_ext_mem_enable(0),
+ m_ext_readlatch(0),
+ m_ext_mem_address_hi(0),
+ m_ext_mem_address_mid(0),
+ m_ext_mem_address(0),
+ m_irq_handler(*this),
+ m_ext_read_handler(*this),
+ m_ext_write_handler(*this)
+{
+ memset(m_voice, 0, sizeof(m_voice));
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void ymz280b_device::device_config_complete()
+{
+}
diff --git a/src/devices/sound/ymz280b.h b/src/devices/sound/ymz280b.h
new file mode 100644
index 00000000000..a67c0d8567b
--- /dev/null
+++ b/src/devices/sound/ymz280b.h
@@ -0,0 +1,133 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/**********************************************************************************************
+ *
+ * Yamaha YMZ280B driver
+ * by Aaron Giles
+ *
+ **********************************************************************************************/
+
+#pragma once
+
+#ifndef __YMZ280B_H__
+#define __YMZ280B_H__
+
+#include "emu.h"
+
+#define MAKE_WAVS 0
+
+#define MCFG_YMZ280B_IRQ_HANDLER(_devcb) \
+ devcb = &ymz280b_device::set_irq_handler(*device, DEVCB_##_devcb);
+
+#define MCFG_YMZ280B_EXT_READ_HANDLER(_devcb) \
+ devcb = &ymz280b_device::set_ext_read_handler(*device, DEVCB_##_devcb);
+
+#define MCFG_YMZ280B_EXT_WRITE_HANDLER(_devcb) \
+ devcb = &ymz280b_device::set_ext_write_handler(*device, DEVCB_##_devcb);
+
+class ymz280b_device : public device_t,
+ public device_sound_interface
+{
+public:
+ ymz280b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<ymz280b_device &>(device).m_irq_handler.set_callback(object); }
+ template<class _Object> static devcb_base &set_ext_read_handler(device_t &device, _Object object) { return downcast<ymz280b_device &>(device).m_ext_read_handler.set_callback(object); }
+ template<class _Object> static devcb_base &set_ext_write_handler(device_t &device, _Object object) { return downcast<ymz280b_device &>(device).m_ext_write_handler.set_callback(object); }
+
+ DECLARE_READ8_MEMBER( read );
+ DECLARE_WRITE8_MEMBER( write );
+
+protected:
+ // device-level overrides
+ virtual void device_config_complete();
+ virtual void device_start();
+ virtual void device_post_load();
+ virtual void device_reset();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+private:
+
+ /* struct describing a single playing ADPCM voice */
+ struct YMZ280BVoice
+ {
+ UINT8 playing; /* 1 if we are actively playing */
+ bool ended; /* indicate voice has ended in case samples_left is 0 */
+
+ UINT8 keyon; /* 1 if the key is on */
+ UINT8 looping; /* 1 if looping is enabled */
+ UINT8 mode; /* current playback mode */
+ UINT16 fnum; /* frequency */
+ UINT8 level; /* output level */
+ UINT8 pan; /* panning */
+
+ UINT32 start; /* start address, in nibbles */
+ UINT32 stop; /* stop address, in nibbles */
+ UINT32 loop_start; /* loop start address, in nibbles */
+ UINT32 loop_end; /* loop end address, in nibbles */
+ UINT32 position; /* current position, in nibbles */
+
+ INT32 signal; /* current ADPCM signal */
+ INT32 step; /* current ADPCM step */
+
+ INT32 loop_signal; /* signal at loop start */
+ INT32 loop_step; /* step at loop start */
+ UINT32 loop_count; /* number of loops so far */
+
+ INT32 output_left; /* output volume (left) */
+ INT32 output_right; /* output volume (right) */
+ INT32 output_step; /* step value for frequency conversion */
+ INT32 output_pos; /* current fractional position */
+ INT16 last_sample; /* last sample output */
+ INT16 curr_sample; /* current sample target */
+ UINT8 irq_schedule; /* 1 if the IRQ state is updated by timer */
+
+ emu_timer *timer;
+ };
+
+ UINT8 ymz280b_read_memory(UINT32 offset);
+ void update_irq_state();
+ void update_step(struct YMZ280BVoice *voice);
+ void update_volumes(struct YMZ280BVoice *voice);
+ void update_irq_state_timer_common(int voicenum);
+ int generate_adpcm(struct YMZ280BVoice *voice, INT16 *buffer, int samples);
+ int generate_pcm8(struct YMZ280BVoice *voice, INT16 *buffer, int samples);
+ int generate_pcm16(struct YMZ280BVoice *voice, INT16 *buffer, int samples);
+ void write_to_register(int data);
+ int compute_status();
+
+ // internal state
+ struct YMZ280BVoice m_voice[8]; /* the 8 voices */
+ UINT8 m_current_register; /* currently accessible register */
+ UINT8 m_status_register; /* current status register */
+ UINT8 m_irq_state; /* current IRQ state */
+ UINT8 m_irq_mask; /* current IRQ mask */
+ UINT8 m_irq_enable; /* current IRQ enable */
+ UINT8 m_keyon_enable; /* key on enable */
+ UINT8 m_ext_mem_enable; /* external memory enable */
+ UINT8 m_ext_readlatch; /* external memory prefetched data */
+ UINT32 m_ext_mem_address_hi;
+ UINT32 m_ext_mem_address_mid;
+ UINT32 m_ext_mem_address; /* where the CPU can read the ROM */
+
+ devcb_write_line m_irq_handler; /* IRQ callback */
+ devcb_read8 m_ext_read_handler; /* external RAM read handler */
+ devcb_write8 m_ext_write_handler;/* external RAM write handler */
+
+ double m_master_clock; /* master clock frequency */
+ UINT8 *m_mem_base; /* pointer to the base of external memory */
+ UINT32 m_mem_size;
+ sound_stream *m_stream; /* which stream are we using */
+ INT16 *m_scratch;
+#if MAKE_WAVS
+ void *m_wavresample; /* resampled waveform */
+#endif
+};
+
+extern const device_type YMZ280B;
+
+
+#endif /* __YMZ280B_H__ */
diff --git a/src/devices/sound/ymz770.c b/src/devices/sound/ymz770.c
new file mode 100644
index 00000000000..f367f4d10eb
--- /dev/null
+++ b/src/devices/sound/ymz770.c
@@ -0,0 +1,345 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert, R. Belmont
+/***************************************************************************
+
+ ymz770.c
+
+ Emulation by R. Belmont
+ AMM decode by Olivier Galibert
+
+-----
+TODO:
+- A lot of unimplemented features, even simple ones like panning,
+ these should be added once we find out any software that uses it.
+- Is channel volume linear(current implementation) or logarithmic?
+- Sequencer is very preliminary
+- What does channel ATBL mean?
+- Is YMZ774(and other variants) the same family as this chip?
+ What are the differences?
+
+***************************************************************************/
+
+#include "emu.h"
+#include "ymz770.h"
+#include "mpeg_audio.h"
+
+// device type definition
+const device_type YMZ770 = &device_creator<ymz770_device>;
+
+//-------------------------------------------------
+// ymz770_device - constructor
+//-------------------------------------------------
+
+ymz770_device::ymz770_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, YMZ770, "Yamaha YMZ770", tag, owner, clock, "ymz770", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_cur_reg(0),
+ m_mute(0),
+ m_doen(0),
+ m_vlma(0),
+ m_bsl(0),
+ m_cpl(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ymz770_device::device_start()
+{
+ // create the stream
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, 16000);
+ m_rom_base = region()->base();
+ m_rom_limit = region()->bytes() * 8;
+
+ for (int i = 0; i < 8; i++)
+ {
+ m_channels[i].is_playing = false;
+ m_channels[i].is_seq_playing = false;
+ m_channels[i].decoder = new mpeg_audio(m_rom_base, mpeg_audio::AMM, false, 0);
+ }
+
+ // register for save states
+ save_item(NAME(m_cur_reg));
+ save_item(NAME(m_mute));
+ save_item(NAME(m_doen));
+ save_item(NAME(m_vlma));
+ save_item(NAME(m_bsl));
+ save_item(NAME(m_cpl));
+
+ for (int ch = 0; ch < 8; ch++)
+ {
+ save_item(NAME(m_channels[ch].phrase), ch);
+ save_item(NAME(m_channels[ch].pan), ch);
+ save_item(NAME(m_channels[ch].volume), ch);
+ save_item(NAME(m_channels[ch].control), ch);
+ save_item(NAME(m_channels[ch].is_playing), ch);
+ save_item(NAME(m_channels[ch].last_block), ch);
+ save_item(NAME(m_channels[ch].output_remaining), ch);
+ save_item(NAME(m_channels[ch].output_ptr), ch);
+ save_item(NAME(m_channels[ch].atbl), ch);
+ save_item(NAME(m_channels[ch].pptr), ch);
+ save_item(NAME(m_channels[ch].sequence), ch);
+ save_item(NAME(m_channels[ch].seqcontrol), ch);
+ save_item(NAME(m_channels[ch].seqdelay), ch);
+ save_item(NAME(m_channels[ch].is_seq_playing), ch);
+ save_item(NAME(m_channels[ch].output_data), ch);
+ }
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void ymz770_device::device_reset()
+{
+ for (int ch = 0; ch < 8; ch++)
+ {
+ m_channels[ch].phrase = 0;
+ m_channels[ch].pan = 8;
+ m_channels[ch].volume = 0;
+ m_channels[ch].control = 0;
+ m_channels[ch].sequence = 0;
+ m_channels[ch].seqcontrol = 0;
+ m_channels[ch].seqdelay = 0;
+ m_channels[ch].is_playing = false;
+ m_channels[ch].is_seq_playing = false;
+ m_channels[ch].output_remaining = 0;
+ m_channels[ch].decoder->clear();
+ }
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests for
+// our sound stream
+//-------------------------------------------------
+
+void ymz770_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ stream_sample_t *outL, *outR;
+
+ outL = outputs[0];
+ outR = outputs[1];
+
+ for (int i = 0; i < samples; i++)
+ {
+ // run sequencers (should probably be in separate timer callbacks)
+ for (int ch = 0; ch < 8; ch++)
+ {
+ if (m_channels[ch].is_seq_playing)
+ {
+ if (m_channels[ch].seqdelay > 0)
+ {
+ m_channels[ch].seqdelay--;
+ }
+ else
+ {
+ int reg = *m_channels[ch].seqdata++;
+ UINT8 data = *m_channels[ch].seqdata++;
+ switch (reg)
+ {
+ case 0x0f:
+ if (m_channels[ch].seqcontrol & 1)
+ {
+ // loop sequence
+ UINT8 sqn = m_channels[ch].sequence;
+ UINT32 pptr = m_rom_base[(4*sqn)+1+0x400]<<16 | m_rom_base[(4*sqn)+2+0x400]<<8 | m_rom_base[(4*sqn)+3+0x400];
+ m_channels[ch].seqdata = &m_rom_base[pptr];
+ }
+ else
+ {
+ m_channels[ch].is_seq_playing = false;
+ }
+ break;
+ case 0x0e:
+ m_channels[ch].seqdelay = 32 - 1;
+ break;
+ default:
+ internal_reg_write(reg, data);
+ break;
+ }
+ }
+ }
+ }
+
+ // process channels
+ INT32 mix = 0;
+
+ for (int ch = 0; ch < 8; ch++)
+ {
+ if (m_channels[ch].output_remaining > 0)
+ {
+ // force finish current block
+ mix += (m_channels[ch].output_data[m_channels[ch].output_ptr++]*m_channels[ch].volume);
+ m_channels[ch].output_remaining--;
+
+ if (m_channels[ch].output_remaining == 0 && !m_channels[ch].is_playing)
+ m_channels[ch].decoder->clear();
+ }
+
+ else if (m_channels[ch].is_playing)
+ {
+retry:
+ if (m_channels[ch].last_block)
+ {
+ if (m_channels[ch].control & 1)
+ {
+ // loop sample
+ UINT8 phrase = m_channels[ch].phrase;
+ m_channels[ch].atbl = m_rom_base[(4*phrase)+0] >> 4 & 7;
+ m_channels[ch].pptr = 8*(m_rom_base[(4*phrase)+1]<<16 | m_rom_base[(4*phrase)+2]<<8 | m_rom_base[(4*phrase)+3]);
+ }
+ else
+ {
+ m_channels[ch].is_playing = false;
+ m_channels[ch].output_remaining = 0;
+ m_channels[ch].decoder->clear();
+ }
+ }
+
+ if (m_channels[ch].is_playing)
+ {
+ // next block
+ int sample_rate, channel_count;
+ if (!m_channels[ch].decoder->decode_buffer(m_channels[ch].pptr, m_rom_limit, m_channels[ch].output_data, m_channels[ch].output_remaining, sample_rate, channel_count) || m_channels[ch].output_remaining == 0)
+ {
+ m_channels[ch].is_playing = !m_channels[ch].last_block; // detect infinite retry loop
+ m_channels[ch].last_block = true;
+ m_channels[ch].output_remaining = 0;
+ goto retry;
+ }
+
+ m_channels[ch].last_block = m_channels[ch].output_remaining < 1152;
+ m_channels[ch].output_remaining--;
+ m_channels[ch].output_ptr = 1;
+
+ mix += (m_channels[ch].output_data[0]*m_channels[ch].volume);
+ }
+ }
+ }
+
+ outL[i] = outR[i] = mix>>8;
+ }
+}
+
+
+//-------------------------------------------------
+// write - write to the chip's registers
+//-------------------------------------------------
+
+WRITE8_MEMBER( ymz770_device::write )
+{
+ if (offset & 1)
+ {
+ m_stream->update();
+ internal_reg_write(m_cur_reg, data);
+ }
+ else
+ {
+ m_cur_reg = data;
+ }
+}
+
+
+void ymz770_device::internal_reg_write(UINT8 reg, UINT8 data)
+{
+ // global registers
+ if (reg < 0x40)
+ {
+ switch (reg)
+ {
+ case 0x00:
+ m_mute = data & 1;
+ m_doen = data >> 1 & 1;
+ break;
+
+ case 0x01:
+ m_vlma = data;
+ break;
+
+ case 0x02:
+ m_bsl = data & 7;
+ m_cpl = data >> 4 & 7;
+ break;
+
+ // unused
+ default:
+ break;
+ }
+ }
+
+ // playback registers
+ else if (reg < 0x60)
+ {
+ int ch = reg >> 2 & 0x07;
+
+ switch (reg & 0x03)
+ {
+ case 0:
+ m_channels[ch].phrase = data;
+ break;
+
+ case 1:
+ m_channels[ch].volume = data;
+ break;
+
+ case 2:
+ m_channels[ch].pan = data;
+ break;
+
+ case 3:
+ if (data & 6)
+ {
+ UINT8 phrase = m_channels[ch].phrase;
+ m_channels[ch].atbl = m_rom_base[(4*phrase)+0] >> 4 & 7;
+ m_channels[ch].pptr = 8*(m_rom_base[(4*phrase)+1]<<16 | m_rom_base[(4*phrase)+2]<<8 | m_rom_base[(4*phrase)+3]);
+ m_channels[ch].last_block = false;
+
+ m_channels[ch].is_playing = true;
+ }
+ else
+ {
+ m_channels[ch].is_playing = false;
+ }
+
+ m_channels[ch].control = data;
+ break;
+ }
+ }
+
+ // sequencer registers
+ else
+ {
+ int ch = reg >> 4 & 0x07;
+
+ switch (reg & 0x0f)
+ {
+ case 0:
+ m_channels[ch].sequence = data;
+ break;
+ case 1:
+ if (data & 6)
+ {
+ UINT8 sqn = m_channels[ch].sequence;
+ UINT32 pptr = m_rom_base[(4*sqn)+1+0x400]<<16 | m_rom_base[(4*sqn)+2+0x400]<<8 | m_rom_base[(4*sqn)+3+0x400];
+ m_channels[ch].seqdata = &m_rom_base[pptr];
+ m_channels[ch].seqdelay = 0;
+ m_channels[ch].is_seq_playing = true;
+ }
+ else
+ {
+ m_channels[ch].is_seq_playing = false;
+ }
+
+ m_channels[ch].seqcontrol = data;
+ break;
+
+ default:
+ break;
+ }
+ }
+}
diff --git a/src/devices/sound/ymz770.h b/src/devices/sound/ymz770.h
new file mode 100644
index 00000000000..d98d40308ca
--- /dev/null
+++ b/src/devices/sound/ymz770.h
@@ -0,0 +1,99 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert, R. Belmont
+/***************************************************************************
+
+ ymz770.h
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __YMZ770_H__
+#define __YMZ770_H__
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_YMZ770_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, YMZ770, _clock)
+
+#define MCFG_YMZ770_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, YMZ770, _clock)
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// forward definition
+class mpeg_audio;
+
+// ======================> ymz770_device
+
+class ymz770_device : public device_t, public device_sound_interface
+{
+ struct ymz_channel
+ {
+ UINT8 phrase;
+ UINT8 pan;
+ UINT8 volume;
+ UINT8 control;
+
+ bool is_playing, last_block;
+
+ mpeg_audio *decoder;
+
+ INT16 output_data[0x1000];
+ int output_remaining;
+ int output_ptr;
+ int atbl;
+ int pptr;
+
+ UINT8 sequence;
+ UINT8 seqcontrol;
+ UINT8 seqdelay;
+ UINT8 *seqdata;
+ bool is_seq_playing;
+ };
+
+
+public:
+ // construction/destruction
+ ymz770_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ DECLARE_WRITE8_MEMBER(write);
+
+ sound_stream *m_stream;
+
+protected:
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+ void internal_reg_write(UINT8 reg, UINT8 data);
+
+ // data
+ UINT8 m_cur_reg;
+ UINT8 m_mute; // mute chip
+ UINT8 m_doen; // digital output enable
+ UINT8 m_vlma; // overall AAM volume
+ UINT8 m_bsl; // boost level
+ UINT8 m_cpl; // clip limiter
+ UINT8 *m_rom_base;
+ int m_rom_limit;
+
+ ymz_channel m_channels[8];
+};
+
+
+// device type definition
+extern const device_type YMZ770;
+
+#endif /* __ymz770_H__ */
diff --git a/src/devices/sound/zsg2.c b/src/devices/sound/zsg2.c
new file mode 100644
index 00000000000..4f3f1a42ca9
--- /dev/null
+++ b/src/devices/sound/zsg2.c
@@ -0,0 +1,464 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert, R. Belmont, hap
+/*
+ ZOOM ZSG-2 custom wavetable synthesizer
+
+ Written by Olivier Galibert
+ MAME conversion by R. Belmont
+ Working emulation by The Talentuous Hands Of The Popularious hap
+ ---------------------------------------------------------
+
+ Additional notes on the sample format, reverse-engineered
+ by Olivier Galibert and David Haywood:
+
+ The zoom sample rom is decomposed in 0x40000 bytes pages. Each page
+ starts by a header and is followed by compressed samples.
+
+ The header is a vector of 16 bytes structures composed of 4 32bits
+ little-endian values representing:
+ - sample start position in bytes, always a multiple of 4
+ - sample end position in bytes, minus 4, always...
+ - loop position in bytes, always....
+ - flags, probably
+
+ It is interesting to note that this header is *not* parsed by the
+ ZSG. The main program reads the rom through appropriate ZSG
+ commands, and use the results in subsequent register setups. It's
+ not even obvious that the ZSG cares about the pages, it may just
+ see the address space as linear. In the same line, the
+ interpretation of the flags is obviously dependent on the main
+ program, not the ZSG, but some of the bits are directly copied to
+ some of the registers.
+
+ The samples are compressed with a 2:1 ratio. Each block of 4-bytes
+ becomes 4 16-bits samples. Reading the 4 bytes as a *little-endian*
+ 32bits values, the structure is:
+
+ 42222222 51111111 60000000 ssss3333
+
+ 's' is a 4-bit scale value. '0000000', '1111111', '2222222' and
+ '6543333' are signed 7-bits values corresponding to the 4 samples.
+ To compute the final 16bits value, left-align and shift right by s.
+ Yes, that simple.
+
+ ---------------------------------------------------------
+
+TODO:
+- volume/panning is linear? volume slides are too steep
+- most music sounds tinny, probably due to missing DSP?
+- what is reg 0xa/0xc? seems related to volume
+- identify sample flags
+ * bassdrum in shikigam level 1 music is a good hint: it should be one octave
+ lower, indicating possible stereo sample, or base octave(like in ymf278)
+- memory reads out of range sometimes
+
+*/
+
+#include "emu.h"
+#include "zsg2.h"
+
+
+// device type definition
+const device_type ZSG2 = &device_creator<zsg2_device>;
+
+//-------------------------------------------------
+// zsg2_device - constructor
+//-------------------------------------------------
+
+zsg2_device::zsg2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, ZSG2, "ZSG-2", tag, owner, clock, "zsg2", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_mem_base(*this, DEVICE_SELF),
+ m_read_address(0),
+ m_ext_read_handler(*this)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void zsg2_device::device_start()
+{
+ m_ext_read_handler.resolve();
+
+ memset(&m_chan, 0, sizeof(m_chan));
+
+ m_stream = stream_alloc(0, 2, clock() / 768);
+
+ m_mem_blocks = m_mem_base.length();
+ m_mem_copy = auto_alloc_array_clear(machine(), UINT32, m_mem_blocks);
+ m_full_samples = auto_alloc_array_clear(machine(), INT16, m_mem_blocks * 4 + 4); // +4 is for empty block
+
+ // register for savestates
+ save_pointer(NAME(m_mem_copy), m_mem_blocks / sizeof(UINT32));
+ save_pointer(NAME(m_full_samples), (m_mem_blocks * 4 + 4) / sizeof(INT16));
+ save_item(NAME(m_read_address));
+
+ for (int ch = 0; ch < 48; ch++)
+ {
+ save_item(NAME(m_chan[ch].v), ch);
+ save_item(NAME(m_chan[ch].is_playing), ch);
+ save_item(NAME(m_chan[ch].cur_pos), ch);
+ save_item(NAME(m_chan[ch].step_ptr), ch);
+ save_item(NAME(m_chan[ch].step), ch);
+ save_item(NAME(m_chan[ch].start_pos), ch);
+ save_item(NAME(m_chan[ch].end_pos), ch);
+ save_item(NAME(m_chan[ch].loop_pos), ch);
+ save_item(NAME(m_chan[ch].page), ch);
+ save_item(NAME(m_chan[ch].vol), ch);
+ save_item(NAME(m_chan[ch].flags), ch);
+ save_item(NAME(m_chan[ch].panl), ch);
+ save_item(NAME(m_chan[ch].panr), ch);
+ }
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void zsg2_device::device_reset()
+{
+ m_read_address = 0;
+
+ // stop playing and clear all channels
+ control_w(4, 0xffff);
+ control_w(5, 0xffff);
+ control_w(6, 0xffff);
+
+ for (int ch = 0; ch < 48; ch++)
+ for (int reg = 0; reg < 0x10; reg++)
+ chan_w(ch, reg, 0);
+
+#if 0
+ for (int i = 0; i < m_mem_blocks; i++)
+ prepare_samples(i);
+
+ FILE* f;
+
+ f = fopen("zoom_samples.bin","wb");
+ fwrite(m_mem_copy,1,m_mem_blocks*4,f);
+ fclose(f);
+
+ f = fopen("zoom_samples.raw","wb");
+ fwrite(m_full_samples,2,m_mem_blocks*4,f);
+ fclose(f);
+#endif
+}
+
+
+/******************************************************************************/
+
+UINT32 zsg2_device::read_memory(UINT32 offset)
+{
+ if (offset >= m_mem_blocks)
+ return 0;
+
+ if (m_ext_read_handler.isnull())
+ return m_mem_base[offset];
+
+ return m_ext_read_handler(offset);
+}
+
+INT16 *zsg2_device::prepare_samples(UINT32 offset)
+{
+ UINT32 block = read_memory(offset);
+
+ if (block == 0)
+ return &m_full_samples[m_mem_blocks]; // overflow or 0
+
+ if (block == m_mem_copy[offset])
+ return &m_full_samples[offset * 4]; // cached
+
+ m_mem_copy[offset] = block;
+ offset *= 4;
+
+ // decompress 32 byte block to 4 16-bit samples
+ // 42222222 51111111 60000000 ssss3333
+ m_full_samples[offset|0] = block >> 8 & 0x7f;
+ m_full_samples[offset|1] = block >> 16 & 0x7f;
+ m_full_samples[offset|2] = block >> 24 & 0x7f;
+ m_full_samples[offset|3] = (block >> (8+1) & 0x40) | (block >> (16+2) & 0x20) | (block >> (24+3) & 0x10) | (block & 0xf);
+
+ // sign-extend and shift
+ UINT8 shift = block >> 4 & 0xf;
+ for (int i = offset; i < (offset + 4); i++)
+ {
+ m_full_samples[i] <<= 9;
+ m_full_samples[i] >>= shift;
+ }
+
+ return &m_full_samples[offset];
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void zsg2_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ for (int i = 0; i < samples; i++)
+ {
+ INT32 mix_l = 0;
+ INT32 mix_r = 0;
+
+ // loop over all channels
+ for (int ch = 0; ch < 48; ch++)
+ {
+ if (!m_chan[ch].is_playing)
+ continue;
+
+ m_chan[ch].step_ptr += m_chan[ch].step;
+ if (m_chan[ch].step_ptr & 0x10000)
+ {
+ m_chan[ch].step_ptr &= 0xffff;
+ if (++m_chan[ch].cur_pos >= m_chan[ch].end_pos)
+ {
+ // loop sample
+ m_chan[ch].cur_pos = m_chan[ch].loop_pos;
+ if ((m_chan[ch].cur_pos + 1) >= m_chan[ch].end_pos)
+ {
+ // end of sample
+ m_chan[ch].is_playing = false;
+ continue;
+ }
+ }
+ m_chan[ch].samples = prepare_samples(m_chan[ch].page | m_chan[ch].cur_pos);
+ }
+
+ INT32 sample = (m_chan[ch].samples[m_chan[ch].step_ptr >> 14 & 3] * m_chan[ch].vol) >> 16;
+
+ mix_l += (sample * m_chan[ch].panl + sample * (0x1f - m_chan[ch].panr)) >> 5;
+ mix_r += (sample * m_chan[ch].panr + sample * (0x1f - m_chan[ch].panl)) >> 5;
+ }
+
+ outputs[0][i] = mix_l;
+ outputs[1][i] = mix_r;
+ }
+}
+
+
+/******************************************************************************/
+
+void zsg2_device::chan_w(int ch, int reg, UINT16 data)
+{
+ switch (reg)
+ {
+ case 0x0:
+ // lo byte: unknown, 0 on most games
+ // hi byte: start address low
+ m_chan[ch].start_pos = (m_chan[ch].start_pos & 0xff00) | (data >> 8 & 0xff);
+ break;
+
+ case 0x1:
+ // lo byte: start address high
+ // hi byte: address page
+ m_chan[ch].start_pos = (m_chan[ch].start_pos & 0x00ff) | (data << 8 & 0xff00);
+ m_chan[ch].page = data << 8 & 0xff0000;
+ break;
+
+ case 0x2:
+ // no function? always 0
+ break;
+
+ case 0x3:
+ // unknown, always 0x0400
+ break;
+
+ case 0x4:
+ // frequency
+ m_chan[ch].step = data + 1;
+ break;
+
+ case 0x5:
+ // lo byte: loop address low
+ // hi byte: right panning (high bits always 0)
+ m_chan[ch].loop_pos = (m_chan[ch].loop_pos & 0xff00) | (data & 0xff);
+ m_chan[ch].panr = data >> 8 & 0x1f;
+ break;
+
+ case 0x6:
+ // end address
+ m_chan[ch].end_pos = data;
+ break;
+
+ case 0x7:
+ // lo byte: loop address high
+ // hi byte: left panning (high bits always 0)
+ m_chan[ch].loop_pos = (m_chan[ch].loop_pos & 0x00ff) | (data << 8 & 0xff00);
+ m_chan[ch].panl = data >> 8 & 0x1f;
+ break;
+
+ case 0x9:
+ // no function? always 0
+ break;
+
+ case 0xb:
+ // always writes 0
+ // this register is read-only
+ break;
+
+ case 0xe:
+ // volume
+ m_chan[ch].vol = data;
+ break;
+
+ case 0xf:
+ // flags
+ m_chan[ch].flags = data;
+ break;
+
+ default:
+ break;
+ }
+
+ m_chan[ch].v[reg] = data;
+}
+
+UINT16 zsg2_device::chan_r(int ch, int reg)
+{
+ switch (reg)
+ {
+ case 0xb:
+ // ?
+ return 0;
+
+ default:
+ break;
+ }
+
+ return m_chan[ch].v[reg];
+}
+
+
+/******************************************************************************/
+
+void zsg2_device::control_w(int reg, UINT16 data)
+{
+ switch (reg)
+ {
+ case 0x00: case 0x01: case 0x02:
+ {
+ // key on
+ int base = (reg & 3) << 4;
+ for (int i = 0; i < 16; i++)
+ {
+ if (data & (1 << i))
+ {
+ int ch = base | i;
+ m_chan[ch].is_playing = true;
+ m_chan[ch].cur_pos = m_chan[ch].start_pos;
+ m_chan[ch].step_ptr = 0;
+ m_chan[ch].samples = prepare_samples(m_chan[ch].page | m_chan[ch].cur_pos);
+ }
+ }
+ break;
+ }
+
+ case 0x04: case 0x05: case 0x06:
+ {
+ // key off
+ int base = (reg & 3) << 4;
+ for (int i = 0; i < 16; i++)
+ {
+ if (data & (1 << i))
+ {
+ int ch = base | i;
+ m_chan[ch].is_playing = false;
+ }
+ }
+ break;
+ }
+
+ case 0x18:
+ break;
+
+ case 0x1c:
+ // rom readback address low (low 2 bits always 0)
+ if (data & 3) popmessage("ZSG2 address %04X, contact MAMEdev", data);
+ m_read_address = (m_read_address & 0x3fffc000) | (data >> 2 & 0x00003fff);
+ break;
+ case 0x1d:
+ // rom readback address high
+ m_read_address = (m_read_address & 0x00003fff) | (data << 14 & 0x3fffc000);
+ break;
+
+ default:
+ break;
+ }
+}
+
+UINT16 zsg2_device::control_r(int reg)
+{
+ switch (reg)
+ {
+ case 0x14:
+ // memory bus busy?
+ // right before reading memory, it polls until low 8 bits are 0
+ return 0;
+
+ case 0x1e:
+ // rom readback word low
+ return read_memory(m_read_address) & 0xffff;
+ case 0x1f:
+ // rom readback word high
+ return read_memory(m_read_address) >> 16;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************/
+
+WRITE16_MEMBER(zsg2_device::write)
+{
+ // we only support full 16-bit accesses
+ if (mem_mask != 0xffff)
+ {
+ popmessage("ZSG2 write mask %04X, contact MAMEdev", mem_mask);
+ return;
+ }
+
+ m_stream->update();
+
+ if (offset < 0x300)
+ {
+ int chan = offset >> 4;
+ int reg = offset & 0xf;
+
+ chan_w(chan, reg, data);
+ }
+ else
+ {
+ control_w(offset - 0x300, data);
+ }
+}
+
+READ16_MEMBER(zsg2_device::read)
+{
+ // we only support full 16-bit accesses
+ if (mem_mask != 0xffff)
+ {
+ popmessage("ZSG2 read mask %04X, contact MAMEdev", mem_mask);
+ return 0;
+ }
+
+ if (offset < 0x300)
+ {
+ int chan = offset >> 4;
+ int reg = offset & 0xf;
+
+ return chan_r(chan, reg);
+ }
+ else
+ {
+ return control_r(offset - 0x300);
+ }
+}
diff --git a/src/devices/sound/zsg2.h b/src/devices/sound/zsg2.h
new file mode 100644
index 00000000000..e98e2c315ae
--- /dev/null
+++ b/src/devices/sound/zsg2.h
@@ -0,0 +1,92 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert, R. Belmont, hap
+/*
+ ZOOM ZSG-2 custom wavetable synthesizer
+*/
+
+#pragma once
+
+#ifndef __ZSG2_H__
+#define __ZSG2_H__
+
+
+//**************************************************************************
+// INTERFACE CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_ZSG2_ADD(_tag, _clock) \
+ MCFG_DEVICE_ADD(_tag, ZSG2, _clock)
+#define MCFG_ZSG2_REPLACE(_tag, _clock) \
+ MCFG_DEVICE_REPLACE(_tag, ZSG2, _clock)
+
+#define MCFG_ZSG2_EXT_READ_HANDLER(_devcb) \
+ devcb = &zsg2_device::set_ext_read_handler(*device, DEVCB_##_devcb);
+
+
+// ======================> zsg2_device
+
+class zsg2_device : public device_t,
+ public device_sound_interface
+{
+public:
+ zsg2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ ~zsg2_device() { }
+
+ // static configuration helpers
+ template<class _Object> static devcb_base &set_ext_read_handler(device_t &device, _Object object) { return downcast<zsg2_device &>(device).m_ext_read_handler.set_callback(object); }
+
+ DECLARE_READ16_MEMBER(read);
+ DECLARE_WRITE16_MEMBER(write);
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_reset();
+
+ // sound stream update overrides
+ virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+private:
+ // 16 registers per channel, 48 channels
+ struct zchan
+ {
+ UINT16 v[16];
+ bool is_playing;
+ INT16 *samples;
+ UINT32 cur_pos;
+ UINT32 step_ptr;
+ UINT32 step;
+ UINT32 start_pos;
+ UINT32 end_pos;
+ UINT32 loop_pos;
+ UINT32 page;
+ UINT16 vol;
+ UINT16 flags;
+ UINT8 panl;
+ UINT8 panr;
+ };
+
+ zchan m_chan[48];
+
+ required_region_ptr<UINT32> m_mem_base;
+ UINT32 m_read_address;
+ UINT32 *m_mem_copy;
+ UINT32 m_mem_blocks;
+ INT16 *m_full_samples;
+
+ sound_stream *m_stream;
+
+ devcb_read32 m_ext_read_handler;
+
+ UINT32 read_memory(UINT32 offset);
+ void chan_w(int ch, int reg, UINT16 data);
+ UINT16 chan_r(int ch, int reg);
+ void control_w(int reg, UINT16 data);
+ UINT16 control_r(int reg);
+ INT16 *prepare_samples(UINT32 offset);
+};
+
+extern const device_type ZSG2;
+
+
+#endif /* __ZSG2_H__ */