From 39ea7e6da0495d40f7a97a6bc9ac347dd274c8e2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 4 Apr 2025 19:28:29 -0500 Subject: [PATCH] sub-blocks, part 2 works but result still kind of big --- papers/export-tech.md | 2 +- src/engine/cmdStream.cpp | 3 + src/engine/cmdStreamOps.cpp | 288 +++++++++++++++++++++++++----------- src/gui/csPlayer.cpp | 6 + 4 files changed, 210 insertions(+), 89 deletions(-) diff --git a/papers/export-tech.md b/papers/export-tech.md index 0404aee5d..6e074b286 100644 --- a/papers/export-tech.md +++ b/papers/export-tech.md @@ -73,7 +73,7 @@ hex | description f1 | no operation f2 | UNUSED - unoptimized extended command f3 | loop (negative offset and count follow... both are 8-bit) - f4 | call symbol (16-bit index follows; only used internally) + f4 | call symbol (32-bit index follows; only used internally) f5 | jump to sub-block (address follows) f6 | go to sub-block (32-bit offset follows) f7 | full command (command and data follows) diff --git a/src/engine/cmdStream.cpp b/src/engine/cmdStream.cpp index 7eeb445f8..f6aa7f120 100644 --- a/src/engine/cmdStream.cpp +++ b/src/engine/cmdStream.cpp @@ -144,6 +144,7 @@ bool DivCSPlayer::tick() { break; case 0xf8: { unsigned int callAddr=chan[i].readPos+2+stream.readS(); + chan[i].readPos=stream.tell(); if (!chan[i].doCall(callAddr)) { logE("%d: (callb16) stack error!",i); } @@ -152,6 +153,7 @@ bool DivCSPlayer::tick() { } case 0xf6: { unsigned int callAddr=chan[i].readPos+4+stream.readI(); + chan[i].readPos=stream.tell(); if (!chan[i].doCall(callAddr)) { logE("%d: (callb32) stack error!",i); } @@ -160,6 +162,7 @@ bool DivCSPlayer::tick() { } case 0xf5: { unsigned int callAddr=stream.readI(); + chan[i].readPos=stream.tell(); if (!chan[i].doCall(callAddr)) { logE("%d: (call) stack error!",i); } diff --git a/src/engine/cmdStreamOps.cpp b/src/engine/cmdStreamOps.cpp index 975dbddb4..589686a3c 100644 --- a/src/engine/cmdStreamOps.cpp +++ b/src/engine/cmdStreamOps.cpp @@ -21,20 +21,7 @@ #include "../ta-log.h" #include -/* -#define WRITE_TICK(x) \ - if (tick-lastTick[x]>255) { \ - chanStream[x]->writeC(0xfc); \ - chanStream[x]->writeS(tick-lastTick[x]); \ - } else if (tick-lastTick[x]>1) { \ - delayPopularity[tick-lastTick[x]]++; \ - chanStream[x]->writeC(0xfd); \ - chanStream[x]->writeC(tick-lastTick[x]); \ - } else if (tick-lastTick[x]>0) { \ - chanStream[x]->writeC(0xfe); \ - } \ - lastTick[x]=tick; \ -*/ +//#define DISABLE_BLOCK_SEARCH int getInsLength(unsigned char ins) { switch (ins) { @@ -64,10 +51,10 @@ int getInsLength(unsigned char ins) { case 0xf2: // opt command case 0xf7: // cmd return 0; - case 0xf4: // callsym case 0xf8: // callb16 case 0xfc: // waits return 3; + case 0xf4: // callsym case 0xf5: // call case 0xf6: // callb32 case 0xfa: // jmp @@ -324,6 +311,122 @@ SafeWriter* stripNops(SafeWriter* s) { return s; } +SafeWriter* findSubBlocks(SafeWriter* stream, std::vector& subBlocks) { + unsigned char* buf=stream->getFinalBuf(); + + for (size_t groupSize=stream->size()>>1; groupSize>=8; groupSize--) { + //for (size_t groupSize=7; groupSize<=stream->size()>>1; groupSize++) { + bool foundSomething=false; + //logD("...try size %d",groupSize); + for (size_t searchPos=0; (searchPos+groupSize)size();) { + const unsigned char* group=&buf[searchPos]; + size_t groupLen=0; + size_t groupInsCount=0; + size_t subBlockID=subBlocks.size(); + int insLen=getInsLength(buf[searchPos]); + bool haveSub=false; + + if (insLen<1) { + logE("INS %x NOT IMPLEMENTED...",buf[searchPos]); + break; + } + + // register this block + for (size_t i=0; isize();) { + int insLenI=getInsLength(buf[searchPos+i]); + if (insLenI<1) { + logE("INS %x NOT IMPLEMENTED...",buf[searchPos+i]); + break; + } + i+=insLenI; + if (groupLen+insLenI>groupSize) break; + groupLen+=insLenI; + groupInsCount++; + } + + // don't do anything if we don't have a block large enough + if (groupLen<5) { + searchPos+=insLen; + continue; + } + + // don't do anything if this is just one or two commands + if (groupInsCount<3) { + searchPos+=insLen; + continue; + } + + // find identical blocks + for (size_t i=searchPos+groupLen; i+groupLensize();) { + int insLenI=getInsLength(buf[i]); + if (insLenI<1) { + logE("INS %x NOT IMPLEMENTED...",buf[i]); + break; + } + + // compare next block to group + if (memcmp(&buf[i],group,groupLen)==0) { + // we have a sub-block + if (!haveSub) { + // isolate this sub-block + SafeWriter* newBlock=new SafeWriter; + newBlock->init(); + newBlock->write(group,groupLen); + newBlock->writeC(0xf9); // ret + subBlocks.push_back(newBlock); + haveSub=true; + logD("- SUB %x (size %d):",searchPos,groupLen); + } + logD(" - %x",i); + // insert call + buf[i]=0xf4; + buf[i+1]=subBlockID&0xff; + buf[i+2]=(subBlockID>>8)&0xff; + buf[i+3]=(subBlockID>>16)&0xff; + buf[i+4]=(subBlockID>>24)&0xff; + + // replace the rest with nop + for (size_t j=i+5; j>8)&0xff; + buf[searchPos+3]=(subBlockID>>16)&0xff; + buf[searchPos+4]=(subBlockID>>24)&0xff; + + // replace the rest with nop + for (size_t j=searchPos+5; jgetFinalBuf(); + } + } + return stream; +} + SafeWriter* DivEngine::saveCommand() { stop(); repeatPattern=false; @@ -569,91 +672,100 @@ SafeWriter* DivEngine::saveCommand() { chanStream[h]=stripNops(chanStream[h]); } - // PASS 2: find sub-blocks and isolate them (TODO: THIS!) +#ifndef DISABLE_BLOCK_SEARCH + // PASS 3: find sub-blocks and isolate them for (int h=0; hgetFinalBuf(); - unsigned char group[256]; // max offset is -255 - size_t groupLen=0; - - memset(group,0,256); + std::vector subBlocks; + size_t beforeSize=chanStream[h]->size(); - // 3 is the minimum loop size that can be reliably optimized - logI("finding loop in chan %d",h); - for (int groupSize=3; groupSize<256; groupSize++) { - bool foundSomething=false; - //logD("...try size %d",groupSize); - for (size_t searchPos=0; searchPossize();) { - int insLen=getInsLength(buf[searchPos]); - groupLen=0; + // 6 is the minimum size that can be reliably optimized + logI("finding sub-blocks in chan %d",h); + chanStream[h]=findSubBlocks(chanStream[h],subBlocks); + // find sub-blocks within sub-blocks + size_t subBlocksLast=0; + size_t subBlocksLen=subBlocks.size(); + logI("finding sub-blocks within sub-blocks",h); + while (subBlocksLast!=subBlocksLen) { + logI("got %d blocks... starting from %d",(int)subBlocksLen,(int)subBlocksLast); + for (size_t i=subBlocksLast; i blockOff; + chanStream[h]->seek(0,SEEK_END); + for (size_t i=0; isize();) { - int insLenI=getInsLength(buf[searchPos+i]); - if (insLenI<1) { - logE("INS %x NOT IMPLEMENTED...",buf[searchPos+i]); + // check whether this block is duplicate + int dupOf=-1; + for (size_t j=0; jsize()==subBlocks[i]->size()) { + if (memcmp(subBlocks[j]->getFinalBuf(),subBlocks[i]->getFinalBuf(),subBlocks[j]->size())==0) { + logW("we have one"); + dupOf=j; break; } - i+=insLenI; - if ((int)groupLen+insLenI>groupSize) break; - groupLen+=insLenI; - } - - // don't do anything if we don't have a block - if (!groupLen) { - searchPos+=insLen; - continue; - } - - memcpy(group,&buf[searchPos],groupLen); - - // find contiguous blocks - size_t searchPos1=searchPos+groupLen; - size_t posOfFirstBlock=searchPos1; - int loopCount=0; - while (true) { - // stop if we're near the end - if (searchPos1>=chanStream[h]->size()) break; - // compare next block to group - if (memcmp(&buf[searchPos1],group,groupLen)!=0) break; - - // if we're here, we found a contiguous block - searchPos1+=groupLen; - loopCount++; - // don't loop more than 255 times - if (loopCount>=255) break; - } - - if (loopCount>0) { - // write loop command - logD("- LOOP: %x (size %d, %d times)",searchPos,groupLen,loopCount); - buf[posOfFirstBlock++]=0xf3; - buf[posOfFirstBlock++]=groupLen; - buf[posOfFirstBlock++]=loopCount; - // set the rest to nop - while (posOfFirstBlockgetFinalBuf(); + + if (dupOf>=0) { + // push address of original block (discard duplicate) + blockOff.push_back(blockOff[dupOf]); + } else { + // write sub-block + blockOff.push_back(chanStream[h]->tell()); + chanStream[h]->write(block->getFinalBuf(),block->size()); } } + + for (SafeWriter* block: subBlocks) { + block->finish(); + delete block; + } + subBlocks.clear(); + + // resolve symbols + unsigned char* buf=chanStream[h]->getFinalBuf(); + for (size_t j=0; jsize();) { + int insLen=getInsLength(buf[j]); + if (insLen<1) { + logE("INS %x NOT IMPLEMENTED...",buf[j]); + break; + } + if (buf[j]==0xf4) { // callsym + unsigned int addr=buf[j+1]|(buf[j+2]<<8)|(buf[j+3]<<8)|(buf[j+4]<<24); + if (addr>8)&0xff; + buf[j+3]=(addr>>16)&0xff; + buf[j+4]=(addr>>24)&0xff; + } else { + logE("requested symbol %d is out of bounds!",addr); + } + } + j+=insLen; + } + + size_t afterSize=chanStream[h]->size(); + logI("(before: %d - after: %d)",(int)beforeSize,(int)afterSize); + } +#endif + + // PASS 4: remove nop's (again) + for (int h=0; hisHalted()) { + if (ImGui::Button("Resume")) e->resume(); + } else { + if (ImGui::Button("Pause")) e->halt(); + } DivCSPlayer* cs=e->getStreamPlayer(); if (cs) {