pcsx2/plugins/CDVDpeops/read.c
2010-04-25 00:31:27 +00:00

911 lines
33 KiB
C

/***************************************************************************
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<MAXQSIZE;dw++) lAddrQ[dw]=0xFFFFFFFF;
iQPos=0;
iQLockPos=-1;
hReadThread=CreateThread(NULL,0,READThreadEx,0,0,&dw);
}
}
}
else
pAsyncBuffer=0;
}
/////////////////////////////////////////////////////////
void FreeREADBufs(void)
{
if(hReadThread) // thread?
{
SetEvent(hThreadEvent[1]); // -> 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<iMaxRetry)
{
if(pReadFunc(TRUE,f)==SS_COMP) break; // try sync read
iRetry++;
}
if(iRetry==iMaxRetry) // no success?
{
if(iShowReadErr) // -> 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)<lMaxAddr) // and big read is possible?
{
f->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)<lMaxAddr)) // mmm, no whole block can be done... so we do no prefetch at all
{lLastAsyncAddr=0xFFFFFFFF;return TRUE;}
f=(FRAMEBUF *)ptrBuffer[iAsyncSel]; // setup prefetch addr
f->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)<lMaxAddr)
{
f->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)<lMaxAddr)) // not possible? do't do prefetch
{lLastAsyncAddr=0xFFFFFFFF;return TRUE;}
f=(FRAMEBUF *)ptrBuffer[1]; // setup prefetch into buf 1
f->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<MAXQSIZE;k++) // loop max of prefetch blocks
{
for(i=0;i<MAXDATACACHE;i++) // loop whole cache
{
if(addr>=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<MAXQSIZE;i++) // loop q list
{
if(addr>=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<MAXQSIZE;i++) // loop q list
{
lAddrQ[i]=addr;
addr=addr+MAXCACHEBLOCK+1;
if(addr>=lMaxAddr) break;
}
for(;i<MAXQSIZE;i++) // loop q list
{
lAddrQ[i]=0xFFFFFFFF;
}
iQPos=0;
SetEvent(hThreadEvent[0]); // kick a read, if neccessary
#else
//-----------------------------------------------------//
// ok, here is the current ReadThreadEx mode: more
// complex, and it doesn't arrange the prefetch sectors
// as straight as the type above, but the final result is
// still smoother (no more pauses on the tested LG drive)
for(k=0;k<MAXQFETCH;k++) // loop max of prefetch blocks
{
if(addr>=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<MAXDATACACHE;i++) // loop whole cache
{
if(addr>=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<MAXQSIZE;i++) // loop prefetch q list
{
if(addr>=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<MAXQSIZE;i++) lAddrQ[i]=0xFFFFFFFF; // -> 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;i++)
{
if(lAddrQ[j]==0xFFFFFFFF) {lAddrQ[j]=addr;break;}
j++;if(j>=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 && addr<lMaxAddr) // nothing in prefetch q?
{
iQIdle++; // count how many empty q's in-a-row are happening
if(iQIdle>10) // 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;i++) // init all cache slots
{
lDataCacheAddr[i] = 0xFFFFFFFF;
pDataCacheBuf[i] = malloc(MAXCDBUFFER-FRAMEBUFEXTRA);
}
}
}
/////////////////////////////////////////////////////////
void FreeDataCache(void)
{
if(iUseDataCache)
{
int i;
for(i=0;i<MAXDATACACHE;i++)
{
free(pDataCacheBuf[i]);
pDataCacheBuf[i]=NULL;
}
}
}
/////////////////////////////////////////////////////////
// easy data cache: stores data blocks
void AddToDataCache(unsigned long addr,unsigned char * pB)
{
static int iPos=0;
if(iPos==iQLockPos) // special thread mode lock?
{iPos++;if(iPos>=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<MAXDATACACHE;i++)
{
if(addr>=lDataCacheAddr[i] &&
addr<=(lDataCacheAddr[i]+MAXCACHEBLOCK))
{
pCurrReadBuf=pDataCacheBuf[i]+
((addr-lDataCacheAddr[i])*iUsedBlockSize);
if(ppfHead) CheckPPFCache(addr,pCurrReadBuf);
return TRUE;
}
}
return FALSE;
}
/////////////////////////////////////////////////////////