From 7b2237ffac7de70578c0d60093acdb9fd0af4fbb Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 20 May 2022 13:22:35 -0500 Subject: [PATCH] we see it --- CMakeLists.txt | 1 + src/engine/platform/namcowsg.cpp | 432 +++++++++++++++++++++ src/engine/platform/namcowsg.h | 106 +++++ src/engine/platform/sound/namco.cpp | 42 +- src/engine/platform/sound/namco.h | 21 +- src/engine/platform/sound/oki/okim6258.cpp | 326 ++++++++++++++++ src/engine/platform/sound/oki/okim6258.h | 74 ++++ 7 files changed, 952 insertions(+), 50 deletions(-) create mode 100644 src/engine/platform/namcowsg.cpp create mode 100644 src/engine/platform/namcowsg.h create mode 100644 src/engine/platform/sound/oki/okim6258.cpp create mode 100644 src/engine/platform/sound/oki/okim6258.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7673086e..a96485d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -384,6 +384,7 @@ src/engine/platform/pet.cpp src/engine/platform/vic20.cpp src/engine/platform/vrc6.cpp src/engine/platform/scc.cpp +src/engine/platform/namcowsg.cpp src/engine/platform/dummy.cpp ) diff --git a/src/engine/platform/namcowsg.cpp b/src/engine/platform/namcowsg.cpp new file mode 100644 index 00000000..ff418dfc --- /dev/null +++ b/src/engine/platform/namcowsg.cpp @@ -0,0 +1,432 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "namcowsg.h" +#include "../engine.h" +#include + +//#define rWrite(a,v) pendingWrites[a]=v; +#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } +#define chWrite(c,a,v) \ + if (!skipRegisterWrites) { \ + if (curChan!=c) { \ + curChan=c; \ + rWrite(0,curChan); \ + } \ + regPool[16+((c)<<4)+((a)&0x0f)]=v; \ + rWrite(a,v); \ + } + +#define CHIP_DIVIDER 32 + +const char* regCheatSheetNamcoWSG[]={ + "Select", "0", + "MasterVol", "1", + "FreqL", "2", + "FreqH", "3", + "DataCtl", "4", + "ChanVol", "5", + "WaveCtl", "6", + "NoiseCtl", "7", + "LFOFreq", "8", + "LFOCtl", "9", + NULL +}; + +const char** DivPlatformNamcoWSG::getRegisterSheet() { + return regCheatSheetNamcoWSG; +} + +const char* DivPlatformNamcoWSG::getEffectName(unsigned char effect) { + switch (effect) { + case 0x10: + return "10xx: Change waveform"; + break; + case 0x11: + return "11xx: Toggle noise mode"; + break; + } + return NULL; +} + +void DivPlatformNamcoWSG::acquire(short* bufL, short* bufR, size_t start, size_t len) { + short* buf[2]={ + bufL+start, bufR+start + }; + while (!writes.empty()) { + QueuedWrite w=writes.front(); + switch (devType) { + case 1: + ((namco_device*)namco)->pacman_sound_w(w.addr,w.val); + break; + case 2: + ((namco_device*)namco)->polepos_sound_w(w.addr,w.val); + break; + case 15: + ((namco_15xx_device*)namco)->sharedram_w(w.addr,w.val); + break; + case 30: + ((namco_cus30_device*)namco)->namcos1_cus30_w(w.addr,w.val); + break; + } + regPool[w.addr]=w.val; + writes.pop(); + } + namco->sound_stream_update(buf,len); +} + +void DivPlatformNamcoWSG::updateWave(int ch) { + chWrite(ch,0x04,0x5f); + chWrite(ch,0x04,0x1f); + for (int i=0; i<32; i++) { + chWrite(ch,0x06,chan[ch].ws.output[i]); + } + if (chan[ch].active) { + chWrite(ch,0x04,0x80|chan[ch].outVol); + } +} + +void DivPlatformNamcoWSG::tick(bool sysTick) { + for (int i=0; i>4; + chWrite(i,0x04,0x80|chan[i].outVol); + } + if (chan[i].std.duty.had && i>=4) { + chan[i].noise=chan[i].std.duty.val; + chan[i].freqChanged=true; + } + if (chan[i].std.arp.had) { + if (!chan[i].inPorta) { + if (chan[i].std.arp.mode) { + chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val); + } else { + chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val); + } + } + chan[i].freqChanged=true; + } else { + if (chan[i].std.arp.mode && chan[i].std.arp.finished) { + chan[i].baseFreq=NOTE_PERIODIC(chan[i].note); + chan[i].freqChanged=true; + } + } + if (chan[i].std.wave.had) { + if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) { + chan[i].wave=chan[i].std.wave.val; + chan[i].ws.changeWave1(chan[i].wave); + if (!chan[i].keyOff) chan[i].keyOn=true; + } + } + if (chan[i].std.panL.had) { + chan[i].pan&=0x0f; + chan[i].pan|=(chan[i].std.panL.val&15)<<4; + } + if (chan[i].std.panR.had) { + chan[i].pan&=0xf0; + chan[i].pan|=chan[i].std.panR.val&15; + } + if (chan[i].std.panL.had || chan[i].std.panR.had) { + chWrite(i,0x05,isMuted[i]?0:chan[i].pan); + } + if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } + chan[i].freqChanged=true; + } + if (chan[i].active) { + if (chan[i].ws.tick() || (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1)) { + updateWave(i); + } + } + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); + if (chan[i].freq>4095) chan[i].freq=4095; + chWrite(i,0x02,chan[i].freq&0xff); + chWrite(i,0x03,chan[i].freq>>8); + if (chan[i].keyOn) { + //rWrite(16+i*5,0x80); + //chWrite(i,0x04,0x80|chan[i].vol); + } + if (chan[i].keyOff) { + chWrite(i,0x04,0); + } + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + chan[i].freqChanged=false; + } + } +} + +int DivPlatformNamcoWSG::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_PCE); + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + } + chan[c.chan].active=true; + chan[c.chan].keyOn=true; + chWrite(c.chan,0x04,0x80|chan[c.chan].vol); + chan[c.chan].macroInit(ins); + if (chan[c.chan].wave<0) { + chan[c.chan].wave=0; + chan[c.chan].ws.changeWave1(chan[c.chan].wave); + } + chan[c.chan].ws.init(ins,32,15,chan[c.chan].insChanged); + chan[c.chan].insChanged=false; + break; + } + case DIV_CMD_NOTE_OFF: + chan[c.chan].active=false; + chan[c.chan].keyOff=true; + chan[c.chan].macroInit(NULL); + break; + case DIV_CMD_NOTE_OFF_ENV: + case DIV_CMD_ENV_RELEASE: + chan[c.chan].std.release(); + break; + case DIV_CMD_INSTRUMENT: + if (chan[c.chan].ins!=c.value || c.value2==1) { + chan[c.chan].ins=c.value; + chan[c.chan].insChanged=true; + } + break; + case DIV_CMD_VOLUME: + if (chan[c.chan].vol!=c.value) { + chan[c.chan].vol=c.value; + if (!chan[c.chan].std.vol.has) { + chan[c.chan].outVol=c.value; + if (chan[c.chan].active) chWrite(c.chan,0x04,0x80|chan[c.chan].outVol); + } + } + break; + case DIV_CMD_GET_VOLUME: + if (chan[c.chan].std.vol.has) { + return chan[c.chan].vol; + } + return chan[c.chan].outVol; + break; + case DIV_CMD_PITCH: + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_WAVE: + chan[c.chan].wave=c.value; + chan[c.chan].ws.changeWave1(chan[c.chan].wave); + chan[c.chan].keyOn=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=NOTE_PERIODIC(c.value2); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } + case DIV_CMD_STD_NOISE_MODE: + chan[c.chan].noise=c.value; + chWrite(c.chan,0x07,chan[c.chan].noise?(0x80|chan[c.chan].note):0); + break; + case DIV_CMD_PANNING: { + chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); + chWrite(c.chan,0x05,isMuted[c.chan]?0:chan[c.chan].pan); + break; + } + case DIV_CMD_LEGATO: + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + break; + case DIV_CMD_PRE_PORTA: + if (chan[c.chan].active && c.value2) { + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE)); + } + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_GET_VOLMAX: + return 15; + break; + case DIV_ALWAYS_SET_VOLUME: + return 1; + break; + default: + break; + } + return 1; +} + +void DivPlatformNamcoWSG::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; + chWrite(ch,0x05,isMuted[ch]?0:chan[ch].pan); +} + +void DivPlatformNamcoWSG::forceIns() { + for (int i=0; idevice_start(NULL); + lastPan=0xff; + cycles=0; + curChan=-1; +} + +bool DivPlatformNamcoWSG::isStereo() { + return (devType==30); +} + +bool DivPlatformNamcoWSG::keyOffAffectsArp(int ch) { + return true; +} + +void DivPlatformNamcoWSG::notifyWaveChange(int wave) { + for (int i=0; idevice_clock_changed(rate); + for (int i=0; irate=rate; + } +} + +void DivPlatformNamcoWSG::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformNamcoWSG::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +int DivPlatformNamcoWSG::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + for (int i=0; i +#include "../macroInt.h" +#include "../waveSynth.h" +#include "sound/namco.h" + +class DivPlatformNamcoWSG: public DivDispatch { + struct Channel { + int freq, baseFreq, pitch, pitch2, note; + int ins; + unsigned char pan; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise; + signed char vol, outVol, wave; + DivMacroInt std; + DivWaveSynth ws; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } + Channel(): + freq(0), + baseFreq(0), + pitch(0), + pitch2(0), + note(0), + ins(-1), + pan(255), + active(false), + insChanged(true), + freqChanged(false), + keyOn(false), + keyOff(false), + inPorta(false), + noise(false), + vol(31), + outVol(31), + wave(-1) {} + }; + Channel chan[8]; + DivDispatchOscBuffer* oscBuf[8]; + bool isMuted[8]; + struct QueuedWrite { + unsigned char addr; + unsigned char val; + QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} + }; + std::queue writes; + unsigned char lastPan; + + int cycles, curChan, delay; + int tempL[32]; + int tempR[32]; + namco_audio_device* namco; + int devType, chans; + unsigned char regPool[512]; + void updateWave(int ch); + friend void putDispatchChan(void*,int,int); + public: + void acquire(short* bufL, short* bufR, size_t start, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(bool sysTick=true); + void muteChannel(int ch, bool mute); + bool isStereo(); + bool keyOffAffectsArp(int ch); + void setDeviceType(int type); + void setFlags(unsigned int flags); + void notifyWaveChange(int wave); + void notifyInsDeletion(void* ins); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + const char* getEffectName(unsigned char effect); + int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); + void quit(); + ~DivPlatformNamcoWSG(); +}; + +#endif diff --git a/src/engine/platform/sound/namco.cpp b/src/engine/platform/sound/namco.cpp index 2ab30462..6cbae821 100644 --- a/src/engine/platform/sound/namco.cpp +++ b/src/engine/platform/sound/namco.cpp @@ -80,11 +80,13 @@ namco_cus30_device::namco_cus30_device(uint32_t clock) // device_start - device-specific startup //------------------------------------------------- -void namco_audio_device::device_start() +void namco_audio_device::device_start(unsigned char* wavePtr) { /* extract globals from the interface */ m_last_channel = m_channel_list + m_voices; + m_wave_ptr = wavePtr; + /* build the waveform table */ build_decoded_waveform(m_wave_ptr); @@ -106,34 +108,19 @@ void namco_audio_device::device_start() } } - -void namco_device::device_start() +void namco_audio_device::device_clock_changed(int clk) { - namco_audio_device::device_start(); + int clock_multiple; -} - - -void namco_15xx_device::device_start() -{ - namco_audio_device::device_start(); -} - - -void namco_audio_device::device_clock_changed() -{ - //int clock_multiple; - - /* // adjust internal clock - m_namco_clock = clock(); + m_namco_clock = clk; 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;*/ + 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); } @@ -791,18 +778,3 @@ void namco_audio_device::sound_stream_update(short** outputs, int len) } } } - -void namco_device::sound_stream_update(short** outputs, int len) -{ - namco_audio_device::sound_stream_update(outputs,len); -} - -void namco_15xx_device::sound_stream_update(short** outputs, int len) -{ - namco_audio_device::sound_stream_update(outputs,len); -} - -void namco_cus30_device::sound_stream_update(short** outputs, int len) -{ - namco_audio_device::sound_stream_update(outputs,len); -} diff --git a/src/engine/platform/sound/namco.h b/src/engine/platform/sound/namco.h index 07bf9bb6..27e2b887 100644 --- a/src/engine/platform/sound/namco.h +++ b/src/engine/platform/sound/namco.h @@ -15,7 +15,6 @@ public: void sound_enable_w(int state); -protected: static constexpr unsigned MAX_VOICES = 8; static constexpr unsigned MAX_VOLUME = 16; @@ -36,8 +35,8 @@ protected: namco_audio_device(uint32_t clock); // device-level overrides - void device_start(); - void device_clock_changed(); + void device_start(unsigned char* wavePtr); + void device_clock_changed(int clk); // internal state @@ -69,6 +68,7 @@ protected: int16_t m_waveform[MAX_VOLUME][512]; virtual void sound_stream_update(short** outputs, int len); + virtual ~namco_audio_device() {} }; class namco_device : public namco_audio_device @@ -81,11 +81,7 @@ public: uint8_t polepos_sound_r(int offset); void polepos_sound_w(int offset, uint8_t data); -protected: - // device-level overrides - virtual void device_start(); - - virtual void sound_stream_update(short** outputs, int len); + ~namco_device() {} private: uint8_t m_soundregs[0x400]; @@ -101,11 +97,7 @@ public: uint8_t sharedram_r(int offset); void sharedram_w(int offset, uint8_t data); -protected: - // device-level overrides - virtual void device_start(); - - virtual void sound_stream_update(short** outputs, int len); + ~namco_15xx_device() {} private: uint8_t m_soundregs[0x400]; @@ -123,8 +115,7 @@ public: void pacman_sound_w(int offset, uint8_t data); -protected: - virtual void sound_stream_update(short** outputs, int len); + ~namco_cus30_device() {} }; #endif // MAME_SOUND_NAMCO_H diff --git a/src/engine/platform/sound/oki/okim6258.cpp b/src/engine/platform/sound/oki/okim6258.cpp new file mode 100644 index 00000000..349fe9ca --- /dev/null +++ b/src/engine/platform/sound/oki/okim6258.cpp @@ -0,0 +1,326 @@ +// 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 +DEFINE_DEVICE_TYPE(OKIM6258, okim6258_device, "okim6258", "OKI MSM6258 ADPCM") + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// okim6258_device - constructor +//------------------------------------------------- + +okim6258_device::okim6258_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, OKIM6258, tag, owner, clock), + device_sound_interface(mconfig, *this), + m_status(0), + m_start_divider(0), + m_divider(512), + m_adpcm_type(0), + m_data_in(0), + m_nibble_shift(0), + m_stream(nullptr), + m_output_bits(0), + m_signal(0), + m_step(0) +{ +} + + + +/********************************************************************************************** + + compute_tables -- compute the difference tables + +***********************************************************************************************/ + +static void 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); + } + } + + tables_computed = 1; +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void okim6258_device::device_start() +{ + compute_tables(); + + m_divider = dividers[m_start_divider]; + + m_stream = stream_alloc(0, 1, clock()/m_divider); + + m_signal = -2; + m_step = 0; + + 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, std::vector const &inputs, std::vector &outputs) +{ + auto &buffer = outputs[0]; + + if (m_status & STATUS_PLAYING) + { + int nibble_shift = m_nibble_shift; + + for (int sampindex = 0; sampindex < buffer.samples(); sampindex++) + { + /* Compute the new amplitude and update the current step */ + int nibble = (m_data_in >> nibble_shift) & 0xf; + + /* Output to the buffer */ + int16_t sample = clock_adpcm(nibble); + + nibble_shift ^= 4; + + buffer.put_int(sampindex, sample, 32768); + } + + /* Update the parameters */ + m_nibble_shift = nibble_shift; + } + else + { + buffer.fill(0); + } +} + + + +/********************************************************************************************** + + state save support for MAME + +***********************************************************************************************/ + +void okim6258_device::state_save_register() +{ + save_item(NAME(m_status)); + 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_t okim6258_device::clock_adpcm(uint8_t nibble) +{ + int32_t max = (1 << (m_output_bits - 1)) - 1; + int32_t 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) +{ + m_divider = dividers[val]; + notify_clock_changed(); +} + + +/********************************************************************************************** + + okim6258::set_clock -- set the master clock + +***********************************************************************************************/ + +void okim6258_device::device_clock_changed() +{ + m_stream->set_sample_rate(clock() / m_divider); +} + + +/********************************************************************************************** + + okim6258::get_vclk -- get the VCLK/sampling frequency + +***********************************************************************************************/ + +int okim6258_device::get_vclk() +{ + return (clock() / m_divider); +} + + +/********************************************************************************************** + + okim6258_status_r -- read the status port of an OKIM6258-compatible chip + +***********************************************************************************************/ + +uint8_t okim6258_device::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 + +***********************************************************************************************/ +void okim6258_device::data_w(uint8_t data) +{ + /* 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 + +***********************************************************************************************/ + +void okim6258_device::ctrl_w(uint8_t data) +{ + 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/engine/platform/sound/oki/okim6258.h b/src/engine/platform/sound/oki/okim6258.h new file mode 100644 index 00000000..95189f0e --- /dev/null +++ b/src/engine/platform/sound/oki/okim6258.h @@ -0,0 +1,74 @@ +// license:BSD-3-Clause +// copyright-holders:Barry Rodewald +#ifndef MAME_SOUND_OKIM6258_H +#define MAME_SOUND_OKIM6258_H + +#pragma once + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> okim6258_device + +class okim6258_device : public device_t, + public device_sound_interface +{ +public: + static constexpr int FOSC_DIV_BY_1024 = 0; + static constexpr int FOSC_DIV_BY_768 = 1; + static constexpr int FOSC_DIV_BY_512 = 2; + + static constexpr int TYPE_3BITS = 0; + static constexpr int TYPE_4BITS = 1; + + static constexpr int OUTPUT_10BITS = 10; + static constexpr int OUTPUT_12BITS = 12; + + okim6258_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // configuration + void set_start_div(int div) { m_start_divider = div; } + void set_type(int type) { m_adpcm_type = type; } + void set_outbits(int outbit) { m_output_bits = outbit; } + + uint8_t status_r(); + void data_w(uint8_t data); + void ctrl_w(uint8_t data); + + void set_divider(int val); + int get_vclk(); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_clock_changed() override; + + // sound stream update overrides + virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; + +private: + void state_save_register(); + int16_t clock_adpcm(uint8_t nibble); + + uint8_t m_status; + + uint32_t m_start_divider; + uint32_t m_divider; /* master clock divider */ + uint8_t m_adpcm_type; /* 3/4 bit ADPCM select */ + uint8_t m_data_in; /* ADPCM data-in register */ + uint8_t m_nibble_shift; /* nibble select */ + sound_stream *m_stream; /* which stream are we playing on? */ + + uint8_t m_output_bits; /* D/A precision is 10-bits but 12-bit data can be + output serially to an external DAC */ + + int32_t m_signal; + int32_t m_step; +}; + +DECLARE_DEVICE_TYPE(OKIM6258, okim6258_device) + +#endif // MAME_SOUND_OKIM6258_H