Merge branch 'master' of https://github.com/tildearrow/furnace into rf5c68
This commit is contained in:
		
						commit
						27a412c134
					
				| 
						 | 
					@ -319,6 +319,8 @@ src/engine/platform/sound/vrcvi/vrcvi.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
src/engine/platform/sound/scc/scc.cpp
 | 
					src/engine/platform/sound/scc/scc.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					src/engine/platform/sound/ymz280b.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
src/engine/platform/sound/rf5c68.cpp
 | 
					src/engine/platform/sound/rf5c68.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
src/engine/platform/oplAInterface.cpp
 | 
					src/engine/platform/oplAInterface.cpp
 | 
				
			||||||
| 
						 | 
					@ -386,6 +388,8 @@ src/engine/platform/pet.cpp
 | 
				
			||||||
src/engine/platform/vic20.cpp
 | 
					src/engine/platform/vic20.cpp
 | 
				
			||||||
src/engine/platform/vrc6.cpp
 | 
					src/engine/platform/vrc6.cpp
 | 
				
			||||||
src/engine/platform/scc.cpp
 | 
					src/engine/platform/scc.cpp
 | 
				
			||||||
 | 
					src/engine/platform/ymz280b.cpp
 | 
				
			||||||
 | 
					src/engine/platform/namcowsg.cpp
 | 
				
			||||||
src/engine/platform/rf5c68.cpp
 | 
					src/engine/platform/rf5c68.cpp
 | 
				
			||||||
src/engine/platform/dummy.cpp
 | 
					src/engine/platform/dummy.cpp
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,6 +59,7 @@
 | 
				
			||||||
#include "platform/fds.h"
 | 
					#include "platform/fds.h"
 | 
				
			||||||
#include "platform/mmc5.h"
 | 
					#include "platform/mmc5.h"
 | 
				
			||||||
#include "platform/scc.h"
 | 
					#include "platform/scc.h"
 | 
				
			||||||
 | 
					#include "platform/ymz280b.h"
 | 
				
			||||||
#include "platform/rf5c68.h"
 | 
					#include "platform/rf5c68.h"
 | 
				
			||||||
#include "platform/dummy.h"
 | 
					#include "platform/dummy.h"
 | 
				
			||||||
#include "../ta-log.h"
 | 
					#include "../ta-log.h"
 | 
				
			||||||
