diff options
| author | ptitSeb <sebastien.chev@gmail.com> | 2023-02-20 21:30:22 +0100 |
|---|---|---|
| committer | ptitSeb <sebastien.chev@gmail.com> | 2023-02-20 21:30:22 +0100 |
| commit | bfef8c9bbe8a16373742cb9c9e5691b93e994f58 (patch) | |
| tree | 246b21e67025f3159006efd04f78fc6e4ec696bf /src | |
| parent | 42b83789880c300711f08e3852784e17cafaf7ac (diff) | |
| parent | 3fe2843bb3b95d8edd63d21c4f6acad7f652be91 (diff) | |
| download | box64-bfef8c9bbe8a16373742cb9c9e5691b93e994f58.tar.gz box64-bfef8c9bbe8a16373742cb9c9e5691b93e994f58.zip | |
Merge branch 'main' into steam_chrome
Diffstat (limited to 'src')
| -rw-r--r-- | src/custommem.c | 47 | ||||
| -rwxr-xr-x | src/dynarec/arm64/dynarec_arm64_f30f.c | 20 | ||||
| -rwxr-xr-x | src/dynarec/arm64/dynarec_arm64_pass2.h | 12 | ||||
| -rwxr-xr-x | src/dynarec/arm64/dynarec_arm64_pass3.h | 9 | ||||
| -rwxr-xr-x | src/dynarec/arm64/dynarec_arm64_private.h | 1 | ||||
| -rwxr-xr-x | src/dynarec/dynablock.c | 17 | ||||
| -rwxr-xr-x | src/dynarec/dynarec_native.c | 25 | ||||
| -rwxr-xr-x | src/include/debug.h | 1 | ||||
| -rwxr-xr-x | src/include/dynarec_native.h | 3 | ||||
| -rwxr-xr-x | src/libtools/signals.c | 5 | ||||
| -rwxr-xr-x | src/main.c | 12 | ||||
| -rw-r--r-- | src/tools/rcfile.c | 8 | ||||
| -rw-r--r-- | src/wrapped/wrappedfaudio_private.h | 6 |
13 files changed, 94 insertions, 72 deletions
diff --git a/src/custommem.c b/src/custommem.c index dfaca0c6..b9abcc3d 100644 --- a/src/custommem.c +++ b/src/custommem.c @@ -557,25 +557,29 @@ void FreeDynarecMap(uintptr_t addr) } } -uintptr_t getSizeJmpDefault(uintptr_t addr, size_t maxsize) +static uintptr_t getDBSize(uintptr_t addr, size_t maxsize, dynablock_t** db) { - uintptr_t idx3, idx2, idx1, idx0; - idx3 = (((uintptr_t)addr)>>48)&0xffff; + const uintptr_t idx3 = (addr>>48)&0xffff; + const uintptr_t idx2 = (addr>>32)&0xffff; + const uintptr_t idx1 = (addr>>16)&0xffff; + uintptr_t idx0 = addr&0xffff; + *db = *(dynablock_t**)(box64_jmptbl3[idx3][idx2][idx1][idx0]- sizeof(void*)); + if(*db) + return 1; if(box64_jmptbl3[idx3] == box64_jmptbldefault2) - return ((addr&~((1LL<<48)-1))|0xffffffffffffLL)-addr + 1; - idx2 = (((uintptr_t)addr)>>32)&0xffff; + return (addr|0xffffffffffffLL)+1-addr; if(box64_jmptbl3[idx3][idx2] == box64_jmptbldefault1) - return ((addr&~((1LL<<32)-1))|0xffffffffLL)-addr + 1; - idx1 = (((uintptr_t)addr)>>16)&0xffff; + return (addr|0xffffffffLL)+1-addr; uintptr_t* block = box64_jmptbl3[idx3][idx2][idx1]; if(block == box64_jmptbldefault0) - return ((addr&~((1LL<<16)-1))|0xffffLL)-addr + 1; - idx0 = addr&0xffff; + return (addr|0xffffLL)+1-addr; if (maxsize>0x10000) maxsize = 0x10000; while(idx0<maxsize && block[idx0]==(uintptr_t)native_next) ++idx0; - return idx0 - (addr&0xffff); + if(idx0<0x10000) + *db = *(dynablock_t**)(block[idx0]- sizeof(void*)); + return idx0+1-(addr&0xffff); } // each dynmap is 64k of size @@ -590,17 +594,15 @@ void cleanDBFromAddressRange(uintptr_t addr, size_t size, int destroy) { uintptr_t start_addr = my_context?((addr<my_context->max_db_size)?0:(addr-my_context->max_db_size)):addr; dynarec_log(LOG_DEBUG, "cleanDBFromAddressRange %p/%p -> %p %s\n", (void*)addr, (void*)start_addr, (void*)(addr+size-1), destroy?"destroy":"mark"); - for (uintptr_t i=start_addr; i<addr+size; ++i) { - dynablock_t* db = getDB(i); + dynablock_t* db = NULL; + uintptr_t end = addr+size; + while (start_addr<end) { + start_addr += getDBSize(start_addr, end-start_addr, &db); if(db) { if(destroy) FreeRangeDynablock(db, addr, size); else MarkRangeDynablock(db, addr, size); - } else { - uintptr_t next = getSizeJmpDefault(i, size-i); - if(next) - i+=next-1; } } } @@ -785,9 +787,6 @@ void protectDB(uintptr_t addr, uintptr_t size) for (uintptr_t i=(idx>>16); i<=(end>>16); ++i) if(memprot[i].prot==memprot_default) { uint8_t* newblock = box_calloc(1<<16, sizeof(uint8_t)); - /*if (native_lock_storeifref(&memprot[i], newblock, memprot_default) != newblock) { - box_free(newblock); - }*/ memprot[i].prot = newblock; } for (uintptr_t i=idx; i<=end; ++i) { @@ -1052,17 +1051,17 @@ void allocProtection(uintptr_t addr, size_t size, uint32_t prot) #ifdef DYNAREC int IsInHotPage(uintptr_t addr) { - if(addr<=(1LL<<48)) + if(addr>=(1LL<<48)) return 0; int idx = (addr>>MEMPROT_SHIFT)>>16; - uint8_t *block = memprot[idx].hot; - if(!block) + uint8_t *hot = memprot[idx].hot; + if(!hot) return 0; int base = (addr>>MEMPROT_SHIFT)&0xffff; - if(!block[base]) + if(!hot[base]) return 0; // decrement hot - native_lock_decifnot0b(&block[base]); + native_lock_decifnot0b(&hot[base]); return 1; } diff --git a/src/dynarec/arm64/dynarec_arm64_f30f.c b/src/dynarec/arm64/dynarec_arm64_f30f.c index 1f60b843..2d0b8c6b 100755 --- a/src/dynarec/arm64/dynarec_arm64_f30f.c +++ b/src/dynarec/arm64/dynarec_arm64_f30f.c @@ -367,13 +367,13 @@ uintptr_t dynarec64_F30F(dynarec_arm_t* dyn, uintptr_t addr, uintptr_t ip, int n GETED(0); GETGD; TSTxw_REG(ed, ed); - CSETw(x1, cEQ); - BFIw(xFlags, x1, F_CF, 1); // CF = is source 0? - RBITxw(x1, ed); // reverse - CLZxw(gd, x1); // x2 gets leading 0 == TZCNT + CSETw(x3, cEQ); + BFIw(xFlags, x3, F_CF, 1); // CF = is source 0? + RBITxw(x3, ed); // reverse + CLZxw(gd, x3); // x2 gets leading 0 == TZCNT TSTxw_REG(gd, gd); - CSETw(x1, cEQ); - BFIw(xFlags, x1, F_ZF, 1); // ZF = is dest 0? + CSETw(x3, cEQ); + BFIw(xFlags, x3, F_ZF, 1); // ZF = is dest 0? break; case 0xBD: INST_NAME("LZCNT Gd, Ed"); @@ -383,12 +383,12 @@ uintptr_t dynarec64_F30F(dynarec_arm_t* dyn, uintptr_t addr, uintptr_t ip, int n GETED(0); GETGD; TSTxw_REG(ed, ed); - CSETw(x1, cEQ); - BFIw(xFlags, x1, F_CF, 1); // CF = is source 0? + CSETw(x3, cEQ); + BFIw(xFlags, x3, F_CF, 1); // CF = is source 0? CLZxw(gd, ed); // x2 gets leading 0 == LZCNT TSTxw_REG(gd, gd); - CSETw(x1, cEQ); - BFIw(xFlags, x1, F_ZF, 1); // ZF = is dest 0? + CSETw(x3, cEQ); + BFIw(xFlags, x3, F_ZF, 1); // ZF = is dest 0? break; case 0xC2: diff --git a/src/dynarec/arm64/dynarec_arm64_pass2.h b/src/dynarec/arm64/dynarec_arm64_pass2.h index 29d5f01a..edc43ae5 100755 --- a/src/dynarec/arm64/dynarec_arm64_pass2.h +++ b/src/dynarec/arm64/dynarec_arm64_pass2.h @@ -1,14 +1,18 @@ #define INIT dyn->native_size = 0 -#define FINI if(ninst) {dyn->insts[ninst].address = (dyn->insts[ninst-1].address+dyn->insts[ninst-1].size);} +#define FINI \ + if(ninst) { \ + dyn->insts[ninst].address = (dyn->insts[ninst-1].address+dyn->insts[ninst-1].size); \ + dyn->insts_size += 1+((dyn->insts[ninst].x64.size>dyn->insts[ninst].size)?dyn->insts[ninst].x64.size:dyn->insts[ninst].size)/15; \ + } #define MESSAGE(A, ...) #define EMIT(A) dyn->insts[ninst].size+=4; dyn->native_size+=4 #define NEW_INST \ if(ninst) { \ dyn->insts[ninst].address = (dyn->insts[ninst-1].address+dyn->insts[ninst-1].size); \ - if(ninst && isInstClean(dyn, ninst)) { \ - if(dyn->last_ip!=ip) dyn->last_ip = 0; \ - } \ + if(isInstClean(dyn, ninst) && dyn->last_ip!=ip) \ + dyn->last_ip = 0; \ + dyn->insts_size += 1+((dyn->insts[ninst-1].x64.size>dyn->insts[ninst-1].size)?dyn->insts[ninst-1].x64.size:dyn->insts[ninst-1].size)/15; \ } #define INST_EPILOG dyn->insts[ninst].epilog = dyn->native_size; #define INST_NAME(name) diff --git a/src/dynarec/arm64/dynarec_arm64_pass3.h b/src/dynarec/arm64/dynarec_arm64_pass3.h index 13b2b323..ae79c11f 100755 --- a/src/dynarec/arm64/dynarec_arm64_pass3.h +++ b/src/dynarec/arm64/dynarec_arm64_pass3.h @@ -1,5 +1,8 @@ #define INIT -#define FINI +#define FINI \ + if(ninst) \ + addInst(dyn->instsize, &dyn->insts_size, dyn->insts[ninst].x64.size, dyn->insts[ninst].size/4); \ + addInst(dyn->instsize, &dyn->insts_size, 0, 0); #define EMIT(A) \ if(box64_dynarec_dump) {dynarec_log(LOG_NONE, "\t%08x\t%s\n", (uint32_t)(A), arm64_print(A, (uintptr_t)dyn->block));} \ *(uint32_t*)(dyn->block) = (uint32_t)(A); \ @@ -10,7 +13,9 @@ #define NEW_INST \ if(ninst && isInstClean(dyn, ninst)) { \ if(dyn->last_ip!=ip) dyn->last_ip = 0; \ - } + } \ + if(ninst) \ + addInst(dyn->instsize, &dyn->insts_size, dyn->insts[ninst-1].x64.size, dyn->insts[ninst-1].size/4); #define INST_EPILOG #define INST_NAME(name) \ if(box64_dynarec_dump) {\ diff --git a/src/dynarec/arm64/dynarec_arm64_private.h b/src/dynarec/arm64/dynarec_arm64_private.h index 6abdf829..1b194e97 100755 --- a/src/dynarec/arm64/dynarec_arm64_private.h +++ b/src/dynarec/arm64/dynarec_arm64_private.h @@ -103,6 +103,7 @@ typedef struct dynarec_arm_s { int* predecessor;// single array of all predecessor dynablock_t* dynablock; instsize_t* instsize; + size_t insts_size; // size of the instruction size array (calculated) uint8_t smread; // for strongmem model emulation uint8_t smwrite; // for strongmem model emulation } dynarec_arm_t; diff --git a/src/dynarec/dynablock.c b/src/dynarec/dynablock.c index 6ff5e53c..59530aed 100755 --- a/src/dynarec/dynablock.c +++ b/src/dynarec/dynablock.c @@ -68,7 +68,7 @@ void MarkDynablock(dynablock_t* db) } } -int IntervalIntersects(uintptr_t start1, uintptr_t end1, uintptr_t start2, uintptr_t end2) +static int IntervalIntersects(uintptr_t start1, uintptr_t end1, uintptr_t start2, uintptr_t end2) { if(start1 > end2 || start2 > end1) return 0; @@ -205,8 +205,19 @@ dynablock_t* DBGetBlock(x64emu_t* emu, uintptr_t addr, int create) dynablock_t *db = internalDBGetBlock(emu, addr, addr, create, 1); if(db && db->done && db->block && db->need_test) { if(AreaInHotPage((uintptr_t)db->x64_addr, (uintptr_t)db->x64_addr + db->x64_size - 1)) { - dynarec_log(LOG_INFO, "Not running block %p from %p:%p with for %p because it's in a hotpage\n", db, db->x64_addr, db->x64_addr+db->x64_size-1, (void*)addr); - return NULL; + if(box64_dynarec_fastpage) { + uint32_t hash = X31_hash_code(db->x64_addr, db->x64_size); + if(hash==db->hash) // seems ok, run it without reprotecting it + return db; + db->done = 0; // invalidating the block, it's already not good + dynarec_log(LOG_DEBUG, "Invalidating block %p from %p:%p (hash:%X/%X) for %p\n", db, db->x64_addr, db->x64_addr+db->x64_size-1, hash, db->hash, (void*)addr); + // Free db, it's now invalid! + FreeDynablock(db, 1); + return NULL; // not building a new one, it's still a hotpage + } else { + dynarec_log(LOG_INFO, "Not running block %p from %p:%p with for %p because it's in a hotpage\n", db, db->x64_addr, db->x64_addr+db->x64_size-1, (void*)addr); + return NULL; + } } uint32_t hash = X31_hash_code(db->x64_addr, db->x64_size); if(mutex_trylock(&my_context->mutex_dyndump)) { diff --git a/src/dynarec/dynarec_native.c b/src/dynarec/dynarec_native.c index f8d29b61..c3f01ff2 100755 --- a/src/dynarec/dynarec_native.c +++ b/src/dynarec/dynarec_native.c @@ -240,7 +240,7 @@ int is_instructions(dynarec_native_t *dyn, uintptr_t addr, int n) return (i==n)?1:0; } -instsize_t* addInst(instsize_t* insts, size_t* size, size_t* cap, int x64_size, int native_size) +void addInst(instsize_t* insts, size_t* size, int x64_size, int native_size) { // x64 instruction is <16 bytes int toadd; @@ -248,10 +248,6 @@ instsize_t* addInst(instsize_t* insts, size_t* size, size_t* cap, int x64_size, toadd = 1 + x64_size/15; else toadd = 1 + native_size/15; - if((*size)+toadd>(*cap)) { - *cap = (*size)+toadd; - insts = (instsize_t*)customRealloc(insts, (*cap)*sizeof(instsize_t)); - } while(toadd) { if(x64_size>15) insts[*size].x64 = 15; @@ -266,7 +262,6 @@ instsize_t* addInst(instsize_t* insts, size_t* size, size_t* cap, int x64_size, ++(*size); --toadd; } - return insts; } // add a value to table64 (if needed) and gives back the imm19 to use in LDR_literal @@ -391,7 +386,6 @@ void CancelBlock64(int need_lock) } customFree(helper->next); customFree(helper->insts); - customFree(helper->instsize); customFree(helper->predecessor); customFree(helper->table64); if(helper->dynablock && helper->dynablock->actual_block) @@ -522,18 +516,7 @@ void* FillBlock64(dynablock_t* block, uintptr_t addr) { // pass 2, instruction size native_pass2(&helper, addr); // keep size of instructions for signal handling - size_t insts_rsize = 0; - { - size_t insts_size = 0; - size_t cap = 1; - for(int i=0; i<helper.size; ++i) - cap += 1 + ((helper.insts[i].x64.size>helper.insts[i].size)?helper.insts[i].x64.size:helper.insts[i].size)/15; - helper.instsize = (instsize_t*)customCalloc(cap, sizeof(instsize_t)); - for(int i=0; i<helper.size; ++i) - helper.instsize = addInst(helper.instsize, &insts_size, &cap, helper.insts[i].x64.size, helper.insts[i].size/4); - helper.instsize = addInst(helper.instsize, &insts_size, &cap, 0, 0); // add a "end of block" mark, just in case - insts_rsize = insts_size*sizeof(instsize_t); - } + size_t insts_rsize = (helper.insts_size+2)*sizeof(instsize_t); insts_rsize = (insts_rsize+7)&~7; // round the size... // ok, now allocate mapped memory, with executable flag on size_t sz = sizeof(void*) + helper.native_size + helper.table64size*sizeof(uint64_t) + 4*sizeof(void*) + insts_rsize; @@ -550,6 +533,8 @@ void* FillBlock64(dynablock_t* block, uintptr_t addr) { helper.block = p; helper.native_start = (uintptr_t)p; helper.tablestart = helper.native_start + helper.native_size; + helper.insts_size = 0; // reset + helper.instsize = (instsize_t*)instsize; *(dynablock_t**)actual_p = block; // pass 3, emit (log emit native opcode) if(box64_dynarec_dump) { @@ -581,14 +566,12 @@ void* FillBlock64(dynablock_t* block, uintptr_t addr) { memcpy((void*)helper.tablestart, helper.table64, helper.table64size*8); } // keep size of instructions for signal handling - memcpy(instsize, helper.instsize, insts_rsize); block->instsize = instsize; // ok, free the helper now customFree(helper.insts); helper.insts = NULL; customFree(helper.table64); helper.table64 = NULL; - customFree(helper.instsize); helper.instsize = NULL; customFree(helper.predecessor); helper.predecessor = NULL; diff --git a/src/include/debug.h b/src/include/debug.h index 024805a1..f4461243 100755 --- a/src/include/debug.h +++ b/src/include/debug.h @@ -23,6 +23,7 @@ extern int box64_dynarec_safeflags; extern int box64_dynarec_callret; extern int box64_dynarec_bleeding_edge; extern int box64_dynarec_hotpage; +extern int box64_dynarec_fastpage; extern int box64_dynarec_wait; #ifdef ARM64 extern int arm64_asimd; diff --git a/src/include/dynarec_native.h b/src/include/dynarec_native.h index 9fe26323..eff5a6bf 100755 --- a/src/include/dynarec_native.h +++ b/src/include/dynarec_native.h @@ -3,6 +3,9 @@ typedef struct dynablock_s dynablock_t; typedef struct x64emu_s x64emu_t; +typedef struct instsize_s instsize_t; + +void addInst(instsize_t* insts, size_t* size, int x64_size, int native_size); void CancelBlock64(int need_lock); void* FillBlock64(dynablock_t* block, uintptr_t addr); diff --git a/src/libtools/signals.c b/src/libtools/signals.c index ffe36ea6..92ab59ef 100755 --- a/src/libtools/signals.c +++ b/src/libtools/signals.c @@ -855,7 +855,7 @@ void my_box64signalhandler(int32_t sig, siginfo_t* info, void * ucntx) } dynablock_t* db = NULL; int db_searched = 0; - if ((sig==SIGSEGV) && (addr) && (info->si_code == SEGV_ACCERR) && (prot&PROT_DYN)) { + if ((sig==SIGSEGV) && (addr) && (info->si_code == SEGV_ACCERR) && (prot&PROT_DYNAREC)) { mutex_lock(&mutex_dynarec_prot); // check if SMC inside block db = FindDynablockFromNativeAddress(pc); @@ -966,6 +966,9 @@ dynarec_log(/*LOG_DEBUG*/LOG_INFO, "Repeated SIGSEGV with Access error on %p for glitch2_prot = 0; } mutex_unlock(&mutex_dynarec_prot); + } else if ((sig==SIGSEGV) && (addr) && (info->si_code == SEGV_ACCERR) && (prot&PROT_DYNAREC_R)) { + // unprotect and continue to signal handler, because Write is not there on purpose + unprotectDB((uintptr_t)addr, 1, 1); // unprotect 1 byte... But then, the whole page will be unprotected } if(!db_searched) db = FindDynablockFromNativeAddress(pc); diff --git a/src/main.c b/src/main.c index ea809e54..e6c29568 100755 --- a/src/main.c +++ b/src/main.c @@ -57,7 +57,8 @@ int box64_dynarec_fastnan = 1; int box64_dynarec_fastround = 1; int box64_dynarec_safeflags = 1; int box64_dynarec_callret = 0; -int box64_dynarec_hotpage = 16; +int box64_dynarec_hotpage = 4; +int box64_dynarec_fastpage = 0; int box64_dynarec_bleeding_edge = 1; int box64_dynarec_wait = 1; uintptr_t box64_nodynarec_start = 0; @@ -564,6 +565,15 @@ void LoadLogEnv() else printf_log(LOG_INFO, "Dynarec will not tag HotPage\n"); } + p = getenv("BOX64_DYNAREC_FASTPAGE"); + if(p) { + if(strlen(p)==1) { + if(p[0]>='0' && p[0]<='1') + box64_dynarec_fastpage = p[0]-'0'; + } + if(box64_dynarec_fastpage) + printf_log(LOG_INFO, "Dynarec will use Fast HotPage\n"); + } p = getenv("BOX64_NODYNAREC"); if(p) { if (strchr(p,'-')) { diff --git a/src/tools/rcfile.c b/src/tools/rcfile.c index 75df7137..04451175 100644 --- a/src/tools/rcfile.c +++ b/src/tools/rcfile.c @@ -113,7 +113,8 @@ ENTRYINT(BOX64_DYNAREC_SAFEFLAGS, box64_dynarec_safeflags, 0, 2, 2) \ ENTRYBOOL(BOX64_DYNAREC_CALLRET, box64_dynarec_callret) \ ENTRYBOOL(BOX64_DYNAREC_BLEEDING_EDGE, box64_dynarec_bleeding_edge) \ ENTRYINT(BOX64_DYNAREC_HOTPAGE, box64_dynarec_hotpage, 0, 255, 8) \ -ENTRYBOOL(box64_dynarec_wait, box64_dynarec_wait) \ +ENTRYBOOL(BOX64_DYNAREC_FASTPAGE, box64_dynarec_fastpage) \ +ENTRYBOOL(BOX64_DYNAREC_WAIT, box64_dynarec_wait) \ ENTRYSTRING_(BOX64_NODYNAREC, box64_nodynarec) \ #else @@ -130,7 +131,8 @@ IGNORE(BOX64_DYNAREC_SAFEFLAGS) \ IGNORE(BOX64_DYNAREC_CALLRET) \ IGNORE(BOX64_DYNAREC_BLEEDING_EDGE) \ IGNORE(BOX64_DYNAREC_HOTPAGE) \ -IGNORE(BOX64_DYNAREC_wait) \ +IGNORE(BOX64_DYNAREC_FASTPAGE) \ +IGNORE(BOX64_DYNAREC_WAIT) \ IGNORE(BOX64_NODYNAREC) \ #endif @@ -355,7 +357,7 @@ void LoadRCFile(const char* filename) if(0) ; SUPER() else if(len && current_name) { - printf_log(LOG_INFO, "Warning, unsupported %s=%s for [%s] in %s", key, val, current_name, filename); + printf_log(LOG_INFO, "Warning, unsupported %s=%s for [%s] in %s\n", key, val, current_name, filename); } #undef ENTRYBOOL #undef CENTRYBOOL diff --git a/src/wrapped/wrappedfaudio_private.h b/src/wrapped/wrappedfaudio_private.h index 3c145ea7..bec2d849 100644 --- a/src/wrapped/wrappedfaudio_private.h +++ b/src/wrapped/wrappedfaudio_private.h @@ -106,13 +106,13 @@ GO(FACTWave_SetMatrixCoefficients, uFpuup) GO(FACTWave_SetPitch, uFpw) GO(FACTWave_SetVolume, uFpf) GO(FACTWave_Stop, uFpu) -//GO(FAPOBase_AddRef, +GO(FAPOBase_AddRef, iFp) //GO(FAPOBase_BeginProcess, //GO(FAPOBase_CalcInputFrames, //GO(FAPOBase_CalcOutputFrames, //GO(FAPOBase_EndProcess, //GO(FAPOBase_GetParameters, -//GO(FAPOBase_GetRegistrationProperties, +GO(FAPOBase_GetRegistrationProperties, uFpp) //GO(FAPOBase_Initialize, //GO(FAPOBase_IsInputFormatSupported, //GO(FAPOBase_IsOutputFormatSupported, @@ -120,7 +120,7 @@ GO(FACTWave_Stop, uFpu) //GO(FAPOBase_OnSetParameters, //GO(FAPOBase_ParametersChanged, //GO(FAPOBase_ProcessThru, -//GO(FAPOBase_Release, +GO(FAPOBase_Release, iFp) //GO(FAPOBase_Reset, //GO(FAPOBase_SetParameters, //GO(FAPOBase_UnlockForProcess, |