178 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/**
 | 
						|
 * Furnace Tracker - multi-system chiptune tracker
 | 
						|
 * Copyright (C) 2021-2023 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 "engine.h"
 | 
						|
#include "../ta-log.h"
 | 
						|
#include "../utfutils.h"
 | 
						|
#include "song.h"
 | 
						|
#include "zsm.h"
 | 
						|
 | 
						|
constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
 | 
						|
constexpr int MASTER_CLOCK_MASK=(sizeof(void*)==8)?0xff:0;
 | 
						|
 | 
						|
SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
 | 
						|
 | 
						|
  int VERA = -1;
 | 
						|
  int YM = -1;
 | 
						|
  int IGNORED = 0;
 | 
						|
 | 
						|
  //loop = false;
 | 
						|
  // find indexes for YM and VERA. Ignore other systems.
 | 
						|
  for (int i=0; i<song.systemLen; i++) {
 | 
						|
    switch (song.system[i]) {
 | 
						|
      case DIV_SYSTEM_VERA:
 | 
						|
        if (VERA >= 0) { IGNORED++;break; }
 | 
						|
        VERA = i;
 | 
						|
        logD("VERA detected as chip id %d",i);
 | 
						|
        break;
 | 
						|
      case DIV_SYSTEM_YM2151:
 | 
						|
        if (YM >= 0) { IGNORED++;break; }
 | 
						|
        YM = i;
 | 
						|
        logD("YM detected as chip id %d",i);
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        IGNORED++;
 | 
						|
        logD("Ignoring chip %d systemID %d",i,song.system[i]);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (VERA < 0 && YM < 0) {
 | 
						|
  logE("No supported systems for ZSM");
 | 
						|
  return NULL;
 | 
						|
  }
 | 
						|
  if (IGNORED > 0)
 | 
						|
    logW("ZSM export ignoring %d unsupported system%c",IGNORED,IGNORED>1?'s':' ');
 | 
						|
 | 
						|
  stop();
 | 
						|
  repeatPattern=false;
 | 
						|
  setOrder(0);
 | 
						|
  BUSY_BEGIN_SOFT;
 | 
						|
 | 
						|
  double origRate=got.rate;
 | 
						|
  got.rate=zsmrate & 0xffff;
 | 
						|
 | 
						|
  // determine loop point
 | 
						|
  int loopOrder=0;
 | 
						|
  int loopRow=0;
 | 
						|
  int loopEnd=0;
 | 
						|
  walkSong(loopOrder,loopRow,loopEnd);
 | 
						|
  logI("loop point: %d %d",loopOrder,loopRow);
 | 
						|
  warnings="";
 | 
						|
 | 
						|
  DivZSM zsm;
 | 
						|
  zsm.init(zsmrate);
 | 
						|
 | 
						|
  // reset the playback state
 | 
						|
  curOrder=0;
 | 
						|
  freelance=false;
 | 
						|
  playing=false;
 | 
						|
  extValuePresent=false;
 | 
						|
  remainingLoops=-1;
 | 
						|
 | 
						|
  // Prepare to write song data
 | 
						|
  playSub(false);
 | 
						|
  //size_t tickCount=0;
 | 
						|
  bool done=false;
 | 
						|
  int loopPos=-1;
 | 
						|
  int writeCount=0;
 | 
						|
  int fracWait=0; // accumulates fractional ticks
 | 
						|
  if (VERA >= 0) disCont[VERA].dispatch->toggleRegisterDump(true);
 | 
						|
  if (YM >= 0) {
 | 
						|
    disCont[YM].dispatch->toggleRegisterDump(true);
 | 
						|
    // emit LFO initialization commands
 | 
						|
    zsm.writeYM(0x18,0);    // freq = 0
 | 
						|
    zsm.writeYM(0x19,0x7F); // AMD  = 7F
 | 
						|
    zsm.writeYM(0x19,0xFF); // PMD  = 7F
 | 
						|
    // TODO: incorporate the Furnace meta-command for init data and filter
 | 
						|
    //       out writes to otherwise-unused channels.
 | 
						|
  }
 | 
						|
 | 
						|
  while (!done) {
 | 
						|
    if (loopPos==-1) {
 | 
						|
      if (loopOrder==curOrder && loopRow==curRow && ticks==1 && loop) {
 | 
						|
        loopPos=zsm.getoffset();
 | 
						|
        zsm.setLoopPoint();
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (nextTick() || !playing) {
 | 
						|
      done=true;
 | 
						|
      if (!loop) {
 | 
						|
        for (int i=0; i<song.systemLen; i++) {
 | 
						|
          disCont[i].dispatch->getRegisterWrites().clear();
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      if (!playing) {
 | 
						|
        loopPos=-1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    // get register dumps
 | 
						|
    for (int j=0; j<2; j++) {
 | 
						|
      int i=0;
 | 
						|
      // dump YM writes first
 | 
						|
      if (j==0) {
 | 
						|
        if (YM < 0)
 | 
						|
          continue;
 | 
						|
        else
 | 
						|
          i=YM;
 | 
						|
      }
 | 
						|
      // dump VERA writes second
 | 
						|
      if (j==1) {
 | 
						|
        if (VERA < 0)
 | 
						|
          continue;
 | 
						|
        else {
 | 
						|
          i=VERA;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      std::vector<DivRegWrite>& writes=disCont[i].dispatch->getRegisterWrites();
 | 
						|
      if (writes.size() > 0)
 | 
						|
        logD("zsmOps: Writing %d messages to chip %d",writes.size(), i);
 | 
						|
      for (DivRegWrite& write: writes) {
 | 
						|
        if (i==YM) zsm.writeYM(write.addr&0xff, write.val);
 | 
						|
        if (i==VERA) zsm.writePSG(write.addr&0xff, write.val);
 | 
						|
        writeCount++;
 | 
						|
      }
 | 
						|
      writes.clear();
 | 
						|
    }
 | 
						|
 | 
						|
    // write wait
 | 
						|
    int totalWait=cycles>>MASTER_CLOCK_PREC;
 | 
						|
    fracWait += cycles & MASTER_CLOCK_MASK;
 | 
						|
    totalWait += fracWait>>MASTER_CLOCK_PREC;
 | 
						|
    fracWait &= MASTER_CLOCK_MASK;
 | 
						|
    if (totalWait>0) {
 | 
						|
      zsm.tick(totalWait);
 | 
						|
      //tickCount+=totalWait;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  // end of song
 | 
						|
 | 
						|
  // done - close out.
 | 
						|
  got.rate = origRate;
 | 
						|
  if (VERA >= 0) disCont[VERA].dispatch->toggleRegisterDump(false);
 | 
						|
  if (YM >= 0) disCont[YM].dispatch->toggleRegisterDump(false);
 | 
						|
 | 
						|
  remainingLoops=-1;
 | 
						|
  playing=false;
 | 
						|
  freelance=false;
 | 
						|
  extValuePresent=false;
 | 
						|
 | 
						|
  BUSY_END;
 | 
						|
  return zsm.finish();
 | 
						|
}
 |