| 
						 | 
					@ -352,6 +353,10 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
 | 
				
			||||||
      dispatch=new DivPlatformSCC;
 | 
					      dispatch=new DivPlatformSCC;
 | 
				
			||||||
      ((DivPlatformSCC*)dispatch)->setChipModel(true);
 | 
					      ((DivPlatformSCC*)dispatch)->setChipModel(true);
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
 | 
					    case DIV_SYSTEM_YMZ280B:
 | 
				
			||||||
 | 
					      dispatch=new DivPlatformYMZ280B;
 | 
				
			||||||
 | 
					      ((DivPlatformYMZ280B*)dispatch)->setChipModel(280);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
    case DIV_SYSTEM_RF5C68:
 | 
					    case DIV_SYSTEM_RF5C68:
 | 
				
			||||||
      dispatch=new DivPlatformRF5C68;
 | 
					      dispatch=new DivPlatformRF5C68;
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										432
									
								
								src/engine/platform/namcowsg.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										432
									
								
								src/engine/platform/namcowsg.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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 <math.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//#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<chans; i++) {
 | 
				
			||||||
 | 
					    chan[i].std.next();
 | 
				
			||||||
 | 
					    if (chan[i].std.vol.had) {
 | 
				
			||||||
 | 
					      chan[i].outVol=((chan[i].vol&15)*MIN(15,chan[i].std.vol.val))>>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; i<chans; i++) {
 | 
				
			||||||
 | 
					    chan[i].insChanged=true;
 | 
				
			||||||
 | 
					    chan[i].freqChanged=true;
 | 
				
			||||||
 | 
					    updateWave(i);
 | 
				
			||||||
 | 
					    chWrite(i,0x05,isMuted[i]?0:chan[i].pan);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void* DivPlatformNamcoWSG::getChanState(int ch) {
 | 
				
			||||||
 | 
					  return &chan[ch];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DivDispatchOscBuffer* DivPlatformNamcoWSG::getOscBuffer(int ch) {
 | 
				
			||||||
 | 
					  return oscBuf[ch];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned char* DivPlatformNamcoWSG::getRegisterPool() {
 | 
				
			||||||
 | 
					  return regPool;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int DivPlatformNamcoWSG::getRegisterPoolSize() {
 | 
				
			||||||
 | 
					  return 112;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformNamcoWSG::reset() {
 | 
				
			||||||
 | 
					  while (!writes.empty()) writes.pop();
 | 
				
			||||||
 | 
					  memset(regPool,0,128);
 | 
				
			||||||
 | 
					  for (int i=0; i<chans; i++) {
 | 
				
			||||||
 | 
					    chan[i]=DivPlatformNamcoWSG::Channel();
 | 
				
			||||||
 | 
					    chan[i].std.setEngine(parent);
 | 
				
			||||||
 | 
					    chan[i].ws.setEngine(parent);
 | 
				
			||||||
 | 
					    chan[i].ws.init(NULL,32,15,false);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (dumpWrites) {
 | 
				
			||||||
 | 
					    addWrite(0xffffffff,0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // TODO: wave memory
 | 
				
			||||||
 | 
					  namco->device_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; i<chans; i++) {
 | 
				
			||||||
 | 
					    if (chan[i].wave==wave) {
 | 
				
			||||||
 | 
					      chan[i].ws.changeWave1(wave);
 | 
				
			||||||
 | 
					      updateWave(i);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformNamcoWSG::notifyInsDeletion(void* ins) {
 | 
				
			||||||
 | 
					  for (int i=0; i<chans; i++) {
 | 
				
			||||||
 | 
					    chan[i].std.notifyInsDeletion((DivInstrument*)ins);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformNamcoWSG::setDeviceType(int type) {
 | 
				
			||||||
 | 
					  devType=type;
 | 
				
			||||||
 | 
					  switch (type) {
 | 
				
			||||||
 | 
					    case 15:
 | 
				
			||||||
 | 
					    case 30:
 | 
				
			||||||
 | 
					      chans=8;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      chans=3;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformNamcoWSG::setFlags(unsigned int flags) {
 | 
				
			||||||
 | 
					  chipClock=3072000;
 | 
				
			||||||
 | 
					  rate=chipClock/16;
 | 
				
			||||||
 | 
					  namco->device_clock_changed(rate);
 | 
				
			||||||
 | 
					  for (int i=0; i<chans; i++) {
 | 
				
			||||||
 | 
					    oscBuf[i]->rate=rate;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformNamcoWSG::poke(unsigned int addr, unsigned short val) {
 | 
				
			||||||
 | 
					  rWrite(addr,val);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformNamcoWSG::poke(std::vector<DivRegWrite>& 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<chans; i++) {
 | 
				
			||||||
 | 
					    isMuted[i]=false;
 | 
				
			||||||
 | 
					    oscBuf[i]=new DivDispatchOscBuffer;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  switch (devType) {
 | 
				
			||||||
 | 
					    case 15:
 | 
				
			||||||
 | 
					      namco=new namco_15xx_device(3072000);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case 30:
 | 
				
			||||||
 | 
					      namco=new namco_cus30_device(3072000);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      namco=new namco_device(3072000);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  setFlags(flags);
 | 
				
			||||||
 | 
					  reset();
 | 
				
			||||||
 | 
					  return 6;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformNamcoWSG::quit() {
 | 
				
			||||||
 | 
					  for (int i=0; i<chans; i++) {
 | 
				
			||||||
 | 
					    delete oscBuf[i];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  delete namco;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DivPlatformNamcoWSG::~DivPlatformNamcoWSG() {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										106
									
								
								src/engine/platform/namcowsg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								src/engine/platform/namcowsg.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,106 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef _NAMCOWSG_H
 | 
				
			||||||
 | 
					#define _NAMCOWSG_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../dispatch.h"
 | 
				
			||||||
 | 
					#include <queue>
 | 
				
			||||||
 | 
					#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<QueuedWrite> 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<DivRegWrite>& 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
 | 
				
			||||||
| 
						 | 
					@ -80,11 +80,13 @@ namco_cus30_device::namco_cus30_device(uint32_t clock)
 | 
				
			||||||
//  device_start - device-specific startup
 | 
					//  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 */
 | 
						/* extract globals from the interface */
 | 
				
			||||||
	m_last_channel = m_channel_list + m_voices;
 | 
						m_last_channel = m_channel_list + m_voices;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        m_wave_ptr = wavePtr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* build the waveform table */
 | 
						/* build the waveform table */
 | 
				
			||||||
	build_decoded_waveform(m_wave_ptr);
 | 
						build_decoded_waveform(m_wave_ptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,34 +108,19 @@ void namco_audio_device::device_start()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void namco_audio_device::device_clock_changed(int clk)
 | 
				
			||||||
void namco_device::device_start()
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	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 
 | 
						// adjust internal clock 
 | 
				
			||||||
	m_namco_clock = clock();
 | 
						m_namco_clock = clk;
 | 
				
			||||||
	for (clock_multiple = 0; m_namco_clock < INTERNAL_RATE; clock_multiple++)
 | 
						for (clock_multiple = 0; m_namco_clock < INTERNAL_RATE; clock_multiple++)
 | 
				
			||||||
		m_namco_clock *= 2;
 | 
							m_namco_clock *= 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m_f_fracbits = clock_multiple + 15;
 | 
						m_f_fracbits = clock_multiple + 15;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// adjust output clock 
 | 
						// 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);
 | 
						//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);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,6 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void sound_enable_w(int state);
 | 
						void sound_enable_w(int state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
	static constexpr unsigned MAX_VOICES = 8;
 | 
						static constexpr unsigned MAX_VOICES = 8;
 | 
				
			||||||
	static constexpr unsigned MAX_VOLUME = 16;
 | 
						static constexpr unsigned MAX_VOLUME = 16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,8 +35,8 @@ protected:
 | 
				
			||||||
	namco_audio_device(uint32_t clock);
 | 
						namco_audio_device(uint32_t clock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// device-level overrides
 | 
						// device-level overrides
 | 
				
			||||||
	void device_start();
 | 
						void device_start(unsigned char* wavePtr);
 | 
				
			||||||
	void device_clock_changed();
 | 
						void device_clock_changed(int clk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// internal state
 | 
						// internal state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,6 +68,7 @@ protected:
 | 
				
			||||||
	int16_t m_waveform[MAX_VOLUME][512];
 | 
						int16_t m_waveform[MAX_VOLUME][512];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	virtual void sound_stream_update(short** outputs, int len);
 | 
						virtual void sound_stream_update(short** outputs, int len);
 | 
				
			||||||
 | 
					        virtual ~namco_audio_device() {}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class namco_device : public namco_audio_device
 | 
					class namco_device : public namco_audio_device
 | 
				
			||||||
| 
						 | 
					@ -81,11 +81,7 @@ public:
 | 
				
			||||||
	uint8_t polepos_sound_r(int offset);
 | 
						uint8_t polepos_sound_r(int offset);
 | 
				
			||||||
	void polepos_sound_w(int offset, uint8_t data);
 | 
						void polepos_sound_w(int offset, uint8_t data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					        ~namco_device() {}
 | 
				
			||||||
	// device-level overrides
 | 
					 | 
				
			||||||
	virtual void device_start();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	virtual void sound_stream_update(short** outputs, int len);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	uint8_t m_soundregs[0x400];
 | 
						uint8_t m_soundregs[0x400];
 | 
				
			||||||
| 
						 | 
					@ -101,11 +97,7 @@ public:
 | 
				
			||||||
	uint8_t sharedram_r(int offset);
 | 
						uint8_t sharedram_r(int offset);
 | 
				
			||||||
	void sharedram_w(int offset, uint8_t data);
 | 
						void sharedram_w(int offset, uint8_t data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					       ~namco_15xx_device() {}
 | 
				
			||||||
	// device-level overrides
 | 
					 | 
				
			||||||
	virtual void device_start();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	virtual void sound_stream_update(short** outputs, int len);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	uint8_t m_soundregs[0x400];
 | 
						uint8_t m_soundregs[0x400];
 | 
				
			||||||
| 
						 | 
					@ -123,8 +115,7 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void pacman_sound_w(int offset, uint8_t data);
 | 
						void pacman_sound_w(int offset, uint8_t data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					        ~namco_cus30_device() {}
 | 
				
			||||||
	virtual void sound_stream_update(short** outputs, int len);
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // MAME_SOUND_NAMCO_H
 | 
					#endif // MAME_SOUND_NAMCO_H
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										326
									
								
								src/engine/platform/sound/oki/okim6258.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										326
									
								
								src/engine/platform/sound/oki/okim6258.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										74
									
								
								src/engine/platform/sound/oki/okim6258.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/engine/platform/sound/oki/okim6258.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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<read_stream_view> const &inputs, std::vector<write_stream_view> &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
 | 
				
			||||||
							
								
								
									
										785
									
								
								src/engine/platform/sound/ymz280b.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										785
									
								
								src/engine/platform/sound/ymz280b.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,785 @@
 | 
				
			||||||
 | 
					// 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.cpp, bfm_sc4/5.cpp) also use ROM readback.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ymz280b.h"
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAX_SAMPLE_CHUNK    10000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static constexpr unsigned FRAC_BITS = 8;
 | 
				
			||||||
 | 
					static constexpr s32      FRAC_ONE = 1 << FRAC_BITS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* step size index shift table */
 | 
				
			||||||
 | 
					static constexpr int index_scale[8] = { 0x0e6, 0x0e6, 0x0e6, 0x0e6, 0x133, 0x199, 0x200, 0x266 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* lookup table for the precomputed difference */
 | 
				
			||||||
 | 
					static int diff_lookup[16];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ymz280b_device::update_step(struct YMZ280BVoice *voice)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int frequency;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* compute the frequency */
 | 
				
			||||||
 | 
						if (voice->mode == 1)
 | 
				
			||||||
 | 
							frequency = voice->fnum & 0x0ff;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							frequency = voice->fnum & 0x1ff;
 | 
				
			||||||
 | 
						voice->output_step = frequency + 1; // ((fnum + 1) * (input clock / 384)) / 256
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**********************************************************************************************
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     compute_tables -- compute the difference tables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					***********************************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void compute_tables()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* 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, s16 *buffer, int samples)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u32 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 = m_ext_mem[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 = m_ext_mem[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, s16 *buffer, int samples)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u32 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 = m_ext_mem[position / 2];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* output to the buffer, scaling by the volume */
 | 
				
			||||||
 | 
								*buffer++ = (s8)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 = m_ext_mem[position / 2];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* output to the buffer, scaling by the volume */
 | 
				
			||||||
 | 
								*buffer++ = (s8)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, s16 *buffer, int samples)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u32 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 = (s16)((m_ext_mem[position / 2 + 1] << 8) + m_ext_mem[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 = (s16)((m_ext_mem[position / 2 + 1] << 8) + m_ext_mem[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(s16 **outputs, int samples)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int v;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* loop over voices */
 | 
				
			||||||
 | 
						for (v = 0; v < 8; v++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							struct YMZ280BVoice *voice = &m_voice[v];
 | 
				
			||||||
 | 
							s16 prev = voice->last_sample;
 | 
				
			||||||
 | 
							s16 curr = voice->curr_sample;
 | 
				
			||||||
 | 
							s16 *curr_data = m_scratch.get();
 | 
				
			||||||
 | 
							s16 *ldest = outputs[v*2];
 | 
				
			||||||
 | 
							s16 *rdest = outputs[v*2+1];
 | 
				
			||||||
 | 
							s32 sampindex = 0;
 | 
				
			||||||
 | 
							u32 new_samples, samples_left;
 | 
				
			||||||
 | 
							u32 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)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								memset(ldest, 0, samples * sizeof(s16));
 | 
				
			||||||
 | 
								memset(rdest, 0, samples * sizeof(s16));
 | 
				
			||||||
 | 
								/* 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)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								s32 interp_sample = ((s32(prev) * (FRAC_ONE - voice->output_pos)) + (s32(curr) * voice->output_pos)) >> FRAC_BITS;
 | 
				
			||||||
 | 
								ldest[sampindex] = (s16)(interp_sample * lvol / 256);
 | 
				
			||||||
 | 
								rdest[sampindex] = (s16)(interp_sample * rvol / 256);
 | 
				
			||||||
 | 
								sampindex++;
 | 
				
			||||||
 | 
								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.get(), new_samples); break;
 | 
				
			||||||
 | 
								case 0x82:  samples_left = generate_pcm8(voice, m_scratch.get(), new_samples); break;
 | 
				
			||||||
 | 
								case 0x83:  samples_left = generate_pcm16(voice, m_scratch.get(), new_samples); break;
 | 
				
			||||||
 | 
								default:    samples_left = 0; memset(m_scratch.get(), 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 t = (base == 0) ? curr : m_scratch[base - 1];
 | 
				
			||||||
 | 
								for (u32 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->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 = ((s32(prev) * (FRAC_ONE - voice->output_pos)) + (s32(curr) * voice->output_pos)) >> FRAC_BITS;
 | 
				
			||||||
 | 
									ldest[sampindex] = (s16)(interp_sample * lvol / 256);
 | 
				
			||||||
 | 
									rdest[sampindex] = (s16)(interp_sample * rvol / 256);
 | 
				
			||||||
 | 
									sampindex++;
 | 
				
			||||||
 | 
									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;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//-------------------------------------------------
 | 
				
			||||||
 | 
					//  device_start - device-specific startup
 | 
				
			||||||
 | 
					//-------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ymz280b_device::device_start(u8 *ext_mem)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						m_ext_mem = ext_mem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* compute ADPCM tables */
 | 
				
			||||||
 | 
						compute_tables();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* allocate memory */
 | 
				
			||||||
 | 
						assert(MAX_SAMPLE_CHUNK < 0x10000);
 | 
				
			||||||
 | 
						m_scratch = std::make_unique<s16[]>(MAX_SAMPLE_CHUNK);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (auto & elem : m_voice)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							update_step(&elem);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//-------------------------------------------------
 | 
				
			||||||
 | 
					//  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 (auto &elem : m_voice)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							struct YMZ280BVoice *voice = &elem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							voice->curr_sample = 0;
 | 
				
			||||||
 | 
							voice->last_sample = 0;
 | 
				
			||||||
 | 
							voice->output_pos = FRAC_ONE;
 | 
				
			||||||
 | 
							voice->playing = 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**********************************************************************************************
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     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:
 | 
				
			||||||
 | 
									if (data != 0)
 | 
				
			||||||
 | 
										printf("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
 | 
				
			||||||
 | 
									//printf("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 = m_ext_mem[m_ext_mem_address];
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case 0x87:      /* RAM write */
 | 
				
			||||||
 | 
									if (m_ext_mem_enable)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										m_ext_mem[m_ext_mem_address] = data;
 | 
				
			||||||
 | 
										m_ext_mem_address = (m_ext_mem_address + 1) & 0xffffff;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case 0xfe:      /* IRQ mask */
 | 
				
			||||||
 | 
									m_irq_mask = data;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case 0xff:      /* IRQ enable, test, etc */
 | 
				
			||||||
 | 
									m_ext_mem_enable = (data & 0x40) >> 6;
 | 
				
			||||||
 | 
									m_irq_enable = (data & 0x10) >> 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									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:
 | 
				
			||||||
 | 
									if (data != 0)
 | 
				
			||||||
 | 
										printf("YMZ280B: unknown register write %02X = %02X\n", m_current_register, data);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**********************************************************************************************
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     compute_status -- determine the status bits
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					***********************************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ymz280b_device::compute_status()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u8 result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result = m_status_register;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* clear the IRQ state */
 | 
				
			||||||
 | 
						m_status_register = 0;
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**********************************************************************************************
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     read/write -- handle external accesses
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					***********************************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u8 ymz280b_device::read(offs_t offset)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if ((offset & 1) == 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (!m_ext_mem_enable)
 | 
				
			||||||
 | 
								return 0xff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* read from external memory */
 | 
				
			||||||
 | 
							u8 ret = m_ext_readlatch;
 | 
				
			||||||
 | 
							m_ext_readlatch = m_ext_mem[m_ext_mem_address];
 | 
				
			||||||
 | 
							m_ext_mem_address = (m_ext_mem_address + 1) & 0xffffff;
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return compute_status();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ymz280b_device::write(offs_t offset, u8 data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if ((offset & 1) == 0)
 | 
				
			||||||
 | 
							m_current_register = data;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							write_to_register(data);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ymz280b_device::ymz280b_device()
 | 
				
			||||||
 | 
						: m_current_register(0)
 | 
				
			||||||
 | 
						, m_status_register(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)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						memset(m_voice, 0, sizeof(m_voice));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										104
									
								
								src/engine/platform/sound/ymz280b.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/engine/platform/sound/ymz280b.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,104 @@
 | 
				
			||||||
 | 
					// license:BSD-3-Clause
 | 
				
			||||||
 | 
					// copyright-holders:Aaron Giles
 | 
				
			||||||
 | 
					/**********************************************************************************************
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   Yamaha YMZ280B driver
 | 
				
			||||||
 | 
					 *   by Aaron Giles
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 **********************************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef MAME_SOUND_YMZ280B_H
 | 
				
			||||||
 | 
					#define MAME_SOUND_YMZ280B_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace ymz280b
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						typedef unsigned char       u8;
 | 
				
			||||||
 | 
						typedef signed char         s8;
 | 
				
			||||||
 | 
						typedef unsigned short     u16;
 | 
				
			||||||
 | 
						typedef signed short       s16;
 | 
				
			||||||
 | 
						typedef unsigned int       u32;
 | 
				
			||||||
 | 
						typedef signed int         s32;
 | 
				
			||||||
 | 
						typedef signed int      offs_t;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace ymz280b;
 | 
				
			||||||
 | 
					class ymz280b_device
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						ymz280b_device();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u8 read(offs_t offset);
 | 
				
			||||||
 | 
						void write(offs_t offset, u8 data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void device_start(u8 *ext_mem);
 | 
				
			||||||
 | 
						void device_reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void sound_stream_update(s16 **outputs, int samples);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						/* struct describing a single playing ADPCM voice */
 | 
				
			||||||
 | 
						struct YMZ280BVoice
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							u8 playing;          /* 1 if we are actively playing */
 | 
				
			||||||
 | 
							bool ended;          /* indicate voice has ended in case samples_left is 0 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							u8 keyon;            /* 1 if the key is on */
 | 
				
			||||||
 | 
							u8 looping;          /* 1 if looping is enabled */
 | 
				
			||||||
 | 
							u8 mode;             /* current playback mode */
 | 
				
			||||||
 | 
							u16 fnum;            /* frequency */
 | 
				
			||||||
 | 
							u8 level;            /* output level */
 | 
				
			||||||
 | 
							u8 pan;              /* panning */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							u32 start;           /* start address, in nibbles */
 | 
				
			||||||
 | 
							u32 stop;            /* stop address, in nibbles */
 | 
				
			||||||
 | 
							u32 loop_start;      /* loop start address, in nibbles */
 | 
				
			||||||
 | 
							u32 loop_end;        /* loop end address, in nibbles */
 | 
				
			||||||
 | 
							u32 position;        /* current position, in nibbles */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							s32 signal;          /* current ADPCM signal */
 | 
				
			||||||
 | 
							s32 step;            /* current ADPCM step */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							s32 loop_signal;     /* signal at loop start */
 | 
				
			||||||
 | 
							s32 loop_step;       /* step at loop start */
 | 
				
			||||||
 | 
							u32 loop_count;      /* number of loops so far */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							s32 output_left;     /* output volume (left) */
 | 
				
			||||||
 | 
							s32 output_right;    /* output volume (right) */
 | 
				
			||||||
 | 
							s32 output_step;     /* step value for frequency conversion */
 | 
				
			||||||
 | 
							s32 output_pos;      /* current fractional position */
 | 
				
			||||||
 | 
							s16 last_sample;     /* last sample output */
 | 
				
			||||||
 | 
							s16 curr_sample;     /* current sample target */
 | 
				
			||||||
 | 
							u8 irq_schedule;     /* 1 if the IRQ state is updated by timer */
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void update_step(struct YMZ280BVoice *voice);
 | 
				
			||||||
 | 
						void update_volumes(struct YMZ280BVoice *voice);
 | 
				
			||||||
 | 
						int generate_adpcm(struct YMZ280BVoice *voice, s16 *buffer, int samples);
 | 
				
			||||||
 | 
						int generate_pcm8(struct YMZ280BVoice *voice, s16 *buffer, int samples);
 | 
				
			||||||
 | 
						int generate_pcm16(struct YMZ280BVoice *voice, s16 *buffer, int samples);
 | 
				
			||||||
 | 
						void write_to_register(int data);
 | 
				
			||||||
 | 
						int compute_status();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// internal state
 | 
				
			||||||
 | 
						struct YMZ280BVoice m_voice[8];   /* the 8 voices */
 | 
				
			||||||
 | 
						u8 m_current_register;            /* currently accessible register */
 | 
				
			||||||
 | 
						u8 m_status_register;             /* current status register */
 | 
				
			||||||
 | 
						u8 m_irq_mask;                    /* current IRQ mask */
 | 
				
			||||||
 | 
						u8 m_irq_enable;                  /* current IRQ enable */
 | 
				
			||||||
 | 
						u8 m_keyon_enable;                /* key on enable */
 | 
				
			||||||
 | 
						u8 m_ext_mem_enable;              /* external memory enable */
 | 
				
			||||||
 | 
						u8 m_ext_readlatch;               /* external memory prefetched data */
 | 
				
			||||||
 | 
						u32 m_ext_mem_address_hi;
 | 
				
			||||||
 | 
						u32 m_ext_mem_address_mid;
 | 
				
			||||||
 | 
						u32 m_ext_mem_address;            /* where the CPU can read the ROM */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u8 *m_ext_mem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::unique_ptr<s16[]> m_scratch;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // MAME_SOUND_YMZ280B_H
 | 
				
			||||||
							
								
								
									
										460
									
								
								src/engine/platform/ymz280b.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										460
									
								
								src/engine/platform/ymz280b.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,460 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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 "ymz280b.h"
 | 
				
			||||||
 | 
					#include "../engine.h"
 | 
				
			||||||
 | 
					#include "../../ta-log.h"
 | 
				
			||||||
 | 
					#include <math.h>
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CHIP_FREQBASE 98304
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define rWrite(a,v) {if(!skipRegisterWrites) {ymz280b.write(0,a); ymz280b.write(1,v); regPool[a]=v; if(dumpWrites) addWrite(a,v); }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char* regCheatSheetYMZ280B[]={
 | 
				
			||||||
 | 
					  "CHx_Freq", "00+x*4",
 | 
				
			||||||
 | 
					  "CHx_Control", "01+x*4",
 | 
				
			||||||
 | 
					  "CHx_Volume", "02+x*4",
 | 
				
			||||||
 | 
					  "CHx_Panning", "03+x*4",
 | 
				
			||||||
 | 
					  "CHx_StartH", "20+x*4",
 | 
				
			||||||
 | 
					  "CHx_LoopStartH", "21+x*4",
 | 
				
			||||||
 | 
					  "CHx_LoopEndH", "22+x*4",
 | 
				
			||||||
 | 
					  "CHx_EndH", "23+x*4",
 | 
				
			||||||
 | 
					  "CHx_StartM", "40+x*4",
 | 
				
			||||||
 | 
					  "CHx_LoopStartM", "41+x*4",
 | 
				
			||||||
 | 
					  "CHx_LoopEndM", "42+x*4",
 | 
				
			||||||
 | 
					  "CHx_EndM", "43+x*4",
 | 
				
			||||||
 | 
					  "CHx_StartL", "60+x*4",
 | 
				
			||||||
 | 
					  "CHx_LoopStartL", "61+x*4",
 | 
				
			||||||
 | 
					  "CHx_LoopEndL", "62+x*4",
 | 
				
			||||||
 | 
					  "CHx_EndL", "63+x*4",
 | 
				
			||||||
 | 
					  "DSP_Channel", "80",
 | 
				
			||||||
 | 
					  "DSP_Enable", "81",
 | 
				
			||||||
 | 
					  "DSP_Data", "82",
 | 
				
			||||||
 | 
					  "RAM_AddrH", "84",
 | 
				
			||||||
 | 
					  "RAM_AddrM", "85",
 | 
				
			||||||
 | 
					  "RAM_AddrL", "86",
 | 
				
			||||||
 | 
					  "RAM_Data", "87",
 | 
				
			||||||
 | 
					  "IRQ_Enable", "E0",
 | 
				
			||||||
 | 
					  "Enable", "FF",
 | 
				
			||||||
 | 
					  NULL
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char** DivPlatformYMZ280B::getRegisterSheet() {
 | 
				
			||||||
 | 
					  return regCheatSheetYMZ280B;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char* DivPlatformYMZ280B::getEffectName(unsigned char effect) {
 | 
				
			||||||
 | 
					  return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformYMZ280B::acquire(short* bufL, short* bufR, size_t start, size_t len) {
 | 
				
			||||||
 | 
					  short buf[16][256];
 | 
				
			||||||
 | 
					  short *bufPtrs[16]={
 | 
				
			||||||
 | 
					    buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],
 | 
				
			||||||
 | 
					    buf[8],buf[9],buf[10],buf[11],buf[12],buf[13],buf[14],buf[15]
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  size_t pos=start;
 | 
				
			||||||
 | 
					  while (len > 0) {
 | 
				
			||||||
 | 
					    size_t blockLen = MIN(len, 256);
 | 
				
			||||||
 | 
					    ymz280b.sound_stream_update(bufPtrs, blockLen);
 | 
				
			||||||
 | 
					    for (size_t i=0; i<blockLen; i++) {
 | 
				
			||||||
 | 
					      int dataL=0;
 | 
				
			||||||
 | 
					      int dataR=0;
 | 
				
			||||||
 | 
					      for (int j=0; j<8; j++) {
 | 
				
			||||||
 | 
					        dataL+=buf[j*2][i];
 | 
				
			||||||
 | 
					        dataR+=buf[j*2+1][i];
 | 
				
			||||||
 | 
					        oscBuf[j]->data[oscBuf[j]->needle++]=(short)(((int)buf[j*2][i]+buf[j*2+1][i])/2);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      bufL[pos]=(short)(dataL/8);
 | 
				
			||||||
 | 
					      bufR[pos]=(short)(dataR/8);
 | 
				
			||||||
 | 
					      pos++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    len-=blockLen;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformYMZ280B::tick(bool sysTick) {
 | 
				
			||||||
 | 
					  for (int i=0; i<8; i++) {
 | 
				
			||||||
 | 
					    chan[i].std.next();
 | 
				
			||||||
 | 
					    if (chan[i].std.vol.had) {
 | 
				
			||||||
 | 
					      chan[i].outVol=((chan[i].vol&0xff)*chan[i].std.vol.val)>>6;
 | 
				
			||||||
 | 
					      writeOutVol(i);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (chan[i].std.arp.had) {
 | 
				
			||||||
 | 
					      if (!chan[i].inPorta) {
 | 
				
			||||||
 | 
					        if (chan[i].std.arp.mode) {
 | 
				
			||||||
 | 
					          chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          chan[i].baseFreq=NOTE_FREQUENCY(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_FREQUENCY(chan[i].note);
 | 
				
			||||||
 | 
					        chan[i].freqChanged=true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    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].std.panL.had) { // panning
 | 
				
			||||||
 | 
					      chan[i].panning=MIN((chan[i].std.panL.val*15/16+15)/2+1,15);
 | 
				
			||||||
 | 
					      rWrite(0x03+i*4,chan[i].panning);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (chan[i].setPos) {
 | 
				
			||||||
 | 
					      // force keyon
 | 
				
			||||||
 | 
					      chan[i].keyOn=true;
 | 
				
			||||||
 | 
					      chan[i].setPos=false;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      chan[i].audPos=0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
 | 
				
			||||||
 | 
					      DivSample* s=parent->getSample(chan[i].sample);
 | 
				
			||||||
 | 
					      unsigned char ctrl;
 | 
				
			||||||
 | 
					      switch (s->depth) {
 | 
				
			||||||
 | 
					        case 3: ctrl=0x20; break;
 | 
				
			||||||
 | 
					        case 8: ctrl=0x40; break;
 | 
				
			||||||
 | 
					        case 16: ctrl=0x60; break;
 | 
				
			||||||
 | 
					        default: ctrl=0;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      double off=(s->centerRate>=1)?((double)s->centerRate/8363.0):1.0;
 | 
				
			||||||
 | 
					      chan[i].freq=(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE))-1;
 | 
				
			||||||
 | 
					      if (chan[i].freq<0) chan[i].freq=0;
 | 
				
			||||||
 | 
					      if (chan[i].freq>511) chan[i].freq=511;
 | 
				
			||||||
 | 
					      // ADPCM has half the range
 | 
				
			||||||
 | 
					      if (s->depth==3 && chan[i].freq>255) chan[i].freq=255;
 | 
				
			||||||
 | 
					      ctrl|=(chan[i].active?0x80:0)|((s->loopStart>=0)?0x10:0)|(chan[i].freq>>8);
 | 
				
			||||||
 | 
					      if (chan[i].keyOn) {
 | 
				
			||||||
 | 
					        unsigned int start=s->offYMZ280B;
 | 
				
			||||||
 | 
					        unsigned int loop=0;
 | 
				
			||||||
 | 
					        unsigned int end=MIN(start+s->getCurBufLen(),getSampleMemCapacity()-1);
 | 
				
			||||||
 | 
					        if (chan[i].audPos>0) {
 | 
				
			||||||
 | 
					          switch (s->depth) {
 | 
				
			||||||
 | 
					            case 3: start+=chan[i].audPos/2; break;
 | 
				
			||||||
 | 
					            case 8: start+=chan[i].audPos; break;
 | 
				
			||||||
 | 
					            case 16: start+=chan[i].audPos*2; break;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          start=MIN(start,end);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (s->loopStart>=0) {
 | 
				
			||||||
 | 
					          switch (s->depth) {
 | 
				
			||||||
 | 
					            case 3: loop=start+s->loopStart/2; break;
 | 
				
			||||||
 | 
					            case 8: loop=start+s->loopStart; break;
 | 
				
			||||||
 | 
					            case 16: loop=start+s->loopStart*2; break;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          loop=MIN(loop,end);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        rWrite(0x01+i*4,ctrl&~0x80); // force keyoff first
 | 
				
			||||||
 | 
					        rWrite(0x20+i*4,(start>>16)&0xff);
 | 
				
			||||||
 | 
					        rWrite(0x21+i*4,(loop>>16)&0xff);
 | 
				
			||||||
 | 
					        rWrite(0x22+i*4,(end>>16)&0xff);
 | 
				
			||||||
 | 
					        rWrite(0x23+i*4,(end>>16)&0xff);
 | 
				
			||||||
 | 
					        rWrite(0x40+i*4,(start>>8)&0xff);
 | 
				
			||||||
 | 
					        rWrite(0x41+i*4,(loop>>8)&0xff);
 | 
				
			||||||
 | 
					        rWrite(0x42+i*4,(end>>8)&0xff);
 | 
				
			||||||
 | 
					        rWrite(0x43+i*4,(end>>8)&0xff);
 | 
				
			||||||
 | 
					        rWrite(0x60+i*4,start&0xff);
 | 
				
			||||||
 | 
					        rWrite(0x61+i*4,loop&0xff);
 | 
				
			||||||
 | 
					        rWrite(0x62+i*4,end&0xff);
 | 
				
			||||||
 | 
					        rWrite(0x63+i*4,end&0xff);
 | 
				
			||||||
 | 
					        if (!chan[i].std.vol.had) {
 | 
				
			||||||
 | 
					          chan[i].outVol=chan[i].vol;
 | 
				
			||||||
 | 
					          writeOutVol(i);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        chan[i].keyOn=false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (chan[i].keyOff) {
 | 
				
			||||||
 | 
					        chan[i].keyOff=false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (chan[i].freqChanged) {
 | 
				
			||||||
 | 
					        rWrite(0x00+i*4,chan[i].freq&0xff);
 | 
				
			||||||
 | 
					        chan[i].freqChanged=false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      rWrite(0x01+i*4,ctrl);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int DivPlatformYMZ280B::dispatch(DivCommand c) {
 | 
				
			||||||
 | 
					  switch (c.cmd) {
 | 
				
			||||||
 | 
					    case DIV_CMD_NOTE_ON: {
 | 
				
			||||||
 | 
					      DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
 | 
				
			||||||
 | 
					      chan[c.chan].sample=ins->amiga.getSample(c.value);
 | 
				
			||||||
 | 
					      if (c.value!=DIV_NOTE_NULL) {
 | 
				
			||||||
 | 
					        chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
 | 
				
			||||||
 | 
					        chan[c.chan].sample=-1;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (c.value!=DIV_NOTE_NULL) {
 | 
				
			||||||
 | 
					        chan[c.chan].freqChanged=true;
 | 
				
			||||||
 | 
					        chan[c.chan].note=c.value;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      chan[c.chan].active=true;
 | 
				
			||||||
 | 
					      chan[c.chan].keyOn=true;
 | 
				
			||||||
 | 
					      chan[c.chan].macroInit(ins);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case DIV_CMD_NOTE_OFF:
 | 
				
			||||||
 | 
					      chan[c.chan].sample=-1;
 | 
				
			||||||
 | 
					      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;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      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;
 | 
				
			||||||
 | 
					          writeOutVol(c.chan);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      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_PANNING:
 | 
				
			||||||
 | 
					      chan[c.chan].panning=MIN(parent->convertPanSplitToLinearLR(c.value,c.value2,15)+1,15);
 | 
				
			||||||
 | 
					      rWrite(0x03+c.chan*4,chan[c.chan].panning);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case DIV_CMD_PITCH:
 | 
				
			||||||
 | 
					      chan[c.chan].pitch=c.value;
 | 
				
			||||||
 | 
					      chan[c.chan].freqChanged=true;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case DIV_CMD_NOTE_PORTA: {
 | 
				
			||||||
 | 
					      int destFreq=NOTE_FREQUENCY(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_LEGATO: {
 | 
				
			||||||
 | 
					      chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val-12):(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_AMIGA));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      chan[c.chan].inPorta=c.value;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case DIV_CMD_SAMPLE_POS:
 | 
				
			||||||
 | 
					      chan[c.chan].audPos=c.value;
 | 
				
			||||||
 | 
					      chan[c.chan].setPos=true;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case DIV_CMD_GET_VOLMAX:
 | 
				
			||||||
 | 
					      return 255;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case DIV_ALWAYS_SET_VOLUME:
 | 
				
			||||||
 | 
					      return 1;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformYMZ280B::writeOutVol(int ch) {
 | 
				
			||||||
 | 
					  unsigned char val=isMuted[ch]?0:chan[ch].outVol;
 | 
				
			||||||
 | 
					  rWrite(0x02+ch*4,val);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformYMZ280B::muteChannel(int ch, bool mute) {
 | 
				
			||||||
 | 
					  isMuted[ch]=mute;
 | 
				
			||||||
 | 
					  writeOutVol(ch);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformYMZ280B::forceIns() {
 | 
				
			||||||
 | 
					  for (int i=0; i<8; i++) {
 | 
				
			||||||
 | 
					    chan[i].insChanged=true;
 | 
				
			||||||
 | 
					    chan[i].freqChanged=true;
 | 
				
			||||||
 | 
					    chan[i].sample=-1;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void* DivPlatformYMZ280B::getChanState(int ch) {
 | 
				
			||||||
 | 
					  return &chan[ch];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DivDispatchOscBuffer* DivPlatformYMZ280B::getOscBuffer(int ch) {
 | 
				
			||||||
 | 
					  return oscBuf[ch];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformYMZ280B::reset() {
 | 
				
			||||||
 | 
					  memset(regPool,0,256);
 | 
				
			||||||
 | 
					  ymz280b.device_reset();
 | 
				
			||||||
 | 
					  rWrite(0xff,0x80); // enable keyon
 | 
				
			||||||
 | 
					  for (int i=0; i<8; i++) {
 | 
				
			||||||
 | 
					    chan[i]=DivPlatformYMZ280B::Channel();
 | 
				
			||||||
 | 
					    chan[i].std.setEngine(parent);
 | 
				
			||||||
 | 
					    rWrite(0x02+i*4,255);
 | 
				
			||||||
 | 
					    rWrite(0x03+i*4,8);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool DivPlatformYMZ280B::isStereo() {
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformYMZ280B::notifyInsChange(int ins) {
 | 
				
			||||||
 | 
					  for (int i=0; i<8; i++) {
 | 
				
			||||||
 | 
					    if (chan[i].ins==ins) {
 | 
				
			||||||
 | 
					      chan[i].insChanged=true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformYMZ280B::notifyWaveChange(int wave) {
 | 
				
			||||||
 | 
					  // TODO when wavetables are added
 | 
				
			||||||
 | 
					  // TODO they probably won't be added unless the samples reside in RAM
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformYMZ280B::notifyInsDeletion(void* ins) {
 | 
				
			||||||
 | 
					  for (int i=0; i<8; i++) {
 | 
				
			||||||
 | 
					    chan[i].std.notifyInsDeletion((DivInstrument*)ins);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformYMZ280B::poke(unsigned int addr, unsigned short val) {
 | 
				
			||||||
 | 
					  rWrite(addr,val);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformYMZ280B::poke(std::vector<DivRegWrite>& wlist) {
 | 
				
			||||||
 | 
					  for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned char* DivPlatformYMZ280B::getRegisterPool() {
 | 
				
			||||||
 | 
					  return regPool;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int DivPlatformYMZ280B::getRegisterPoolSize() {
 | 
				
			||||||
 | 
					  return 256;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float DivPlatformYMZ280B::getPostAmp() {
 | 
				
			||||||
 | 
					  // according to MAME core's mixing
 | 
				
			||||||
 | 
					  return 4.0f;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const void* DivPlatformYMZ280B::getSampleMem(int index) {
 | 
				
			||||||
 | 
					  return index == 0 ? sampleMem : NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t DivPlatformYMZ280B::getSampleMemCapacity(int index) {
 | 
				
			||||||
 | 
					  return index == 0 ? 16777216 : 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t DivPlatformYMZ280B::getSampleMemUsage(int index) {
 | 
				
			||||||
 | 
					  return index == 0 ? sampleMemLen : 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformYMZ280B::renderSamples() {
 | 
				
			||||||
 | 
					  memset(sampleMem,0,getSampleMemCapacity());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  size_t memPos=0;
 | 
				
			||||||
 | 
					  for (int i=0; i<parent->song.sampleLen; i++) {
 | 
				
			||||||
 | 
					    DivSample* s=parent->song.sample[i];
 | 
				
			||||||
 | 
					    int length=s->getCurBufLen();
 | 
				
			||||||
 | 
					    unsigned char* src=(unsigned char*)s->getCurBuf();
 | 
				
			||||||
 | 
					    int actualLength=MIN((int)(getSampleMemCapacity()-memPos),length);
 | 
				
			||||||
 | 
					    if (actualLength>0) {
 | 
				
			||||||
 | 
					      memcpy(&sampleMem[memPos],src,actualLength);
 | 
				
			||||||
 | 
					      s->offYMZ280B=memPos;
 | 
				
			||||||
 | 
					      memPos+=length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (actualLength<length) {
 | 
				
			||||||
 | 
					      logW("out of YMZ280B PCM memory for sample %d!",i);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  sampleMemLen=memPos;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformYMZ280B::setChipModel(int type) {
 | 
				
			||||||
 | 
					  chipType=type;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int DivPlatformYMZ280B::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
 | 
				
			||||||
 | 
					  parent=p;
 | 
				
			||||||
 | 
					  dumpWrites=false;
 | 
				
			||||||
 | 
					  skipRegisterWrites=false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (int i=0; i<8; i++) {
 | 
				
			||||||
 | 
					    isMuted[i]=false;
 | 
				
			||||||
 | 
					    oscBuf[i]=new DivDispatchOscBuffer;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  setFlags(flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  rate=(chipType==759)?32000:44100;
 | 
				
			||||||
 | 
					  chipClock=rate*384;
 | 
				
			||||||
 | 
					  sampleMem=new unsigned char[getSampleMemCapacity()];
 | 
				
			||||||
 | 
					  sampleMemLen=0;
 | 
				
			||||||
 | 
					  ymz280b.device_start(sampleMem);
 | 
				
			||||||
 | 
					  reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (int i=0; i<8; i++) {
 | 
				
			||||||
 | 
					    oscBuf[i]->rate=rate;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return 8;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DivPlatformYMZ280B::quit() {
 | 
				
			||||||
 | 
					  delete[] sampleMem;
 | 
				
			||||||
 | 
					  for (int i=0; i<8; i++) {
 | 
				
			||||||
 | 
					    delete oscBuf[i];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										104
									
								
								src/engine/platform/ymz280b.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/engine/platform/ymz280b.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,104 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef _YMZ280B_H
 | 
				
			||||||
 | 
					#define _YMZ280B_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../dispatch.h"
 | 
				
			||||||
 | 
					#include <queue>
 | 
				
			||||||
 | 
					#include "../macroInt.h"
 | 
				
			||||||
 | 
					#include "sound/ymz280b.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DivPlatformYMZ280B: public DivDispatch {
 | 
				
			||||||
 | 
					  struct Channel {
 | 
				
			||||||
 | 
					    int freq, baseFreq, pitch, pitch2;
 | 
				
			||||||
 | 
					    unsigned int audPos;
 | 
				
			||||||
 | 
					    int sample, wave, ins;
 | 
				
			||||||
 | 
					    int note;
 | 
				
			||||||
 | 
					    int panning;
 | 
				
			||||||
 | 
					    bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, setPos;
 | 
				
			||||||
 | 
					    int vol, outVol;
 | 
				
			||||||
 | 
					    DivMacroInt std;
 | 
				
			||||||
 | 
					    void macroInit(DivInstrument* which) {
 | 
				
			||||||
 | 
					      std.init(which);
 | 
				
			||||||
 | 
					      pitch2=0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    Channel():
 | 
				
			||||||
 | 
					      freq(0),
 | 
				
			||||||
 | 
					      baseFreq(0),
 | 
				
			||||||
 | 
					      pitch(0),
 | 
				
			||||||
 | 
					      pitch2(0),
 | 
				
			||||||
 | 
					      audPos(0),
 | 
				
			||||||
 | 
					      sample(-1),
 | 
				
			||||||
 | 
					      ins(-1),
 | 
				
			||||||
 | 
					      note(0),
 | 
				
			||||||
 | 
					      panning(8),
 | 
				
			||||||
 | 
					      active(false),
 | 
				
			||||||
 | 
					      insChanged(true),
 | 
				
			||||||
 | 
					      freqChanged(false),
 | 
				
			||||||
 | 
					      keyOn(false),
 | 
				
			||||||
 | 
					      keyOff(false),
 | 
				
			||||||
 | 
					      inPorta(false),
 | 
				
			||||||
 | 
					      setPos(false),
 | 
				
			||||||
 | 
					      vol(255),
 | 
				
			||||||
 | 
					      outVol(255) {}
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  Channel chan[8];
 | 
				
			||||||
 | 
					  DivDispatchOscBuffer* oscBuf[8];
 | 
				
			||||||
 | 
					  bool isMuted[8];
 | 
				
			||||||
 | 
					  int chipType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  unsigned char* sampleMem;
 | 
				
			||||||
 | 
					  size_t sampleMemLen;
 | 
				
			||||||
 | 
					  ymz280b_device ymz280b;
 | 
				
			||||||
 | 
					  unsigned char regPool[256];
 | 
				
			||||||
 | 
					  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);
 | 
				
			||||||
 | 
					    float getPostAmp();
 | 
				
			||||||
 | 
					    bool isStereo();
 | 
				
			||||||
 | 
					    void setChipModel(int type);
 | 
				
			||||||
 | 
					    void notifyInsChange(int ins);
 | 
				
			||||||
 | 
					    void notifyWaveChange(int wave);
 | 
				
			||||||
 | 
					    void notifyInsDeletion(void* ins);
 | 
				
			||||||
 | 
					    void poke(unsigned int addr, unsigned short val);
 | 
				
			||||||
 | 
					    void poke(std::vector<DivRegWrite>& wlist);
 | 
				
			||||||
 | 
					    const char** getRegisterSheet();
 | 
				
			||||||
 | 
					    const char* getEffectName(unsigned char effect);
 | 
				
			||||||
 | 
					    const void* getSampleMem(int index = 0);
 | 
				
			||||||
 | 
					    size_t getSampleMemCapacity(int index = 0);
 | 
				
			||||||
 | 
					    size_t getSampleMemUsage(int index = 0);
 | 
				
			||||||
 | 
					    void renderSamples();
 | 
				
			||||||
 | 
					    int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
 | 
				
			||||||
 | 
					    void quit();
 | 
				
			||||||
 | 
					  private:
 | 
				
			||||||
 | 
					    void writeOutVol(int ch);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -96,8 +96,9 @@ bool DivSample::initInternal(unsigned char d, int count) {
 | 
				
			||||||
    case 3: // YMZ ADPCM
 | 
					    case 3: // YMZ ADPCM
 | 
				
			||||||
      if (dataZ!=NULL) delete[] dataZ;
 | 
					      if (dataZ!=NULL) delete[] dataZ;
 | 
				
			||||||
      lengthZ=(count+1)/2;
 | 
					      lengthZ=(count+1)/2;
 | 
				
			||||||
      dataZ=new unsigned char[(lengthZ+255)&(~0xff)];
 | 
					      // for padding AICA sample
 | 
				
			||||||
      memset(dataZ,0,(lengthZ+255)&(~0xff));
 | 
					      dataZ=new unsigned char[(lengthZ+3)&(~0x03)];
 | 
				
			||||||
 | 
					      memset(dataZ,0,(lengthZ+3)&(~0x03));
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    case 4: // QSound ADPCM
 | 
					    case 4: // QSound ADPCM
 | 
				
			||||||
      if (dataQSoundA!=NULL) delete[] dataQSoundA;
 | 
					      if (dataQSoundA!=NULL) delete[] dataQSoundA;
 | 
				
			||||||
| 
						 | 
					@ -711,7 +712,7 @@ void DivSample::render() {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (depth!=3) { // YMZ ADPCM
 | 
					  if (depth!=3) { // YMZ ADPCM
 | 
				
			||||||
    if (!initInternal(3,samples)) return;
 | 
					    if (!initInternal(3,samples)) return;
 | 
				
			||||||
    ymz_encode(data16,dataZ,(samples+511)&(~0x1ff));
 | 
					    ymz_encode(data16,dataZ,(samples+7)&(~0x7));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (depth!=4) { // QSound ADPCM
 | 
					  if (depth!=4) { // QSound ADPCM
 | 
				
			||||||
    if (!initInternal(4,samples)) return;
 | 
					    if (!initInternal(4,samples)) return;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -86,7 +86,7 @@ struct DivSample {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  unsigned int length8, length16, length1, lengthDPCM, lengthZ, lengthQSoundA, lengthA, lengthB, lengthBRR, lengthVOX;
 | 
					  unsigned int length8, length16, length1, lengthDPCM, lengthZ, lengthQSoundA, lengthA, lengthB, lengthBRR, lengthVOX;
 | 
				
			||||||
  unsigned int off8, off16, off1, offDPCM, offZ, offQSoundA, offA, offB, offBRR, offVOX;
 | 
					  unsigned int off8, off16, off1, offDPCM, offZ, offQSoundA, offA, offB, offBRR, offVOX;
 | 
				
			||||||
  unsigned int offSegaPCM, offQSound, offX1_010, offSU, offRF5C68;
 | 
					  unsigned int offSegaPCM, offQSound, offX1_010, offSU, offYMZ280B, offRF5C68;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  unsigned int samples;
 | 
					  unsigned int samples;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -107,6 +107,7 @@ enum DivSystem {
 | 
				
			||||||
  DIV_SYSTEM_SOUND_UNIT,
 | 
					  DIV_SYSTEM_SOUND_UNIT,
 | 
				
			||||||
  DIV_SYSTEM_MSM6295,
 | 
					  DIV_SYSTEM_MSM6295,
 | 
				
			||||||
  DIV_SYSTEM_MSM6258,
 | 
					  DIV_SYSTEM_MSM6258,
 | 
				
			||||||
 | 
					  DIV_SYSTEM_YMZ280B,
 | 
				
			||||||
  DIV_SYSTEM_NAMCO,
 | 
					  DIV_SYSTEM_NAMCO,
 | 
				
			||||||
  DIV_SYSTEM_NAMCO_15XX,
 | 
					  DIV_SYSTEM_NAMCO_15XX,
 | 
				
			||||||
  DIV_SYSTEM_NAMCO_CUS30,
 | 
					  DIV_SYSTEM_NAMCO_CUS30,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1966,6 +1966,15 @@ void DivEngine::registerSystems() {
 | 
				
			||||||
    {DIV_INS_AMIGA}
 | 
					    {DIV_INS_AMIGA}
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sysDefs[DIV_SYSTEM_YMZ280B]=new DivSysDef(
 | 
				
			||||||
 | 
					    "Yamaha YMZ280B", NULL, 0xb8, 0, 8, false, true, 0x151, false,
 | 
				
			||||||
 | 
					    "used in some arcade boards. Can play back either 4-bit ADPCM, 8-bit PCM or 16-bit PCM.",
 | 
				
			||||||
 | 
					    {"PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8"},
 | 
				
			||||||
 | 
					    {"1", "2", "3", "4", "5", "6", "7", "8"},
 | 
				
			||||||
 | 
					    {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
 | 
				
			||||||
 | 
					    {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sysDefs[DIV_SYSTEM_NAMCO]=new DivSysDef(
 | 
					  sysDefs[DIV_SYSTEM_NAMCO]=new DivSysDef(
 | 
				
			||||||
    "Namco WSG", NULL, 0xb9, 0, 3, false, true, 0, false,
 | 
					    "Namco WSG", NULL, 0xb9, 0, 3, false, true, 0, false,
 | 
				
			||||||
    "a wavetable sound chip used in Pac-Man, among other early Namco arcade games.",
 | 
					    "a wavetable sound chip used in Pac-Man, among other early Namco arcade games.",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -769,6 +769,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
 | 
				
			||||||
        logW("SCC+: writing to unmapped address %.2x!",write.addr);
 | 
					        logW("SCC+: writing to unmapped address %.2x!",write.addr);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
 | 
					    case DIV_SYSTEM_YMZ280B:
 | 
				
			||||||
 | 
					      w->writeC(0x0d|baseAddr1);
 | 
				
			||||||
 | 
					      w->writeC(write.addr&0xff);
 | 
				
			||||||
 | 
					      w->writeC(write.val&0xff);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
    case DIV_SYSTEM_RF5C68:
 | 
					    case DIV_SYSTEM_RF5C68:
 | 
				
			||||||
      w->writeC(rf5c68Addr);
 | 
					      w->writeC(rf5c68Addr);
 | 
				
			||||||
      w->writeC(write.addr&0xff);
 | 
					      w->writeC(write.addr&0xff);
 | 
				
			||||||
| 
						 | 
					@ -895,6 +900,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
 | 
				
			||||||
  int writeSegaPCM=0;
 | 
					  int writeSegaPCM=0;
 | 
				
			||||||
  DivDispatch* writeX1010[2]={NULL,NULL};
 | 
					  DivDispatch* writeX1010[2]={NULL,NULL};
 | 
				
			||||||
  DivDispatch* writeQSound[2]={NULL,NULL};
 | 
					  DivDispatch* writeQSound[2]={NULL,NULL};
 | 
				
			||||||
 | 
					  DivDispatch* writeZ280[2]={NULL,NULL};
 | 
				
			||||||
  DivDispatch* writeRF5C68[2]={NULL,NULL};
 | 
					  DivDispatch* writeRF5C68[2]={NULL,NULL};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (int i=0; i<song.systemLen; i++) {
 | 
					  for (int i=0; i<song.systemLen; i++) {
 | 
				
			||||||
| 
						 | 
					@ -1257,6 +1263,19 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
 | 
				
			||||||
          howManyChips++;
 | 
					          howManyChips++;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					      case DIV_SYSTEM_YMZ280B:
 | 
				
			||||||
 | 
					        if (!hasZ280) {
 | 
				
			||||||
 | 
					          hasZ280=disCont[i].dispatch->chipClock;
 | 
				
			||||||
 | 
					          willExport[i]=true;
 | 
				
			||||||
 | 
					          writeZ280[0]=disCont[i].dispatch;
 | 
				
			||||||
 | 
					        } else if (!(hasZ280&0x40000000)) {
 | 
				
			||||||
 | 
					          isSecond[i]=true;
 | 
				
			||||||
 | 
					          willExport[i]=true;
 | 
				
			||||||
 | 
					          writeZ280[1]=disCont[i].dispatch;
 | 
				
			||||||
 | 
					          hasZ280|=0x40000000;
 | 
				
			||||||
 | 
					          howManyChips++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
      case DIV_SYSTEM_RF5C68:
 | 
					      case DIV_SYSTEM_RF5C68:
 | 
				
			||||||
        // here's the dumb part: VGM thinks RF5C68 and RF5C164 are different
 | 
					        // here's the dumb part: VGM thinks RF5C68 and RF5C164 are different
 | 
				
			||||||
        // chips even though the only difference is the output resolution
 | 
					        // chips even though the only difference is the output resolution
 | 
				
			||||||
| 
						 | 
					@ -1273,7 +1292,6 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
 | 
				
			||||||
          hasRFC=disCont[i].dispatch->chipClock;
 | 
					          hasRFC=disCont[i].dispatch->chipClock;
 | 
				
			||||||
          willExport[i]=true;
 | 
					          willExport[i]=true;
 | 
				
			||||||
          writeRF5C68[0]=disCont[i].dispatch;
 | 
					          writeRF5C68[0]=disCont[i].dispatch;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
| 
						 | 
					@ -1537,8 +1555,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
 | 
				
			||||||
    delete[] pcmMem;
 | 
					    delete[] pcmMem;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // ADPCM (OPNA)
 | 
					 | 
				
			||||||
  for (int i=0; i<2; i++) {
 | 
					  for (int i=0; i<2; i++) {
 | 
				
			||||||
 | 
					    // ADPCM (OPNA)
 | 
				
			||||||
    if (writeADPCM_OPNA[i]!=NULL && writeADPCM_OPNA[i]->getSampleMemUsage(0)>0) {
 | 
					    if (writeADPCM_OPNA[i]!=NULL && writeADPCM_OPNA[i]->getSampleMemUsage(0)>0) {
 | 
				
			||||||
      w->writeC(0x67);
 | 
					      w->writeC(0x67);
 | 
				
			||||||
      w->writeC(0x66);
 | 
					      w->writeC(0x66);
 | 
				
			||||||
| 
						 | 
					@ -1548,10 +1566,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
 | 
				
			||||||
      w->writeI(0);
 | 
					      w->writeI(0);
 | 
				
			||||||
      w->write(writeADPCM_OPNA[i]->getSampleMem(0),writeADPCM_OPNA[i]->getSampleMemUsage(0));
 | 
					      w->write(writeADPCM_OPNA[i]->getSampleMem(0),writeADPCM_OPNA[i]->getSampleMemUsage(0));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					    // ADPCM-A (OPNB)
 | 
				
			||||||
 | 
					 | 
				
			||||||
  // ADPCM-A (OPNB)
 | 
					 | 
				
			||||||
  for (int i=0; i<2; i++) {
 | 
					 | 
				
			||||||
    if (writeADPCM_OPNB[i]!=NULL && writeADPCM_OPNB[i]->getSampleMemUsage(0)>0) {
 | 
					    if (writeADPCM_OPNB[i]!=NULL && writeADPCM_OPNB[i]->getSampleMemUsage(0)>0) {
 | 
				
			||||||
      w->writeC(0x67);
 | 
					      w->writeC(0x67);
 | 
				
			||||||
      w->writeC(0x66);
 | 
					      w->writeC(0x66);
 | 
				
			||||||
| 
						 | 
					@ -1561,10 +1576,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
 | 
				
			||||||
      w->writeI(0);
 | 
					      w->writeI(0);
 | 
				
			||||||
      w->write(writeADPCM_OPNB[i]->getSampleMem(0),writeADPCM_OPNB[i]->getSampleMemUsage(0));
 | 
					      w->write(writeADPCM_OPNB[i]->getSampleMem(0),writeADPCM_OPNB[i]->getSampleMemUsage(0));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					    // ADPCM-B (OPNB)
 | 
				
			||||||
 | 
					 | 
				
			||||||
  // ADPCM-B (OPNB)
 | 
					 | 
				
			||||||
  for (int i=0; i<2; i++) {
 | 
					 | 
				
			||||||
    if (writeADPCM_OPNB[i]!=NULL && writeADPCM_OPNB[i]->getSampleMemUsage(1)>0) {
 | 
					    if (writeADPCM_OPNB[i]!=NULL && writeADPCM_OPNB[i]->getSampleMemUsage(1)>0) {
 | 
				
			||||||
      w->writeC(0x67);
 | 
					      w->writeC(0x67);
 | 
				
			||||||
      w->writeC(0x66);
 | 
					      w->writeC(0x66);
 | 
				
			||||||
| 
						 | 
					@ -1574,10 +1586,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
 | 
				
			||||||
      w->writeI(0);
 | 
					      w->writeI(0);
 | 
				
			||||||
      w->write(writeADPCM_OPNB[i]->getSampleMem(1),writeADPCM_OPNB[i]->getSampleMemUsage(1));
 | 
					      w->write(writeADPCM_OPNB[i]->getSampleMem(1),writeADPCM_OPNB[i]->getSampleMemUsage(1));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					    // ADPCM (Y8950)
 | 
				
			||||||
 | 
					 | 
				
			||||||
  // ADPCM (Y8950)
 | 
					 | 
				
			||||||
  for (int i=0; i<2; i++) {
 | 
					 | 
				
			||||||
    if (writeADPCM_Y8950[i]!=NULL && writeADPCM_Y8950[i]->getSampleMemUsage(0)>0) {
 | 
					    if (writeADPCM_Y8950[i]!=NULL && writeADPCM_Y8950[i]->getSampleMemUsage(0)>0) {
 | 
				
			||||||
      w->writeC(0x67);
 | 
					      w->writeC(0x67);
 | 
				
			||||||
      w->writeC(0x66);
 | 
					      w->writeC(0x66);
 | 
				
			||||||
| 
						 | 
					@ -1587,9 +1596,6 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
 | 
				
			||||||
      w->writeI(0);
 | 
					      w->writeI(0);
 | 
				
			||||||
      w->write(writeADPCM_Y8950[i]->getSampleMem(0),writeADPCM_Y8950[i]->getSampleMemUsage(0));
 | 
					      w->write(writeADPCM_Y8950[i]->getSampleMem(0),writeADPCM_Y8950[i]->getSampleMemUsage(0));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (int i=0; i<2; i++) {
 | 
					 | 
				
			||||||
    if (writeQSound[i]!=NULL && writeQSound[i]->getSampleMemUsage()>0) {
 | 
					    if (writeQSound[i]!=NULL && writeQSound[i]->getSampleMemUsage()>0) {
 | 
				
			||||||
      unsigned int blockSize=(writeQSound[i]->getSampleMemUsage()+0xffff)&(~0xffff);
 | 
					      unsigned int blockSize=(writeQSound[i]->getSampleMemUsage()+0xffff)&(~0xffff);
 | 
				
			||||||
      if (blockSize > 0x1000000) {
 | 
					      if (blockSize > 0x1000000) {
 | 
				
			||||||
| 
						 | 
					@ -1603,9 +1609,6 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
 | 
				
			||||||
      w->writeI(0);
 | 
					      w->writeI(0);
 | 
				
			||||||
      w->write(writeQSound[i]->getSampleMem(),blockSize);
 | 
					      w->write(writeQSound[i]->getSampleMem(),blockSize);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (int i=0; i<2; i++) {
 | 
					 | 
				
			||||||
    if (writeX1010[i]!=NULL && writeX1010[i]->getSampleMemUsage()>0) {
 | 
					    if (writeX1010[i]!=NULL && writeX1010[i]->getSampleMemUsage()>0) {
 | 
				
			||||||
      w->writeC(0x67);
 | 
					      w->writeC(0x67);
 | 
				
			||||||
      w->writeC(0x66);
 | 
					      w->writeC(0x66);
 | 
				
			||||||
| 
						 | 
					@ -1615,6 +1618,15 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
 | 
				
			||||||
      w->writeI(0);
 | 
					      w->writeI(0);
 | 
				
			||||||
      w->write(writeX1010[i]->getSampleMem(),writeX1010[i]->getSampleMemUsage());
 | 
					      w->write(writeX1010[i]->getSampleMem(),writeX1010[i]->getSampleMemUsage());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (writeZ280[i]!=NULL && writeZ280[i]->getSampleMemUsage()>0) {
 | 
				
			||||||
 | 
					      w->writeC(0x67);
 | 
				
			||||||
 | 
					      w->writeC(0x66);
 | 
				
			||||||
 | 
					      w->writeC(0x86);
 | 
				
			||||||
 | 
					      w->writeI((writeZ280[i]->getSampleMemUsage()+8)|(i*0x80000000));
 | 
				
			||||||
 | 
					      w->writeI(writeZ280[i]->getSampleMemCapacity());
 | 
				
			||||||
 | 
					      w->writeI(0);
 | 
				
			||||||
 | 
					      w->write(writeZ280[i]->getSampleMem(),writeZ280[i]->getSampleMemUsage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (int i=0; i<2; i++) {
 | 
					  for (int i=0; i<2; i++) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -880,6 +880,7 @@ const int availableSystems[]={
 | 
				
			||||||
  DIV_SYSTEM_MMC5,
 | 
					  DIV_SYSTEM_MMC5,
 | 
				
			||||||
  DIV_SYSTEM_SCC,
 | 
					  DIV_SYSTEM_SCC,
 | 
				
			||||||
  DIV_SYSTEM_SCC_PLUS,
 | 
					  DIV_SYSTEM_SCC_PLUS,
 | 
				
			||||||
 | 
					  DIV_SYSTEM_YMZ280B,
 | 
				
			||||||
  DIV_SYSTEM_MSM6258,
 | 
					  DIV_SYSTEM_MSM6258,
 | 
				
			||||||
  DIV_SYSTEM_MSM6295,
 | 
					  DIV_SYSTEM_MSM6295,
 | 
				
			||||||
  DIV_SYSTEM_NAMCO,
 | 
					  DIV_SYSTEM_NAMCO,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -260,6 +260,12 @@ void FurnaceGUI::initSystemPresets() {
 | 
				
			||||||
      0
 | 
					      0
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ));
 | 
					  ));
 | 
				
			||||||
 | 
					  cat.systems.push_back(FurnaceGUISysDef(
 | 
				
			||||||
 | 
					    "Yamaha YMZ280B", {
 | 
				
			||||||
 | 
					      DIV_SYSTEM_YMZ280B, 64, 0, 0,
 | 
				
			||||||
 | 
					      0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ));
 | 
				
			||||||
  cat.systems.push_back(FurnaceGUISysDef(
 | 
					  cat.systems.push_back(FurnaceGUISysDef(
 | 
				
			||||||
    "Ricoh RF5C68", {
 | 
					    "Ricoh RF5C68", {
 | 
				
			||||||
      DIV_SYSTEM_RF5C68, 64, 0, 0,
 | 
					      DIV_SYSTEM_RF5C68, 64, 0, 0,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -427,6 +427,7 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
 | 
				
			||||||
    case DIV_SYSTEM_PET:
 | 
					    case DIV_SYSTEM_PET:
 | 
				
			||||||
    case DIV_SYSTEM_SCC:
 | 
					    case DIV_SYSTEM_SCC:
 | 
				
			||||||
    case DIV_SYSTEM_SCC_PLUS:
 | 
					    case DIV_SYSTEM_SCC_PLUS:
 | 
				
			||||||
 | 
					    case DIV_SYSTEM_YMZ280B:
 | 
				
			||||||
      ImGui::Text("nothing to configure");
 | 
					      ImGui::Text("nothing to configure");
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue