/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**********************************************************************/
/*                                                                    */
/*   Module          = EDDNCACH                                       */
/*                                                                    */
/*   Description     = Display Device Driver minor function handlers  */
/*                     Routines for handling XWAY text caching        */
/*                                                                    */
/*   Function        =                                                */
/*                                                                    */
/*   Reference       = Winthorn Functional Specification              */
/*                     Device Driver Interface Specification          */
/*                     Display Device Driver Design Specification     */
/*                                                                    */
/*                                                                    */
/**********************************************************************/
#define INCL_DOSDEVICES
#define INCL_DDIFONTSTRUCS
#define INCL_DDIMISC
#define INCL_ERRORS
#define INCL_GRE_FONTS
#include <eddinclt.h>

#include <edddtypt.h>
#include <eddttypt.h>
#include <eddtcone.h>
#include <eddtextf.h>

#include <eddhcone.h>
#include <eddhtype.h>
#include <eddhmacr.h>

#include <hwaccess.h>
#include <memman.h>
#ifdef VRAMPTR
#include <eddncach.h>
#endif /* VRAMPTR */

extern SHORT            foregroundSession;

extern USHORT           ResidentFonts;
extern DDTType          DDT;
extern BYTE             SPad[];

extern SHORT            softDrawInUse;
extern VOID             eddf_MESS(VOID);
extern ULONG            Cache8514Char(ULONG,ULONG,ULONG,PVOID,PULONG);  // ekf

#ifndef   _8514
extern MMReg            ShadowXGARegs;
extern FLATLOCKDATA     flatlockdata;
extern HFILE            ring0_handle;
#else
#include <8514.h>
#include <cacheman.h>
extern MM8514Reg        Shadow8514Regs;
extern ULONG            Screen8514VisWidth;
#endif

#ifdef SEAMLESS
extern ULONG            fSeamlessActive;
extern ULONG            fSeamlessCCConflict;
#endif /* SEAMLESS */

/**********************************************************************/
/* At 2.0 there are two identical copies of the cache - one in        */
/* system memory (locked contiguous) and one in VRAM.  All character  */
/* addresses stored in fontinfo are OFFSETS from the base address     */
/* of the cache so that they can be used with either cache.  To keep  */
/* track of the cache we have the following pointers..                */
/*                                                                    */
/* ULONG pSysCacheStartPhy -                                          */
/*   Physical address of start of cache in system memory              */
/*                                                                    */
/* ULONG pSysCacheStart -                                             */
/*   Logical (80386) address of start of cache in system memory       */
/*   *** at 1.3 this is a PBYTE ***                                   */
/*                                                                    */
/* ULONG charCacheLength -                                            */
/*   The size of both caches                                          */
/*                                                                    */
/* ULONG pVRAMCacheStart -                                            */
/*   Physical address of start of cache in VRAM.                      */
/*                                                                    */
/* ULONG offNextFree11Pos -                                           */
/*   Offset from start of cache to next free position for 1,1 data    */
/*                                                                    */
/* ULONG offLastFree81Pos -                                           */
/*   Offset from start of cache to last position used for 8,1 data    */
/*                                                                    */
/* ULONG pCurCacheBasePhy -                                           */
/*   This is the address to add offsets to to get the physical        */
/*   addresses to the character cache that the hardware is to use.    */
/*   This will be either pVRAMCacheStart or pSysCacheStartPhy.  If    */
/*   there are any seamless         VDM device drivers running then   */
/*   this must be pSysCacheStartPhy because the VRAM normally used    */
/*   for our character cache will be used by the         driver.      */
/*                                                                    */
/* The cache is arranged with the 1,1 data growing from the base      */
/* upwards and the 8,1 data growing from the end downwards - hence    */
/* NextFree11Pos and LastFree81Pos.                                   */
/*                                                                    */
/* To cache a character the character is copied into the cache in     */
/* system memory (converting formats as necessary on the way) and     */
/* then blt'ed from here to the VRAM cache.                           */
/*                                                                    */
/* At Resurrection time the system memory cache is blt'ed to the VRAM */
/* in two operations - one for the 1,1 data and one for the 8,1 data  */
/*                                                                    */
/* The 1,1 data and 8,1 data must be done seperately because the 1,1  */
/* needs to be transformed from moto to intel and the 8,1 must not.   */
/**********************************************************************/
ULONG   pSysCacheStartPhy;
PBYTE   pSysCacheStart;
ULONG   charCacheLength;

ULONG   pVRAMCacheStart;
ULONG   offNextFree11Pos;
ULONG   offLastFree81Pos;
ULONG   pCurCacheBasePhy;

PFONTCACHEINFO          pFontCacheInfo;
PVOID                   pDefaultFont;
PVOID                   pResourceFont;
USHORT                  usEvictionIndex = 0;

#ifdef _8514
ULONG   p8514FontCache[ 16 ];   // Best case, 16 fonts will be cached.

/**********************************************************************/
/* Defect 75206. A global to keep track of whether or not we've       */
/* to plane + 8.                                                      */
/**********************************************************************/
ULONG   aulFontPlaneOffset[ 16 ];   // Best case, 16 fonts will be cached.

extern PCACHEMAP        pCacheMap;
extern PHWMAP           pHWMap;
#endif

#ifdef VRAMPTR
extern ULONG    FreeVRAM;
extern PVOID    pPhunkVirt;
PBMCACHE        bm_cache;
ULONG           max_cached_bitmaps;
ULONG           next_eviction;
#endif /* VRAMPTR */

/**********************************************************************/
/* Initialise the current Font ID to zero.                            */
/**********************************************************************/
static SHORT    CurrentFontID = 0;

#ifdef EDD_DEBUG
/**********************************************************************/
/* Debug variables                                                    */
/**********************************************************************/
SHORT    DebugCacheOn = 0;
SHORT    DebugCharOn = 0;
SHORT    CacheIndex = 0;

#ifdef DEBUG_CACHE
/**********************************************************************/
/* Declare functions.                                                 */
/**********************************************************************/
VOID    DebugCache(VOID);
#endif DEBUG_CACHE
#endif  /* EDD_DEBUG */

/**********************************************************************/
/* CreateFontCache                                                    */
/* Creates and initialises the FontCacheInfo structure.               */
/* and creates a locked character cache as well.                      */
/**********************************************************************/
BOOL CreateFontCache(VOID)

{
    /******************************************************************/
    /* Local variables                                                */
    /******************************************************************/
    ULONG       i;

    DebugOutput("Creating font cache...\n\r");
    /******************************************************************/
    /* Allocate memory for the FontCacheInfo                          */
    /******************************************************************/
    pFontCacheInfo =
#ifdef XGA
        AllocateMemory( MAX_NUM_CACHED_FONTS * sizeof(FONTCACHEINFO),
#else
        AllocateMemory( pHWMap->num_font_planes * sizeof(FONTCACHEINFO),
#endif
                        MT_FONTCACHEINFO,
                        MO_SHARED );

    if (pFontCacheInfo == NULL)
    {
        /**************************************************************/
        /* The allocation failed.                                     */
        /**************************************************************/
        return(FALSE);
    }

    /******************************************************************/
    /* Initialise cache entries to all free                           */
    /******************************************************************/
#ifdef XGA
    for (i = 0; i < MAX_NUM_CACHED_FONTS; i++)
#else
    for (i = 0; i < pHWMap->num_font_planes; i++)
#endif
    {
        pFontCacheInfo[i].usUsageCount = (USHORT) -1;
    }

    /******************************************************************/
    /* Allocate some memory for a text cache in system memory         */
    /******************************************************************/
    pSysCacheStart = AllocateMemory(SYS_CHAR_CACHE_SIZE,
                                    MT_CHAR_CACHE,
                                    MO_SSALLOC);

    /******************************************************************/
    /* Lock down the character cache.                                 */
    /******************************************************************/
#ifndef   _8514
    flatlockdata.VirtualAddress = pSysCacheStart;
    flatlockdata.cMemorySize    = SYS_CHAR_CACHE_SIZE;
    flatlockdata.ActionFlags    = 0x1e; /* block till ready           */
                                        /* contiguous memory          */
                                        /* below 16Mb                 */
                                        /* mark as dirty              */
                                        /* long term lock             */
    FlatLock(&flatlockdata);
    pSysCacheStartPhy = flatlockdata.PhysicalAddress;
#else
    // @DMS 8514 cannot directly access system memory so we wil not lock it
    // put the real cache start here RCW <- DMS !!!
    pSysCacheStartPhy = NULL;
#endif


    /******************************************************************/
    /* The length just now must be 64K-2 for the spare start and end  */
    /* bytes                                                          */
    /******************************************************************/
    charCacheLength = SYS_CHAR_CACHE_SIZE-2;

    /******************************************************************/
    /* Initialise these to good values                                */
    /* The 1 in NextFree11Pos does two things..                       */
    /* 1. Means this will not take the reserved 0 value (0 means      */
    /*    not cached)                                                 */
    /* 2. Gives the MESS a spare byte at the beginning which it needs */
    /******************************************************************/
    offNextFree11Pos = 1;
    offLastFree81Pos = charCacheLength + 1;

    /******************************************************************/
    /* Lastly make sure that we will use the correct copy of the      */
    /* cache when using the HW.                                       */
    /******************************************************************/
#ifdef FIREWALLS
    if (pVRAMCacheStart == 0)
    {
        /**************************************************************/
        /* InitialiseVRAM has not been called yet!                    */
        /**************************************************************/
        haltproc();
    }
#endif
    pCurCacheBasePhy = pVRAMCacheStart;

    return(TRUE);

} /* CreateFontCache */




/**********************************************************************/
/* LocateCachedFont:                                                  */
/* Find the given font in the FontCacheInfo structure.                */
/* If it cannot be located, a new entry is created.                   */
/*                                                                    */
/* pFontDetails parameter holds the font to be cached                 */
/**********************************************************************/

VOID eddt_LocateCachedFont ( PFONTDETAILS pFontDetails )

{
    /******************************************************************/
    /* Local variables                                                */
    /******************************************************************/
    ULONG       i;
    PFOCAFONT   pFocaFont;
    BOOL        FontLocated;
    BOOL        fMetricsMatch;

    pFocaFont = pFontDetails->pFocaFont;

    /******************************************************************/
    /* We'll start off being pessimistic                              */
    /******************************************************************/
    FontLocated = FALSE;

    /******************************************************************/
    /* Now search through the CacheInfo table to see if we've         */
    /* already cached this font.                                      */
    /******************************************************************/
    for ( i = 0;
#ifdef XGA
          (i < MAX_NUM_CACHED_FONTS) &&
#else
          (i < pHWMap->num_font_planes) &&
#endif
                           ((SHORT)pFontCacheInfo[i].usUsageCount >= 0);
          i++ )
    {
        /**************************************************************/
        /* Compare the metrics of the new font with the current       */
        /* font in the table.                                         */
        /**************************************************************/
        fMetricsMatch =
               ( 0 == memcmp( (PVOID)&(pFontCacheInfo[i].fmFontMetrics),
                              (PVOID)&(pFocaFont->fmMetrics),
                              sizeof(FOCAMETRICS) ) );
        if ( fMetricsMatch &&
             (pFontCacheInfo[i].usCodePage == pFontDetails->usCodePage) )
        {
            /**********************************************************/
            /* Found it !                                             */
            /**********************************************************/
            FontLocated = TRUE;
            break;
        }
    }

    if (FontLocated)
    {
        pFontCacheInfo[i].usUsageCount++;
    }
    else
    {
#ifdef XGA
        if (i == MAX_NUM_CACHED_FONTS)
#else
        if (i == pHWMap->num_font_planes)
#endif
        {
            /**********************************************************/
            /* The CacheInfo segment is full!                         */
            /* We'll have to search for an entry with a zero usage    */
            /* count and evict it.                                    */
            /**********************************************************/
#ifdef XGA
            for (i = 0; i < MAX_NUM_CACHED_FONTS; i++)
#else
            for (i = 0; i < pHWMap->num_font_planes; i++)
#endif
            {
                if (pFontCacheInfo[usEvictionIndex].usUsageCount == 0)
                    break;

#ifdef XGA
                if (++usEvictionIndex >= MAX_NUM_CACHED_FONTS)
#else
                if (++usEvictionIndex >= pHWMap->num_font_planes)
#endif
                    usEvictionIndex = 0;
            }

            /**********************************************************/
            /* Now evict the selected entry.                          */
            /* Note that if we do not find an entry with a zero usage */
            /* count we just boot out the entry that we happen to be  */
            /* at. When the DCs that had that index try to draw       */
            /* text, they will spot that the FontID has changed and   */
            /* recreate a new entry for their font.                   */
            /**********************************************************/
            i = usEvictionIndex;
        }

        /**************************************************************/
        /* We now have an entry number in the CacheInfo table (i)     */
        /* which we can write over.                                   */
        /**************************************************************/
        OutputString("Fill in CacheInfo entry");

#ifdef _8514
        /**************************************************************/
        /* Don't forget to reset the VRAM cache pointer.              */
        /**************************************************************/
        p8514FontCache[i] = pCacheMap->font_cache_start;
        aulFontPlaneOffset[i] = 0;    // start out in first plane
#endif

        /**************************************************************/
        /* Copy the metrics across.                                   */
        /**************************************************************/
        memcpy( (PVOID)&(pFontCacheInfo[i].fmFontMetrics),
                (PVOID)&(pFocaFont->fmMetrics),
                sizeof(FOCAMETRICS) );

        /**************************************************************/
        /* Initialise the usage count to 1.                           */
        /**************************************************************/
        pFontCacheInfo[i].usUsageCount = 1;

        /**************************************************************/
        /* Store the current Font ID in the CacheInfo entry.          */
        /* Increment the current ID so that the next font is          */
        /* given a new ID.                                            */
        /**************************************************************/
        pFontCacheInfo[i].usFontID = CurrentFontID++;

        /**************************************************************/
        /* Check that the CurrentFontID has not reached -1 - this     */
        /* is our invalid value.                                      */
        /**************************************************************/
        if ((SHORT)CurrentFontID == -1)
        {
            CurrentFontID++;
        }

        /**************************************************************/
        /* Set all of the character cache addresses to 0, to          */
        /* indicate that nothing has been cached yet.                 */
        /**************************************************************/
        memset( (PVOID)&(pFontCacheInfo[i].aulCachedCharOffset[0]),
                (CHAR) 0,
                MAX_NUM_CODEPOINTS * sizeof(ULONG) );

        /**************************************************************/
        /* Store the codepage so we know what codepage this           */
        /* cache entry refers to.                                     */
        /**************************************************************/
        pFontCacheInfo[i].usCodePage = pFontDetails->usCodePage;
    }

    pFontDetails->usCachedFontIndex = i;
    pFontDetails->usFontID = pFontCacheInfo[i].usFontID;

#ifdef EDD_DEBUG
#ifdef DEBUG_CACHE
    if (DebugCacheOn)
    {
        OutputString("Post caching...");
        DebugCache();
    }
#endif
#endif
}


/**********************************************************************/
/* FreeCachedFont:                                                    */
/* Frees the given font in the FontCacheInfo structure. This routine  */
/* is called when a DC is being deleted or a different font is being  */
/* selected.                                                          */
/**********************************************************************/

VOID eddt_FreeCachedFont ( USHORT usFontID,
                           USHORT usCachedFontIndex )

{
    if ( (usFontID == pFontCacheInfo[usCachedFontIndex].usFontID) &&
         (SHORT)pFontCacheInfo[usCachedFontIndex].usUsageCount > 0 )
    {
        pFontCacheInfo[usCachedFontIndex].usUsageCount--;
    }

#ifdef EDD_DEBUG
#ifdef DEBUG_CACHE
    if (DebugCacheOn)
    {
        OutputString("Freed font...");
        DebugCache();
    }
#endif
#endif
}




/**********************************************************************/
/* InvalidateCache:                                                   */
/* Invalidates the whole text cache.                                  */
/**********************************************************************/

VOID eddt_InvalidateCache (VOID)

{
    /******************************************************************/
    /* Local variables                                                */
    /******************************************************************/
    ULONG   i;

    DebugOutput("******************************************\n\r");
    DebugOutput("* Font cache is full...invalidating it.. *\n\r");
    DebugOutput("******************************************\n\r");

    /******************************************************************/
    /* Initialise cache entries to all free, and set the Font ID      */
    /* to an invalid value.                                           */
    /******************************************************************/
#ifdef XGA
    for (i = 0; i < MAX_NUM_CACHED_FONTS; i++)
#else
    for (i = 0; i < pHWMap->num_font_planes; i++)
#endif
    {
        pFontCacheInfo[i].usUsageCount =
        pFontCacheInfo[i].usFontID     = (USHORT) -1;

#ifdef _8514
        p8514FontCache[i] = pCacheMap->font_cache_start;
        aulFontPlaneOffset[i] = 0;        // start out in first plane
#endif

    }

    /******************************************************************/
    /* Reset these to intitial values                                 */
    /* The 1 in NextFree11Pos does two things..                       */
    /* 1. Means this will not take the reserved 0 value (0 means      */
    /*    not cached)                                                 */
    /* 2. Gives the MESS a spare byte at the beginning which it needs */
    /******************************************************************/
    offNextFree11Pos = 1;
    offLastFree81Pos = charCacheLength + 1;
}




/**********************************************************************/
/* CacheCharacter puts the given character into the VRAM cache        */
/* and updates the CacheInfo with the necessary details.              */
/*                                                                    */
/* Assumptions on entry:                                              */
/*  - The font used is the one currently selected in DCIData.         */
/*  - A call to LocateCachedFont has been performed with this font.   */
/*  - The character is not already cached.                            */
/*                                                                    */
/* pFontDetails points to info on font to which codepoint belongs.    */
/**********************************************************************/
USHORT eddt_CacheCharacter( CHAR         chArgCodePoint,
                            PFONTDETAILS pFontDetails )
{
    USHORT      usGlyphIndex;
    PCHAR_DEFN  pCharDefn;
    PBYTE       pImageData;
    USHORT      xGlyphWidth;
    USHORT      yGlyphHeight;
    USHORT      xGlyphWidthInBytes;
    USHORT      usImageSize;
    USHORT      xGlyphWidthHW;
    PBYTE       pCacheDest;
    USHORT      i;
    USHORT      j;
    PBYTE       pDest;
    PFOCAFONT   pFocaFont;
    SHORT       tempDrawMode;
    ULONG       pVRAM;
    ULONG       FontPlaneOffset;                // Defect 75206
#ifdef EDD_DEBUG
    PBYTE       pSrc;
    BYTE        Mask;
#endif /* EDD_DEBUG */
#ifdef _8514
    USHORT      destX;
    USHORT      destY;
#endif

    pFocaFont = pFontDetails->pFocaFont;

    /******************************************************************/
    /* Convert the supplied codepoint to a glyph index                */
    /******************************************************************/
    if (pFontDetails->pCodePageVector)
    {
        /**************************************************************/
        /* There is a codepage vector - use it.                       */
        /**************************************************************/
        usGlyphIndex = pFontDetails->pCodePageVector[ chArgCodePoint ];
    }
    else
    {
        /**************************************************************/
        /* There is no codepage vector - the codepoints map directly  */
        /* to glyphs.                                                 */
        /**************************************************************/
        usGlyphIndex = (USHORT)chArgCodePoint;
    }

    /******************************************************************/
    /* Now validate the glyph index, and make it relative to the      */
    /* first character of the font.                                   */
    /******************************************************************/
    if ( (usGlyphIndex <  pFocaFont->fmMetrics.usFirstChar) ||
         (usGlyphIndex > (pFocaFont->fmMetrics.usLastChar +
                          pFocaFont->fmMetrics.usFirstChar)) )
    {
        /**************************************************************/
        /* The glyph index is invalid. Use the default char from the  */
        /* metrics. Note that this value is relative to the first     */
        /* character in the font (which is what we want).             */
        /**************************************************************/
        usGlyphIndex = pFocaFont->fmMetrics.usDefaultChar;
    }
    else
    {
        /**************************************************************/
        /* The glyph index is valid. Adjust it so that it is relative */
        /* to the first character in the font.                        */
        /**************************************************************/
        usGlyphIndex -= pFocaFont->fmMetrics.usFirstChar;
    }

    /******************************************************************/
    /* Set up values which are independent of the font type           */
    /******************************************************************/
    yGlyphHeight = pFocaFont->fdDefinitions.yCellHeight;

    /******************************************************************/
    /* Now set up the values which are dependent on the font type     */
    /******************************************************************/
    if (pFocaFont->fdDefinitions.fsChardef != FONTDEFCHAR3)
    {
        /**************************************************************/
        /* The font is not ABC spaced.                                */
        /**************************************************************/
        pCharDefn = (PCHAR_DEFN)((PBYTE)pFocaFont + sizeof(FOCAFONT) +
                              usGlyphIndex * sizeof(NON_ABC_CHAR_DEFN));
        xGlyphWidth = pCharDefn->nonabcDefn.usCharWidth;
        pImageData  = (PBYTE)pFocaFont +
                                    pCharDefn->nonabcDefn.ulImageOffset;
    }
    else
    {
        /**************************************************************/
        /* The font is ABC spaced.                                    */
        /**************************************************************/
        pCharDefn = (PCHAR_DEFN)((PBYTE)pFocaFont + sizeof(FOCAFONT) +
                                  usGlyphIndex * sizeof(ABC_CHAR_DEFN));
        xGlyphWidth = pCharDefn->abcDefn.usBSpace;
        pImageData  = (PBYTE)pFocaFont +
                                     (pCharDefn->abcDefn.ulImageOffset);
    }

    /******************************************************************/
    /* Calculate how much memory the image definition takes up.       */
    /* In Winthorn format the rows are padded to byte boundaries.     */
    /* In the cache they are also padded this way for the benefit of  */
    /* the MESS code.                                                 */
    /******************************************************************/
    xGlyphWidthInBytes = (xGlyphWidth + 7)/8;
    xGlyphWidthHW = (xGlyphWidthInBytes*8)-1;
    usImageSize = xGlyphWidthInBytes * yGlyphHeight;

    /******************************************************************/
    /* First check whether there is enough room in the cache for      */
    /* this character. If not, trash the cache and return zero to     */
    /* indicate that the character was not cached. The main problem   */
    /* will now be that any earlier characters in the current string  */
    /* are now no longer cached! The calling routine will therefore   */
    /* have to call LocateFont to set up a new CacheInfo structure    */
    /* and re-cache all of the characters in the string.              */
    /******************************************************************/
    if (offNextFree11Pos + (ULONG)usImageSize >= offLastFree81Pos )
    {
        eddt_InvalidateCache();
        return(ERROR_ZERO);
    }

#ifdef _8514
    //*****************************************************************
    // 8514 NOTES
    //
    // At this point, we have already checked to see whether the height
    // of one character in this font exceeds the height of the entire
    // font cache.  If so, we abandoned any attempt to cache the font
    // and, instead, use the SrcDestBlt() code to blt the monochrome
    // glyph image directly to the screen.
    //
    // Therefore, we now must check to see if the current cache request
    // will fit into the cache at the current cache destination.  Note
    // that we first check to see if the width of the character exceeds
    // the screen width.  If so, we will start a new row in the font
    // cache and the Y coordinate must be adjusted before we make the
    // check to see if enough room exists.
    //
    // SEAMLESS NOTES
    //
    // Bottom8514FontCache is a global variable that is normally set
    // to the bottom of off-screen VRAM.  This provides us with a
    // 128x1024 area for our PM font cache.  However, when seamless
    // enters the picture, we move the font cache to reside next to
    // the bitmap cache.  The font cache will then cover scan lines
    // 0 through 127 of off-screen VRAM from column 256 to 1016.
    // This provides a 128x1024 area for the Window's driver to use for
    // its' fonts, bitmaps, and screen save area.
    //*****************************************************************
    pVRAM = p8514FontCache[pFontDetails->usCachedFontIndex];

    //*****************************************************************
    // Defect 75206. 800x600x16bit in 1 MB VRAM doesn't leave enough room 
    // to cache large AVio fonts in one plane.  So, for this resolution,  
    // we reduce the number of cached fonts to 8 and allow them to wrap   
    // (once) to plane index + 8. aulFontPlaneOffset indicates whether    
    // we've wraped yet:                                                  
    //              0 - in plane 0 through 7                              
    //              1 - in plane 8 through 15                             
    //             -1 - not in 16 bit color, ignore                       
    //*****************************************************************
    if ( DDT.BitCount == 16 )
       FontPlaneOffset = aulFontPlaneOffset[pFontDetails->usCachedFontIndex];
    else
       FontPlaneOffset = -1;

    destY = (USHORT)pVRAM;
    destX = (USHORT)(pVRAM << 16);

    if (destX +  xGlyphWidth >= (USHORT) pCacheMap->font_cache_right)
        destY += yGlyphHeight;

    if (destY +  yGlyphHeight > (USHORT) pCacheMap->font_cache_bottom)
    {
        eddt_InvalidateCache();
        return(ERROR_ZERO);
    }
#endif

    pCacheDest = pSysCacheStart + offNextFree11Pos;

    for (i = xGlyphWidthInBytes; i--; )
    {
        pDest = pCacheDest++;
        for (j = yGlyphHeight; j--; )
        {
            *pDest = *pImageData++;
            pDest += xGlyphWidthInBytes;
        }
    }

    /******************************************************************/
    /* The character image is now in the buffer, in horizontal row    */
    /* format, but still with rows padded to byte boundaries.         */
    /******************************************************************/
#ifdef EDD_DEBUG
    if (DebugCharOn)
    {
        DebugOutput("Caching char:\n\r");
        pSrc = SPad;
        for (i = 0; i < yGlyphHeight; i++ )
        {
            Mask = 0x80;
            for (j = 0; j < xGlyphWidth; j++ )
            {
                if (*pSrc & Mask)
                    DebugOutput("*");
                else
                    DebugOutput(" ");

                if ((Mask >>= 1) == 0)
                {
                    Mask = 0x80;
                    pSrc++;
                }
            }
            DebugOutput("\n\r");
            if (Mask != 0x80)
            {
                pSrc++;
            }
        }
        DebugInput();
    }
#endif /* EDD_DEBUG */

    /******************************************************************/
    /* If we are using the VRAM character cache then we must copy the */
    /* character into it.                                             */
    /******************************************************************/
#ifdef SEAMLESS
    if (foregroundSession && !(fSeamlessActive && fSeamlessCCConflict))
#else
    if (foregroundSession)
#endif
    {
        yGlyphHeight--;

        /**************************************************************/
        /* Set up the hardware mixes.                                 */
        /**************************************************************/
#ifndef   _8514
        ShadowXGARegs.FgMix = HWMIX_SOURCE;
        ShadowXGARegs.BgMix = HWMIX_SOURCE;

        ShadowXGARegs.ColCompCond = COLCOMP_ALWAYS;

        /**************************************************************/
        /* Set up destination pixmap details.                         */
        /**************************************************************/
        ShadowXGARegs.PixMapBaseA   = pVRAMCacheStart + offNextFree11Pos;
        ShadowXGARegs.PixMapWidthA  = xGlyphWidthHW;
        ShadowXGARegs.PixMapHeightA = yGlyphHeight;
        ShadowXGARegs.PixMapFormatA = ONE_BPP;

        /**************************************************************/
        /* Set up source pixmap details.                              */
        /**************************************************************/
        ShadowXGARegs.PixMapBaseB = pSysCacheStartPhy + offNextFree11Pos;
        ShadowXGARegs.PixMapWidthB  = xGlyphWidthHW;
        ShadowXGARegs.PixMapHeightB = yGlyphHeight;
        ShadowXGARegs.PixMapFormatB = ONE_BPP | MOTOROLA;

        /**************************************************************/
        /* Set up blt details - we want to copy the whole character.  */
        /**************************************************************/
        ShadowXGARegs.SrcXAddr =
         ShadowXGARegs.SrcYAddr =
          ShadowXGARegs.DstXAddr =
           ShadowXGARegs.DstYAddr = 0;

        ShadowXGARegs.OpDim1 = xGlyphWidthHW;
        ShadowXGARegs.OpDim2 = yGlyphHeight;

        /**************************************************************/
        /* Set up the pixel op to do the blt we want.                 */
        /**************************************************************/
        ShadowXGARegs.PixOp = BACK_SRC_SRC_PIX_MAP |
                              FORE_SRC_SRC_PIX_MAP |
                              STEP_PXBLT |
                              SRC_PIX_MAP_B |
                              DST_PIX_MAP_A |
                              PAT_PIX_MAP_FORE |
                              MASK_PIX_MAP_OFF |
                              DRAW_MODE_DONTCARE |
                              DIR_OCTANT_LRTB;

        /**************************************************************/
        /* Now do the blt. We have to use the hardware to do this.    */
        /* Set softDrawInUse to false, and then restore it after the  */
        /* blt has been done. (Higher level functions may be in       */
        /* software drawing mode, but we must still keep the VRAM     */
        /* cache copy up to date.)                                    */
        /**************************************************************/
        tempDrawMode = softDrawInUse;
        softDrawInUse = FALSE;
        TransferShadowRegisters( TSR_MAP_A |
                                 TSR_MAP_B |
                                 TSR_COLOUR_MIX |
                                 TSR_COORDINATES |
                                 TSR_PIXELOP );
        softDrawInUse = tempDrawMode;


    }
#else
        /**************************************************************/
        /* Set up destination pixmap details.                         */
        /**************************************************************/
//        Shadow8514Regs.PixMapBaseA   = pVRAMCacheStart + offNextFree11Pos;
//        Shadow8514Regs.PixMapWidthA  = xGlyphWidthHW;
//        Shadow8514Regs.PixMapHeightA = yGlyphHeight;
//        Shadow8514Regs.PixMapFormatA = ONE_BPP;

        /**************************************************************/
        /* Set up source pixmap details.                              */
        /**************************************************************/
//        Shadow8514Regs.PixMapBaseB   = pSysCacheStartPhy + offNextFree11Pos;
//        Shadow8514Regs.PixMapWidthB  = xGlyphWidthHW;
//        Shadow8514Regs.PixMapHeightB = yGlyphHeight;
//        Shadow8514Regs.PixMapFormatB = ONE_BPP | MOTOROLA;

        /**************************************************************/
        /* Set up blt details - we want to copy the whole character.  */
        /**************************************************************/
//        Shadow8514Regs.SrcXAddr =
//         Shadow8514Regs.SrcYAddr =
//          Shadow8514Regs.DstXAddr =
//           Shadow8514Regs.DstYAddr = 0;

//        Shadow8514Regs.OpDim1 = xGlyphWidthHW;
//        Shadow8514Regs.OpDim2 = yGlyphHeight;

        /**************************************************************/
        /* Set up the pixel op to do the blt we want.                 */
        /**************************************************************/
//        Shadow8514Regs.PixOp = BACK_SRC_SRC_PIX_MAP |
//                               FORE_SRC_SRC_PIX_MAP |
//                               STEP_PXBLT |
//                               SRC_PIX_MAP_B |
//                               DST_PIX_MAP_A |
//                               PAT_PIX_MAP_FORE |
//                               MASK_PIX_MAP_OFF |
//                               DRAW_MODE_DONTCARE |
//                               DIR_OCTANT_LRTB;

        /**************************************************************/
        /* Now do the blt. We have to use the hardware to do this.    */
        /* Set softDrawInUse to false, and then restore it after the  */
        /* blt has been done. (Higher level functions may be in       */
        /* software drawing mode, but we must still keep the VRAM     */
        /* cache copy up to date.)                                    */
        /**************************************************************/
        tempDrawMode = softDrawInUse;
        softDrawInUse = FALSE;
//        TransferShadowRegisters( TSR_COLOUR_MIX );

        pVRAM = Cache8514Char( (ULONG) pFontDetails->usCachedFontIndex,
                               (ULONG) xGlyphWidth,
                               (ULONG) yGlyphHeight,
                               (PVOID) (pSysCacheStart + offNextFree11Pos),
                               (PULONG) &FontPlaneOffset );

        softDrawInUse = tempDrawMode;

    }
#endif
    /******************************************************************/
    /* Store the address offset in the FontCacheInfo structure        */
    /******************************************************************/
#ifdef _8514
    pFontCacheInfo[pFontDetails->usCachedFontIndex].
                 aulCachedCharOffset[chArgCodePoint] = pVRAM;

    /******************************************************************/
    /* Defect 75206. Update the font plane offset for this character  */
    /* and our global for the font.                                   */
    /******************************************************************/
    aulFontPlaneOffset[pFontDetails->usCachedFontIndex] =
    pFontCacheInfo[pFontDetails->usCachedFontIndex].
                 ausPlaneOffset[chArgCodePoint] = FontPlaneOffset;

#else
    pFontCacheInfo[pFontDetails->usCachedFontIndex].
                 aulCachedCharOffset[chArgCodePoint] = offNextFree11Pos;
#endif

#ifdef FULL_ADDRESS
    pFontCacheInfo[pFontDetails->usCachedFontIndex].
                                  apCharDef[chArgCodePoint] = pCharDefn;
#else /* ndef FULL_ADDRESS */
    pFontCacheInfo[pFontDetails->usCachedFontIndex].
                                  apCharDef[chArgCodePoint] =
                          (USHORT)((PBYTE)pCharDefn - (PBYTE)pFocaFont);
#endif /* ndef FULL_ADDRESS */

    /******************************************************************/
    /* Update the cache pointer to the next free space.               */
    /******************************************************************/
    offNextFree11Pos += usImageSize;

    return(OK);
}



/**********************************************************************/
/* TidyUpAfterCaching cleans up after CacheCharacter has been used.   */
/* CacheCharacter can allocate a segment and lock it down if a very   */
/* big font is being used. We just need to free this up if it was     */
/* created.                                                           */
/* This function MUST be called after any caching has been done.      */
/**********************************************************************/
VOID eddt_TidyUpAfterCaching(VOID)
{
    /******************************************************************/
    /* At 1.3 this routine freed any short term locked memory used    */
    /* when caching the character.                                    */
    /* At 2.0 this is a nop.                                          */
    /******************************************************************/
    return;
}





#ifdef DEBUG_CACHE
/**********************************************************************/
/* DebugCache helps us debug the cache                                */
/**********************************************************************/
#define SCREEN_WIDTH 80

VOID    DebugCache(VOID)
{
    CHAR    achBuffer[50];
    CHAR    chKeyPressed;
    ULONG   ulCacheMemoryUsed;
    ULONG   ulTotalCacheMemory;
    USHORT  xScreenPos;
    USHORT  i;
    USHORT  usTotal;
    CHAR    szBuffer[10];

    /******************************************************************/
    /* Display cache                                                  */
    /******************************************************************/
START:

    if (CacheIndex < 0)
        CacheIndex = 0;

    ulCacheMemoryUsed = pCacheNextFreePos-pCacheStart;
    ulTotalCacheMemory = pCacheEnd - pCacheStart + 1;
    xScreenPos = (USHORT)((SCREEN_WIDTH * ulCacheMemoryUsed)
                                                  / ulTotalCacheMemory);

    CrLf();
    DebugOutput("Cache memory usage: ");
    OutputValue(ulCacheMemoryUsed,10);
    DebugOutput(" bytes out of a total ");
    OutputValue(ulTotalCacheMemory,10);
    CrLf();

    for ( i = 0; i <= xScreenPos; i++ )
        DebugOutput("*");

    for ( i = xScreenPos+1; i < SCREEN_WIDTH; i++ )
        DebugOutput(".");

    CrLf();
    for (i = 0; i < MAX_NUM_CACHED_FONTS; i++)
    {
        if ((SHORT)pFontCacheInfo[i].usUsageCount >= 0)
        {
            OutputValue(pFontCacheInfo[i].usUsageCount / 16, 16);
        }
        else
        {
            DebugOutput("X");
        }
    }
    CrLf();
    for (i = 0; i < MAX_NUM_CACHED_FONTS; i++)
    {
        if ((SHORT)pFontCacheInfo[i].usUsageCount >= 0)
        {
            OutputValue(pFontCacheInfo[i].usUsageCount % 16, 16);
        }
        else
        {
            DebugOutput("X");
        }
    }
    CrLf();
    for (i = 0; i < MAX_NUM_CACHED_FONTS; i++)
    {
        if (i == CacheIndex)
        {
            DebugOutput("^");
        }
        else
        {
            DebugOutput(" ");
        }
    }
    CrLf();


    if ((SHORT)pFontCacheInfo[CacheIndex].usUsageCount < 0)
    {
        DebugOutput("This cache entry is unused!\n\r");
    }
    else
    {
        OutputPair("Cache number:",CacheIndex, DECIMAL);

        OutputString("Cached characters:");
        for (i = (USHORT)' '; i <= (USHORT)'z'; i++)
        {
            if (pFontCacheInfo[CacheIndex].aulCachedCharAddress[i])
            {
                szBuffer[0] = (CHAR)i;
                szBuffer[1] = 0;
                DebugOutput(szBuffer);
            }
            else
            {
                DebugOutput(".");
            }
        }
        CrLf();

        usTotal = 0;
        for (i = 0; i < 256; i++)
        {
            if (pFontCacheInfo[CacheIndex].aulCachedCharAddress[i])
                usTotal++;
        }
        OutputPair("Total number of cached chars = ", usTotal, DECIMAL);

        DebugOutput("Family name:");
        DebugOutput(pFontCacheInfo[CacheIndex].fmFontMetrics.szFamilyname);
        CrLf();

        DebugOutput("Face name:");
        DebugOutput(pFontCacheInfo[CacheIndex].fmFontMetrics.szFacename);
        CrLf();

        OutputPair("Codepage:",pFontCacheInfo[CacheIndex].usCodePage,DECIMAL);

        OutputPair("Nominal point size:",pFontCacheInfo[CacheIndex].fmFontMetrics.usNominalPointSize,DECIMAL);

        OutputPair("Max character increment:",pFontCacheInfo[CacheIndex].fmFontMetrics.xMaxCharInc,DECIMAL);

        OutputPair("Max baseline extent:",pFontCacheInfo[CacheIndex].fmFontMetrics.yMaxBaselineExt,DECIMAL);
        OutputPair("FirstChar:",pFontCacheInfo[CacheIndex].fmFontMetrics.usFirstChar,DECIMAL);
        OutputPair("LastChar:",pFontCacheInfo[CacheIndex].fmFontMetrics.usLastChar,DECIMAL);
    }

    /******************************************************************/
    /* Print menu                                                     */
    /******************************************************************/
    DebugOutput("--------------------------\n\r");
    DebugOutput("N: Next cache\n\r");
    DebugOutput("P: Previous cache\n\r");
    DebugOutput("C: Continue and crash\n\r");
    DebugOutput("Please select your pleasure...");
    chKeyPressed = DebugInput();
    CrLf();
    if ( (chKeyPressed == 'C') ||
         (chKeyPressed == 'c') )
    {
        return;
    }
    if ( (chKeyPressed == 'N') ||
         (chKeyPressed == 'n') )
    {
        CacheIndex += 1;
    }
    if ( (chKeyPressed == 'P') ||
         (chKeyPressed == 'p') )
    {
        CacheIndex -= 1;
    }
    goto START;
}
#endif /* EDD_DEBUG */


