2009-02-09 21:15:56 +00:00
/***************************************************************************
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 ;
}
/////////////////////////////////////////////////////////