/*************************************************************************** read.c - description ------------------- begin : Sun Nov 16 2003 copyright : (C) 2003 by Pete Bernert email : BlackDove@addcom.de ***************************************************************************/ /*************************************************************************** * * * 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. See also the license.txt file for * * additional informations. * * * ***************************************************************************/ //*************************************************************************// // History of changes: // // 2003/11/16 - Pete // - generic cleanup for the Peops release // //*************************************************************************// ///////////////////////////////////////////////////////// #include "stdafx.h" #define _IN_READ #include "externals.h" ///////////////////////////////////////////////////////// READTRACKFUNC pReadTrackFunc=NULL; GETPTRFUNC pGetPtrFunc=NULL; int iUseCaching=0; int iTryAsync=0; int iBufSel=0; unsigned char * pMainBuffer=0; unsigned char * pCurrReadBuf=0; unsigned char * pFirstReadBuf=0; unsigned char * pAsyncBuffer=0; unsigned long lMaxAddr=0; unsigned long lLastAddr = 0xFFFFFFFF; unsigned long lLastAsyncAddr = 0xFFFFFFFF; unsigned long lNeededAddr = 0xFFFFFFFF; unsigned long lLastAccessedAddr = 0xFFFFFFFF; int iLastAccessedMode=0; unsigned char * ptrBuffer[2]; unsigned char * pAsyncFirstReadBuf[2]; #define MAXQSIZE 16 #define MAXQFETCH 8 unsigned long lAddrQ[MAXQSIZE]; int iQPos=0; int iQIdle=0; int iQLockPos=-1; ///////////////////////////////////////////////////////// // thread helper vars HANDLE hReadThread = NULL; BOOL bThreadEnded = FALSE; HANDLE hThreadEvent[3]; HANDLE hThreadMutex[2]; ///////////////////////////////////////////////////////// // internal MAXDATACACHE*64KB (4MB) data cache #define MAXDATACACHE 64 unsigned long lDataCacheAddr[MAXDATACACHE]; unsigned char * pDataCacheBuf[MAXDATACACHE]; BOOL bDataCacheHit=FALSE; int iUseDataCache=0; ///////////////////////////////////////////////////////// // main init func void CreateREADBufs(void) { switch(iUseCaching) { case 4: iUseDataCache = 2; // use a special data cache on threadex reading pReadTrackFunc = DoReadThreadEx; pGetPtrFunc = GetREADThreadExPtr; break; case 3: pReadTrackFunc = DoReadThread; pGetPtrFunc = GetREADThreadPtr; break; case 2: pReadTrackFunc = DoReadAsync; pGetPtrFunc = NULL; break; default: pReadTrackFunc = DoRead; pGetPtrFunc = NULL; break; } hThreadEvent[0]=NULL; // clear events/mutex hThreadEvent[1]=NULL; hThreadEvent[2]=NULL; hThreadMutex[0]=NULL; hThreadMutex[1]=NULL; AllocDataCache(); // build data cache, if wanted lLastAddr = 0xFFFFFFFF; lLastAsyncAddr = 0xFFFFFFFF; iBufSel = 0; if(iUseCaching) // some caching? need bigger buffer pMainBuffer=(unsigned char *)malloc(MAXCDBUFFER); else pMainBuffer=(unsigned char *)malloc(CDSECTOR+208+96); pCurrReadBuf=pFirstReadBuf=pMainBuffer+FRAMEBUFEXTRA; if(iUseCaching>=2) // async/thread mode { pAsyncBuffer=(unsigned char *)malloc(MAXCDBUFFER); ptrBuffer[0]=pMainBuffer; ptrBuffer[1]=pAsyncBuffer; pAsyncFirstReadBuf[0]=pFirstReadBuf; pAsyncFirstReadBuf[1]=pAsyncBuffer+FRAMEBUFEXTRA; if(iUseCaching>=3) // thread mode { DWORD dw; bThreadEnded = FALSE; for(dw=0;dw<3;dw++) // -> create events { hThreadEvent[dw]=CreateEvent(NULL,TRUE,FALSE,NULL); ResetEvent(hThreadEvent[dw]); } for(dw=0;dw<2;dw++) // -> create mutex { hThreadMutex[dw]=CreateMutex(NULL,FALSE,NULL); } if(iUseCaching==3) // -> create thread hReadThread=CreateThread(NULL,0,READThread,0,0,&dw); else { for(dw=0;dw signal: end thread while(!bThreadEnded) {Sleep(5L);} // -> wait til ended WaitForSingleObject(hThreadMutex[1],INFINITE); ReleaseMutex(hThreadMutex[1]); hReadThread=NULL; // -> clear handle } if(hThreadEvent[0]) CloseHandle(hThreadEvent[0]); // kill events/mutex if(hThreadEvent[1]) CloseHandle(hThreadEvent[1]); if(hThreadEvent[2]) CloseHandle(hThreadEvent[2]); if(hThreadMutex[0]) CloseHandle(hThreadMutex[0]); if(hThreadMutex[1]) CloseHandle(hThreadMutex[1]); if(pMainBuffer) free(pMainBuffer); // free main data buf pMainBuffer=NULL; if(pAsyncBuffer) free(pAsyncBuffer); // free async data buf pAsyncBuffer=NULL; FreeDataCache(); } ///////////////////////////////////////////////////////// // retry on readng error (blocking) BOOL bReadRetry(FRAMEBUF * f) { int iRetry=0; while (iRetry tell it to user { char szB[64]; wsprintf(szB,"Read error on address %08lx!",f->dwFrame); MessageBox(NULL,szB,libraryName,MB_OK); } return FALSE; // -> tell emu: bad } return TRUE; } //////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// // sync modes (caching 0 and 1) // just reads one or more blocks, and always waits until // reading is done ///////////////////////////////////////////////////////// BOOL DoRead(unsigned long addr) { FRAMEBUF * f; //////////////////////////////////////////////////////// if(iUseCaching && // cache block available? lLastAddr!=0xFFFFFFFF && addr>=lLastAddr && // and addr in block? addr<=(lLastAddr+MAXCACHEBLOCK)) { pCurrReadBuf=pFirstReadBuf+ // -> calc data ptr ((addr-lLastAddr)*iUsedBlockSize); if(ppfHead) CheckPPFCache(addr,pCurrReadBuf); // -> apply ppf return TRUE; // -> done } //////////////////////////////////////////////////////// if(iUseDataCache && CheckDataCache(addr)) // cache used and data is in cache? set read ptr, if yes return TRUE; // -> also fine //////////////////////////////////////////////////////// f=(FRAMEBUF *)pMainBuffer; // setup read for one sector f->dwFrameCnt = 1; f->dwBufLen = iUsedBlockSize; f->dwFrame = addr; pCurrReadBuf=pFirstReadBuf; //////////////////////////////////////////////////////// if(iUseCaching) // cache block? { if((addr+MAXCACHEBLOCK)dwFrameCnt = MAXCACHEBLOCK+1; // -> set bigger read f->dwBufLen = (MAXCACHEBLOCK+1)*iUsedBlockSize; lLastAddr = addr; // -> store addr of block } else { lLastAddr=0xFFFFFFFF; // no caching, no block addr } } //////////////////////////////////////////////////////// if(pReadFunc(TRUE,f)!=SS_COMP) // do a waiting read { if(!bReadRetry(f)) return FALSE; // and retry on error } if(iUseDataCache && lLastAddr!=0xFFFFFFFF) // data cache used? and whole 64 k read block? AddToDataCache(addr,pFirstReadBuf); // -> add the complete data to cache if(ppfHead) CheckPPFCache(addr,pCurrReadBuf); // apply ppf return TRUE; } ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// // async mode (caching 2) // this mode works fine with ASPI... // the first read will be done sync, though, only the // additional pre-fetching will be done async... // well, with mdecs most reads will be prefetched, so // speed is good... with IOCTL this mode is more like // a 'double sync' reading, since IOCTL seems always // to be blocking (see also notes for caching mode 3) ///////////////////////////////////////////////////////// BOOL DoReadAsync(unsigned long addr) { FRAMEBUF * f; //////////////////////////////////////////////////////// // 1. check if data is in already filled buffer if(lLastAddr!=0xFFFFFFFF && addr>=lLastAddr && addr<=(lLastAddr+MAXCACHEBLOCK)) { pCurrReadBuf=pAsyncFirstReadBuf[iBufSel]+ ((addr-lLastAddr)*iUsedBlockSize); if(ppfHead) CheckPPFCache(addr,pCurrReadBuf); iTryAsync=0; return TRUE; } //////////////////////////////////////////////////////// // check data cache if(iUseDataCache && CheckDataCache(addr)) // cache used and data is in cache? set read ptr, if yes return TRUE; // -> also fine //////////////////////////////////////////////////////// // 2. not in main buffer? wait for async to be finished if(bDoWaiting) { WaitGenEvent(0xFFFFFFFF);bDoWaiting=FALSE; if(sx.SRB_Status!=SS_COMP) lLastAsyncAddr=0xFFFFFFFF; } //////////////////////////////////////////////////////// // 3. check in asyncbuffer. if yes, swap buffers and do next async read if(lLastAsyncAddr!=0xFFFFFFFF && addr>=lLastAsyncAddr && addr<=(lLastAsyncAddr+MAXCACHEBLOCK)) { int iAsyncSel=iBufSel; // store old buf num if(iBufSel==0) iBufSel=1; else iBufSel=0; // toggle to new num lLastAddr=lLastAsyncAddr; // set adr of block pCurrReadBuf=pAsyncFirstReadBuf[iBufSel]+ // set data ptr ((addr-lLastAddr)*iUsedBlockSize); if(iUseDataCache) // data cache used? AddToDataCache(lLastAddr,pAsyncFirstReadBuf[iBufSel]); // -> add the complete 64k data to cache if(ppfHead) CheckPPFCache(addr,pCurrReadBuf); // apply ppf iTryAsync=0; // data was async, reset count addr=lLastAddr+MAXCACHEBLOCK+1; // calc adr of next prefetch if(!((addr+MAXCACHEBLOCK)dwFrameCnt = MAXCACHEBLOCK+1; f->dwBufLen = (MAXCACHEBLOCK+1)*iUsedBlockSize; f->dwFrame = addr; lLastAsyncAddr=addr; // store prefetch addr if(pReadFunc(FALSE,f)!=SS_COMP) // start the async read lLastAsyncAddr=0xFFFFFFFF; // -> if no success, no async prefetch buf available return TRUE; } //////////////////////////////////////////////////////// // here we do a sync read iBufSel=0; // read in buf 0 f=(FRAMEBUF *)ptrBuffer[0]; f->dwFrame = addr; pCurrReadBuf=pFirstReadBuf; //////////////////////////////////////////////////////// // if it's possible, we do a bigger read if((addr+MAXCACHEBLOCK)dwFrameCnt = MAXCACHEBLOCK+1; f->dwBufLen = (MAXCACHEBLOCK+1)*iUsedBlockSize; lLastAddr = addr; } else { f->dwFrameCnt = 1; f->dwBufLen = iUsedBlockSize; lLastAddr = 0xFFFFFFFF; } //////////////////////////////////////////////////////// // start read, wait til finished if(pReadFunc(TRUE,f)!=SS_COMP) { if(!bReadRetry(f)) return FALSE; } if(iUseDataCache && lLastAddr!=0xFFFFFFFF) // data cache used? and complete 64k block? AddToDataCache(addr,pAsyncFirstReadBuf[0]); // -> add the complete data to cache if(ppfHead) CheckPPFCache(addr,pCurrReadBuf); //////////////////////////////////////////////////////// // start additional async prefetch read, if it's ok iTryAsync++; if(iTryAsync>1) {iTryAsync=2;return TRUE;} // prefetches seems to be useless right now, so turn them off until next real read addr+=MAXCACHEBLOCK+1; // prefetch addr if(!((addr+MAXCACHEBLOCK)dwFrameCnt = MAXCACHEBLOCK+1; f->dwBufLen = (MAXCACHEBLOCK+1)*iUsedBlockSize; f->dwFrame = addr; lLastAsyncAddr= addr; if(pReadFunc(FALSE,f)!=SS_COMP) // start the async prefetch lLastAsyncAddr=0xFFFFFFFF; return TRUE; } ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// // thread mode (caching 3) // this mode helps with slower drives using the IOCTL // interface (since that one seems to do always blocking // reads, even when they are done overlapped). // With ASPI, the thread mode performance will be more or less // the same as with async caching mode 2... // thread reading would be much more powerful, if the main // emu would do: // ... // CDRreadTrack() // ... do some other stuff here ... // CDRgetBuffer() // ... // but lazy main emu coders seem to prefer: // ... // CDRreadTrack() // CDRgetBuffer() // ... // so there is no time between the calls to do a good // asynchronous read... sad, sad... ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// // reading thread... sleeps until a new read is signaled DWORD WINAPI READThread(LPVOID lpParameter) { FRAMEBUF * f; while(WaitForMultipleObjects(2,hThreadEvent,FALSE, // wait until event to start (or event to end) get raised INFINITE)==WAIT_OBJECT_0) { WaitForSingleObject(hThreadMutex[0],INFINITE); // read mutex: nobody else is allowed to read now WaitForSingleObject(hThreadMutex[1],INFINITE); // variable access mutex: nobody else can change the vars now ResetEvent(hThreadEvent[0]); // ok, kick event has been handled SetEvent(hThreadEvent[2]); // set flag: we have started the read lLastAsyncAddr = lNeededAddr; // setup read and vars f=(FRAMEBUF *)ptrBuffer[!iBufSel]; // !iSel = async buffer f->dwFrame = lNeededAddr; f->dwFrameCnt = min((lMaxAddr-lNeededAddr+1),(MAXCACHEBLOCK+1)); f->dwBufLen = f->dwFrameCnt*iUsedBlockSize; ReleaseMutex(hThreadMutex[1]); // ok, vars have been changed, now that vars are again available for all if(pReadFunc(TRUE,f)!=SS_COMP) // do a blocking (sync) read { bReadRetry(f); // mmm... if reading fails a number of times, we don't have a chance to return 'bad' to emu with tread reading... life is hard :) } ReleaseMutex(hThreadMutex[0]); // ok, read has done } bThreadEnded=1; return 0; } ///////////////////////////////////////////////////////// // emu says: we need data at given addr soon... // so, if we don't have it in any buffer, we kick a read // ... called on CDRreadTrack() BOOL DoReadThread(unsigned long addr) { if(!hReadThread) return FALSE; // no thread, no fun bDataCacheHit=FALSE; // init data cache hit flag (even if no cache is used...) if(lLastAddr!=0xFFFFFFFF && // data is in curr data buffer? addr>=lLastAddr && addr<=(lLastAddr+MAXCACHEBLOCK)) return TRUE; // -> fine if(iUseDataCache && CheckDataCache(addr)) // data cache used and data is in cache? set read ptr, if yes {bDataCacheHit=TRUE;return TRUE;} // -> and raise 'hit' flag, so we don't need to do anything in 'getbuffer' WaitForSingleObject(hThreadMutex[1],INFINITE); // wait to access 'buffer 1 vars' if(lLastAsyncAddr!=0xFFFFFFFF && // data is (or will be soon if reading is going on in thread now) in async buffer? addr>=lLastAsyncAddr && addr<=(lLastAsyncAddr+MAXCACHEBLOCK)) { ReleaseMutex(hThreadMutex[1]); // -> fine return TRUE; } // data is not in buf0 and not in buf1: lNeededAddr=addr; // set needed adr (mutex is active, so it's safe to change that) ResetEvent(hThreadEvent[2]); // reset "read has started" flag SetEvent(hThreadEvent[0]); // set "start read" flag... the read will start reading soon after ReleaseMutex(hThreadMutex[1]); // done with var access return TRUE; } ///////////////////////////////////////////////////////// // emu says: gimme ptr to needed data... this will // automatically do an async data prefetch read as well // ... called on CDRgetBuffer() void GetREADThreadPtr(void) { unsigned long addr=lLastAccessedAddr; if(bDataCacheHit) return; // if we had a data cache hit, the readbuf ptr is already fine, nothing else to do if(lLastAddr!=0xFFFFFFFF && // data is in buffer 0? addr>=lLastAddr && addr<=(lLastAddr+MAXCACHEBLOCK)) { pCurrReadBuf=pAsyncFirstReadBuf[iBufSel]+ // -> ok, return curr data buffer ptr ((addr-lLastAddr)*iUsedBlockSize); if(ppfHead) CheckPPFCache(addr,pCurrReadBuf); return; } WaitForSingleObject(hThreadEvent[2],INFINITE); // wait until reading has started (it will take a small time after the read start kick, so we have to go sure that it has _really_ started) WaitForSingleObject(hThreadMutex[0],INFINITE); // wait until reading has finished lLastAddr=lLastAsyncAddr; // move data to from async data to curr data buffer (by toggling iSel) iBufSel=!iBufSel; lLastAsyncAddr=0xFFFFFFFF; // nothing in async data buffer now lNeededAddr=addr+MAXCACHEBLOCK+1; // prefetch read addr ResetEvent(hThreadEvent[2]); // reset "read has started" flag SetEvent(hThreadEvent[0]); // signal for start next read ReleaseMutex(hThreadMutex[0]); // ok, now reading in buffer 1 can start if(iUseDataCache) // data cache used? can be less then 64 kb with thread reading, but that doesn't matter here... will be either 64 k or (max-addr) sectors AddToDataCache(lLastAddr, pAsyncFirstReadBuf[iBufSel]); // -> add the complete data to cache pCurrReadBuf=pAsyncFirstReadBuf[iBufSel]+ // -> return the curr data buffer ptr ((addr-lLastAddr)*iUsedBlockSize); if(ppfHead) CheckPPFCache(addr,pCurrReadBuf); } ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// // special thread mode (caching 4) // this mode helps with certain drives // basically it does the following: // It has a queue for n prefetch reads. If the main emu is // asking for an addr, the mode will check, if // this addr is a) getting read right now, b) already // in our 4 MB cache, c) already in the q. // If no condition matches, it will add it in q... // the same is done with the next n/2 addr blocks, so // the q will keep the drive busy... also, if everything // is cached (and the q is empty), we will add additional // addresses to read, also to keep the drive busy, and to // do the needed reading as soon as possible :) ///////////////////////////////////////////////////////// // reading thread... DWORD WINAPI READThreadEx(LPVOID lpParameter) { FRAMEBUF * f; while(WaitForMultipleObjects(2,hThreadEvent,FALSE, // wait until event to start (or event to end) get raised INFINITE)==WAIT_OBJECT_0) { while(1) { //------------------------------------------------// WaitForSingleObject(hThreadMutex[1],INFINITE); // variable access mutex: nobody else can change the vars now ResetEvent(hThreadEvent[0]); // ok, kick event has been handled if(lAddrQ[iQPos]==0xFFFFFFFF) // nothing to do? strange :) {ReleaseMutex(hThreadMutex[1]);break;} f=(FRAMEBUF *)ptrBuffer[0]; lNeededAddr = lAddrQ[iQPos]; // store it in 'Neededaddr' for checks outside the thread f->dwFrame = lNeededAddr; f->dwFrameCnt = min((lMaxAddr-f->dwFrame+1),(MAXCACHEBLOCK+1)); f->dwBufLen = f->dwFrameCnt*iUsedBlockSize; lAddrQ[iQPos++]=0xFFFFFFFF; // set this slot as 'done' if(iQPos>=MAXQSIZE) iQPos=0; // amnd inc the head pos ReleaseMutex(hThreadMutex[1]); // ok, vars have been changed, now that vars are again available for all //------------------------------------------------// WaitForSingleObject(hThreadMutex[0],INFINITE); // read mutex: nobody else is allowed to read now if(!iCDROK) { ReleaseMutex(hThreadMutex[0]); break; } if(bCDDAPlay) // some cdda security... { // it should just prevent prefetch reads happening in cdda mode, if this one breaks a 'needed' read, we are lost... lNeededAddr=0xFFFFFFFF; // so maybe we should remove this check? mmm, we will see ReleaseMutex(hThreadMutex[0]); break; } if(pReadFunc(TRUE,f)!=SS_COMP) // do a blocking (sync) read { // mmm... if reading fails a number of times, we don't have a chance to return 'bad' to emu with thread reading... life is hard :) bReadRetry(f); // but at least our 'wait for data in cache' getptr will not wait forever (just returning wrong data, ehehe) } ReleaseMutex(hThreadMutex[0]); // ok, read has done //------------------------------------------------// WaitForSingleObject(hThreadMutex[1],INFINITE); // variable access mutex: nobody else can change the vars now lNeededAddr=0xFFFFFFFF; // no read is now active AddToDataCache(f->dwFrame,pFirstReadBuf); // add the complete data to cache ReleaseMutex(hThreadMutex[1]); // ok, vars have been changed, now that vars are again available for all //------------------------------------------------// if(WaitForSingleObject(hThreadEvent[0],0)!=WAIT_OBJECT_0) Sleep(1); // if nobody has started a new kick, let's sleep awhile to give Windows more room to breath } } bThreadEnded=1; return 0; } ///////////////////////////////////////////////////////// // emu says: we need data at given addr soon... // so, if we don't have it in any buffer, we kick a read // ... called on CDRreadTrack() //#define THREADEX_STRAIGHT BOOL DoReadThreadEx(unsigned long addr) { int i,k,j=0; if(!hReadThread) return FALSE; // no thread, no fun WaitForSingleObject(hThreadMutex[1],INFINITE); // wait for data access //-----------------------------------------------------// // straight reading try... should have been faster, but // in 'real life' this approach keeps the cdrom drive // spinning too much, giving other pc resources no room // to breath... by increasing the thread 'Sleep' value // the performance can get better, but then the annoying // breaks we wanted to fight will show up again... // so this type is disabled as long as nobody enables the // define again :) #ifdef THREADEX_STRAIGHT if(addr>=lNeededAddr && addr<=(lNeededAddr+MAXCACHEBLOCK)) { ReleaseMutex(hThreadMutex[1]); return TRUE; } for(k=0;k=lDataCacheAddr[i] && // -> addr found? addr<=(lDataCacheAddr[i]+MAXCACHEBLOCK)) { if(k==0) iQLockPos=i; // -> if it's the current main addr, lock it, so no prefetch read overwrites its content break; } } if(i!=MAXDATACACHE) // found in data cache? {addr=addr+MAXCACHEBLOCK+1;continue;} // -> do nothing with this addr, we have its content else break; // here is the first unknown addr } if(addr>=lMaxAddr) // check, if addr too big { ReleaseMutex(hThreadMutex[1]); if(k==0) return FALSE; // -> if it's the main addr, there is an error return TRUE; // -> otherwise we can't simply cache that addr } for(i=0;i=lAddrQ[i] && // -> addr will be read soon? addr<=(lAddrQ[i]+MAXCACHEBLOCK)) { addr=lAddrQ[i]; // --> take this aligned addr for new header break; } } for(i=0;i=lMaxAddr) break; } for(;i=lNeededAddr && // addr is getting read right now? addr<=(lNeededAddr+MAXCACHEBLOCK)) // -> ok, we do nothing with it {addr=addr+MAXCACHEBLOCK+1;continue;} for(i=0;i=lDataCacheAddr[i] && // -> addr found? addr<=(lDataCacheAddr[i]+MAXCACHEBLOCK)) { if(k==0) iQLockPos=i; // -> if it's the current main addr, lock it, so no other prefetch read overwrites its content break; } } if(i!=MAXDATACACHE) // found in data cache? {addr=addr+MAXCACHEBLOCK+1;continue;} // -> do nothing with this addr, we have its content for(i=0;i=lAddrQ[i] && // -> addr will be read soon? addr<=(lAddrQ[i]+MAXCACHEBLOCK)) { if(k==0 && i!=iQPos) // curr needed addr is not on top of the q? { addr=lAddrQ[i]; // -> get the addr (our main addr is in it, but that one is more aligned to prev reads) for(i=0;i clear whole q (we will fill the slots in that loop again) i=MAXQSIZE; // -> sign for storing the addr in q } break; } } if(i!=MAXQSIZE) // found in q? {addr=addr+MAXCACHEBLOCK+1;continue;} // -> do nothing with this addr, we will have its content soon // not in q or data cache? if(k==0) lAddrQ[iQPos]=addr; // -> if it's the main addr, store it on top of list else // -> if it's a prefetch addr, try to store it elsewhere at the end of the q { j=iQPos; for(i=0;i=MAXQSIZE) j=0; } } SetEvent(hThreadEvent[0]); // kick a read, if neccessary addr=addr+MAXCACHEBLOCK+1; // next prefetch addr if(addr>=lMaxAddr) break; // security, for detecting if we are at the end of cd } //----------------------------------------------------// ok, and here's something to keep the drive busy... if(lAddrQ[iQPos]==0xFFFFFFFF && addr10) // more then x times? { iQIdle=0; lAddrQ[iQPos]=addr; // we add the farest prefetch addr SetEvent(hThreadEvent[0]); // and do an additional kick } } else iQIdle=0; // not idling? ok //----------------------------------------------------// #endif ReleaseMutex(hThreadMutex[1]); return TRUE; } ///////////////////////////////////////////////////////// // emu says: gimme ptr to needed data... this will // automatically do an async data prefetch read as well // ... called on CDRgetBuffer() void GetREADThreadExPtr(void) { unsigned long addr=lLastAccessedAddr; while(1) { if(bThreadEnded) return; // main emu is already closing (thread is down)? bye WaitForSingleObject(hThreadMutex[1],INFINITE); // wait for data access if(CheckDataCache(addr)) // data now in cache? { ReleaseMutex(hThreadMutex[1]); // -> ok, done return; } ReleaseMutex(hThreadMutex[1]); // else try again (no sleep here, we are blocking everything anyway) } } ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// // simple data cache void AllocDataCache(void) { bDataCacheHit=FALSE; // init thread cache hit flag if(!iUseCaching) iUseDataCache=0; // security: no additinal data cache, if no caching active if(iUseDataCache) { int i; for(i=0;i=MAXDATACACHE) iPos=0;} // -> don't use that pos, use next one lDataCacheAddr[iPos]=addr; memcpy(pDataCacheBuf[iPos],pB, MAXCDBUFFER-FRAMEBUFEXTRA); iPos++; if(iPos>=MAXDATACACHE) iPos=0; } ///////////////////////////////////////////////////////// // easy data cache check: loop MAXDATACACHE blocks, set ptr if addr found BOOL CheckDataCache(unsigned long addr) { int i; for(i=0;i=lDataCacheAddr[i] && addr<=(lDataCacheAddr[i]+MAXCACHEBLOCK)) { pCurrReadBuf=pDataCacheBuf[i]+ ((addr-lDataCacheAddr[i])*iUsedBlockSize); if(ppfHead) CheckPPFCache(addr,pCurrReadBuf); return TRUE; } } return FALSE; } /////////////////////////////////////////////////////////