#ifdef VRAMPTR

ULONG free_bm_cache_slot(VOID)

/**********************************************************************/
/* Returns the slot number of the first unused VRAM bitmap cache slot */
/* or NO_FREE_BM_CACHE_SLOTS if all are in use.                       */
/**********************************************************************/

{
  register ULONG i;

  for ( i = max_cached_bitmaps; i--; )
  {
    if ( UNUSED_BM_CACHE_SLOT(bm_cache[i]) )
    {
      return(i);
    }
  }

  return(NO_FREE_BM_CACHE_SLOTS);

} /* free_bm_cache_slot */


VOID evict_cached_bitmap(ULONG eviction_slot)

/**********************************************************************/
/* Removes the bitmap at cache slot eviction_slot from the VRAM       */
/* bitmap cache.                                                      */
/**********************************************************************/

{

  RELEASE_BM_CACHE_SLOT(bm_cache[eviction_slot]);

  MARK_BMH_NOT_CACHED(bm_cache[eviction_slot]);

  CACHED_BM_PHYS_ADDR(bm_cache[eviction_slot]) = NULL;

} /* evict_cached_bitmap */


ULONG evicted_cache_slot(VOID)

/**********************************************************************/
/* Throws a bitmap out of the cache and returns its slot number for   */
/* use by a new bitmap.                                               */
/*                                                                    */
/* @RCW - This scheme sucks.  We can find a better way to determine   */
/*        which bitmap will be evicted next.                          */
/**********************************************************************/

