2022-05-26 05:24:21 +00:00
|
|
|
/**
|
|
|
|
* 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 "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++) {
|
2022-09-24 07:31:10 +00:00
|
|
|
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]);
|
|
|
|
}
|
2022-05-26 05:24:21 +00:00
|
|
|
}
|
|
|
|
if (VERA < 0 && YM < 0) {
|
2022-09-24 07:31:10 +00:00
|
|
|
logE("No supported systems for ZSM");
|
|
|
|
return NULL;
|
2022-05-26 05:24:21 +00:00
|
|
|
}
|
|
|
|
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="";
|
|
|
|
|
2022-09-24 04:23:03 +00:00
|
|
|
DivZSM zsm;
|
2022-05-26 05:24:21 +00:00
|
|
|
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);
|
|
|
|
zsm.writeYM(0x18,0); // initialize the LFO freq to 0
|
|
|
|
// note - I think there's a bug where Furnace writes AMD/PMD=max
|
|
|
|
// that shouldn't be there, requiring this initialization that shouldn't
|
|
|
|
// be there for ZSM.
|
|
|
|
}
|
|
|
|
|
|
|
|
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++) {
|
2022-09-24 07:31:10 +00:00
|
|
|
int i=0;
|
2022-05-26 05:24:21 +00:00
|
|
|
// dump YM writes first
|
2022-09-24 07:31:10 +00:00
|
|
|
if (j==0) {
|
|
|
|
if (YM < 0)
|
|
|
|
continue;
|
|
|
|
else
|
|
|
|
i=YM;
|
|
|
|
}
|
2022-05-26 05:24:21 +00:00
|
|
|
// dump VERA writes second
|
2022-09-24 07:31:10 +00:00
|
|
|
if (j==1) {
|
|
|
|
if (VERA < 0)
|
|
|
|
continue;
|
|
|
|
else {
|
2022-05-26 05:24:21 +00:00
|
|
|
i=VERA;
|
2022-09-24 07:31:10 +00:00
|
|
|
}
|
|
|
|
}
|
2022-05-26 05:24:21 +00:00
|
|
|
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();
|
|
|
|
}
|