mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-01 02:22:39 +00:00
parent
5a587a6af6
commit
54745bec3b
5 changed files with 136 additions and 409 deletions
|
@ -671,6 +671,7 @@ class DivEngine {
|
||||||
// add every export method here
|
// add every export method here
|
||||||
friend class DivROMExport;
|
friend class DivROMExport;
|
||||||
friend class DivExportAmigaValidation;
|
friend class DivExportAmigaValidation;
|
||||||
|
friend class DivExportSAPR;
|
||||||
friend class DivExportTiuna;
|
friend class DivExportTiuna;
|
||||||
friend class DivExportZSM;
|
friend class DivExportZSM;
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ void DivROMExport::logAppend(String what) {
|
||||||
logLock.lock();
|
logLock.lock();
|
||||||
exportLog.push_back(what);
|
exportLog.push_back(what);
|
||||||
logLock.unlock();
|
logLock.unlock();
|
||||||
|
logD("export: %s",what);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivROMExport::wait() {
|
void DivROMExport::wait() {
|
||||||
|
|
|
@ -17,159 +17,140 @@
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// thanks asiekierka!
|
||||||
|
// I have ported your code to this ROM export framework.
|
||||||
|
|
||||||
#include "sapr.h"
|
#include "sapr.h"
|
||||||
#include "../engine.h"
|
#include "../engine.h"
|
||||||
#include "../ta-log.h"
|
#include "../ta-log.h"
|
||||||
#include <fmt/printf.h>
|
#include <fmt/printf.h>
|
||||||
#include <algorithm>
|
#include <array>
|
||||||
#include <map>
|
|
||||||
#include <tuple>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
void DivExportSAPR::run() {
|
constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
|
||||||
int loopOrder, loopOrderRow, loopEnd;
|
constexpr int MASTER_CLOCK_MASK=(sizeof(void*)==8)?0xff:0;
|
||||||
int tick=0;
|
|
||||||
SafeWriter* w;
|
|
||||||
std::map<int,SAPRCmd> allCmds[2];
|
|
||||||
|
|
||||||
// config
|
static String ticksToTime(double rate, int ticks) {
|
||||||
String baseLabel=conf.getString("baseLabel","song");
|
double timing = ticks / rate;
|
||||||
int firstBankSize=conf.getInt("firstBankSize",3072);
|
|
||||||
int otherBankSize=conf.getInt("otherBankSize",4096-48);
|
return fmt::sprintf("%02d:%02d.%03d",
|
||||||
int tiaIdx=conf.getInt("sysToExport",-1);
|
(int) timing / 60,
|
||||||
|
(int) timing % 60,
|
||||||
|
(int) (timing * 1000) % 1000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivExportSAPR::run() {
|
||||||
|
int sapScanlines=0; // TODO: property!
|
||||||
|
int POKEY=-1;
|
||||||
|
int IGNORED=0;
|
||||||
|
bool palTiming=(e->song.systemFlags[POKEY].getInt("clockSel",0) != 0);
|
||||||
|
int scanlinesPerFrame = (palTiming?312:262);
|
||||||
|
size_t tickCount=0;
|
||||||
|
std::vector<std::array<uint8_t, 9>> regs;
|
||||||
|
|
||||||
|
if (sapScanlines <= 0) {
|
||||||
|
sapScanlines = scanlinesPerFrame;
|
||||||
|
}
|
||||||
|
//double sapRate = (palTiming?49.86:59.92) * scanlinesPerFrame / sapScanlines;
|
||||||
|
double sapRate = (palTiming?50:60) * (double)scanlinesPerFrame / (double)sapScanlines;
|
||||||
|
|
||||||
|
// Locate system index.
|
||||||
|
for (int i=0; i<e->song.systemLen; i++) {
|
||||||
|
if (e->song.system[i] == DIV_SYSTEM_POKEY) {
|
||||||
|
if (POKEY>=0) {
|
||||||
|
IGNORED++;
|
||||||
|
logAppendf("Ignoring duplicate POKEY id %d",i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
POKEY=i;
|
||||||
|
logAppendf("POKEY detected as chip id %d",i);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
IGNORED++;
|
||||||
|
logAppendf("Ignoring chip id %d, system id %d",i,(int)e->song.system[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (POKEY<0) {
|
||||||
|
logAppendf("ERROR: Could not find POKEY");
|
||||||
|
failed=true;
|
||||||
|
running=false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (IGNORED>0) {
|
||||||
|
logAppendf("WARNING: SAP export ignoring %d unsupported system%c",IGNORED,IGNORED>1?'s':' ');
|
||||||
|
}
|
||||||
|
|
||||||
e->stop();
|
e->stop();
|
||||||
e->repeatPattern=false;
|
e->repeatPattern=false;
|
||||||
e->shallStop=false;
|
|
||||||
e->setOrder(0);
|
e->setOrder(0);
|
||||||
|
|
||||||
|
logAppend("playing and logging register writes...");
|
||||||
|
|
||||||
e->synchronizedSoft([&]() {
|
e->synchronizedSoft([&]() {
|
||||||
// determine loop point
|
double origRate = e->got.rate;
|
||||||
// bool stopped=false;
|
e->got.rate=sapRate;
|
||||||
loopOrder=0;
|
|
||||||
loopOrderRow=0;
|
|
||||||
loopEnd=0;
|
|
||||||
e->walkSong(loopOrder,loopOrderRow,loopEnd);
|
|
||||||
logAppendf("loop point: %d %d",loopOrder,loopOrderRow);
|
|
||||||
|
|
||||||
w=new SafeWriter;
|
// Determine loop point.
|
||||||
w->init();
|
int loopOrder=0;
|
||||||
|
int loopRow=0;
|
||||||
|
int loopEnd=0;
|
||||||
|
e->walkSong(loopOrder,loopRow,loopEnd);
|
||||||
|
logAppendf("loop point: %d %d",loopOrder,loopRow);
|
||||||
|
e->warnings="";
|
||||||
|
|
||||||
if (tiaIdx<0 || tiaIdx>=e->song.systemLen) {
|
// Reset the playback state.
|
||||||
tiaIdx=-1;
|
e->curOrder=0;
|
||||||
for (int i=0; i<e->song.systemLen; i++) {
|
e->freelance=false;
|
||||||
if (e->song.system[i]==DIV_SYSTEM_TIA) {
|
e->playing=false;
|
||||||
tiaIdx=i;
|
e->extValuePresent=false;
|
||||||
break;
|
e->remainingLoops=-1;
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tiaIdx<0) {
|
|
||||||
logAppend("ERROR: selected TIA system not found");
|
|
||||||
failed=true;
|
|
||||||
running=false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (e->song.system[tiaIdx]!=DIV_SYSTEM_TIA) {
|
|
||||||
logAppend("ERROR: selected chip is not a TIA!");
|
|
||||||
failed=true;
|
|
||||||
running=false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
e->disCont[tiaIdx].dispatch->toggleRegisterDump(true);
|
// Prepare to write song data.
|
||||||
|
|
||||||
// write patterns
|
|
||||||
// bool writeLoop=false;
|
|
||||||
logAppend("recording sequence...");
|
|
||||||
bool done=false;
|
|
||||||
e->playSub(false);
|
e->playSub(false);
|
||||||
|
bool done=false;
|
||||||
// int loopTick=-1;
|
int fracWait=0; // accumulates fractional ticks
|
||||||
SAPRLast last[2];
|
e->disCont[POKEY].dispatch->toggleRegisterDump(true);
|
||||||
SAPRNew news[2];
|
std::array<uint8_t, 9> currRegs;
|
||||||
|
|
||||||
while (!done) {
|
while (!done) {
|
||||||
// TODO implement loop
|
if (e->nextTick() || !e->playing) {
|
||||||
// if (loopTick<0 && loopOrder==curOrder && loopOrderRow==curRow
|
|
||||||
// && (ticks-((tempoAccum+virtualTempoN)/virtualTempoD))<=0
|
|
||||||
// ) {
|
|
||||||
// writeLoop=true;
|
|
||||||
// loopTick=tick;
|
|
||||||
// // invalidate last register state so it always force an absolute write after loop
|
|
||||||
// for (int i=0; i<2; i++) {
|
|
||||||
// last[i]=SAPRLast();
|
|
||||||
// last[i].pitch=-1;
|
|
||||||
// last[i].ins=-1;
|
|
||||||
// last[i].vol=-1;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
if (e->nextTick(false,true) || !e->playing) {
|
|
||||||
// stopped=!playing;
|
|
||||||
done=true;
|
done=true;
|
||||||
|
for (int i=0; i<e->song.systemLen; i++) {
|
||||||
|
e->disCont[i].dispatch->getRegisterWrites().clear();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (int i=0; i<2; i++) {
|
|
||||||
news[i]=SAPRNew();
|
|
||||||
}
|
|
||||||
// get register dumps
|
// get register dumps
|
||||||
std::vector<DivRegWrite>& writes=e->disCont[tiaIdx].dispatch->getRegisterWrites();
|
std::vector<DivRegWrite>& writes=e->disCont[POKEY].dispatch->getRegisterWrites();
|
||||||
for (const DivRegWrite& i: writes) {
|
if (writes.size() > 0) {
|
||||||
switch (i.addr) {
|
logAppendf("saprOps: found %d messages",writes.size());
|
||||||
case 0xfffe0000:
|
for (DivRegWrite& write: writes)
|
||||||
case 0xfffe0001:
|
if ((write.addr & 0xF) < 9)
|
||||||
news[i.addr&1].pitch=i.val;
|
currRegs[write.addr & 0xF] = write.val;
|
||||||
break;
|
writes.clear();
|
||||||
case 0xfffe0002:
|
}
|
||||||
news[0].sync=i.val;
|
|
||||||
break;
|
// write wait
|
||||||
case 0x15:
|
tickCount++;
|
||||||
case 0x16:
|
int totalWait=e->cycles>>MASTER_CLOCK_PREC;
|
||||||
news[i.addr-0x15].ins=i.val;
|
fracWait+=e->cycles&MASTER_CLOCK_MASK;
|
||||||
break;
|
totalWait+=fracWait>>MASTER_CLOCK_PREC;
|
||||||
case 0x19:
|
fracWait&=MASTER_CLOCK_MASK;
|
||||||
case 0x1a:
|
if (totalWait>0 && !done) {
|
||||||
news[i.addr-0x19].vol=i.val;
|
while (totalWait) {
|
||||||
break;
|
regs.push_back(currRegs);
|
||||||
default: break;
|
totalWait--;
|
||||||
|
tickCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writes.clear();
|
|
||||||
// collect changes
|
|
||||||
for (int i=0; i<2; i++) {
|
|
||||||
SAPRCmd cmds;
|
|
||||||
bool hasCmd=false;
|
|
||||||
if (news[i].pitch>=0 && (last[i].forcePitch || news[i].pitch!=last[i].pitch)) {
|
|
||||||
int dt=news[i].pitch-last[i].pitch;
|
|
||||||
if (!last[i].forcePitch && abs(dt)<=16) {
|
|
||||||
if (dt<0) cmds.pitchChange=15-dt;
|
|
||||||
else cmds.pitchChange=dt-1;
|
|
||||||
}
|
|
||||||
else cmds.pitchSet=news[i].pitch;
|
|
||||||
last[i].pitch=news[i].pitch;
|
|
||||||
last[i].forcePitch=false;
|
|
||||||
hasCmd=true;
|
|
||||||
}
|
|
||||||
if (news[i].ins>=0 && news[i].ins!=last[i].ins) {
|
|
||||||
cmds.ins=news[i].ins;
|
|
||||||
last[i].ins=news[i].ins;
|
|
||||||
hasCmd=true;
|
|
||||||
}
|
|
||||||
if (news[i].vol>=0 && news[i].vol!=last[i].vol) {
|
|
||||||
cmds.vol=(news[i].vol-last[i].vol)&0xf;
|
|
||||||
last[i].vol=news[i].vol;
|
|
||||||
hasCmd=true;
|
|
||||||
}
|
|
||||||
if (news[i].sync>=0) {
|
|
||||||
cmds.sync=news[i].sync;
|
|
||||||
hasCmd=true;
|
|
||||||
}
|
|
||||||
if (hasCmd) allCmds[i][tick]=cmds;
|
|
||||||
}
|
|
||||||
e->cmdStream.clear();
|
|
||||||
tick++;
|
|
||||||
}
|
|
||||||
for (int i=0; i<e->song.systemLen; i++) {
|
|
||||||
e->disCont[i].dispatch->getRegisterWrites().clear();
|
|
||||||
e->disCont[i].dispatch->toggleRegisterDump(false);
|
|
||||||
}
|
}
|
||||||
|
// end of song
|
||||||
|
|
||||||
|
// done - close out.
|
||||||
|
e->got.rate=origRate;
|
||||||
|
e->disCont[POKEY].dispatch->toggleRegisterDump(false);
|
||||||
|
|
||||||
e->remainingLoops=-1;
|
e->remainingLoops=-1;
|
||||||
e->playing=false;
|
e->playing=false;
|
||||||
|
@ -177,286 +158,30 @@ void DivExportSAPR::run() {
|
||||||
e->extValuePresent=false;
|
e->extValuePresent=false;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (failed) return;
|
logAppend("writing data...");
|
||||||
|
progress[0].amount=0.95f;
|
||||||
|
|
||||||
// render commands
|
auto w = new SafeWriter;
|
||||||
logAppend("rendering commands...");
|
w->init();
|
||||||
std::vector<SAPRBytes> renderedCmds;
|
// Write SAP header: Author, name, timing, type.
|
||||||
w->writeText(fmt::format(
|
w->writeText(fmt::sprintf("SAP\r\nAUTHOR \"%s\"\r\nNAME \"%s\"\r\n%s\r\nTYPE R\r\n",
|
||||||
"; Generated by Furnace " DIV_VERSION "\n"
|
e->song.author, e->song.name, palTiming ? "PAL" : "NTSC"
|
||||||
"; Name: {}\n"
|
|
||||||
"; Author: {}\n"
|
|
||||||
"; Album: {}\n"
|
|
||||||
"; Subsong #{}: {}\n\n",
|
|
||||||
e->song.name,e->song.author,e->song.category,e->curSubSongIndex+1,e->curSubSong->name
|
|
||||||
));
|
));
|
||||||
for (int i=0; i<2; i++) {
|
if (sapScanlines != scanlinesPerFrame) {
|
||||||
SAPRCmd lastCmd;
|
// Fastplay.
|
||||||
int lastTick=0;
|
w->writeText(fmt::sprintf("FASTPLAY %d\r\n", sapScanlines));
|
||||||
int lastWait=0;
|
|
||||||
// bool looped=false;
|
|
||||||
for (auto& kv: allCmds[i]) {
|
|
||||||
// if (!looped && !stopped && loopTick>=0 && kv.first>=loopTick) {
|
|
||||||
// writeCmd(w,&lastCmd,&lastWait,loopTick-lastTick);
|
|
||||||
// w->writeText(".loop\n");
|
|
||||||
// lastTick=loopTick;
|
|
||||||
// looped=true;
|
|
||||||
// }
|
|
||||||
writeCmd(renderedCmds,lastCmd,i,lastWait,lastTick,kv.first);
|
|
||||||
lastTick=kv.first;
|
|
||||||
lastCmd=kv.second;
|
|
||||||
}
|
|
||||||
writeCmd(renderedCmds,lastCmd,i,lastWait,lastTick,tick);
|
|
||||||
// if (stopped || loopTick<0) w->writeText(".loop\n db 0\n");
|
|
||||||
}
|
}
|
||||||
// compress commands
|
// Time.
|
||||||
std::vector<SAPRMatch> confirmedMatches;
|
w->writeText(fmt::sprintf("TIME %s\r\n", ticksToTime(sapRate, tickCount)));
|
||||||
std::vector<int> callTicks;
|
w->writeText("\r\n");
|
||||||
int cmId=0;
|
// Registers.
|
||||||
int cmdSize=renderedCmds.size();
|
for (auto atRegs : regs) {
|
||||||
bool* processed=new bool[cmdSize];
|
w->write(atRegs.data(), 9 * sizeof(uint8_t));
|
||||||
memset(processed,0,cmdSize*sizeof(bool));
|
|
||||||
logAppend("compressing! this may take a while.");
|
|
||||||
int maxCmId=(MAX(firstBankSize/1024,1))*256;
|
|
||||||
int lastMaxPMVal=100000;
|
|
||||||
logAppendf("max cmId: %d",maxCmId);
|
|
||||||
logAppendf("commands: %d",cmdSize);
|
|
||||||
while (firstBankSize>768 && cmId<maxCmId) {
|
|
||||||
if (mustAbort) {
|
|
||||||
logAppend("aborted!");
|
|
||||||
failed=true;
|
|
||||||
running=false;
|
|
||||||
delete[] processed;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float theOtherSide=pow(1.0/float(MAX(1,lastMaxPMVal)),0.2)*0.98;
|
|
||||||
progress[0].amount=theOtherSide+(1.0-theOtherSide)*((float)cmId/(float)maxCmId);
|
|
||||||
|
|
||||||
logAppendf("start CM %04x...",cmId);
|
|
||||||
std::map<int,SAPRMatches> potentialMatches;
|
|
||||||
for (int i=0; i<cmdSize-1;) {
|
|
||||||
// continue and skip if it's part of previous confirmed matches
|
|
||||||
while (i<cmdSize-1 && processed[i]) i++;
|
|
||||||
if (i>=cmdSize-1) break;
|
|
||||||
progress[1].amount=(float)i/(float)(cmdSize-1);
|
|
||||||
std::vector<SAPRMatch> match;
|
|
||||||
int ch=renderedCmds[i].ch;
|
|
||||||
for (int j=i+1; j<cmdSize;) {
|
|
||||||
if (processed[i]) break;
|
|
||||||
//while (j<cmdSize && processed[i]) j++;
|
|
||||||
if (j>=cmdSize) break;
|
|
||||||
int k=0;
|
|
||||||
int ticks=0;
|
|
||||||
int size=0;
|
|
||||||
while (
|
|
||||||
(i+k)<j && (i+k)<cmdSize && (j+k)<cmdSize &&
|
|
||||||
(ticks+renderedCmds[i+k].ticks)<=256 &&
|
|
||||||
// match runs can't cross channels
|
|
||||||
// as channel end command would be insterted there later
|
|
||||||
renderedCmds[i+k].ch==ch &&
|
|
||||||
renderedCmds[j+k].ch==ch &&
|
|
||||||
renderedCmds[i+k]==renderedCmds[j+k] &&
|
|
||||||
!processed[i+k] && !processed[j+k]
|
|
||||||
) {
|
|
||||||
ticks+=renderedCmds[i+k].ticks;
|
|
||||||
size+=renderedCmds[i+k].size;
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
if (size>2) match.push_back(SAPRMatch(j,j+k,size,0));
|
|
||||||
if (k==0) k++;
|
|
||||||
j+=k;
|
|
||||||
}
|
|
||||||
if (match.empty()) {
|
|
||||||
i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// find a length that results in most bytes saved
|
|
||||||
SAPRMatches matches;
|
|
||||||
int curSize=0;
|
|
||||||
int curLength=1;
|
|
||||||
int curTicks=0;
|
|
||||||
while (true) {
|
|
||||||
int bytesSaved=-4;
|
|
||||||
bool found=false;
|
|
||||||
for (const SAPRMatch& j: match) {
|
|
||||||
if ((j.endPos-j.pos)>=curLength) {
|
|
||||||
if (!found) {
|
|
||||||
found=true;
|
|
||||||
curSize+=renderedCmds[i+curLength-1].size;
|
|
||||||
curTicks+=renderedCmds[i+curLength-1].ticks;
|
|
||||||
}
|
|
||||||
bytesSaved+=curSize-2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) break;
|
|
||||||
if (bytesSaved>matches.bytesSaved) {
|
|
||||||
matches.length=curLength;
|
|
||||||
matches.bytesSaved=bytesSaved;
|
|
||||||
matches.ticks=curTicks;
|
|
||||||
}
|
|
||||||
curLength++;
|
|
||||||
}
|
|
||||||
if (matches.bytesSaved>0) {
|
|
||||||
matches.pos.push_back(i);
|
|
||||||
for (const SAPRMatch& j: match) {
|
|
||||||
if ((j.endPos-j.pos)>=matches.length) {
|
|
||||||
matches.pos.push_back(j.pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
potentialMatches[i]=matches;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (potentialMatches.empty()) {
|
|
||||||
logAppend("potentialMatches is empty");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int maxPMIdx=0;
|
|
||||||
int maxPMVal=0;
|
|
||||||
for (const auto& i: potentialMatches) {
|
|
||||||
if (i.second.bytesSaved>maxPMVal) {
|
|
||||||
maxPMVal=i.second.bytesSaved;
|
|
||||||
maxPMIdx=i.first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int maxPMLen=potentialMatches[maxPMIdx].length;
|
|
||||||
for (const int i: potentialMatches[maxPMIdx].pos) {
|
|
||||||
confirmedMatches.push_back({i,i+maxPMLen,0,cmId});
|
|
||||||
memset(processed+i,1,maxPMLen);
|
|
||||||
//std::fill(processed.begin()+i,processed.begin()+(i+maxPMLen),true);
|
|
||||||
}
|
|
||||||
callTicks.push_back(potentialMatches[maxPMIdx].ticks);
|
|
||||||
logAppendf("CM %04x added: pos=%d,len=%d,matches=%d,saved=%d",cmId,maxPMIdx,maxPMLen,potentialMatches[maxPMIdx].pos.size(),maxPMVal);
|
|
||||||
lastMaxPMVal=maxPMVal;
|
|
||||||
cmId++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output.push_back(DivROMExportOutput("export.sap",w));
|
||||||
|
|
||||||
progress[0].amount=1.0f;
|
progress[0].amount=1.0f;
|
||||||
progress[1].amount=1.0f;
|
|
||||||
logAppend("generating data...");
|
|
||||||
delete[] processed;
|
|
||||||
std::sort(confirmedMatches.begin(),confirmedMatches.end(),[](const SAPRMatch& l, const SAPRMatch& r){
|
|
||||||
return l.pos<r.pos;
|
|
||||||
});
|
|
||||||
// ignore last call IDs >256 that don't fill up a page
|
|
||||||
// as they tends to increase the final size due to page alignment
|
|
||||||
int cmIdLen=cmId>256?(cmId&~255):cmId;
|
|
||||||
// overlap check
|
|
||||||
for (int i=1; i<(int)confirmedMatches.size(); i++) {
|
|
||||||
if (confirmedMatches[i-1].endPos<=confirmedMatches[i].pos) continue;
|
|
||||||
logAppend("ERROR: impossible overlap found in matches list, please report");
|
|
||||||
failed=true;
|
|
||||||
running=false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write commands
|
|
||||||
int totalSize=0;
|
|
||||||
int cnt=cmIdLen;
|
|
||||||
w->writeText(fmt::format(" .section {0}_bank0\n .align $100\n{0}_calltable",baseLabel));
|
|
||||||
while (cnt>0) {
|
|
||||||
int cnt2=MIN(cnt,256);
|
|
||||||
w->writeText("\n .byte ");
|
|
||||||
for (int j=0; j<cnt2; j++) {
|
|
||||||
w->writeText(fmt::format("<{}_c{},",baseLabel,cmIdLen-cnt+j));
|
|
||||||
}
|
|
||||||
for (int j=cnt2; j<256; j++) {
|
|
||||||
w->writeText("0,");
|
|
||||||
}
|
|
||||||
w->seek(-1,SEEK_CUR);
|
|
||||||
w->writeText("\n .byte ");
|
|
||||||
for (int j=0; j<cnt2; j++) {
|
|
||||||
w->writeText(fmt::format(">{}_c{},",baseLabel,cmIdLen-cnt+j));
|
|
||||||
}
|
|
||||||
for (int j=cnt2; j<256; j++) {
|
|
||||||
w->writeText("0,");
|
|
||||||
}
|
|
||||||
w->seek(-1,SEEK_CUR);
|
|
||||||
w->writeText("\n .byte ");
|
|
||||||
for (int j=0; j<cnt2; j++) {
|
|
||||||
w->writeText(fmt::format("{}_c{}>>13,",baseLabel,cmIdLen-cnt+j));
|
|
||||||
}
|
|
||||||
for (int j=cnt2; j<256; j++) {
|
|
||||||
w->writeText("0,");
|
|
||||||
}
|
|
||||||
w->seek(-1,SEEK_CUR);
|
|
||||||
w->writeText("\n .byte ");
|
|
||||||
for (int j=0; j<cnt2; j++) {
|
|
||||||
w->writeText(fmt::format("{},",callTicks[cmIdLen-cnt+j]&0xff));
|
|
||||||
}
|
|
||||||
w->seek(-1,SEEK_CUR);
|
|
||||||
totalSize+=768+cnt2;
|
|
||||||
cnt-=cnt2;
|
|
||||||
}
|
|
||||||
w->writeC('\n');
|
|
||||||
if (totalSize>firstBankSize) {
|
|
||||||
logAppend("ERROR: first bank is not large enough to contain call table");
|
|
||||||
failed=true;
|
|
||||||
running=false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int curBank=0;
|
|
||||||
int bankSize=totalSize;
|
|
||||||
int maxBankSize=firstBankSize;
|
|
||||||
int curCh=-1;
|
|
||||||
std::vector<bool> callVisited=std::vector<bool>(cmIdLen,false);
|
|
||||||
auto cmIter=confirmedMatches.begin();
|
|
||||||
for (int i=0; i<(int)renderedCmds.size(); i++) {
|
|
||||||
int writeCall=-1;
|
|
||||||
SAPRBytes cmd=renderedCmds[i];
|
|
||||||
if (cmIter!=confirmedMatches.end() && i==cmIter->pos) {
|
|
||||||
if (cmIter->id<cmIdLen) {
|
|
||||||
if (callVisited[cmIter->id]) {
|
|
||||||
unsigned char idLo=cmIter->id&0xff;
|
|
||||||
unsigned char idHi=cmIter->id>>8;
|
|
||||||
cmd=SAPRBytes(cmd.ch,0,2,{idHi,idLo});
|
|
||||||
i=cmIter->endPos-1;
|
|
||||||
} else {
|
|
||||||
writeCall=cmIter->id;
|
|
||||||
callVisited[writeCall]=true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmIter++;
|
|
||||||
}
|
|
||||||
if (cmd.ch!=curCh) {
|
|
||||||
if (curCh>=0) {
|
|
||||||
w->writeText(" .text x\"e0\"\n");
|
|
||||||
totalSize++;
|
|
||||||
bankSize++;
|
|
||||||
}
|
|
||||||
if (bankSize+cmd.size>=maxBankSize) {
|
|
||||||
maxBankSize=otherBankSize;
|
|
||||||
curBank++;
|
|
||||||
w->writeText(fmt::format(" .endsection\n\n .section {}_bank{}",baseLabel,curBank));
|
|
||||||
bankSize=0;
|
|
||||||
}
|
|
||||||
w->writeText(fmt::format("\n{}_ch{}\n",baseLabel,cmd.ch));
|
|
||||||
curCh=cmd.ch;
|
|
||||||
}
|
|
||||||
if (bankSize+cmd.size+1>=maxBankSize) {
|
|
||||||
maxBankSize=otherBankSize;
|
|
||||||
curBank++;
|
|
||||||
w->writeText(fmt::format(" .text x\"c0\"\n .endsection\n\n .section {}_bank{}\n",baseLabel,curBank));
|
|
||||||
totalSize++;
|
|
||||||
bankSize=0;
|
|
||||||
}
|
|
||||||
if (writeCall>=0) {
|
|
||||||
w->writeText(fmt::format("{}_c{}\n",baseLabel,writeCall));
|
|
||||||
}
|
|
||||||
w->writeText(" .text x\"");
|
|
||||||
for (int j=0; j<cmd.size; j++) {
|
|
||||||
w->writeText(fmt::format("{:02x}",cmd.buf[j]));
|
|
||||||
}
|
|
||||||
w->writeText("\"\n");
|
|
||||||
totalSize+=cmd.size;
|
|
||||||
bankSize+=cmd.size;
|
|
||||||
}
|
|
||||||
w->writeText(" .text x\"e0\"\n .endsection\n");
|
|
||||||
totalSize++;
|
|
||||||
logAppendf("total size: %d bytes (%d banks)",totalSize,curBank+1);
|
|
||||||
|
|
||||||
output.push_back(DivROMExportOutput("export.asm",w));
|
|
||||||
|
|
||||||
logAppend("finished!");
|
logAppend("finished!");
|
||||||
|
|
||||||
|
@ -464,10 +189,8 @@ void DivExportSAPR::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DivExportSAPR::go(DivEngine* eng) {
|
bool DivExportSAPR::go(DivEngine* eng) {
|
||||||
progress[0].name="Compression";
|
progress[0].name="Progress";
|
||||||
progress[0].amount=0.0f;
|
progress[0].amount=0.0f;
|
||||||
progress[1].name="Confirmed Matches";
|
|
||||||
progress[1].amount=0.0f;
|
|
||||||
|
|
||||||
e=eng;
|
e=eng;
|
||||||
running=true;
|
running=true;
|
||||||
|
@ -479,6 +202,7 @@ bool DivExportSAPR::go(DivEngine* eng) {
|
||||||
|
|
||||||
void DivExportSAPR::wait() {
|
void DivExportSAPR::wait() {
|
||||||
if (exportThread!=NULL) {
|
if (exportThread!=NULL) {
|
||||||
|
logV("waiting for export thread...");
|
||||||
exportThread->join();
|
exportThread->join();
|
||||||
delete exportThread;
|
delete exportThread;
|
||||||
}
|
}
|
||||||
|
@ -498,6 +222,6 @@ bool DivExportSAPR::hasFailed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
DivROMExportProgress DivExportSAPR::getProgress(int index) {
|
DivROMExportProgress DivExportSAPR::getProgress(int index) {
|
||||||
if (index<0 || index>2) return progress[2];
|
if (index<0 || index>1) return progress[1];
|
||||||
return progress[index];
|
return progress[index];
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
class DivExportSAPR: public DivROMExport {
|
class DivExportSAPR: public DivROMExport {
|
||||||
DivEngine* e;
|
DivEngine* e;
|
||||||
std::thread* exportThread;
|
std::thread* exportThread;
|
||||||
DivROMExportProgress progress[3];
|
DivROMExportProgress progress[2];
|
||||||
bool running, failed, mustAbort;
|
bool running, failed, mustAbort;
|
||||||
void run();
|
void run();
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -37,6 +37,7 @@ const char* aboutLine[]={
|
||||||
_N("A M 4 N (intro tune)"),
|
_N("A M 4 N (intro tune)"),
|
||||||
"Adam Lederer",
|
"Adam Lederer",
|
||||||
"akumanatt",
|
"akumanatt",
|
||||||
|
"asiekierka",
|
||||||
"cam900",
|
"cam900",
|
||||||
"djtuBIG-MaliceX",
|
"djtuBIG-MaliceX",
|
||||||
"Eknous",
|
"Eknous",
|
||||||
|
|
Loading…
Reference in a new issue