{

  evict_cached_bitmap(next_eviction);

  if ( next_eviction == 0 )
  {
    next_eviction = max_cached_bitmaps - 1;
    return(0);
  }
  else
  {
    return(next_eviction--);
  }

} /* evicted_cache_slot */


VOID add_bitmap_to_cache(pBitmapHeader pbmh, ULONG slot)

/**********************************************************************/
/* Adds the bitmap specified by pbmh to the VRAM bitmap cache at slot */
/* number slot.                                                       */
/**********************************************************************/

{

  RESERVE_BM_CACHE_SLOT(bm_cache[slot]);
  CACHED_BM_HEADER(bm_cache[slot]) = pbmh;
  CACHED_BM_PHYS_ADDR(bm_cache[slot]) = CACHE_PHYS_ADDR(bm_cache[slot]);
  CACHED_BM_SLOT(bm_cache[slot]) = slot;

  CopyMemoryToVRAM( CACHED_BM_VIRT_ADDR(bm_cache[slot]),
                    CACHE_PHYS_ADDR(bm_cache[slot]),
                    pbmh->Info.HWWidth,
                    pbmh->Info.HWHeight,
                    pbmh->Info.HWFormat);

} /* add_bitmap_to_cache */


BOOL cache_bitmap(pBitmapHeader pbmh)

/**********************************************************************/
/* Copies the bitmap specified by pbmh into the VRAM cache. Assumes   */
/* the bitmap is less than VRAM_BM_CACHE_SIZE bytes.                  */
/**********************************************************************/

{
  register ULONG free_slot;

  #ifdef   _8514
  if ( !DDT.fCaching ) {
     return(FALSE);
  }
  #endif

  free_slot = free_bm_cache_slot();
  if ( free_slot == NO_FREE_BM_CACHE_SLOTS )
  {
    free_slot = evicted_cache_slot();
  }

  add_bitmap_to_cache(pbmh, free_slot);

  return(TRUE);

} /* cache_bitmap */


BOOL initialise_bm_cache()

/**********************************************************************/
/* Sets up the VRAM bitmap cache by calculating the number of slots   */
/* available and allocating enough memory to keep the cache data for  */
/* them. Initialises the cache data.                                  */
/**********************************************************************/

{
#ifndef _8514
  ULONG free_VRAM_size;
#endif
  register ULONG cache_offset;
  register ULONG i;

  /********************************************************************/
  /* Reserve the rest of VRAM for the bitmap cache. VRAM is divided   */
  /* into the maximum possible number of slots of size                */
  /* VRAM_BM_CACHE_SIZE. Bitmaps of up to this size are cached in     */
  /* VRAM to avoid having to blt through the PHUNK.                   */
  /* Begin on a 32-bit boundary.                                      */
  /********************************************************************/

#ifndef   _8514
  FreeVRAM = (FreeVRAM + 3) & 0xfffffffc;

  free_VRAM_size = aiXGAAdapter.lMemorySize -
                             (FreeVRAM - aiXGAAdapter.ulVRAMBase);

  max_cached_bitmaps = free_VRAM_size / VRAM_BM_CACHE_SIZE;
  next_eviction = max_cached_bitmaps - 1;

  bm_cache = AllocateMemory( sizeof(BMCACHE) * max_cached_bitmaps,
                             MT_BMCACHE,
                             MO_SHARED );
  if (bm_cache == NULL)
  {
    return(FALSE);
  }

  cache_offset = 0;

  for ( i = 0; i < max_cached_bitmaps; i++ )
  {
    bm_cache[i].flags = 0;
    CACHE_PHYS_ADDR(bm_cache[i]) = FreeVRAM + cache_offset;
    cache_offset += VRAM_BM_CACHE_SIZE;
  }

#else

  //******************************************************************
  // We will use most of the top 128K of off-screen VRAM for our
  // bitmap cache.  The only portion we don't use is the 128x128 pel
  // are at the top left corner of off-screen VRAM.  That area is used
  // by the cursor for AND,XOR,Color, and Save areas.
  //
  // For now, we have 14 cache slots available.  Each slot is 64 pels
  // wide by 128 pels high.
  //******************************************************************

  cache_offset       = pHWMap->start_bm_cache;
  max_cached_bitmaps = pCacheMap->max_bitmaps;
  next_eviction      = max_cached_bitmaps == 0 ? 0 : pCacheMap->max_bitmaps - 1;

  bm_cache = AllocateMemory( sizeof(BMCACHE) * max_cached_bitmaps,
                             MT_BMCACHE,
                             MO_SHARED );
  if (bm_cache == NULL)
  {
    return(FALSE);
  }

  for ( i = 0; i < max_cached_bitmaps; i++ )
  {
    bm_cache[i].flags = 0;
    CACHE_PHYS_ADDR(bm_cache[i]) = cache_offset;

    #ifdef   BPP24
    if ( pHWMap->vertical_bmaps ) {
       cache_offset += ( VRAM_BM_VERT_CACHE_VERT_SIZE );
    }
    else
    {
       cache_offset += ( VRAM_BM_CACHE_HOR_SIZE << 16 );
    }
    #else
    cache_offset += ( VRAM_BM_CACHE_HOR_SIZE << 16 );
    #endif
  }

#endif

  return(TRUE);

} /* initialise_bm_cache */


VOID shutdown_bm_cache(VOID)

/**********************************************************************/
/* Evicts all bitmaps in the bitmap VRAM cache. Called at Death.      */
/**********************************************************************/

{
  register ULONG i;

  for ( i = max_cached_bitmaps; i--; )
  {
    if ( USED_BM_CACHE_SLOT(bm_cache[i]) )
    {
      evict_cached_bitmap(i);
    }
  }

} /* shutdown_bm_cache */

#endif /* VRAMPTR */
