/***************************************************************************
 *                                                                         *
 * Module  : texcache.c                                                    *
 *                                                                         *
 * Purpose : Texture cache for Playstation II                              *
 *                                                                         *
 **************************************************************************/

/* Note : In the rewrite of this code I have preserved the distinction betweem
   cache functions and memory functions even though they now use a unified
   structure. Cache functions deal with allocation and rejection, memory
   functions deal with heap implementation */

#ifdef GSB
/* This code has gained conditional compile options based on GSPLUS. This
   adds new registers to hold the top 3 bits of all the addresses. */
#endif /* GSB */

/*
 * GJD
 *
 * Copyright (c) 2000 Criterion Software Ltd.
 */

/***************************************************************************
 Includes
 */

#include "baraster.h"
#include "rwstring.h"

#include "balibtyp.h"
#include "badebug.h"

#include "badma.h"
#include "gs.h"
#include "basky.h"

#include "texcache.h"

/******* Temporary dual code block. We include the standard texture cache
 ******* code followed by the async one. This will allow a build time
 ******* decision on type. */
#ifndef ASYNCTEXTURES

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>

#include <eekernel.h>
#include <eetypes.h>
#include <eeregs.h>
#include <libgraph.h>
#include <libdma.h>
#include <libdev.h>

#include "batypes.h"
#include "badevice.h"
#include "bamemory.h"
#include "barwtyp.h"

/* Drag in the vertex format */
#include "drvmodel.h"

#include "badma.h"
#include "dmaalloc.h"
/* baasm.s externs */
#include "baasm.h"

#include "devprofile.h"

static const char rcsid[] __RWUNUSED__ =
"@@(#)$Id: texcache.c,v 1.121 2001/09/18 12:01:02 Rabin Exp $";

/****************************************************************************
 Local Types
 */

/****************************************************************************
 Local (Static) Prototypes
 */
static _SkyMemBlock *_SkyCacheReleaseCacheEntry(_SkyMemBlock *mpBlock);

/****************************************************************************
 Local Defines
 */

#define TEXCACHELISTNONE    -1
#define TEXCACHELISTBUCKET  -2
#define TEXCACHELISTLOCKED  -3

/****************************************************************************
 Globals (across program)
 */

RwUInt32 skyGNumTexMemBlocks	=	128;

#ifdef RWDEBUG
long skyGCurrentTexMemBlocks;
long skyGMaxTexMemBlocks;
#endif /* RWDEBUG */

/****************************************************************************
 Local (static) Globals
 */

static RwRaster *lastRaster = (RwRaster *)NULL;
static RwRaster *thisRaster = (RwRaster *)NULL;
static int lastContext = 1;
static int thisContext = 1;
static int tlock = 0;

#if (0)
static int lastError = 0;
#endif (0)

/* we need to know where the textures can start */

typedef struct TexCacheState TexCacheState;

struct TexCacheState
{
     RwInt32 TextureBaseAddressInLocalMemory ;
     RwBool needTexCache ;
     u_long128 texFlushGsCommand;
     u_long128 trxPos0GsCommand;
     u_long128 trxDir0GsCommand;
     u_long128 dmaTagCnt1;
#if defined(GSB) && defined(GSPLUS)
     u_long128 refUploadSetupGifTag6;
#else /* defined(GSB) && defined(GSPLUS) */
     u_long128 refUploadSetupGifTag5;
#endif /* defined(GSB) && defined(GSPLUS) */
};

static TexCacheState state =
{
    (RwInt32) -1 /* TextureBaseAddressInLocalMemory */,
    (RwBool) TRUE /* needTexCache */,
    (u_long128) 0  /* texFlushGsCommand */,
    (u_long128) 0  /* trxPos0GsCommand */,
    (u_long128) 0  /* trxDir0GsCommand */,
    (u_long128) 0  /* dmaTagCnt1 */,
#if defined(GSB) && defined(GSPLUS)
    (u_long128) 0  /* refUploadSetupGifTag6 */,
#else /* defined(GSB) && defined(GSPLUS) */
    (u_long128) 0  /* refUploadSetupGifTag5 */,
#endif /* defined(GSB) && defined(GSPLUS) */
};

static _SkyCache cGCache;

/****************************************************************************
 Local (static) Functions
 */

static void
_SkyNullCacheUploadRaster(RwRaster * __RWUNUSED__ r,
                          RwBool __RWUNUSED__ speculative)
{
    RWFUNCTION(RWSTRING("_SkyNullCacheUploadRaster"));

    /* does nothing */

    RWRETURNVOID();
}

/******************************************************************************
 This is now separate from the "_SkyBuildPktForUpLoadAlignedContiguousRectangle"
 function below. This is only so it can be called externally to RW, in order
 to fill in pre-allocated DMA packets.

 It's an easy way to get texture streaming to work (allocate contiguous memory
 for texture/raster/pixels etc, fill it in and set up the pointers properly,
 write the memory to disk. Then to use, read memory block in and fixup the
 pointers, then use this function to rebuild the DMA packets in the already
 allocated memory) :-)

 It NEEDS TO BE NON-STATIC so we can use it as a stealth function.

 AlexF 21/11/2000

 The need for this to be visible has now been removed

 RE 03/04/2001

 So the extern prototype has been removed from texcache.h

 Johns Thu 17/05/2001

*/

static u_long128*
skyFillInPktForUpLoadAlignedContiguousRectangle(u_long128 * pkt,
                                                RwUInt8 * src,
                                                RwUInt64 bytesInRectangle,
                                                RwUInt8 cachedPacket)
{
    RwUInt64            tmp, tmp1;
    u_long128           gifTagCmd;
    u_long128           dmaTag1Direct, ltmp;
    int                 i, k, l;
    u_long128          *ptr;

    RWFUNCTION(RWSTRING("skyFillInPktForUpLoadAlignedContiguousRectangle"));
    RWASSERT(pkt);
    RWASSERT(src);

    tmp = 1l | (1l << 28);
    tmp1 = ((0x50l << 24) | (1l)) << 32;
    MAKE128(dmaTag1Direct, tmp1, tmp);

    /* We attempt to shift via the vif.  We use tte */
    /* Build a ref chain, where the last one is a refe(i) */
    /* Note that here we can use 32750 as the data is not inlined */
    ptr = (u_long128 *) (((RwUInt32) pkt + 63) & ~63);

    /* Number of qwords */
    k = l = ((bytesInRectangle + 15) / 16);
    for (i = 0; i < (k + 32749) / 32750; i++)
    {
        u_long128           dmaRefCmd;
        RwUInt32            nloop, eop, dmaID;

        /* count */
        *ptr++ = dmaTag1Direct;

        if (l > 32750)
        {
            /* Not last ref (ref) */
            nloop = 32750;
            eop = 0;
            dmaID = 3;
        }
        else
        {
            /* Last ref (ref + int) */
            nloop = l;
            eop = 0;
            dmaID = 3;
        }
        /* gif tag */
        tmp = /* NLOOP */ (nloop) |
            /* EOP */ (eop << 15) |
            /* FLG */ (2l << 58);
        MAKE128(gifTagCmd, 0l, tmp);
        *ptr++ = gifTagCmd;

        /* ref(e)(i) */
        tmp =
            (nloop) | (dmaID << 28) | ((RwUInt64) (RwUInt32) src << 32);
        tmp1 = ((0x50l << 24) | (nloop)) << 32;
        MAKE128(dmaRefCmd, tmp1, tmp);
        *ptr++ = dmaRefCmd;
        src += 32750 * 16;
        l -= 32750;
    }

    if (cachedPacket)
    {
        tmp = (0x6l << 28) | 2;
    }
    else
    {
        tmp = (0xfl << 28) | 2;
    }
    tmp1 = ((0x50l << 24) | 2l) << 32;
    MAKE128(ltmp, tmp1, tmp);
    *ptr++ = ltmp;

    tmp = /* NLOOP */ 1 |
        /* EOP */ (1 << 15) |
        /* FLG */ (0l << 58) |
        /* NREG */ (1l << 60);
    tmp1 = 0xe;
    MAKE128(gifTagCmd, tmp1, tmp);
    *ptr++ = gifTagCmd;

    *ptr++ = state.texFlushGsCommand;

    /* Push it out of cache into memory */
    SyncDCache((void *) (((RwUInt32) pkt + 63) & ~63),
               SCESYNCDCACHEROUNDUP(ptr));

    RWRETURN(pkt);
}

static u_long128*
_SkyBuildPktForUpLoadAlignedContiguousRectangle(RwUInt32 pixelWidth,
                                            RwUInt32 pixelHeight,
                                            RwUInt8 *srcAddress,
                                            RwUInt32 bitsPerPixel,
                                            RwUInt8 cachedPacket)
{
    RwUInt8   *src = (RwUInt8 *)srcAddress;
    RwUInt64  bytesInRectangle = (pixelWidth * pixelHeight * bitsPerPixel) >> 3;
    u_long128 *pkt;

    RWFUNCTION(RWSTRING("_SkyBuildPktForUpLoadAlignedContiguousRectangle"));
    RWASSERT(pixelWidth);
    RWASSERT(pixelHeight);
    RWASSERT(srcAddress);

    /* Get the memory for it */
    if (cachedPacket)
    {
        pkt = (u_long128 *)RwMalloc((((bytesInRectangle+15)/16)
                                     /32750+1)*16 *3+3*16 +128);
    }
    else
    {
        pkt = (u_long128 *)circularMalloc(circAllocator,
                                          (((bytesInRectangle+15)/16)
                                           /32750 + 1)*16 *3+3*16 +128,
                                          calDCACHE_ALLOC);
    }

    /* Now fill it in */
    pkt = skyFillInPktForUpLoadAlignedContiguousRectangle(pkt, src, bytesInRectangle, cachedPacket);

    RWRETURN(pkt);
}

#define ContiguousRectangleType                                 \
 ( SWE_PKT_VU1 |                                                \
   SWE_PKT_CIRCALLOC |                                          \
   SWE_PKT_DMA_MODE_CHAIN_TTE |                                 \
   SWE_LPS_NOFIXUP )

static void
_SkyDdispatchUpLoadAlignedContiguousRectangle(RwUInt32 pixelWidth,
                                         RwUInt32 pixelHeight,
                                         RwUInt32 dstAddress,
                                         RwUInt32 dstWidthBy64,
                                         RwUInt32 pixelFormat,
                                         u_long128 *pkt,
                                         RwUInt8 cachedPacket)
{
    RwUInt64  tmp;
    u_long128 bitBltBufCmd, trxRegCmd, ltmp;

    RWFUNCTION(RWSTRING("_SkyDdispatchUpLoadAlignedContiguousRectangle"));
    RWASSERT(pixelWidth);
    RWASSERT(pixelHeight);
    RWASSERT(dstAddress);
    RWASSERT(dstWidthBy64);

    if (cachedPacket)
    {

        /* we now create a special ref(e)(i) dma pkt, but */
        /* marked as local so that it will be freed by */
        /* the pkt garbage collector */
        sweOpenLocalPkt(SWE_LPS_CONT, 0);
        /* Prepare the GS to accept the texture */
#if defined(GSB) && defined(GSPLUS)
        SWEADDCONTGIFFAST(state.refUploadSetupGifTag6, 6);
#else /* defined(GSB) && defined(GSPLUS) */
        SWEADDCONTGIFFAST(state.refUploadSetupGifTag5, 5);
#endif /* defined(GSB) && defined(GSPLUS) */

        /* Flush - It is uncertain that we need this */
        SWEADDCONTFAST(state.texFlushGsCommand);

        /* BitBltbuf */
        tmp = ((RwUInt64)pixelFormat << 56) |
            ((RwUInt64)dstWidthBy64 << 48) |
            ((RwUInt64)(dstAddress & 0x3fff)<< 32) |
            ((RwUInt64)pixelFormat << 24) |
            (10l << 16);  /* Doesn't matter - feeding from FIFO */
        MAKE128(bitBltBufCmd, GS_BITBLTBUF, tmp);
        SWEADDCONTFAST(bitBltBufCmd);

#if defined(GSB) && defined(GSPLUS)
        tmp = ((RwUInt64)(dstAddress & 0x1ffff)<< 32);
        MAKE128(bitBltBufCmd, GS_BITBLTBUF2, tmp);
        SWEADDCONTFAST(bitBltBufCmd);
#endif /* defined(GSB) && defined(GSPLUS) */

        /* TrxPos */
        SWEADDCONTFAST(state.trxPos0GsCommand);

        /* TrxReg */
        tmp = ((RwUInt64)pixelHeight << 32) | (RwUInt64)pixelWidth;
        MAKE128(trxRegCmd, GS_TRXREG, tmp);
        SWEADDCONTFAST(trxRegCmd);

        /* TrxDir */
        SWEADDCONTFAST(state.trxDir0GsCommand);

        /* This will also add the command packet first */

#if (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO))
        {
            RwBool status;
            int size = -4;

            sweFinaliseOpenLocalPktMacro(status,
                                         ContiguousRectangleType, size);
        }
#else /* (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO)) */
        sweFinaliseOpenLocalPkt(ContiguousRectangleType, -4);
#endif /* (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO)) */

        tmp = (5l<<28) | ((RwUInt64)(((RwUInt32)pkt + 63) & ~63)<<32);
        MAKE128(ltmp, 0l, tmp);
        SWEADDCONTFAST(ltmp);

        tmp = (0xfl<<28);
        MAKE128(ltmp, 0l, tmp);
        SWEADDCONTFAST(ltmp);

#if (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO))
        {
            RwBool status;
            int size = 0;

            sweFinaliseOpenLocalPktMacro(status, SWE_LPS_CONT, size);
        }
#else /* (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO)) */
        sweFinaliseOpenLocalPkt(SWE_LPS_CONT, 0);
#endif /* (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO)) */
    }
    else
    {
        /* Use by ref */
        sweCloseLocalPkt();
        /* we now create a special ref(e)(i) dma pkt, but */
        /* marked as local so that it will be freed by */
        /* the pkt garbage collector */
        sweOpenLocalPkt(SWE_LPS_CONT, 0);
        /* Prepare the GS to accept the texture */
#if defined(GSB) && defined(GSPLUS)
        SWEADDCONTGIFFAST(state.refUploadSetupGifTag6, 6);
#else /* defined(GSB) && defined(GSPLUS) */
        SWEADDCONTGIFFAST(state.refUploadSetupGifTag5, 5);
#endif /* defined(GSB) && defined(GSPLUS) */

        /* Flush - It is uncertain that we need this */
        SWEADDCONTFAST(state.texFlushGsCommand);

        /* BitBltbuf */
        tmp = ((RwUInt64)pixelFormat << 56) |
            ((RwUInt64)dstWidthBy64 << 48) |
            ((RwUInt64)(dstAddress & 0x3fff)<< 32) |
            ((RwUInt64)pixelFormat << 24) |
            (10l << 16);  /* Doesn't matter - feeding from FIFO */
        MAKE128(bitBltBufCmd, GS_BITBLTBUF, tmp);
        SWEADDCONTFAST(bitBltBufCmd);

#if defined(GSB) && defined(GSPLUS)
        tmp = ((RwUInt64)(dstAddress & 0x1ffff)<< 32);
        MAKE128(bitBltBufCmd, GS_BITBLTBUF2, tmp);
        SWEADDCONTFAST(bitBltBufCmd);
#endif /* defined(GSB) && defined(GSPLUS) */

        /* TrxPos */
        SWEADDCONTFAST(state.trxPos0GsCommand);

        /* TrxReg */
        tmp = ((RwUInt64)pixelHeight << 32) | (RwUInt64)pixelWidth;
        MAKE128(trxRegCmd, GS_TRXREG, tmp);
        SWEADDCONTFAST(trxRegCmd);

        /* TrxDir */
        SWEADDCONTFAST(state.trxDir0GsCommand);

        /* This will also add the command packet first */
        _sweAddPkt((void *)(((RwUInt32)pkt + 63) & ~63),
                   SWE_PKT_GIF | SWE_PKT_LOCAL | SWE_PKT_CIRCALLOC
                   | SWE_PKT_DMA_MODE_CHAIN);

        _sweFlush();
    }

    RWRETURNVOID();
}

static void
_SkyUpLoadAlignedRectangle(RwUInt32 pixelWidth, RwUInt32 pixelHeight,
                       RwUInt8 *srcAddress, RwUInt32 dstAddress,
                       RwUInt32 dstWidthBy64, RwUInt32 bitsPerPixel,
                       RwUInt32 pixelFormat, RwUInt32 srcStride)
{
    RwUInt8   *src;
    RwUInt64  tmp, tmp1;
    u_long128 gifTagCmd, bitBltBufCmd, trxRegCmd;
    RwUInt32  i;
    RwUInt64  qWordsInLine;
    u_long128 *ptr, *pkt;
    u_long128 dmaTag1Direct, ltmp;

    RWFUNCTION(RWSTRING("_SkyUpLoadAlignedRectangle"));
    RWASSERT(pixelWidth);
    RWASSERT(pixelHeight);
    RWASSERT(srcAddress);
    RWASSERT(dstWidthBy64);

    tmp = 1l | (1l<<28);
    tmp1 = ((0x50l<<24) | (1l))<<32;
    MAKE128(dmaTag1Direct, tmp1, tmp);

    /* Make it a multiple of qWords for upload... :-) */
    qWordsInLine = (((pixelWidth * bitsPerPixel) >> 3) + 15) / 16;
    pixelWidth = (qWordsInLine*128)/bitsPerPixel;

    /* Use by ref */
    sweCloseLocalPkt();
    /* we now create a special ref(e)(i) dma pkt, but marked as local so that
     * it will be freed by the pkt garbage collector.  We also assume that
     * each scanline is less than 32750 qWords...!
     */
    sweOpenLocalPkt(SWE_LPS_CONT, 0);
    pkt = (u_long128 *)circularMalloc(circAllocator,
                                      pixelHeight * 16 * 3 +3*16,
                                      calDCACHE_ALLOC);

    /* Prepare the GS to accept the texture */
#if defined(GSB) && defined(GSPLUS)
    SWEADDCONTGIFFAST(state.refUploadSetupGifTag6, 6);
#else /* defined(GSB) && defined(GSPLUS) */
    SWEADDCONTGIFFAST(state.refUploadSetupGifTag5, 5);
#endif /* defined(GSB) && defined(GSPLUS) */

    /* Flush - It is uncertain that we need this */
    SWEADDCONTFAST(state.texFlushGsCommand);

    /* BitBltbuf */
    tmp = ((RwUInt64)pixelFormat << 56) |
        ((RwUInt64)dstWidthBy64 << 48) |
        ((RwUInt64)(dstAddress & 0x3fff)<< 32) |
        ((RwUInt64)pixelFormat << 24) |
        (10l << 16);  /* Doesn't matter - feeding from FIFO */
    MAKE128(bitBltBufCmd, GS_BITBLTBUF, tmp);
    SWEADDCONTFAST(bitBltBufCmd);

#if defined(GSB) && defined(GSPLUS)
    tmp = ((RwUInt64)(dstAddress & 0x1ffff)<< 32);
    MAKE128(bitBltBufCmd, GS_BITBLTBUF2, tmp);
    SWEADDCONTFAST(bitBltBufCmd);
#endif /* defined(GSB) && defined(GSPLUS) */

    /* TrxPos */
    SWEADDCONTFAST(state.trxPos0GsCommand);

    /* TrxReg */
    tmp = ((RwUInt64)pixelHeight << 32) | (RwUInt64)pixelWidth;
    MAKE128(trxRegCmd, GS_TRXREG, tmp);
    SWEADDCONTFAST(trxRegCmd);

    /* TrxDir */
    SWEADDCONTFAST(state.trxDir0GsCommand);

    /* Build a ref chain, where the last one is a refe(i) */
    ptr = pkt;
    src = srcAddress;
    for (i=0; i < pixelHeight; i++)
    {
        u_long128    dmaRefCmd;
        RwUInt32     eop, dmaID;

        /* count */
        *ptr++ = dmaTag1Direct;

        if (i < (pixelHeight-1))
        {
            /* Not the last line */
            eop = 0;
            dmaID = 3;

        }
        else
        {
            /* The last line */
            eop = 0;
            dmaID = 3;
        }

        /* DMAtag */
        tmp = /* NLOOP */ (qWordsInLine) |
            /* EOP */   (eop << 15) |
            /* FLG */   (2l << 58);
        MAKE128(gifTagCmd, 0l, tmp);
        *ptr++ = gifTagCmd;

        /* ref */
        tmp = (qWordsInLine) | (dmaID << 28) |
            ((RwUInt64)(RwUInt32)src << 32);
        tmp1 = (((0x50l<<24) | (qWordsInLine))<<32);
        MAKE128(dmaRefCmd, tmp1, tmp);
        *ptr++ = dmaRefCmd;
        src += srcStride;
    }
    tmp = (0xfl<<28) | 2;
    tmp1 = ((0x50l<<24) | 2l)<<32;
    MAKE128(ltmp, tmp1, tmp);
    *ptr++ = ltmp;

    tmp = /* NLOOP */ 1 |
        /* EOP */   (1<<15) |
        /* FLG */   (0l<<58) |
        /* NREG */ (1l<<60);
    tmp1 = 0xe;
    MAKE128(gifTagCmd, tmp1, tmp);
    *ptr++ = gifTagCmd;

    *ptr++ = state.texFlushGsCommand;

    /* Sync ref packet and source data out of DCache */
    SyncDCache(pkt, SCESYNCDCACHEROUNDUP(ptr));

    /* This will also add the command packet first */
    _sweAddPkt(pkt, SWE_PKT_GIF | SWE_PKT_LOCAL | SWE_PKT_CIRCALLOC |
               SWE_PKT_DMA_MODE_CHAIN);
    _sweFlush();

    RWRETURNVOID();
}

static void
_SkyUpLoadRectangle(RwUInt32 pixelWidth, RwUInt32 pixelHeight,
                RwUInt8 * srcAddress,
                RwUInt32 dstAddress, RwUInt32 dstWidthBy64,
                RwUInt32 bitsPerPixel, RwUInt32 pixelFormat,
                RwUInt32 srcStride)
{
    RwUInt8            *src8;
    RwUInt16           *src16;
    RwUInt32           *src32;
    RwUInt32            scanLineAdjust;
    RwUInt64            tmp, tmp1;
    u_long128           gifTagCmd, bitBltBufCmd, trxRegCmd;
    RwUInt64            qcount;
    RwUInt32            i, j, k, l;
    RwUInt64            bytesInRectangle =
        (pixelWidth * pixelHeight * bitsPerPixel) >> 3;

    RWFUNCTION(RWSTRING("_SkyUpLoadRectangle"));
    RWASSERT(pixelWidth);
    RWASSERT(pixelHeight);
    RWASSERT(srcAddress);
    RWASSERT(dstWidthBy64);

    /* If not qword aligned or not contiguous, create
     * a packet big enough to hold copy by value data.
     */
    /* Copy by value */
    sweCloseLocalPkt();
    /* Force creation of a packet of sufficient or max size */
    sweOpenLocalPkt(SWE_LPS_CONT, ((bytesInRectangle + 15) >> 4) + 7);

    /* Prepare the GS to accept the texture */
#if defined(GSB) && defined(GSPLUS)
    tmp = /* NLOOP */ 6l
        | /* EOP */ (0l << 15)
        | /* PRE */ (0l << 46)
        | /* FLG */ (0l << 58)
        | /* NREG */ (1l << 60);
    tmp1 = /* A+D */ (0xel << (64 - 64));
    MAKE128(gifTagCmd, tmp1, tmp);
    SWEADDCONTGIFFAST(gifTagCmd, 6);
#else /* defined(GSB) && defined(GSPLUS) */
    tmp = /* NLOOP */ 5l
        | /* EOP */ (0l << 15)
        | /* PRE */ (0l << 46)
        | /* FLG */ (0l << 58)
        | /* NREG */ (1l << 60);
    tmp1 = /* A+D */ (0xel << (64 - 64));
    MAKE128(gifTagCmd, tmp1, tmp);
    SWEADDCONTGIFFAST(gifTagCmd, 5);
#endif /* defined(GSB) && defined(GSPLUS) */

    /* Flush - It is uncertain that we need this */
    SWEADDCONTFAST(state.texFlushGsCommand);

    /* BitBltbuf */
    tmp = ((RwUInt64) pixelFormat << 56) |
        ((RwUInt64) dstWidthBy64 << 48) |
        ((RwUInt64) (dstAddress & 0x3fff) << 32) |
        ((RwUInt64) pixelFormat << 24) | (10l << 16); /* Doesn't matter - feeding from FIFO */
    MAKE128(bitBltBufCmd, GS_BITBLTBUF, tmp);
    SWEADDCONTFAST(bitBltBufCmd);

#if defined(GSB) && defined(GSPLUS)
    tmp = ((RwUInt64) (dstAddress & 0x1ffff) << 32);
    MAKE128(bitBltBufCmd, GS_BITBLTBUF2, tmp);
    SWEADDCONTFAST(bitBltBufCmd);
#endif /* defined(GSB) && defined(GSPLUS) */

    /* TrxPos */
    SWEADDCONTFAST(state.trxPos0GsCommand);

    /* TrxReg */
    tmp = ((RwUInt64) pixelHeight << 32) | (RwUInt64) pixelWidth;
    MAKE128(trxRegCmd, GS_TRXREG, tmp);
    SWEADDCONTFAST(trxRegCmd);

    /* TrxDir */
    SWEADDCONTFAST(state.trxDir0GsCommand);

    /* Now pack and write data to HWReg */
    /* We can't necessarily move the data in one chunk as NLOOP */
    /* is a 15 bit field, so max is 32767 words */
    /* However for efficiency we need to fit in a max size DMA */
    /* pkt so we back off the size a bit */

    /* How many quads do we need to write? */
    qcount = (bytesInRectangle + 15) >> 4;
    /* Set up a GifTag for writing raw to the GS Fifo */
    tmp =
        /* NLOOP */
        (qcount > SWE_LPS_MAX_IMG_QWC ? SWE_LPS_MAX_IMG_QWC : qcount)
#ifdef LESSEOPS
        | /* EOP */ (0 << 15)
#else /* LESSEOPS */
        | /* EOP */ ((qcount > SWE_LPS_MAX_IMG_QWC ? 1 : 1) << 15)
#endif /* LESSEOPS */
        | /* FLG */ (2l << 58);
    MAKE128(gifTagCmd, 0l, tmp);
    SWEADDCONTGIFFAST(gifTagCmd,
                      (qcount >
                       SWE_LPS_MAX_IMG_QWC ? SWE_LPS_MAX_IMG_QWC :
                       qcount));

    k = 0;                     /* Number of bytes accumulated */
    l = 0;
    tmp = 0;
    tmp1 = 0;

    src8 = (RwUInt8 *) srcAddress;
    src16 = (RwUInt16 *) srcAddress;
    src32 = (RwUInt32 *) srcAddress;
    scanLineAdjust = ((srcStride << 3) / bitsPerPixel) - pixelWidth;

#if (defined(RWVERBOSE))
    RWMESSAGE(("%d == bitsPerPixel", bitsPerPixel));
#endif /* (defined(RWVERBOSE)) */

    switch (bitsPerPixel)
    {
        case (4):
            for (i = 0; i < pixelHeight; i++)
            {
                for (j = 0; j < pixelWidth; j++)
                {
                    RwUInt64            pixel4;

                    /* Build two 64 bit words with 16 pixels each */
                    if (k & 4)
                    {
                        pixel4 = (((RwUInt64) * src8) >> 4) & 0xf;
                        src8++;
                    }
                    else
                    {
                        pixel4 = ((RwUInt64) * src8) & 0xf;
                    }

                    if (k < 64)
                    {
                        tmp |= pixel4 << k;
                    }
                    else
                    {
                        tmp1 |= pixel4 << (k - 64);
                    }
                    k += bitsPerPixel;

                    if (k >= 128)
                    {
                        u_long128           manyPixels;

                        /* Now combine into a 128 bit word with 8 pixels */
                        MAKE128(manyPixels, tmp1, tmp);
                        SWEADDCONTFAST(manyPixels);

                        l++;
                        if (l >= SWE_LPS_MAX_IMG_QWC)
                        {
                            qcount -= SWE_LPS_MAX_IMG_QWC;
                            if (qcount)
                            {
                                sweCloseLocalPkt();
                                sweOpenLocalPkt(SWE_LPS_CONT,
                                                qcount + 4);

                                tmp =
                                    /* NLOOP */
                                    (qcount
                                     >
                                     SWE_LPS_MAX_IMG_QWC ?
                                     SWE_LPS_MAX_IMG_QWC : qcount)
#ifdef LESSEOPS
                                    | /* EOP */ (0 << 15)
#else /* LESSEOPS */
                                    | /* EOP */
                                    ((qcount
                                      >
                                      SWE_LPS_MAX_IMG_QWC ? 1 : 1l) <<
                                     15)
#endif /* LESSEOPS */
                                    | /* FLG */ (2l << 58);
                                MAKE128(gifTagCmd, 0l, tmp);
                                SWEADDCONTGIFFAST(gifTagCmd,
                                                  (qcount >
                                                   SWE_LPS_MAX_IMG_QWC ?
                                                   SWE_LPS_MAX_IMG_QWC :
                                                   qcount));

                                l = 0;
                            }
                        }
                        k = 0;
                        tmp = 0;
                        tmp1 = 0;
                    }
                }              /*         for (j=0; j<pixelWidth; j++) */
                src8 += scanLineAdjust;
                src16 += scanLineAdjust;
                src32 += scanLineAdjust;
            }                  /*     for (i=0; i<pixelHeight; i++) */
            break;

        case (8):
            for (i = 0; i < pixelHeight; i++)
            {
                for (j = 0; j < pixelWidth; j++)
                {
                    /* Build two 64 bit words with 8 pixels each */
                    if (k < 64)
                    {
                        tmp |= ((RwUInt64) * src8) << k;
                    }
                    else
                    {
                        tmp1 |= ((RwUInt64) * src8) << (k - 64);
                    }
                    src8++;
                    k += bitsPerPixel;

                    if (k >= 128)
                    {
                        u_long128           manyPixels;

                        /* Now combine into a 128 bit word with 8 pixels */
                        MAKE128(manyPixels, tmp1, tmp);
                        SWEADDCONTFAST(manyPixels);

                        l++;
                        if (l >= SWE_LPS_MAX_IMG_QWC)
                        {
                            qcount -= SWE_LPS_MAX_IMG_QWC;
                            if (qcount)
                            {
                                sweCloseLocalPkt();
                                sweOpenLocalPkt(SWE_LPS_CONT,
                                                qcount + 4);

                                tmp =
                                    /* NLOOP */
                                    (qcount
                                     >
                                     SWE_LPS_MAX_IMG_QWC ?
                                     SWE_LPS_MAX_IMG_QWC : qcount)
#ifdef LESSEOPS
                                    | /* EOP */ (0 << 15)
#else /* LESSEOPS */
                                    | /* EOP */
                                    ((qcount
                                      >
                                      SWE_LPS_MAX_IMG_QWC ? 1 : 1l) <<
                                     15)
#endif /* LESSEOPS */
                                    | /* FLG */ (2l << 58);
                                MAKE128(gifTagCmd, 0l, tmp);
                                SWEADDCONTGIFFAST(gifTagCmd,
                                                  (qcount >
                                                   SWE_LPS_MAX_IMG_QWC ?
                                                   SWE_LPS_MAX_IMG_QWC :
                                                   qcount));

                                l = 0;
                            }
                        }
                        k = 0;
                        tmp = 0;
                        tmp1 = 0;
                    }
                }              /*         for (j=0; j<pixelWidth; j++) */
                src8 += scanLineAdjust;
                src16 += scanLineAdjust;
                src32 += scanLineAdjust;
            }                  /*     for (i=0; i<pixelHeight; i++) */
            break;

        case (16):
            for (i = 0; i < pixelHeight; i++)
            {
                for (j = 0; j < pixelWidth; j++)
                {
                    /* Build two 64 bit words with 4 pixels each */
                    if (k < 64)
                    {
                        tmp |= ((RwUInt64) * src16) << k;
                    }
                    else
                    {
                        tmp1 |= ((RwUInt64) * src16) << (k - 64);
                    }
                    src16++;
                    k += bitsPerPixel;

                    if (k >= 128)
                    {
                        u_long128           manyPixels;

                        /* Now combine into a 128 bit word with 8 pixels */
                        MAKE128(manyPixels, tmp1, tmp);
                        SWEADDCONTFAST(manyPixels);

                        l++;
                        if (l >= SWE_LPS_MAX_IMG_QWC)
                        {
                            qcount -= SWE_LPS_MAX_IMG_QWC;
                            if (qcount)
                            {
                                sweCloseLocalPkt();
                                sweOpenLocalPkt(SWE_LPS_CONT,
                                                qcount + 4);

                                tmp =
                                    /* NLOOP */
                                    (qcount
                                     >
                                     SWE_LPS_MAX_IMG_QWC ?
                                     SWE_LPS_MAX_IMG_QWC : qcount)
#ifdef LESSEOPS
                                    | /* EOP */ (0 << 15)
#else /* LESSEOPS */
                                    | /* EOP */
                                    ((qcount
                                      >
                                      SWE_LPS_MAX_IMG_QWC ? 1 : 1l) <<
                                     15)
#endif /* LESSEOPS */
                                    | /* FLG */ (2l << 58);
                                MAKE128(gifTagCmd, 0l, tmp);
                                SWEADDCONTGIFFAST(gifTagCmd,
                                                  (qcount >
                                                   SWE_LPS_MAX_IMG_QWC ?
                                                   SWE_LPS_MAX_IMG_QWC :
                                                   qcount));

                                l = 0;
                            }
                        }
                        k = 0;
                        tmp = 0;
                        tmp1 = 0;
                    }
                }              /*         for (j=0; j<pixelWidth; j++) */
                src8 += scanLineAdjust;
                src16 += scanLineAdjust;
                src32 += scanLineAdjust;
            }                  /*     for (i=0; i<pixelHeight; i++) */
            break;

        case (32):
        default:
            for (i = 0; i < pixelHeight; i++)
            {
                for (j = 0; j < pixelWidth; j++)
                {
                    /* Build two 64 bit words with 2 pixels each */
                    if (k < 64)
                    {
                        tmp |= ((RwUInt64) * src32) << k;
                    }
                    else
                    {
                        tmp1 |= ((RwUInt64) * src32) << (k - 64);
                    }
                    src32++;
                    k += bitsPerPixel;

                    if (k >= 128)
                    {
                        u_long128           manyPixels;

                        /* Now combine into a 128 bit word with 8 pixels */
                        MAKE128(manyPixels, tmp1, tmp);
                        SWEADDCONTFAST(manyPixels);

                        l++;
                        if (l >= SWE_LPS_MAX_IMG_QWC)
                        {
                            qcount -= SWE_LPS_MAX_IMG_QWC;
                            if (qcount)
                            {
                                sweCloseLocalPkt();
                                sweOpenLocalPkt(SWE_LPS_CONT,
                                                qcount + 4);

                                tmp =
                                    /* NLOOP */
                                    (qcount
                                     >
                                     SWE_LPS_MAX_IMG_QWC ?
                                     SWE_LPS_MAX_IMG_QWC : qcount)
#ifdef LESSEOPS
                                    | /* EOP */ (0 << 15)
#else /* LESSEOPS */
                                    | /* EOP */
                                    ((qcount
                                      >
                                      SWE_LPS_MAX_IMG_QWC ? 1 : 1l) <<
                                     15)
#endif /* LESSEOPS */
                                    | /* FLG */ (2l << 58);
                                MAKE128(gifTagCmd, 0l, tmp);
                                SWEADDCONTGIFFAST(gifTagCmd,
                                                  (qcount >
                                                   SWE_LPS_MAX_IMG_QWC ?
                                                   SWE_LPS_MAX_IMG_QWC :
                                                   qcount));

                                l = 0;
                            }
                        }
                        k = 0;
                        tmp = 0;
                        tmp1 = 0;
                    }
                }              /*         for (j=0; j<pixelWidth; j++) */
                src8 += scanLineAdjust;
                src16 += scanLineAdjust;
                src32 += scanLineAdjust;
            }                  /*     for (i=0; i<pixelHeight; i++) */
            break;
    }                          /*    switch (bitsPerPixel) */

    if (k != 0)
    {
        u_long128           manyPixels;

        /* Need to kick out remaining frag */
        MAKE128(manyPixels, tmp1, tmp);
        SWEADDCONTFAST(manyPixels);
    }

    /* Close potentially large packet to prevent realloc */
    sweCloseLocalPkt();

    /* We need to texflush here */
    sweOpenLocalPkt(SWE_LPS_CONT, 3);

    tmp = /* NLOOP */ 1 |
        /* EOP */ (1 << 15) |
        /* FLG */ (0l << 58) |
        /* NREG */ (1l << 60);
    tmp1 = 0xe;
    MAKE128(gifTagCmd, tmp1, tmp);
    SWEADDCONTGIFFAST(gifTagCmd, 1);

    SWEADDCONTFAST(state.texFlushGsCommand);

    sweCloseLocalPkt();

    _sweFlush();

    RWRETURNVOID();
}

static void
_SkyUploadRaster(RwRaster *raster, RwUInt32 texAddr)
{
    RwUInt64      miptbp1, miptbp2;
#if defined(GSB) && defined(GSPLUS)
    RwUInt64      miptbp3, miptbp4;
#endif /* defined(GSB) && defined(GSPLUS) */
    RwInt32       level;
    RwUInt32      offsetBy64[7], widthBy64[7];
    RwUInt32      bitsPerPixel, pixelFormat;
    RwBool        contiguous;
    _SkyRasterExt *rasExt = RASTEREXTFROMRASTER(raster);
    RwUInt8       *srcAddress = raster->cpPixels;

    RWFUNCTION(RWSTRING("_SkyUploadRaster"));
    RWASSERT(raster);

#ifdef RWMETRICS
    RWSRCGLOBAL(metrics)->numTextureUploads++;
#endif /* RWMETRICS */

#ifdef ALTTEXFORM
    if (rasExt->flags & 1)
    {
        RwUInt32  pktSize;
        unsigned long tmp, tmp1;
        u_long128 ltmp;
        unsigned long txadj;
        u_long128 *data1;
        RwUInt32 i;

        /* Do upload directly here */
        pktSize = ((RwUInt32*)(raster->originalPixels))[0];
        data1 = (u_long128*)(raster->originalPixels) + 1;

        /* The actual pkt is 3 qwc longer; dma tag, gif EOP, end tag */
#if (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO))
        {
            RwBool status;
            int size = -((pktSize<<2)+3);

            sweFinaliseOpenLocalPktMacro(status, ContiguousRectangleType, size);
        }
#else /* (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO)) */
        sweFinaliseOpenLocalPkt(ContiguousRectangleType, -((pktSize<<2)+4));
#endif /* (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO)) */

        txadj = (unsigned long)texAddr << 32;

        /* Now copy the prebuilt dma packet, offsetting the blit register */
        for (i=0; i<pktSize; i++)
        {
            /* Frist the dma tag */
            SWEADDCONTFAST(*data1);
            data1++;

            /* First the gif tag */
            SWEADDCONTFAST(*data1);
            data1++;

            /* Then the bitbltbuf reg */
            tmp = *(unsigned long *)data1;
            tmp1 = *((unsigned long *)data1+1);
            tmp += txadj;
            MAKE128(ltmp, tmp1, tmp);
            SWEADDCONTFAST(ltmp);
            data1++;

            /* then the ref */
            SWEADDCONTFAST(*data1);
            data1++;
        }
        tmp = (1l<<28) | 2l;
        tmp1 = ((0x50l << 24) | (2l)) << 32;
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);

        tmp = /* NLOOP */ 1 |
              /* EOP */  (1 << 15) |
              /* FLG */  (0l << 58) |
              /* NREG */ (1l << 60);
        tmp1 = 0xe;
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);

        SWEADDCONTFAST(state.texFlushGsCommand);

        tmp = (0xfl<<28);
        MAKE128(ltmp, 0l, tmp);
        SWEADDCONTFAST(ltmp);

#if (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO))
        {
            RwBool status;
            int size = 0;

            sweFinaliseOpenLocalPktMacro(status, SWE_LPS_CONT, size);
        }
#else /* (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO)) */
        sweFinaliseOpenLocalPkt(SWE_LPS_CONT, 0);
#endif /* (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO)) */
#ifdef RWMETRICS
        RWSRCGLOBAL(metrics)->sizeTextureUploads += rasExt->sysMemSize
                                                    + rasExt->sysMemPalSize;
#endif /* RWMETRICS */
    }
    else
#endif /* ALTTEXFORM */
    {
    /* start of old style, unindented section */

    bitsPerPixel = raster->depth;
    pixelFormat = (rasExt->lsb >> 20) & 0x3f;

    contiguous =
        (raster->stride == ((raster->width*(RwInt32)bitsPerPixel)>>3));

    switch (rasExt->maxMipLevel>>2)
    {
        default: /* Be safe */
        case (6): case (5): case (4):
            miptbp2 = ( rasExt->miptbp2Lsb |
                        ((RwUInt64)rasExt->miptbp2Msb << 32) );
#if defined(GSB) && defined(GSPLUS)
            miptbp4 = ( rasExt->miptbp4Lsb |
                        ((RwUInt64)rasExt->miptbp4Msb << 32) );
            offsetBy64[4] = (miptbp4 >>  0) & 0x1ffff;
            offsetBy64[5] = (miptbp4 >> 20) & 0x1ffff;
            offsetBy64[6] = (miptbp4 >> 40) & 0x1ffff;
#else /* defined(GSB) && defined(GSPLUS) */
            offsetBy64[4] = (miptbp2 >>  0) & 0x3fff;
            offsetBy64[5] = (miptbp2 >> 20) & 0x3fff;
            offsetBy64[6] = (miptbp2 >> 40) & 0x3fff;
#endif /* defined(GSB) && defined(GSPLUS) */
            widthBy64[4] = (miptbp2 >> 14) & 0x3f;
            widthBy64[5] = (miptbp2 >> 34) & 0x3f;
            widthBy64[6] = (miptbp2 >> 54) & 0x3f;
            /* Drop through to next case */
        case (3): case (2): case (1):
            miptbp1 = ( rasExt->miptbp1Lsb |
                        ((RwUInt64)rasExt->miptbp1Msb << 32) );
#if defined(GSB) && defined(GSPLUS)
            miptbp3 = ( rasExt->miptbp3Lsb |
                        ((RwUInt64)rasExt->miptbp3Msb << 32) );
            offsetBy64[1] = (miptbp3 >>  0) & 0x1ffff;
            offsetBy64[2] = (miptbp3 >> 20) & 0x1ffff;
            offsetBy64[3] = (miptbp3 >> 40) & 0x1ffff;
#else /* defined(GSB) && defined(GSPLUS) */
            offsetBy64[1] = (miptbp1 >>  0) & 0x3fff;
            offsetBy64[2] = (miptbp1 >> 20) & 0x3fff;
            offsetBy64[3] = (miptbp1 >> 40) & 0x3fff;
#endif /* defined(GSB) && defined(GSPLUS) */
            widthBy64[1] = (miptbp1 >> 14) & 0x3f;
            widthBy64[2] = (miptbp1 >> 34) & 0x3f;
            widthBy64[3] = (miptbp1 >> 54) & 0x3f;
            /* Drop through to next case */
        case (0):
            offsetBy64[0] = 0;
            widthBy64[0] = (rasExt->lsb >> 14) & 0x3f;
            break;
    }

    /* Upload each of the mipmap levels */
    for (level = 0; level <= (rasExt->maxMipLevel>>2); level++)
    {
        RwUInt32 mipWidth = (raster->width>>level);
        RwUInt32 mipHeight = (raster->height>>level);
        RwUInt32 mipStride = (raster->stride>>level);
        RwUInt32 dstAddress = (texAddr + offsetBy64[level]);
        RwBool   aligned = (!((RwUInt32)srcAddress & 0xf));

#ifdef RWMETRICS
        RWSRCGLOBAL(metrics)->sizeTextureUploads
            += (mipWidth * mipHeight * bitsPerPixel) >> 3;
#endif /* RWMETRICS */
        if (aligned)
        {
            /* Aligned - choose whether to use ref per scanline or not */
            if (contiguous)
            {
                if (!rasExt->mipUploadPkts[level])
                {
                    rasExt->mipUploadPkts[level] =
                        _SkyBuildPktForUpLoadAlignedContiguousRectangle(mipWidth,
                                                                    mipHeight,
                                                                    srcAddress,
                                                                    bitsPerPixel,
                                                                    rasExt->cachePkts);
                }
#if 1
                if ((rasExt->flags & 2)
                        && (((rasExt->lsb>>20)&0x3f) == SCE_GS_PSMT8)
                        && (mipStride >= 16))
                {
                    _SkyDdispatchUpLoadAlignedContiguousRectangle(mipWidth>>1,
                                                 mipHeight>>1,
                                                 dstAddress,
                                                 widthBy64[level] >>1,
                                                 SCE_GS_PSMCT32,
                                                 rasExt->mipUploadPkts[level],
                                                 rasExt->cachePkts);
                }
                else if ((rasExt->flags & 4)
                        && (((rasExt->lsb>>20)&0x3f) == SCE_GS_PSMT4)
                        && (mipStride >= 16))
                {
                    _SkyDdispatchUpLoadAlignedContiguousRectangle(mipWidth>>1,
                                                 mipHeight>>1,
                                                 dstAddress,
                                                 widthBy64[level] >>1,
                                                 SCE_GS_PSMCT16,
                                                 rasExt->mipUploadPkts[level],
                                                 rasExt->cachePkts);
                }
                else
#endif
                {
                    _SkyDdispatchUpLoadAlignedContiguousRectangle(mipWidth,
                                                 mipHeight,
                                                 dstAddress,
                                                 widthBy64[level],
                                                 pixelFormat,
                                                 rasExt->mipUploadPkts[level],
                                                 rasExt->cachePkts);
                }
                if (!rasExt->cachePkts)
                {
                    rasExt->mipUploadPkts[level] = (u_long128 *)NULL;
                }

            }
            else
            {
#if 1
                if ((rasExt->flags & 2)
                        && (((rasExt->lsb>>20)&0x3f) == SCE_GS_PSMT8)
                        && (mipStride >= 16))
                {
                    _SkyUpLoadAlignedRectangle(mipWidth>>1, mipHeight>>1,
                                           srcAddress, dstAddress,
                                           widthBy64[level]>>1,
                                           32, SCE_GS_PSMCT32,
                                           mipStride);
                }
                else if ((rasExt->flags & 4)
                        && (((rasExt->lsb>>20)&0x3f) == SCE_GS_PSMT4)
                        && (mipStride >= 16))
                {
                    _SkyUpLoadAlignedRectangle(mipWidth>>1, mipHeight>>1,
                                           srcAddress, dstAddress,
                                           widthBy64[level]>>1,
                                           16, SCE_GS_PSMCT16,
                                           mipStride);
                }
                else
#endif
                {
                    /* Load aligned non contiguous (ref per scanline) */
                    _SkyUpLoadAlignedRectangle(mipWidth, mipHeight, srcAddress,
                                           dstAddress, widthBy64[level],
                                           bitsPerPixel, pixelFormat,
                                           mipStride);
                }
            }
        }
        else
        {
#if 1
            if ((rasExt->flags & 2)
                    && (((rasExt->lsb>>20)&0x3f) == SCE_GS_PSMT8)
                    && (mipStride >= 16))
            {
                _SkyUpLoadRectangle(mipWidth>>1, mipHeight>>1, srcAddress,
                                dstAddress, widthBy64[level]>>1, 32,
                                SCE_GS_PSMCT32, mipStride);
            }
            else if ((rasExt->flags & 4)
                    && (((rasExt->lsb>>20)&0x3f) == SCE_GS_PSMT4)
                    && (mipStride >= 16))
            {
                _SkyUpLoadRectangle(mipWidth>>1, mipHeight>>1, srcAddress,
                                dstAddress, widthBy64[level]>>1, 16,
                                SCE_GS_PSMCT16, mipStride);
            }
            else
#endif
            {
                /* Not aligned, load by value */
                _SkyUpLoadRectangle(mipWidth, mipHeight, srcAddress, dstAddress,
                                widthBy64[level], bitsPerPixel, pixelFormat,
                                mipStride);
            }
        }

        /* Breaks if we upload mipmapped subrastered texture
           - but we're not going to do that, are we... :-) */
        srcAddress += mipStride * mipHeight;

        /* Align for later mip levels */
        if((RwUInt32)srcAddress & 0xf)
        {
            srcAddress = (RwUInt8 *)(((RwUInt32)srcAddress + 15) & ~15);
        }
    }

    /* Upload a palette if applicable */
    if ((rasExt->sysMemPalSize) && (raster->depth <= 8))
    {
        static const RwUInt32 palWidths[8] = {8, 8, 8, 8, 16, 16, 16, 16};
        static const RwUInt32 palHeights[8] = {2, 2, 2, 2, 16, 16, 16, 16};
        RwInt32  palBPP;
        RwUInt32 dstAddress, palWidth, palHeight;
        RwUInt32 format = (rasExt->msb >> (51-32)) & 0xF;

        dstAddress = (texAddr + rasExt->palOffset);
        palWidth = palWidths[raster->depth-1];
        palHeight = palHeights[raster->depth-1];

        switch (format)
        {
            case (PSMCT32):
                {
                    palBPP = 32;
                    break;
                }
            case (PSMCT16):
            case (PSMCT16S):
            default:
                {
                    palBPP = 16;
                    break;
                }
        }

        if (!rasExt->palUploadPkt)
        {
            rasExt->palUploadPkt =
                _SkyBuildPktForUpLoadAlignedContiguousRectangle(palWidth,
                                                            palHeight,
                                                            raster->palette,
                                                            palBPP,
                                                            rasExt->cachePkts);
        }

        _SkyDdispatchUpLoadAlignedContiguousRectangle(palWidth, palHeight,
                                                 dstAddress, 1, format,
                                                 rasExt->palUploadPkt,
                                                 rasExt->cachePkts);

        if (!rasExt->cachePkts)
        {
            rasExt->palUploadPkt = (u_long128 *)NULL;
        }
#ifdef RWMETRICS
        RWSRCGLOBAL(metrics)->sizeTextureUploads
            += (palWidth * palHeight * palBPP) >> 3;
#endif /* RWMETRICS */
    }
    /* end of old style, unindented section */
    }

    /* inc ref count and add a dec ref count packet */
    /* This has to be done with interupts disabled */
    DI();

    RWASSERT(0<= rasExt->dmaRefCount);
    rasExt->dmaRefCount++;

    EI();
#if 0
    _sweAddPkt(&(rasExt->dmaRefCount), SWE_PKT_RASUREF);
#else
    rasExt->dmaClrCount += 1;
    if (rasExt->dmaClrCount == 1)
    {
        _sweProcrastinatedAddURef((unsigned int*)&(rasExt->dmaRefCount));
    }
#endif

    RWASSERT(0<= rasExt->dmaRefCount);

    RWRETURNVOID();
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                           Memory functions

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/****************************************************************************
 _SkyInsertIntoFreeList

 Inserts the empty block into the free list at the correct point

 On entry   : Memory cache
            : Block
 On exit    :
 */

static void
_SkyInsertIntoFreeList(_SkyMemory *mpMem, _SkyMemBlock *block)
{
    RwLLLink        *cur, *end;
    _SkyMemBlock   *curBlock;
    RWFUNCTION(RWSTRING("_SkyInsertIntoFreeList"));
    RWASSERT(mpMem);
    RWASSERT(block);

    cur = rwLinkListGetFirstLLLink(&mpMem->llFreeMem);
    end = rwLinkListGetTerminator(&mpMem->llFreeMem);

    /* If the block is larger than median, we start from the small end
       and place it as soon as it is greater or equal */
    /* If the block is smaller then start from big end and work down
       (so equal size blocks are FIFO) */

    while (cur != end)
    {
        curBlock = rwLLLinkGetData(cur, _SkyMemBlock, lCacheList);

        if (curBlock->nSize >= block->nSize)
        {
            break;
        }

        /* Next */
        cur = rwLLLinkGetNext(cur);
    }

    /* Attach it to the very end of the list */
    cur = rwLLLinkGetPrevious(cur);

    /* printf("Insert Block %x Size %d\n", block->nAddr, block->nSize); */

    /* Need to attach before cur block */
    rwLinkListAddLLLink(((RwLinkList *)cur) , &block->lCacheList);

    RWRETURNVOID();
}

/****************************************************************************
 _SkyMemBlockUseEnd

 Uses the end of this block

 On entry   : Block
            : Size
 On exit    :
 */

static void
_SkyMemoryUseMemBlockEnd(_SkyMemory *mpMem, _SkyMemBlock *mbpBlock,
                         RwInt32 nSize)
{
    RWFUNCTION(RWSTRING("_SkyMemoryUseMemBlockEnd"));
    RWASSERT(mpMem);
    RWASSERT(mbpBlock);
    RWASSERT(mbpBlock != &cGCache.mMemory.mbDummy);

    /* Remove from the free list */
    rwLinkListRemoveLLLink(&mbpBlock->lCacheList);
    rwLLLinkInitialize(&mbpBlock->lCacheList);
    mbpBlock->cUsed = TRUE;
    mbpBlock->nCacheList = TEXCACHELISTNONE;

    /* We have found a suitable block -> we need to release remaining
     * memory, because it is oversize
     */
    if (mbpBlock->nSize != nSize)
    {
        _SkyMemBlock *mbpRemaining;
        RwLLLink     *lpLink;

        /* Remove first entry from free list */
        lpLink = rwLinkListGetFirstLLLink(&mpMem->llUnusedBlocks);

        /* Can we subdivide memory anymore */
        if (lpLink != rwLinkListGetTerminator(&mpMem->llUnusedBlocks))
        {
            rwLinkListRemoveLLLink(lpLink);

#ifdef RWDEBUG
            skyGCurrentTexMemBlocks++;
            if (skyGCurrentTexMemBlocks > skyGMaxTexMemBlocks)
            {
                skyGMaxTexMemBlocks = skyGCurrentTexMemBlocks;
            }
#endif /* RWDEBUG */

            mbpRemaining = (_SkyMemBlock *) rwLLLinkGetData(lpLink, _SkyMemBlock, lCacheList);

            /* This is free */
            RWASSERT(mbpRemaining != &cGCache.mMemory.mbDummy);
            mbpRemaining->cUsed = FALSE;

            /* Release the remaining */
            mbpRemaining->rpRas = (RwRaster *)NULL;
            mbpRemaining->nAddr = mbpBlock->nAddr;
            mbpBlock->nAddr = mbpRemaining->nAddr + mbpBlock->nSize - nSize;

            mbpRemaining->nSize = mbpBlock->nSize - nSize;
            mbpBlock->nSize = nSize;

            /* Link it into the chain */
            mbpRemaining->mbpNext = mbpBlock;
            mbpRemaining->mbpPrev = mbpBlock->mbpPrev;

            mbpBlock->mbpPrev = mbpRemaining;
            (mbpRemaining->mbpPrev)->mbpNext = mbpRemaining;

            rwLLLinkInitialize(&mbpRemaining->lUsageList);

            /* Correct the blocks size */

            /* Link it into the free list in the right place */
            _SkyInsertIntoFreeList(mpMem, mbpRemaining);
        }
        else
        {
            RWMESSAGE(("TexCache MemBlock Pool Exhausted\n"));
        }
    }

    RWRETURNVOID();
}

/****************************************************************************
 _SkyMemBlockUse

 Uses the start of this block

 On entry   : Block
            : Size
 On exit    :
 */

static void
_SkyMemoryUseMemBlock(_SkyMemory *mpMem, _SkyMemBlock *mbpBlock, RwInt32 nSize)
{
    RWFUNCTION(RWSTRING("_SkyMemoryUseMemBlock"));
    RWASSERT(mpMem);
    RWASSERT(mbpBlock);
    RWASSERT(mbpBlock != &cGCache.mMemory.mbDummy);

    /* Remove from the free list */
    rwLinkListRemoveLLLink(&mbpBlock->lCacheList);
    rwLLLinkInitialize(&mbpBlock->lCacheList);
    mbpBlock->cUsed = TRUE;
    mbpBlock->nCacheList = TEXCACHELISTNONE;

    /* We have found a suitable block -> we need to release remaining
     * memory, because it is oversize
     */
    if (mbpBlock->nSize != nSize)
    {
        _SkyMemBlock *mbpRemaining;
		RwLLLink	 *lpLink;

		/* Remove first entry from free list */
		lpLink = rwLinkListGetFirstLLLink(&mpMem->llUnusedBlocks);

		/* Can we subdivide memory anymore */
		if (lpLink != rwLinkListGetTerminator(&mpMem->llUnusedBlocks))
		{
			rwLinkListRemoveLLLink(lpLink);

#ifdef RWDEBUG
			skyGCurrentTexMemBlocks++;
			if (skyGCurrentTexMemBlocks > skyGMaxTexMemBlocks)
			{
				skyGMaxTexMemBlocks = skyGCurrentTexMemBlocks;
			}
#endif /* RWDEBUG */

			mbpRemaining = (_SkyMemBlock *) rwLLLinkGetData(lpLink, _SkyMemBlock, lCacheList);

			/* This is free */
			RWASSERT(mbpRemaining != &cGCache.mMemory.mbDummy);
			mbpRemaining->cUsed = FALSE;

			/* Release the remaining */
			mbpRemaining->rpRas = (RwRaster *)NULL;
			mbpRemaining->nAddr = mbpBlock->nAddr + nSize;
			mbpRemaining->nSize = mbpBlock->nSize - nSize;

			/* Link it into the chain */
			mbpRemaining->mbpNext = mbpBlock->mbpNext;
			mbpRemaining->mbpPrev = mbpBlock;

			(mbpBlock->mbpNext)->mbpPrev = mbpRemaining;
			mbpBlock->mbpNext = mbpRemaining;

			rwLLLinkInitialize(&mbpRemaining->lUsageList);

			/* Correct the blocks size */
			mbpBlock->nSize = nSize;

			/* Link it into the free list in the right place */
			_SkyInsertIntoFreeList(mpMem, mbpRemaining);
		}
		else
		{
			RWMESSAGE(("TexCache MemBlock Pool Exhausted\n"));
		}
    }

    RWRETURNVOID();
}

/****************************************************************************
 _SkyMemoryAllocEnd

 On entry   : Memory
            : Size
 On exit    : Block (or NULL on error)

 We alloc a space at the top of memory that isn't already taken by a
 locked texture
 */

static _SkyMemBlock *
_SkyMemoryAllocEnd(_SkyMemory *mpMem, RwInt32 nSize)
{
    _SkyMemBlock *cur,*first;
    _SkyMemBlock *mbpBlock;
    _SkyMemBlock *mbMergeBlock;
    int csize = 0;

    RWFUNCTION(RWSTRING("_SkyMemoryAllocEnd"));
    RWASSERT(mpMem);

    /* Grab the total memory block list */
    first = mpMem->mbDummy.mbpNext;
    cur = mpMem->mbDummy.mbpPrev;

    csize = cur->nSize;
    mbpBlock = cur;

    /* Search of the top most block not occupied by a locked texture */
    if (cur->rpRas)
    {
        if (RASTEREXTFROMRASTER(mbpBlock->rpRas)->bLocked)
        {
            csize = 0;
        }
    }
    while (cur != first)
    {
        while (cur != first)
        {
            mbpBlock = cur;

            /* Who is using this block? */
            if (mbpBlock->rpRas)
            {
                if (RASTEREXTFROMRASTER(mbpBlock->rpRas)->bLocked)
                {
                    cur = cur->mbpPrev;
                    mbpBlock = cur;
                    continue;
                }
                else
                {
                    break;
                }
            }
            break;
        }
        /* We have a candidate block? */
        if (mbpBlock->rpRas)
        {
            if (RASTEREXTFROMRASTER(mbpBlock->rpRas)->bLocked)
            {
                /* No */
                break;
            }
        }
        /* Yes */
        csize = mbpBlock->nSize;
        while ((csize < nSize) && (cur != first))
        {
            /* Block is too small so we merge with the previous block if
               possible */
            cur = cur->mbpPrev;
            mbpBlock = cur;
            if (mbpBlock->rpRas)
            {
                if (RASTEREXTFROMRASTER(mbpBlock->rpRas)->bLocked)
                {
                    /* We can't extend as we've run into a locked texture */
                    break;
                }
            }
            csize += mbpBlock->nSize;
        }

        /* We have a location? */
        if (csize >= nSize)
        {
            /* Yes */
            break;
        }
        /* No */
    }

    /* At this point, we either have a run of memory blocks which will be
       big enough when freed and merged, or we failed */
    if (csize < nSize)
    {
        /* A suitable block could not be found */
        RWRETURN((_SkyMemBlock *)NULL);
    }

    /* Free blocks and coalesce */
    csize = 0;
    mbMergeBlock = mbpBlock;
    while (csize < nSize)
    {
        if (mbMergeBlock->rpRas)
        {
            mbpBlock = _SkyCacheReleaseCacheEntry(mbMergeBlock);
        }
        csize = mbpBlock->nSize;
        mbMergeBlock = mbpBlock->mbpNext;
    }

    /* At this point we have a block of >= size. We want to use the top
       part of it */
    _SkyMemoryUseMemBlockEnd(mpMem,mbpBlock,nSize);

    RWRETURN(mbpBlock);
}

/****************************************************************************
 _SkyMemoryAlloc

 On entry   : Memory
            : Size
 On exit    : Block (or NULL on error)
 */

static _SkyMemBlock *
_SkyMemoryAlloc(_SkyMemory *mpMem, RwInt32 nSize)
{
    RwLLLink *cur,*end;
    _SkyMemBlock *mbpBlock;

    RWFUNCTION(RWSTRING("_SkyMemoryAlloc"));
    RWASSERT(mpMem);

    /* Search the freelist for a block of the appropriate size */
    cur = rwLinkListGetFirstLLLink(&mpMem->llFreeMem);
    end = rwLinkListGetTerminator(&mpMem->llFreeMem);

    while (cur!=end)
    {
        /* Get the block pointer */
        mbpBlock = rwLLLinkGetData(cur, _SkyMemBlock, lCacheList);
        if (mbpBlock->nSize >= nSize)
        {
            /* printf("Alloc %x Size %d\n", mbpBlock->nAddr, nSize); */

            _SkyMemoryUseMemBlock(mpMem,mbpBlock,nSize);

            RWRETURN(mbpBlock);
        }

        /* Next */
        cur = rwLLLinkGetNext(cur);
    }

    /* skyDumpFreeList(); */

    /* A suitable block could not be found */
    RWRETURN((_SkyMemBlock *)NULL);
}

/****************************************************************************
 _SkyMemoryFree

 On entry   : Memory
            : Block
 On exit    : The actual block freed
 */

static _SkyMemBlock *
_SkyMemoryFree(_SkyMemory *mpMem, _SkyMemBlock *mbpBlock)
{
    RWFUNCTION(RWSTRING("_SkyMemoryFree"));
    RWASSERT(mpMem);
    RWASSERT(mbpBlock);
    RWASSERT(mbpBlock != &cGCache.mMemory.mbDummy);
    RWASSERT(mbpBlock->cUsed);

    /* This block is no longer used */
    mbpBlock->cUsed = FALSE;

    /* Combine with previous if we can */
    if (!(mbpBlock->mbpPrev)->cUsed)
    {
        _SkyMemBlock *mbpPrev = mbpBlock->mbpPrev;

        rwLinkListRemoveLLLink(&mbpPrev->lCacheList);
        rwLLLinkInitialize(&mbpPrev->lCacheList);

        /* Merge with the previous block */
        mbpPrev->nSize += mbpBlock->nSize;

        /* Link into chain */
        mbpPrev->mbpNext = mbpBlock->mbpNext;
        (mbpBlock->mbpNext)->mbpPrev = mbpPrev;

        /* Free the spare memory block */
		rwLinkListAddLLLink(&mpMem->llUnusedBlocks, &mbpBlock->lCacheList);
#ifdef RWDEBUG
		skyGCurrentTexMemBlocks--;
#endif /* RWDEBUG */

        /* The block now becomes the previous block */
        mbpBlock = mbpPrev;
    }

    /* Combine next if we can */
    if (!((mbpBlock->mbpNext)->cUsed))
    {
        _SkyMemBlock *mbpNext = mbpBlock->mbpNext;

        rwLinkListRemoveLLLink(&mbpNext->lCacheList);
        rwLLLinkInitialize(&mbpNext->lCacheList);

        mbpNext->nAddr -= mbpBlock->nSize;
        mbpNext->nSize += mbpBlock->nSize;

        /* Link into chain */
        mbpNext->mbpPrev = mbpBlock->mbpPrev;
        (mbpBlock->mbpPrev)->mbpNext = mbpNext;

        /* Free the spare memory block */
		rwLinkListAddLLLink(&mpMem->llUnusedBlocks, &mbpBlock->lCacheList);
#ifdef RWDEBUG
		skyGCurrentTexMemBlocks--;
#endif /* RWDEBUG */

        /* The block now becomes the previous block */
        mbpBlock = mbpNext;
    }

    /* Put the new block in the right place in the list */
    _SkyInsertIntoFreeList(mpMem, mbpBlock);

    /* Done */
    RWRETURN(mbpBlock);
}

#ifdef NOTDEFINED
static _SkyMemory  *
_SkyMemoryPurge(_SkyMemory * mpMem)
{
    RWFUNCTION(RWSTRING("_SkyMemoryPurge"));

    if (mpMem)
    {
        RwFreeList         *flpBlocks = mpMem->flpBlocks;

        if (flpBlocks)
        {
            RwLLLink           *cur =
                rwLinkListGetFirstLLLink(&mpMem->llFreeMem);
            RwLLLink           *const end =
                rwLinkListGetTerminator(&mpMem->llFreeMem);

            while (cur != end)
            {
                _SkyMemBlock       *curBlock =
                    rwLLLinkGetData(cur, _SkyMemBlock, lCacheList);

                /* Next */
                cur = rwLLLinkGetNext(cur);

                if (NULL != curBlock)
                {
                    RwFreeListFree(flpBlocks, curBlock);
                    curBlock = NULL;
                }
            }

            RwFreeListDestroy(flpBlocks);
            flpBlocks = NULL;
            mpMem->flpBlocks = flpBlocks;
        }
    }

    RWRETURN(mpMem);
}
#endif

/****************************************************************************
 _SkyMemoryRelease

 On entry   : Memory
 On exit    :
 */

static void
_SkyMemoryRelease(_SkyMemory *mpMem)
{
    RWFUNCTION(RWSTRING("_SkyMemoryRelease"));
    RWASSERT(mpMem);

    if (mpMem->bpBlocks)
    {
        RwFree(mpMem->bpBlocks);
        mpMem->bpBlocks = (_SkyMemBlock *)NULL;
    }

    RWRETURNVOID();
}

/****************************************************************************
 _SkyMemoryInitialise

 On entry   : Memory
            : Addr
            : Size
 On exit    : TRUE on success
 */

static RwBool
_SkyMemoryInitialise(_SkyMemory *mpMem, RwUInt32 nAddr, RwUInt32 nSize)
{
    _SkyMemBlock *        mbpBlock;
    RwUInt32		  nIndex;

    RWFUNCTION(RWSTRING("_SkyMemoryInitialise"));
    RWASSERT(mpMem);

    /* Allocate the maximum number of GS memory fragments that will ever exist */
    mpMem->bpBlocks = (_SkyMemBlock *)
        RwMalloc(sizeof(_SkyMemBlock) * skyGNumTexMemBlocks);
    RWASSERT(mpMem->bpBlocks);

    /* We start of with a single fragment in use */
    mbpBlock = &mpMem->bpBlocks[0];

    /* Build something approaching a freelist from the rest */
    rwLinkListInitialize(&mpMem->llUnusedBlocks);
    for (nIndex=1; nIndex<skyGNumTexMemBlocks; nIndex++)
    {
        rwLinkListAddLLLink(&mpMem->llUnusedBlocks,
                            &mpMem->bpBlocks[nIndex].lCacheList);
    }

#ifdef RWDEBUG
    skyGCurrentTexMemBlocks=1;
    skyGMaxTexMemBlocks=1;
#endif /* RWDEBUG */

    /* Set up the simple Free Mem list */
    rwLinkListInitialize(&mpMem->llFreeMem);

    /* Set up the dummy terminator block */
    cGCache.mMemory.mbDummy.cUsed = TRUE;
    cGCache.mMemory.mbDummy.rpRas = (RwRaster *)NULL;
    cGCache.mMemory.mbDummy.nAddr = 0;
    cGCache.mMemory.mbDummy.nSize = 0;
    cGCache.mMemory.mbDummy.mbpPrev = mbpBlock;
    cGCache.mMemory.mbDummy.mbpNext = mbpBlock;

    /* Set up the block */
    mbpBlock->mbpPrev = &cGCache.mMemory.mbDummy;
    mbpBlock->mbpNext = &cGCache.mMemory.mbDummy;

    mbpBlock->rpRas = (RwRaster *)NULL;
    mbpBlock->nAddr = nAddr;
    mbpBlock->nSize = nSize;

    RWASSERT(mbpBlock != &cGCache.mMemory.mbDummy);
    mbpBlock->cUsed = FALSE;
    mbpBlock->rpRas = (RwRaster *)NULL;

    rwLLLinkInitialize(&mbpBlock->lUsageList);

    /* Attach the block to the free list */
    rwLinkListAddLLLink(&mpMem->llFreeMem,&mbpBlock->lCacheList);

    /* All done */
    RWRETURN(TRUE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                            Cache handling

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/****************************************************************************
 _SkyCacheReleaseCacheEntry

 Removes the entry and frees any memory used by the entry.

 On entry   : Cache
            : Cache entry
 On exit    : The memory block pointer (after heap merge)
 */

static _SkyMemBlock *
_SkyCacheReleaseCacheEntry(_SkyMemBlock *mpBlock)
{
    RwRaster       *raster = mpBlock->rpRas;
    _SkyRasterExt  *rasExt = RASTEREXTFROMRASTER(raster);

    RWFUNCTION(RWSTRING("_SkyCacheReleaseCacheEntry"));
    RWASSERT(mpBlock);
    RWASSERT(raster);
    RWASSERT(rasExt);

    /* printf("raster %x, rasext %x\n", raster, rasExt); */
    /* if (mpBlock)
       {
       printf("mpblock ok %x\n", mpBlock);
       } */

    /* Mark the raster as no longer in cache */
    mpBlock->rpRas = (RwRaster *)NULL;
    rasExt->mpCacheEntry = (_SkyMemBlock *)NULL;

    /* Take it out of any cache list it may be in */
    if (rwLLLinkAttached(&mpBlock->lCacheList))
    {
        /* If this list is now empty then
         * hand it back to the available list */
        if ((mpBlock->lCacheList.next == mpBlock->lCacheList.prev) &&
            (mpBlock->nCacheList >= 0))
        {
            /* printf("Freed Cache List to %d\n", mpBlock->nSize); */

            cGCache.claCacheLists[mpBlock->nCacheList].bUsed=FALSE;
        }
        mpBlock->nCacheList=TEXCACHELISTNONE;
        rwLinkListRemoveLLLink(&mpBlock->lCacheList);
        rwLLLinkInitialize(&mpBlock->lCacheList);
    }

    /* And remove it from frame calculations */
    if (rwLLLinkAttached(&mpBlock->lUsageList))
    {
        /* printf("Usage Released Ras %x Block %x Size %d\n", mpBlock->rpRas,
           mpBlock->nAddr, mpBlock->nSize); */
        rwLinkListRemoveLLLink(&mpBlock->lUsageList);
        rwLLLinkInitialize(&mpBlock->lUsageList);
    }

    if (cGCache.rpCurrentRaster == raster)
    {
        /* It certainly isn't the current raster any more */
        cGCache.rpCurrentRaster = (RwRaster *)NULL;
    }

    /* Free up the memory used by the cache entry */
    mpBlock = _SkyMemoryFree(&cGCache.mMemory, mpBlock);

    RWRETURN(mpBlock);
}

/***************************************************************************
 _SkyCacheFlushUnused

Tidy up them there cache lists

 On entry   :
 On entry   :
 */
static RwBool
_SkyCacheFlushUnused(void)
{
    RwLLLink *cur = rwLinkListGetFirstLLLink(&cGCache.llUnusedTextures);
    RwLLLink *end   = rwLinkListGetTerminator(&cGCache.llUnusedTextures);
    RWFUNCTION(RWSTRING("_SkyCacheFlushUnused"));

    if (cur!=end)
    {

        /* skyDumpUnusedList(); */

        while (cur!=end)
        {
            _SkyMemBlock *mbBlock = rwLLLinkGetData(cur, _SkyMemBlock,
                                                    lUsageList);
            cur = rwLLLinkGetNext(cur);

            /* printf("Freed unused ras %x block %x size %d\n", mbBlock->rpRas,
               mbBlock->nAddr, mbBlock->nSize); */

            _SkyCacheReleaseCacheEntry(mbBlock);
        }
        RWRETURN(TRUE);
    }
    RWRETURN(FALSE);
}

/***************************************************************************
 _SkyCacheRasterAccess

Insert a cache entry into the correct cache lists

 On entry   : Size in Tex memory required
 On entry   :
 */
static void
_SkyCacheRasterAccess(_SkyMemBlock *mbpBlock)
{
    _SkyCacheList *clpCacheList = (_SkyCacheList *)NULL;
    RWFUNCTION(RWSTRING("_SkyCacheRasterAccess"));
    RWASSERT(mbpBlock);

    /* Get it out of anything else */
    if (rwLLLinkAttached(&mbpBlock->lCacheList))
    {
        rwLinkListRemoveLLLink(&mbpBlock->lCacheList);
        rwLLLinkInitialize(&mbpBlock->lCacheList);
    }

    switch (mbpBlock->nCacheList)
    {
        case TEXCACHELISTNONE:
            {
                /* Need to search the array for the correct size */
                RwInt32 nIndex;
                RwInt32 nFreeCache = TEXCACHELISTBUCKET;

                _SkyCacheList *clpTempCache = cGCache.claCacheLists;
                for(nIndex=0; nIndex < skyMAXCACHELISTS; nIndex++)
                {
                    if (clpTempCache->bUsed)
                    {
                        if (clpTempCache->nListSize==mbpBlock->nSize)
                        {
                            /* printf ("Matched size\n"); */

                            mbpBlock->nCacheList = nIndex;
                            clpCacheList = clpTempCache;
                            break;
                        }
                    }
                    else
                    {
                        nFreeCache = nIndex;
                    }
                    /* Yikes - pointer arithmetic! */
                    clpTempCache++;
                }

                if (!clpCacheList)
                {
                    /* No Cache List this size, do we have one free */
                    if (nFreeCache>=0)
                    {
                        /* printf("Allocated Cache List %d to %d\n", nFreeCache,
                           mbpBlock->nSize); */

                        mbpBlock->nCacheList = nFreeCache;
                        clpCacheList = &cGCache.claCacheLists[nFreeCache];
                        clpCacheList->bUsed=TRUE;
                        clpCacheList->bMRU=0;
                        clpCacheList->nListSize = mbpBlock->nSize;
                        clpCacheList->nAccessCountThisFrame=0;
                        clpCacheList->nRejectCountThisFrame=0;
                    }
                    else
                    {
                        /* Gotta go in the bucket then */

                        /* printf("Allocating to Bucket\n"); */

                        mbpBlock->nCacheList = TEXCACHELISTBUCKET;
                        clpCacheList = &cGCache.clTextureBucket;
                    }
                }
            }
            break;
        case TEXCACHELISTBUCKET:
            {
                clpCacheList = &cGCache.clTextureBucket;
            }
            break;
        case TEXCACHELISTLOCKED:
            {
                clpCacheList = &cGCache.clLockedTextures;
            }
            break;
        default:
            {
                clpCacheList = &cGCache.claCacheLists[mbpBlock->nCacheList];
            }
            break;
    }

    clpCacheList->nAccessCountThisFrame++;
    rwLinkListAddLLLink(&clpCacheList->llTextureList, &mbpBlock->lCacheList);

    /* We've used the texture this frame */
    if (rwLLLinkAttached(&mbpBlock->lUsageList))
    {
        rwLinkListRemoveLLLink(&mbpBlock->lUsageList);
        rwLLLinkInitialize(&mbpBlock->lUsageList);
    }
    /* Add it to the used list, unless its locked in which case we don't
       want to take it into account */
    if (clpCacheList!=&cGCache.clLockedTextures)
    {
        /* If the cache is set to static then mark this as being unused,
           will get flushed first */
        if (cGCache.nCacheControlFlags & RWSKYCACHESTATIC)
        {
            rwLinkListAddLLLink(&cGCache.llUnusedTextures,
                                &mbpBlock->lUsageList);
        }
        else
        {
            rwLinkListAddLLLink(&cGCache.llTexturesUsedThisFrame,
                                &mbpBlock->lUsageList);
        }
    }
    else
    {
        if (clpCacheList->nAccessCountThisFrame < 0x3fff)
        {
            clpCacheList->nAccessCountThisFrame |= 0x3fff;
        }
    }

    /* printf("Access Ras %x Block %x Size %d\n", mbpBlock->rpRas,
       mbpBlock->nAddr, mbpBlock->nSize); */

    RWRETURNVOID();
}

/***************************************************************************
 _SkyCacheCreateSpace

Efficiently [?] reject cache entries to make space for this alloc

 On entry   : Size in Tex memory required
 On entry   :
 */
static _SkyMemBlock *
_SkyCacheCreateSpace(RwInt32 allocSize)
{
    RwInt32 nIndex;
    RwBool  bFreeRight = FALSE;
    _SkyMemBlock *mbBlock = (_SkyMemBlock *)NULL;
    _SkyCacheList *clpRejectList = (_SkyCacheList *)NULL;

    RWFUNCTION(RWSTRING("_SkyCacheCreateSpace"));

    /* Get the list that this texture should live in */
    for (nIndex=0; nIndex<skyMAXCACHELISTS; nIndex++)
    {
        if (cGCache.claCacheLists[nIndex].nListSize==allocSize)
        {
            clpRejectList = &cGCache.claCacheLists[nIndex];
            break;
        }
    }

    /* Is this going to have to be the texture bucket? */
    if (!clpRejectList)
    {
        clpRejectList = &cGCache.clTextureBucket;
    }

    /* If this list is empty then we need to throw away the most
       LRU'd texture of all */
    if (!rwLinkListEmpty(&clpRejectList->llTextureList))
    {
        clpRejectList->nRejectCountThisFrame++;

        /* Get the LRU from the list */
        if (clpRejectList->bMRU)
        {
            mbBlock =
                rwLLLinkGetData(rwLinkListGetFirstLLLink(&clpRejectList->llTextureList),
                                _SkyMemBlock, lCacheList);
        }
        else
        {
            mbBlock =
                rwLLLinkGetData(rwLinkListGetLastLLLink(&clpRejectList->llTextureList),
                                _SkyMemBlock,
                                lCacheList);
        }
    }
    else
    {
        /* Fall back list by list........... */
        if (!rwLinkListEmpty(&cGCache.llTexturesUsedLastFrame))
        {
            /* printf("Fall back to last frame list\n"); */

            mbBlock =
                rwLLLinkGetData(rwLinkListGetLastLLLink(&cGCache.llTexturesUsedLastFrame),
                                _SkyMemBlock, lUsageList);
        }
        else if (!rwLinkListEmpty(&cGCache.llTexturesUsedThisFrame))
        {
            /* printf("Fall back to this frame\n"); */

            mbBlock =
                rwLLLinkGetData(rwLinkListGetLastLLLink(&cGCache.llTexturesUsedThisFrame),
                                _SkyMemBlock, lUsageList);
        }
        else
        {
            /* Things are getting desperate if we get this far, must be
               stuffed with locked textures. Kill One. */
            mbBlock =
                rwLLLinkGetData(rwLinkListGetLastLLLink(&cGCache.clLockedTextures.llTextureList),
                                _SkyMemBlock, lUsageList);
        }
    }

    /* Kick out this texture */

    /* printf("Reject tex %x block %x, size %d\n", (int)mbBlock->rpRas,
       (int)mbBlock->nAddr, mbBlock->nSize); */

#if 0
    if (RASTEREXTFROMRASTER(mbBlock->rpRas)->bLocked)
    {
        printf("Freeing locked texture\n");
    }

    if ((lastRaster == mbBlock->rpRas) && (lastContext != thisContext) )
    {
        printf("Error? %d\n", lastError++);
    }
#endif

    mbBlock = _SkyCacheReleaseCacheEntry(mbBlock);

    /* If we need more space then kick adjacent blocks  */
    while (allocSize > mbBlock->nSize)
    {
        _SkyMemBlock *mbMergeBlock;

        /* Do we kick the left or right block? */
        if ((bFreeRight) && (mbBlock->mbpNext->rpRas) &&(!RASTEREXTFROMRASTER(mbBlock->mbpNext->rpRas)->bLocked))
        {
            mbMergeBlock = mbBlock->mbpNext;
        }
        else
        {
            mbMergeBlock = mbBlock->mbpPrev;
        }

        /* Go to the otherside next time */
        bFreeRight = !bFreeRight;

        /* Make sure we wern't at the end of memory */
        if (mbMergeBlock!=&cGCache.mMemory.mbDummy)
        {
            /* The magic of heap block merging with our initial block
               makes this work */

            /* printf("Reject %x, size %d Multi\n",
               (int)mbMergeBlock->nAddr,
               mbMergeBlock->nSize); */
#if 0
            if (RASTEREXTFROMRASTER(mbMergeBlock->rpRas)->bLocked)
            {
                printf("Freeing locked texture\n");
            }

            if ((lastRaster == mbMergeBlock->rpRas) &&
                (lastContext != thisContext) )
            {
                printf("Error? %d\n", lastError++);
            }
#endif

            mbBlock = _SkyCacheReleaseCacheEntry(mbMergeBlock);
        }
    }

    /* Got enough space now! */
    _SkyMemoryUseMemBlock(&cGCache.mMemory, mbBlock, allocSize);
    RWRETURN(mbBlock);
}

/***************************************************************************
 _SkyCacheUploadRaster

 Need to find memory on the hardware card in order to upload this stuff.
 Then upload it ready for access.

 On entry   : Raster to upload
 On entry   :
*/

static void
_SkyCacheUploadRaster(RwRaster *rpRas, RwBool speculative)
{
    _SkyMemBlock  *mbpBlock;
    _SkyRasterExt *rasExt = RASTEREXTFROMRASTER(rpRas);
    RwInt32       allocSize = (rasExt->nTexCacheSize + 2047) & ~2047;
    RwUInt32      destAddr;

    RWFUNCTION(RWSTRING("_SkyCacheUploadRaster"));
    RWASSERT(rpRas);

    if (speculative)
    {
        RWRETURNVOID();
    }

    /* printf("Upload Tex %x, size %d\n", (int)rpRas, allocSize); */

    /* Try and do an allocation */
    if (tlock)
        mbpBlock = _SkyMemoryAllocEnd(&cGCache.mMemory, allocSize);
    else
        mbpBlock = _SkyMemoryAlloc(&cGCache.mMemory, allocSize);

    if ((tlock) && (!mbpBlock))
        RWRETURNVOID();

    /* First up try and free unused textures */
    if (!mbpBlock)
    {
        if (_SkyCacheFlushUnused())
        {
            mbpBlock = _SkyMemoryAlloc(&cGCache.mMemory, allocSize);
        }
    }

    /* We'll have to free stuff from the cache */
    if (!mbpBlock)
    {
        mbpBlock = _SkyCacheCreateSpace(allocSize);
    }

    /* Point the raster to the cache entry and vice versa */
    mbpBlock->rpRas = rpRas;
    rasExt->mpCacheEntry  = mbpBlock;

    /* No need to access the texture, this happens in the calling function */

    /* Slam the raster onto the hardware card */
    destAddr = mbpBlock->nAddr >> 6;
    rasExt->lsb &= ~0x3fffl;
    rasExt->lsb |= (destAddr&0x3fff);
#if defined(GSB) && defined(GSPLUS)
    rasExt->lsb3 = destAddr;
#endif /* defined(GSB) && defined(GSPLUS) */
    destAddr += rasExt->palOffset;
#if defined(GSB) && defined(GSPLUS)
    destAddr &= 0x1ffff;
#else /* defined(GSB) && defined(GSPLUS) */
    destAddr &= 0x3fff;
#endif /* defined(GSB) && defined(GSPLUS) */
    rasExt->msb &= ~(0x3fffl << (37-32));
    rasExt->msb |= ((destAddr&0x3fffl) << (37 - 32));
#if defined(GSB) && defined(GSPLUS)
    rasExt->msb3 = (destAddr << (37 - 32));
#endif /* defined(GSB) && defined(GSPLUS) */
#if defined(GSB) && defined(GSPLUS)
    _SkyUploadRaster(rpRas, rasExt->lsb3 & 0x1ffff);
#else /* defined(GSB) && defined(GSPLUS) */
    _SkyUploadRaster(rpRas, rasExt->lsb & 0x3fff);
#endif /* defined(GSB) && defined(GSPLUS) */

    RWRETURNVOID();
}

/****************************************************************************
 _SkyCacheInitialise

 On entry   : Cache
 On exit    : TRUE on success
 */

static RwBool
_SkyCacheInitialise(_SkyCache *cpCache, RwUInt32 nStart, RwUInt32 nSize)
{
    RwInt32 nIndex;
    RWFUNCTION(RWSTRING("_SkyCacheInitialise"));
    RWASSERT(cpCache);

    /* now that the user can turn off the cache we do not need this memory */
    if (state.needTexCache)
    {
        /* set up the memory first */
        if (!_SkyMemoryInitialise(&cpCache->mMemory, nStart, nSize))
        {
            RWERROR((E_RW_DEVICEERROR,
                     RWSTRING("Unable to initialiase memory for Cache")));
            RWRETURN(FALSE);
        }

        rwLinkListInitialize(&cpCache->llTexturesUsedThisFrame);
        rwLinkListInitialize(&cpCache->llTexturesUsedLastFrame);
        rwLinkListInitialize(&cpCache->llUnusedTextures);

        /* Setup the default texture list */
        rwLinkListInitialize(&cpCache->clTextureBucket.llTextureList);

        /* Setup the locked texture list */
        rwLinkListInitialize(&cpCache->clLockedTextures.llTextureList);

        for (nIndex=0; nIndex<skyMAXCACHELISTS; nIndex++)
        {
            _SkyCacheList *clpCacheList = &cpCache->claCacheLists[nIndex];
            clpCacheList->bUsed=FALSE;
            rwLinkListInitialize(&clpCacheList->llTextureList);
        }
    }
    /* steal the address */
    state.TextureBaseAddressInLocalMemory = (RwInt32)nStart;

    /* Set up the current texture as nothing */
    cpCache->rpCurrentRaster = (RwRaster *)NULL;

    /* Default Flags are NULL */
    cpCache->nCacheControlFlags = RWSKYCACHENORMAL;

    /* we use the cache by default */
    cpCache->bEnabled = state.needTexCache;

    /* by default there's no snoop callback */
    cpCache->fpCacheSnoopRasterCB = (SkyCacheSnoopCallBack)NULL;

    /* default callback needed */
    if (cpCache->bEnabled)
    {
        cpCache->fpCacheUploadRasterCB = _SkyCacheUploadRaster;
    }
    else
    {
        cpCache->fpCacheUploadRasterCB = _SkyNullCacheUploadRaster;
    }

    RWRETURN(TRUE);
}

void
skyPrepareUploadRaster(RwRaster *raster)
{
    RwInt32       level;
    RwUInt32      bitsPerPixel, pixelFormat;
    RwBool        contiguous;
    _SkyRasterExt *rasExt = RASTEREXTFROMRASTER(raster);
    RwUInt8       *srcAddress = raster->cpPixels;

    RWFUNCTION(RWSTRING("skyPrepareUploadRaster"));
    RWASSERT(raster);

    bitsPerPixel = raster->depth;
    pixelFormat = (rasExt->lsb >> 20) & 0x3f;

    contiguous =
        (raster->stride == ((raster->width*(RwInt32)bitsPerPixel)>>3));

    /* Upload each of the mipmap levels */
    for (level = 0; level <= (rasExt->maxMipLevel>>2); level++)
    {
        RwUInt32 mipWidth = (raster->width>>level);
        RwUInt32 mipHeight = (raster->height>>level);
        RwUInt32 mipStride = (raster->stride>>level);
        RwBool   aligned = (!((RwUInt32)srcAddress & 0xf));

        if (aligned)
        {
            /* Aligned - choose whether to use ref per scanline or not */
            if (contiguous)
            {
                if (!rasExt->mipUploadPkts[level])
                {
                    rasExt->mipUploadPkts[level] =
                        _SkyBuildPktForUpLoadAlignedContiguousRectangle(mipWidth,
                                                                    mipHeight,
                                                                    srcAddress,
                                                                    bitsPerPixel,
                                                                    rasExt->cachePkts);
                }
                if (!rasExt->cachePkts)
                {
                    rasExt->mipUploadPkts[level] = (u_long128 *)NULL;
                }

            }
        }

        /* Breaks if we upload mipmapped subrastered texture
           - but we're not going to do that, are we... :-) */
        srcAddress += mipStride * mipHeight;

        /* Align for later mip levels */
        if((RwUInt32)srcAddress & 0xf)
        {
            srcAddress = (RwUInt8 *)(((RwUInt32)srcAddress + 15) & ~15);
        }
    }

    /* Upload a palette if applicable */
    if ((rasExt->sysMemPalSize) && (raster->depth <= 8))
    {
        static const RwUInt32 palWidths[8] = {8, 8, 8, 8, 16, 16, 16, 16};
        static const RwUInt32 palHeights[8] = {2, 2, 2, 2, 16, 16, 16, 16};
        RwInt32  palBPP;
        RwUInt32 palWidth, palHeight;
        RwUInt32 format = (rasExt->msb >> (51-32)) & 0xF;

        palWidth = palWidths[raster->depth-1];
        palHeight = palHeights[raster->depth-1];

        switch (format)
        {
            case (PSMCT32):
                {
                    palBPP = 32;
                    break;
                }
            case (PSMCT16):
            case (PSMCT16S):
            default:
                {
                    palBPP = 16;
                    break;
                }
        }

        if (!rasExt->palUploadPkt)
        {
            rasExt->palUploadPkt =
                _SkyBuildPktForUpLoadAlignedContiguousRectangle(palWidth,
                                                            palHeight,
                                                            raster->palette,
                                                            palBPP,
                                                            rasExt->cachePkts);
        }

        if (!rasExt->cachePkts)
        {
            rasExt->palUploadPkt = (u_long128 *)NULL;
        }
    }

    RWRETURNVOID();
}

#ifdef SHOWUPLOADEDTEXTURES

void
skyTexCacheShowUploads(RwBool bShow)
{
    RWFUNCTION(RWTSRING("skyTexCacheShowUploads"));

    bGShowUploadTextures = bShow;

    RWRETURNVOID();
}

#define RWTABLEHEADER()                                                     \
MACRO_START                                                                 \
{                                                                           \
    if (bGShowUploadTextures)                                               \
    {                                                                       \
        RwInt32 nIndex;                                                     \
        /* Find this texture */                                             \
        for (nIndex=0; nIndex<SHOWUPLOADEDTEXTUREMAXTEXTURES; nIndex++)     \
        {                                                                   \
            if (rpShowUploadedRasters[nIndex]==skyTextureRaster)            \
            {                                                               \
                printf("Tex %s\t",                                          \
                       (skyTextureRaster)?                                  \
                       RwTextureGetName(tpShowUploadedTextures[nIndex])     \
                       :"(null)");                                          \
                break;                                                      \
            }                                                               \
        }                                                                   \
    }                                                                       \
}                                                                           \
MACRO_STOP

#define RWTABLECACHEMISS()                                      \
MACRO_START                                                     \
{                                                               \
    if(bGShowUploadTextures)                                    \
    {                                                           \
        if (cGCache.nCacheControlFlags&RWSKYCACHESTATIC)        \
        {                                                       \
            printf("M*\n");                                     \
        }                                                       \
        else                                                    \
        {                                                       \
            printf("M\n");                                      \
        }                                                       \
    }                                                           \
}                                                               \
MACRO_STOP

#define RWTABLECACHEHIT()                                       \
MACRO_START                                                     \
{                                                               \
    if (bGShowUploadTextures)                                   \
    {                                                           \
        if (cGCache.nCacheControlFlags&RWSKYCACHESTATIC)        \
        {                                                       \
            printf("H*\n");                                     \
        }                                                       \
        else                                                    \
        {                                                       \
            printf("H\n");                                      \
        }                                                       \
    }                                                           \
}                                                               \
MACRO_STOP

#define RWTABLETRAILER()                                        \
MACRO_START                                                     \
{                                                               \
    if (bGShowUploadTextures)                                   \
    {                                                           \
        printf("Current\n");                                    \
    }                                                           \
}                                                               \
MACRO_STOP

#endif /* SHOWUPLOADEDTEXTURES */

#if (!defined(RWTABLEHEADER))
#define RWTABLEHEADER() /* No op */
#endif /* (!defined(RWTABLEHEADER)) */

#if (!defined(RWTABLECACHEMISS))
#define RWTABLECACHEMISS() /* No op */
#endif /* (!defined(RWTABLECACHEMISS)) */

#if (!defined(RWTABLECACHEHIT))
#define RWTABLECACHEHIT() /* No op */
#endif /* (!defined(RWTABLECACHEHIT)) */

#if (!defined(RWTABLETRAILER))
#define RWTABLETRAILER() /* No op */
#endif /* (!defined(RWTABLETRAILER)) */

/****************************************************************************
 skyTexCacheRestore

 Restore current raster

 On entry   : Raster
 On exit
*/
void
skyTexCacheRestore(void)
{
    RwRaster *rpRas;
    _SkyRasterExt *rasExt;
    RwBool useContext2;

    RWFUNCTION(RWSTRING("skyTexCacheRestore"));

    if (cGCache.rpCurrentRaster)
    {
        RwUInt64  mipAddr1, mipAddr2, texAddr;
        RwUInt64  tmp1, tmp;
        u_long128 gifTagCmd;
        u_long128 miptbp11Cmd, miptbp21Cmd;
        u_long128 tex01Cmd, tex11Cmd;

        rasExt = RASTEREXTFROMRASTER(cGCache.rpCurrentRaster);
        rpRas = cGCache.rpCurrentRaster;
        useContext2 = cGCache.context;

        sweOpenLocalPkt(SWE_LPS_CONT, 0);

        /* Select it as the texture source */
#if defined(GSB) && defined(GSPLUS)
        tmp = /* NLOOP */ 7l
#ifdef LESSEOPS
            | /* EOP */ (0l<<15)
#else /* LESSEOPS */
            | /* EOP */ (1l<<15)
#endif /* LESSEOPS */
            | /* PRE */ (0l<<46)
            | /* FLG */ (0l<<58)
            | /* NREG */(1l<<60);
        tmp1 = /* A+D */ (0xel<<(64-64));
        MAKE128(gifTagCmd, tmp1, tmp);
        SWEADDCONTGIFFAST(gifTagCmd, 7);
#else /* defined(GSB) && defined(GSPLUS) */
        tmp = /* NLOOP */ 4l
#ifdef LESSEOPS
            | /* EOP */ (0l<<15)
#else /* LESSEOPS */
            | /* EOP */ (1l<<15)
#endif /* LESSEOPS */
            | /* PRE */ (0l<<46)
            | /* FLG */ (0l<<58)
            | /* NREG */(1l<<60);
        tmp1 = /* A+D */ (0xel<<(64-64));
        MAKE128(gifTagCmd, tmp1, tmp);
        SWEADDCONTGIFFAST(gifTagCmd, 4);
#endif /* defined(GSB) && defined(GSPLUS) */

        tmp = rasExt->lsb | (RwUInt64)rasExt->msb << 32;

        /* If this texture is actually the camera then setup the framebuffer
           pointer */
        if ((rpRas->cType&rwRASTERTYPEMASK)==rwRASTERTYPECAMERA)
        {
            long skyBackFrame;
            if (skyFrameBit & 0x1)
            {
#if defined(GSB) && defined(GSPLUS)
                skyBackFrame = *(long*)&sweDb.draw11.eframe;
#else /* defined(GSB) && defined(GSPLUS) */
                skyBackFrame = *(long*)&sweDb.draw11.frame1;
#endif /* defined(GSB) && defined(GSPLUS) */
            }
            else
            {
#if defined(GSB) && defined(GSPLUS)
                skyBackFrame = *(long*)&sweDb.draw01.eframe;
#else /* defined(GSB) && defined(GSPLUS) */
                skyBackFrame = *(long*)&sweDb.draw01.frame1;
#endif /* defined(GSB) && defined(GSPLUS) */
            }

            /* add frame buffer to texture base pointer -fbp is in 2048
               alignment, -tex in 64 align */
            tmp = (tmp & ~0x3fffl)
                | ((rasExt->lsb&0x3fff)+((skyBackFrame&0x1ff)<<5));
        }

        tmp1 = ((useContext2?GS_TEX0_2:GS_TEX0_1) << (64-64));
        MAKE128(tex01Cmd, tmp1, tmp);
        SWEADDCONTFAST(tex01Cmd);

#if defined(GSB) && defined(GSPLUS)
        tmp = rasExt->lsb3 & 0x1ffff;

        /* If this texture is actually the camera then setup the framebuffer
           pointer */
        if ((rpRas->cType&rwRASTERTYPEMASK)==rwRASTERTYPECAMERA)
        {
            long skyBackFrame;
            if (skyFrameBit & 0x1)
            {
                skyBackFrame = *(long*)&sweDb.draw11.eframe;
            }
            else
            {
                skyBackFrame = *(long*)&sweDb.draw01.eframe;
            }

            /* add frame buffer to texture base pointer -fbp is in 2048
               alignment, -tex in 64 align */
            tmp = (rasExt->lsb&0x1ffff)+((skyBackFrame&0xfff)<<5);
        }

        tmp1 = ((useContext2?GS_TEX3_2:GS_TEX3_1) << (64-64));
        MAKE128(tex01Cmd, tmp1, tmp);
        SWEADDCONTFAST(tex01Cmd);
#endif /* defined(GSB) && defined(GSPLUS) */

		/* mask out the K and L values, also the MXL and MTBA fields */
        skyTex1_1 &= ~0xfff0018021cl;
        skyTex1_1 |= ( ( rasExt->maxMipLevel ) |
                       ( (RwUInt64)(rasExt->mipmapKL &0xfff)<< 32 ) |
                       ( (RwUInt64)((rasExt->mipmapKL >> 12) &0x3)<< 19 ) );
        MAKE128(tex11Cmd, (useContext2?GS_TEX1_2:GS_TEX1_1), skyTex1_1);
        SWEADDCONTFAST(tex11Cmd);

#if defined(GSB) && defined(GSPLUS)
        texAddr = rasExt->lsb & 0x1ffff;
        mipAddr1 = texAddr | (texAddr<<20) | (texAddr<<40);
        mipAddr2 = ((RwUInt64)rasExt->miptbp3Lsb |
                    ((RwUInt64)rasExt->miptbp3Msb)<<32);
        mipAddr2 += mipAddr1;
        mipAddr2 &= 0x3fff03fff03fffl;
        mipAddr2 |= ((RwUInt64)rasExt->miptbp1Lsb |
                     ((RwUInt64)rasExt->miptbp1Msb)<<32) & 0xfc000fc000fc000l;
        MAKE128(miptbp11Cmd,
                (useContext2?GS_MIPTBP1_2:GS_MIPTBP1_1), mipAddr2);
        SWEADDCONTFAST(miptbp11Cmd);

        mipAddr2 = ((RwUInt64)rasExt->miptbp4Lsb |
                    ((RwUInt64)rasExt->miptbp4Msb)<<32);
        mipAddr2 += mipAddr1;
        mipAddr2 &= 0x3fff03fff03fffl;
        mipAddr2 |= ((RwUInt64)rasExt->miptbp2Lsb |
                     ((RwUInt64)rasExt->miptbp2Msb)<<32) & 0xfc000fc000fc000l;
        MAKE128(miptbp21Cmd,
                (useContext2?GS_MIPTBP2_2:GS_MIPTBP2_1), mipAddr2);
        SWEADDCONTFAST(miptbp21Cmd);

        texAddr = rasExt->lsb3 & 0x1ffff;
        mipAddr1 = texAddr | (texAddr<<20) | (texAddr<<40);
        mipAddr2 = ((RwUInt64)rasExt->miptbp3Lsb |
                    ((RwUInt64)rasExt->miptbp3Msb)<<32) & 0x1ffff1ffff1ffffl;
        mipAddr2 += mipAddr1;
        MAKE128(miptbp11Cmd,
                (useContext2?GS_MIPTBP3_2:GS_MIPTBP3_1), mipAddr2);
        SWEADDCONTFAST(miptbp11Cmd);

        mipAddr2 = ((RwUInt64)rasExt->miptbp4Lsb |
                    ((RwUInt64)rasExt->miptbp4Msb)<<32) & 0x1ffff1ffff1ffffl;
        mipAddr2 += mipAddr1;
        MAKE128(miptbp21Cmd,
                (useContext2?GS_MIPTBP4_2:GS_MIPTBP4_1), mipAddr2);
        SWEADDCONTFAST(miptbp21Cmd);

#else /* defined(GSB) && defined(GSPLUS) */

        texAddr = rasExt->lsb & 0x3fff;
        mipAddr1 = texAddr | (texAddr<<20) | (texAddr<<40);

        if (cGCache.bEnabled)
        {
            /* the miptbp1 and 2 registers store just offset and width
             * so we have to compute the actual register values, knowing
             * the location in local memory where the raster starts...
             */
            mipAddr2 = ( (RwUInt64)rasExt->miptbp1Lsb |
                         ((RwUInt64)rasExt->miptbp1Msb)<<32 );
            mipAddr2 += mipAddr1;
            MAKE128(miptbp11Cmd,
                    (useContext2?GS_MIPTBP1_2:GS_MIPTBP1_1), mipAddr2);
            SWEADDCONTFAST(miptbp11Cmd);
        }
        else /* disabled */
        {
            /* just write the data as stored in the extension */
            tmp = rasExt->miptbp1Lsb | (RwUInt64)rasExt->miptbp1Msb << 32;
            MAKE128(miptbp11Cmd,
                    (useContext2?GS_MIPTBP1_2:GS_MIPTBP1_1), tmp);
            SWEADDCONTFAST(miptbp11Cmd);

        }

        /* I'm not bothering to deal with miptbp2 since we do not
         * store enough levels in a raster.  Who is going to
         * be the first to complain about that???
         */
        mipAddr2 = ((RwUInt64)rasExt->miptbp2Lsb |
                    ((RwUInt64)rasExt->miptbp2Msb)<<32);
        mipAddr2 += mipAddr1;
        MAKE128(miptbp21Cmd,
                (useContext2?GS_MIPTBP2_2:GS_MIPTBP2_1), mipAddr2);
        SWEADDCONTFAST(miptbp21Cmd);

#endif /* defined(GSB) && defined(GSPLUS) */
    }

    RWRETURNVOID();
}
/****************************************************************************
 skyTexCacheAccessRaster

 Makes the raster the current rendering context

 On entry   : Cache number, Raster
 On exit    :
 */

RwBool
skyTexCacheAccessRaster(RwRaster *rpRas, RwBool useContext2)
{
    _SkyRasterExt *rasExt;

    RWFUNCTION(RWSTRING("skyTexCacheAccessRaster"));
    RWASSERT(rpRas);

    /* IF the snoop CB is present, call it so it straight away so
     * it may change the raster to be uploaded at the last minute */
    if (NULL != cGCache.fpCacheSnoopRasterCB)
    {
        cGCache.fpCacheSnoopRasterCB(&rpRas);
        /* The CB could decide not to upload */
        if (NULL == rpRas)
        {
            RWRETURN(TRUE);
        }
    }

    thisRaster = rpRas;
    thisContext = useContext2?2:1;

    rasExt = RASTEREXTFROMRASTER(rpRas);

    RWTABLEHEADER();

    /* Do we need to do any work to make this happen? */
    if (cGCache.rpCurrentRaster != rpRas)
    {
        RwUInt64  mipAddr1, mipAddr2, texAddr;
        RwUInt64  tmp1, tmp;
        u_long128 gifTagCmd;
        u_long128 miptbp11Cmd, miptbp21Cmd;
        u_long128 tex01Cmd, tex11Cmd;

        /* Texture test removed to make blit work again */
        if (!rasExt->mpCacheEntry && !(rpRas->cType & rwRASTERDONTALLOCATE)
            /*  &&  (rpRas->cType & rwRASTERTYPETEXTURE)*/)
        {
            /* Its not in the cache -> so upload it */
            cGCache.fpCacheUploadRasterCB(rpRas, FALSE);

            /* Temporary fail */
            if ((tlock) && (!rasExt->mpCacheEntry))
            {
                RWRETURN(FALSE);
            }

            RWTABLECACHEMISS();
        }
#ifdef SHOWUPLOADEDTEXTURES
        else
        {
            RWTABLECACHEHIT();
        }
#endif

        sweOpenLocalPkt(SWE_LPS_CONT, 0);

        /* Select it as the texture source */
#if defined(GSB) && defined(GSPLUS)
        tmp = /* NLOOP */ 7l
#ifdef LESSEOPS
            | /* EOP */ (0l<<15)
#else /* LESSEOPS */
            | /* EOP */ (1l<<15)
#endif /* LESSEOPS */
            | /* PRE */ (0l<<46)
            | /* FLG */ (0l<<58)
            | /* NREG */(1l<<60);
        tmp1 = /* A+D */ (0xel<<(64-64));
        MAKE128(gifTagCmd, tmp1, tmp);
        SWEADDCONTGIFFAST(gifTagCmd, 7);
#else /* defined(GSB) && defined(GSPLUS) */
        tmp = /* NLOOP */ 4l
#ifdef LESSEOPS
            | /* EOP */ (0l<<15)
#else /* LESSEOPS */
            | /* EOP */ (1l<<15)
#endif /* LESSEOPS */
            | /* PRE */ (0l<<46)
            | /* FLG */ (0l<<58)
            | /* NREG */(1l<<60);
        tmp1 = /* A+D */ (0xel<<(64-64));
        MAKE128(gifTagCmd, tmp1, tmp);
        SWEADDCONTGIFFAST(gifTagCmd, 4);
#endif /* defined(GSB) && defined(GSPLUS) */

        tmp = rasExt->lsb | (RwUInt64)rasExt->msb << 32;

        /* If this texture is actually the camera then setup the framebuffer
           pointer */
        if ((rpRas->cType&rwRASTERTYPEMASK)==rwRASTERTYPECAMERA)
        {
            long skyBackFrame;
            if (skyFrameBit & 0x1)
            {
#if defined(GSB) && defined(GSPLUS)
                skyBackFrame = *(long*)&sweDb.draw11.eframe;
#else /* defined(GSB) && defined(GSPLUS) */
                skyBackFrame = *(long*)&sweDb.draw11.frame1;
#endif /* defined(GSB) && defined(GSPLUS) */
            }
            else
            {
#if defined(GSB) && defined(GSPLUS)
                skyBackFrame = *(long*)&sweDb.draw01.eframe;
#else /* defined(GSB) && defined(GSPLUS) */
                skyBackFrame = *(long*)&sweDb.draw01.frame1;
#endif /* defined(GSB) && defined(GSPLUS) */
            }

            /* add frame buffer to texture base pointer -fbp is in 2048
               alignment, -tex in 64 align */
            tmp = (tmp & ~0x3fffl)
                | ((rasExt->lsb&0x3fff)+((skyBackFrame&0x1ff)<<5));
        }

        tmp1 = ((useContext2?GS_TEX0_2:GS_TEX0_1) << (64-64));
        MAKE128(tex01Cmd, tmp1, tmp);
        SWEADDCONTFAST(tex01Cmd);

#if defined(GSB) && defined(GSPLUS)
        tmp = rasExt->lsb3 & 0x1ffff;

        /* If this texture is actually the camera then setup the framebuffer
           pointer */
        if ((rpRas->cType&rwRASTERTYPEMASK)==rwRASTERTYPECAMERA)
        {
            long skyBackFrame;
            if (skyFrameBit & 0x1)
            {
                skyBackFrame = *(long*)&sweDb.draw11.eframe;
            }
            else
            {
                skyBackFrame = *(long*)&sweDb.draw01.eframe;
            }

            /* add frame buffer to texture base pointer -fbp is in 2048
               alignment, -tex in 64 align */
            tmp = (rasExt->lsb&0x1ffff)+((skyBackFrame&0xfff)<<5);
        }

        tmp1 = ((useContext2?GS_TEX3_2:GS_TEX3_1) << (64-64));
        MAKE128(tex01Cmd, tmp1, tmp);
        SWEADDCONTFAST(tex01Cmd);
#endif /* defined(GSB) && defined(GSPLUS) */

		/* mask out the K and L values, also the MXL and MTBA fields */
        skyTex1_1 &= ~0xfff0018021cl;
        skyTex1_1 |= ( ( rasExt->maxMipLevel ) |
                       ( (RwUInt64)(rasExt->mipmapKL &0xfff)<< 32 ) |
                       ( (RwUInt64)((rasExt->mipmapKL >> 12) &0x3)<< 19 ) );
        MAKE128(tex11Cmd, (useContext2?GS_TEX1_2:GS_TEX1_1), skyTex1_1);
        SWEADDCONTFAST(tex11Cmd);

#if defined(GSB) && defined(GSPLUS)
        texAddr = rasExt->lsb & 0x1ffff;
        mipAddr1 = texAddr | (texAddr<<20) | (texAddr<<40);
        mipAddr2 = ((RwUInt64)rasExt->miptbp3Lsb |
                    ((RwUInt64)rasExt->miptbp3Msb)<<32);
        mipAddr2 += mipAddr1;
        mipAddr2 &= 0x3fff03fff03fffl;
        mipAddr2 |= ((RwUInt64)rasExt->miptbp1Lsb |
                     ((RwUInt64)rasExt->miptbp1Msb)<<32) & 0xfc000fc000fc000l;
        MAKE128(miptbp11Cmd,
                (useContext2?GS_MIPTBP1_2:GS_MIPTBP1_1), mipAddr2);
        SWEADDCONTFAST(miptbp11Cmd);

        mipAddr2 = ((RwUInt64)rasExt->miptbp4Lsb |
                    ((RwUInt64)rasExt->miptbp4Msb)<<32);
        mipAddr2 += mipAddr1;
        mipAddr2 &= 0x3fff03fff03fffl;
        mipAddr2 |= ((RwUInt64)rasExt->miptbp2Lsb |
                     ((RwUInt64)rasExt->miptbp2Msb)<<32) & 0xfc000fc000fc000l;
        MAKE128(miptbp21Cmd,
                (useContext2?GS_MIPTBP2_2:GS_MIPTBP2_1), mipAddr2);
        SWEADDCONTFAST(miptbp21Cmd);

        texAddr = rasExt->lsb3 & 0x1ffff;
        mipAddr1 = texAddr | (texAddr<<20) | (texAddr<<40);
        mipAddr2 = ((RwUInt64)rasExt->miptbp3Lsb |
                    ((RwUInt64)rasExt->miptbp3Msb)<<32) & 0x1ffff1ffff1ffffl;
        mipAddr2 += mipAddr1;
        MAKE128(miptbp11Cmd,
                (useContext2?GS_MIPTBP3_2:GS_MIPTBP3_1), mipAddr2);
        SWEADDCONTFAST(miptbp11Cmd);

        mipAddr2 = ((RwUInt64)rasExt->miptbp4Lsb |
                    ((RwUInt64)rasExt->miptbp4Msb)<<32) & 0x1ffff1ffff1ffffl;
        mipAddr2 += mipAddr1;
        MAKE128(miptbp21Cmd,
                (useContext2?GS_MIPTBP4_2:GS_MIPTBP4_1), mipAddr2);
        SWEADDCONTFAST(miptbp21Cmd);

#else /* defined(GSB) && defined(GSPLUS) */

        texAddr = rasExt->lsb & 0x3fff;
        mipAddr1 = texAddr | (texAddr<<20) | (texAddr<<40);

        if (cGCache.bEnabled)
        {
            /* the miptbp1 and 2 registers store just offset and width
             * so we have to compute the actual register values, knowing
             * the location in local memory where the raster starts...
             */
            mipAddr2 = ( (RwUInt64)rasExt->miptbp1Lsb |
                         ((RwUInt64)rasExt->miptbp1Msb)<<32 );
            mipAddr2 += mipAddr1;
            MAKE128(miptbp11Cmd,
                    (useContext2?GS_MIPTBP1_2:GS_MIPTBP1_1), mipAddr2);
            SWEADDCONTFAST(miptbp11Cmd);
        }
        else /* disabled */
        {
            /* just write the data as stored in the extension */
            tmp = rasExt->miptbp1Lsb | (RwUInt64)rasExt->miptbp1Msb << 32;
            MAKE128(miptbp11Cmd,
                    (useContext2?GS_MIPTBP1_2:GS_MIPTBP1_1), tmp);
            SWEADDCONTFAST(miptbp11Cmd);

        }

        /* I'm not bothering to deal with miptbp2 since we do not
         * store enough levels in a raster.  Who is going to
         * be the first to complain about that???
         */
        mipAddr2 = ((RwUInt64)rasExt->miptbp2Lsb |
                    ((RwUInt64)rasExt->miptbp2Msb)<<32);
        mipAddr2 += mipAddr1;
        MAKE128(miptbp21Cmd,
                (useContext2?GS_MIPTBP2_2:GS_MIPTBP2_1), mipAddr2);
        SWEADDCONTFAST(miptbp21Cmd);

#endif /* defined(GSB) && defined(GSPLUS) */

        /* Say that this is the current raster */
        cGCache.rpCurrentRaster = rpRas;
        cGCache.context = useContext2;
    }
#ifdef SHOWUPLOADEDTEXTURES
    else
    {
        RWTABLETRAILER();
    }
#endif

    /* Do usage based optimisation for real textures here */
    if ((cGCache.bEnabled) && (rasExt->mpCacheEntry))
    {
        _SkyCacheRasterAccess(rasExt->mpCacheEntry);
    }

    lastRaster = thisRaster;
    lastContext = thisContext;

    RWRETURN(TRUE);
}

/****************************************************************************
 _SkyTexCacheReleaseRaster

 Should be performed before a raster is destroyed so it is known that
 the raster isn't blocking the cache.

 On entry   : Cache
            : Raster to release
 On exit    :
 */

void
skyTexCacheReleaseRaster(RwRaster *rpRas)
{
    _SkyRasterExt *rasExt = RASTEREXTFROMRASTER(rpRas);

    RWFUNCTION(RWSTRING("skyTexCacheReleaseRaster"));
    RWASSERT(rpRas);

    /* Is it in cache */
    if ((cGCache.bEnabled) && (rasExt->mpCacheEntry))
    {
        /* Need to release this from the cache */
        _SkyCacheReleaseCacheEntry(rasExt->mpCacheEntry);
    }

    RWRETURNVOID();
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                             API

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

RwBool
skyTexCacheOpenCache(RwInt32 start, RwInt32 size)
{
    RwUInt64 tmp, tmp1;
    RWFUNCTION(RWSTRING("skyTexCacheOpenCache"));

    tmp = 1l | (1l<<28);
    MAKE128(state.dmaTagCnt1, 0l, tmp);

    /* Giftag to prepare the GS to accept the texture */
#if defined(GSB) && defined(GSPLUS)
    tmp = /* NLOOP */ 6l
#ifdef LESSEOPS
        | /* EOP */ (0l<<15)
#else /* LESSEOPS */
        | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
        | /* PRE */ (0l<<46)
        | /* FLG */ (0l<<58)
        | /* NREG */(1l<<60);
    tmp1 = /* A+D */ (0xel<<(64-64));
    MAKE128(state.refUploadSetupGifTag6, tmp1, tmp);
#else /* defined(GSB) && defined(GSPLUS) */
    tmp = /* NLOOP */ 5l
#ifdef LESSEOPS
        | /* EOP */ (0l<<15)
#else /* LESSEOPS */
        | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
        | /* PRE */ (0l<<46)
        | /* FLG */ (0l<<58)
        | /* NREG */(1l<<60);
    tmp1 = /* A+D */ (0xel<<(64-64));
    MAKE128(state.refUploadSetupGifTag5, tmp1, tmp);
#endif /* defined(GSB) && defined(GSPLUS) */

    /* Gs command to flush texture memory accesses */
    MAKE128(state.texFlushGsCommand, GS_TEXFLUSH, 0);

    /* Gs command to set upload to top left of texture */
    MAKE128(state.trxPos0GsCommand, GS_TRXPOS, 0);

    /* Gs command to set direction of transmission */
    MAKE128(state.trxDir0GsCommand, GS_TRXDIR, 0);

    /* Set up the cache */
    if (_SkyCacheInitialise(&cGCache, start, size))
    {
        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

/****************************************************************************
 _SkyTexCacheClose

 On entry   :
 On exit    :
 */

RwBool
skyTexCacheCloseCache(void)
{
    RWFUNCTION(RWSTRING("skyTexCacheCloseCache"));

    if (cGCache.bEnabled)
    {
        /* Remove all of the rasters from the cache */
        skyTexCacheFlush();

        /* Release the memory used */
        _SkyMemoryRelease(&cGCache.mMemory);
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 _SkyTexCacheFlush

 On entry   :
 On exit    :
 */

void
skyTexCacheFlush(void)
{
    _SkyMemBlock   *curBlock;

    RWFUNCTION(RWSTRING("skyTexCacheFlush"));

    /* Go through each memory block and expunge it */
    curBlock = cGCache.mMemory.mbDummy.mbpNext;

    do
    {
        if (curBlock->cUsed)
        {
            curBlock = _SkyCacheReleaseCacheEntry(curBlock);
        }
        curBlock = curBlock->mbpNext;
    }
    while (curBlock != &cGCache.mMemory.mbDummy);

    cGCache.rpCurrentRaster = (RwRaster *)NULL;

    RWRETURNVOID();
}

/****************************************************************************
 skyDumpFreeList

 On entry   :
 On exit    :
 */
#ifdef SKYTEXCACHEDUMPFREELIST
void
skyDumpFreeList(void)
{
    RwLLLink *cur,*end;
    _SkyMemBlock *mbpBlock;

    RWFUNCTION(RWSTRING("skyDumpFreeList"));

    /* Search the freelist for a block of the appropriate size */
    cur = rwLinkListGetFirstLLLink(&cGCache.mMemory.llFreeMem);
    end = rwLinkListGetTerminator(&cGCache.mMemory.llFreeMem);

    /* printf("Dump\n"); */

    while (cur!=end)
    {
        /* Get the block pointer */
        mbpBlock = rwLLLinkGetData(cur, _SkyMemBlock, lCacheList);

        /* printf("Block %x Size %d\n", mbpBlock->nAddr, mbpBlock->nSize);  */

        /* Next */
        cur = rwLLLinkGetNext(cur);
    }
    /* printf("End Dump\n"); */

    RWRETURNVOID();
}

void
skyDumpUnusedList(void)
{
    RwLLLink *cur,*end;
    _SkyMemBlock *mbpBlock;
    RwInt32 nIndex, nSize;

    RWFUNCTION(RWSTRING("skyDumpUnusedList"));

    cur = rwLinkListGetFirstLLLink(&cGCache.llTexturesUsedThisFrame);
    end = rwLinkListGetTerminator(&cGCache.llTexturesUsedThisFrame);
    for (nIndex=0; nIndex<1000000; nIndex++);
    printf("This Frame\n");
    nSize=0;
    while (cur!=end)
    {
        /* Get the block pointer */
        mbpBlock = rwLLLinkGetData(cur, _SkyMemBlock, lUsageList);

        printf("Ras %x Block %x Size %d Cache %d\n", mbpBlock->rpRas,
               mbpBlock->nAddr, mbpBlock->nSize, mbpBlock->nCacheList);
        nSize+=mbpBlock->nSize;

        /* Next */
        cur = rwLLLinkGetNext(cur);
    }

    cur = rwLinkListGetFirstLLLink(&cGCache.llTexturesUsedLastFrame);
    end = rwLinkListGetTerminator(&cGCache.llTexturesUsedLastFrame);

    printf("Total %d Last Frame\n", nSize);
    nSize=0;
    while (cur!=end)
    {
        /* Get the block pointer */
        mbpBlock = rwLLLinkGetData(cur, _SkyMemBlock, lUsageList);

        printf("Ras %x Block %x Size %d\n", mbpBlock->rpRas, mbpBlock->nAddr,
               mbpBlock->nSize);
        nSize+=mbpBlock->nSize;

        /* Next */
        cur = rwLLLinkGetNext(cur);
    }

    /* Search the freelist for a block of the appropriate size */
    cur = rwLinkListGetFirstLLLink(&cGCache.llUnusedTextures);
    end = rwLinkListGetTerminator(&cGCache.llUnusedTextures);

    printf("Total %d Unused\n", nSize);
    nSize=0;
    while (cur!=end)
    {
        /* Get the block pointer */
        mbpBlock = rwLLLinkGetData(cur, _SkyMemBlock, lUsageList);

        printf("Ras %x Block %x Size %d\n", mbpBlock->rpRas, mbpBlock->nAddr,
               mbpBlock->nSize);
        nSize+=mbpBlock->nSize;

        /* Next */
        cur = rwLLLinkGetNext(cur);
    }

    printf("Total %d End Dump\n", nSize);

    RWRETURNVOID();
}

#endif

/****************************************************************************
 skyTexCacheEndFrame

  Resets cache usage statistics

 On entry   :
 On exit    :
 */
void
skyTexCacheEndFrame(void)
{
    RWFUNCTION(RWSTRING("skyTexCacheEndFrame"));

    if (cGCache.bEnabled)
    {
        RwLLLink *start
            = rwLinkListGetFirstLLLink(&cGCache.llTexturesUsedLastFrame);
        RwLLLink *end
            = rwLinkListGetTerminator(&cGCache.llTexturesUsedLastFrame);

        /* skyDumpUnusedList(); */
        /* Anything left in the last frame list is pollution, move it to
           the unused list */
        if (start!=end)
        {
            /* relink into the unused list */
            start->prev = &cGCache.llUnusedTextures.link;
            rwLLLinkGetPrevious(end)->next =
                cGCache.llUnusedTextures.link.next;
            cGCache.llUnusedTextures.link.next->prev =
                rwLLLinkGetPrevious(end);
            cGCache.llUnusedTextures.link.next = start;

            rwLinkListInitialize(&cGCache.llTexturesUsedLastFrame);
        }

        /* Now set the current list to the last frame list */
        if (!rwLinkListEmpty(&cGCache.llTexturesUsedThisFrame))
        {
            cGCache.llTexturesUsedLastFrame.link.next
                = cGCache.llTexturesUsedThisFrame.link.next;
            cGCache.llTexturesUsedLastFrame.link.prev
                = cGCache.llTexturesUsedThisFrame.link.prev;
            cGCache.llTexturesUsedLastFrame.link.next->prev
                = &cGCache.llTexturesUsedLastFrame.link;
            cGCache.llTexturesUsedLastFrame.link.prev->next
                = &cGCache.llTexturesUsedLastFrame.link;
        }

        /* Current List is now empty */
        rwLinkListInitialize(&cGCache.llTexturesUsedThisFrame);
    }

    {
        RwInt32 nIndex;
        _SkyCacheList *clpCacheList = cGCache.claCacheLists;

        /* printf("Bucket Access %d, Reject %d\n",
           cGCache.clTextureBucket.nAccessCountThisFrame,
           cGCache.clTextureBucket.nRejectCountThisFrame); */

        cGCache.clTextureBucket.nAccessCountThisFrame=0;
        cGCache.clTextureBucket.nRejectCountThisFrame=0;

        for (nIndex=0; nIndex<skyMAXCACHELISTS; nIndex++)
        {
            RwInt32 nThrash;

            if (clpCacheList->bUsed)
            {
                /* Get Thrashing Rate - only works if we had some rejects  */
                if (clpCacheList->nRejectCountThisFrame)
                {
                    /* Are we already using MRU, and should we still be */
                    /* If its less efficient then an LRU frame will
                       restructure the cache */
                    if (clpCacheList->bMRU > 0)
                    {
                        if (clpCacheList->nRejectCountThisFrame
                            > clpCacheList->bMRU)
                        {
                            /* Ok, this is working less well,
                               try another frame of LRU */

                            /* printf("MRU Safety Valve before %d, after %d\n",
                               clpCacheList->bMRU,
                               clpCacheList->nRejectCountThisFrame); */

                            clpCacheList->bMRU=0;
                        }
                    }
                    else
                    {
                        /* Are we thrashing the cache enough to consider
                           a switch from LRU to MRU? */
                        nThrash = clpCacheList->nRejectCountThisFrame
                            * clpCacheList->nListSize;
                        nThrash /= clpCacheList->nAccessCountThisFrame;

                        /* Set pass mark to be more then 1/2 the texture size */
                        if (nThrash > (clpCacheList->nListSize/2))
                        {
                            /* Set the MRU to be the number of rejects the
                               LRU did, it must do better */
                            clpCacheList->bMRU
                                = clpCacheList->nRejectCountThisFrame;
                        }
                    }
                }
                else
                {
                    /* Reset back to LRU */
                    clpCacheList->bMRU = 0;
                }

                /* printf("%d. Size %d Access %d, Reject %d Alg %d \n", nIndex,
                   clpCacheList->nListSize,
                   clpCacheList->nAccessCountThisFrame,
                   clpCacheList->nRejectCountThisFrame,
                   clpCacheList->bMRU); */

                clpCacheList->nAccessCountThisFrame = 0;
                clpCacheList->nRejectCountThisFrame = 0;

                /* Yikes! Pointer Arithmetic */
                clpCacheList++;
            }
        }
    }

#ifdef SHOWUPLOADEDTEXTURES
    bGShowUploadTextures = FALSE;
#endif

    RWRETURNVOID();
}

/****************************************************************************
 skyTexCacheRasterLock

  Try and keep this raster in memory

 On entry   :
 On exit    :
 */
RwBool
skyTexCacheRasterLock(RwRaster *rpRas, RwBool bLocked)
{
    _SkyRasterExt *reExt = RASTEREXTFROMRASTER(rpRas);
    RWFUNCTION(RWSTRING("skyTexCacheRasterLock"));

    if (cGCache.bEnabled)
    {
        /* Need to set the lock? */
        if (bLocked && reExt->bLocked==FALSE)
        {
            /* Throw out texture from cache is required */
            if (reExt->mpCacheEntry)
            {
                skyTexCacheReleaseRaster(rpRas);
            }
tlock = 1;
            if (!skyTexCacheAccessRaster(rpRas, FALSE))
            {
tlock = 0;
                RWRETURN(FALSE);
            }
tlock = 0;
            reExt->bLocked=TRUE;
            reExt->mpCacheEntry->nCacheList = TEXCACHELISTLOCKED;
            _SkyCacheRasterAccess(reExt->mpCacheEntry);
        }
        else if (!bLocked && reExt->bLocked==TRUE)
        {
            reExt->bLocked=FALSE;

            if (reExt->mpCacheEntry)
            {
                /* Need to work out where this would go normally */
                reExt->mpCacheEntry->nCacheList = TEXCACHELISTNONE;
                /* Now touch the texture to get it sorted (nb, this will make
                   it to MRU) */
                _SkyCacheRasterAccess(reExt->mpCacheEntry);
            }
        }
    }
    RWRETURN(TRUE);
}

/*
 * GJD
 *
 * Copyright (c) 2000 Criterion Software Ltd.
 */

/**
 * \ingroup sky2unsupported
 * \ref skyTexCacheRasterGetAddr allows the app to get the address in
 * GS memory where a textureTEX0 parameters for the
 * raster.  The value can also be obtained using \ref skyTexGetTex0
 *
 * \param r  Pointer to the raster.
 * \return Address in local memory (word address divided by 64).
 *
 */
RwUInt32
skyTexCacheRasterGetAddr(RwRaster *r)
{
    _SkyRasterExt *rasExt;

    RWFUNCTION(RWSTRING("skyTexCacheRasterGetAddr"));

    rasExt = RASTEREXTFROMRASTER(r);

#if defined(GSB) && defined(GSPLUS)
    RWRETURN((rasExt->lsb3) & 0x1ffffl);
#else /* defined(GSB) && defined(GSPLUS) */
    RWRETURN((rasExt->lsb) & 0x3fffl);
#endif /* defined(GSB) && defined(GSPLUS) */
}

/**
 * \ingroup sky2unsupported
 * \ref skyTexSetTex0 allows the app to set TEX0 parameters for the
 * raster.  This function is typically used in a cache callback function.
 *
 * \param r  Pointer to the raster.
 * \param msb  Integer that sets msb of the tex0 register data.
 * \param lsb  Integer that sets lsb of the tex0 register data.
 * \return Returns the raster on success, or NULL on failure.
 *
 */

RwRaster *
skyTexSetTex0(RwRaster *r, RwUInt32 msb, RwUInt32 lsb)
{
    _SkyRasterExt *rasExt;

    RWFUNCTION(RWSTRING("skyTexSetTex0"));

    rasExt = RASTEREXTFROMRASTER(r);

    if (rasExt)
    {
        rasExt->lsb = lsb;
        rasExt->msb = msb;
    }

    RWRETURN(r);
}

/**
 * \ingroup sky2unsupported
 * \ref skyTexGetTex0 allows the app to retrieve the value used to program
 * the TEX0 register of the GS.
 *
 * \param r  Pointer to the raster.
 * \param msb  Pointer to word that will receive the msb of the tex0 register.
 * \param lsb  Pointer to word that will receive the lsb of the tex0 register.
 * \return Returns the raster on success, or NULL on failure.
 */

RwRaster *
skyTexGetTex0(RwRaster *r, RwUInt32 *msb, RwUInt32 *lsb)
{
    _SkyRasterExt *rasExt;

    RWFUNCTION(RWSTRING("skyTexGetTex0"));

    rasExt = RASTEREXTFROMRASTER(r);

    if (rasExt)
    {
        *msb = rasExt->msb;
        *lsb = rasExt->lsb;
    }

    RWRETURN(r);
}

/**
 * \ingroup sky2unsupported
 * \ref skyTexSetMiptbp1 allows the app to set mipmap parameters for the
 * raster. This function is typically used in a cache callback function.
 *
 * \param r  Pointer to the raster.
 * \param msb  Integer that sets msb of the Miptbp1 register data.
 * \param lsb  Integer that sets lsb of the Miptbp1 register data.
 * \return Returns the raster on success, or NULL on failure.
 *
 */

RwRaster *
skyTexSetMiptbp1(RwRaster *r, RwUInt32 msb, RwUInt32 lsb)
{
    _SkyRasterExt *rasExt;

    RWFUNCTION(RWSTRING("skyTexSetMiptbp1"));

    rasExt = RASTEREXTFROMRASTER(r);

    if (rasExt)
    {
        rasExt->miptbp1Lsb = lsb;
        rasExt->miptbp1Msb = msb;
    }

    RWRETURN(r);
}

/**
 * \ingroup sky2unsupported
 * \ref skyTexGetMiptbp1 allows the app to get mipmap parameters from the
 * raster.
 *
 * \param r  Pointer to the raster.
 * \param msb  Pointer to integer that receives msb of the Miptbp1 register data.
 * \param lsb  Pointer to integer that receives lsb of the Miptbp1 register data.
 * \return Returns the raster on success, or NULL on failure.
 */

RwRaster *
skyTexGetMiptbp1(RwRaster *r, RwUInt32 *msb, RwUInt32 *lsb)
{
    _SkyRasterExt *rasExt;

    RWFUNCTION(RWSTRING("skyTexGetMiptbp1"));

    rasExt = RASTEREXTFROMRASTER(r);

    if (rasExt)
    {
        *msb = rasExt->miptbp1Msb;
        *lsb = rasExt->miptbp1Lsb;
    }

    RWRETURN(r);
}

/**
 * \ingroup sky2unsupported
 * \ref skyTexGetStartAddress allows the application to query the start
 * address in local memory of the texture cache.  The result is the
 * address expressed in words.  (The PS2 has 1024*1024 words of local memory).
 * This function should be called only after RW has been opened.
 *
 * The application typically needs to know the first free page in GS
 * local memory.  This value can be obtained using this function.
 *
 *\return The start address of the texture cache.  If the cache has
 * not been initialized the result will be -1.
 *
 */
RwUInt32
skyTexGetStartAddress(void)
{
    RWFUNCTION(RWSTRING("skyTexGetStartAddress"));
    RWRETURN((RwUInt32)state.TextureBaseAddressInLocalMemory);
}

/**
 * \ingroup sky2unsupported
 * \ref skyTexCacheSetCallback allows the application to specify the
 * callback function used to indicate that a raster must be uploaded to local
 * memory.  The function will fail if the texture cache is enabled.
 *
 * The format of the callback function is:
 *
 * void (*SkyCacheCallBack)(RwRaster *raster, RwBool false)
 *
 * where raster is the raster to be uploaded and false is unused.
 *
 * \param fpCacheCB  Callback function.
 *
 * \return Returns TRUE on success, FALSE if the cache is not disabled or if
 * the callback NULL.
 *
 * \see skyTexCacheSetSnoopCallback
 * \see skyTexCacheDisable
 * \see skyTexCacheEnable
 */
RwBool
skyTexCacheSetCallback(SkyCacheCallBack fpCacheCB)
{
    RWFUNCTION(RWSTRING("skyTexCacheSetCallback"));

    if (!cGCache.bEnabled && fpCacheCB)
    {
        /* printf("set new cache callback function\n"); */
        cGCache.fpCacheUploadRasterCB = fpCacheCB;
        RWRETURN(TRUE);
    }

    /* printf("callback setting failed\n"); */
    RWRETURN(FALSE);
}

/**
 * \ingroup sky2unsupported
 * \ref skyTexCacheSetSnoopCallback allows the application to specify
 * an optional callback function which will be called within tecture
 * cache code.
 *
 * This function will be called at the start of the code to upload
 * a raster to the RW texture cache. It may be used to alter the
 * raster to be uploaded at the last minute (dependent on raster
 * plugin data, say) or merely to snoop the rasters being uploaded.
 * This function will be called whether the raster in question is
 * already in the texture cache or not and whether the RW texture
 * cache is enabled or not.
 *
 * The format of the callback function is:
 *
 * void (*SkyCacheSnoopCallBack)(RwRaster **raster)
 *
 * \param fpCacheSnoopCB  Callback function.
 *
 * \return Returns TRUE on success, FALSE otherwise
 *
 * \see skyTexCacheSetCallback
 * \see skyTexCacheDisable
 * \see skyTexCacheEnable
 */
RwBool
skyTexCacheSetSnoopCallback(SkyCacheSnoopCallBack fpCacheCB)
{
    RWFUNCTION(RWSTRING("skyTexCacheSetSnoopCallback"));

    /* NULL is valid and indeed default */
    cGCache.fpCacheSnoopRasterCB = fpCacheCB;

    RWRETURN(TRUE);
}

/**
 * \ingroup sky2unsupported
 * \ref skyTexCacheDisable prevents RenderWare from operating a texture
 * cache.  If this function is called, then the application must control how
 * textures are stored in local memory.  This function must be called before
 * the call to RwEngineStart().
 *
 * \return Returns TRUE on success, FALSE if the engine has been started.
 */
RwBool
skyTexCacheDisable(void)
{
    RWFUNCTION(RWSTRING("skyTexCacheDisable"));

    if (!cacheOpened)
    {
        /* printf("cache disabled\n"); */
        state.needTexCache = FALSE;
        RWRETURN(TRUE);
    }
    else
    {
        /* printf("cache disabled occured too late\n"); */
        /* too late */
        RWRETURN(FALSE);
    }
}

/**
 * \ingroup sky2unsupported
 * \ref skyTexCacheValidateRaster marks the raster as no longer in the
 * GS local memory (FALSE) or in memory (TRUE).  The function will fail if
 * the texture cache is enabled.
 *
 * The cache manager will call the callback for the raster repeatedly
 * until the raster has been validated as in the cache.  If the application
 * wants to "evict" the raster from the cache it can invalidate the raster by
 * calling this function with FALSE as the second parameter.
 *
 * \param r  Pointer to the raster.
 * \param valid  Boolean determining raster state.
 * \return Returns the raster on success, NULL if the function fails.
 *
 */
RwRaster *
skyTexCacheValidateRaster(RwRaster *r, RwBool valid)
{
    RWFUNCTION(RWSTRING("skyTexCacheValidateRaster"));

    if (!state.needTexCache)
    {
        _SkyRasterExt *rasExt;

        rasExt = RASTEREXTFROMRASTER(r);

        if (rasExt)
        {
            rasExt->mpCacheEntry =  ( valid ?
                                      (_SkyMemBlock *)1 :
                                      (_SkyMemBlock *) NULL );
            RWRETURN(r);
        }
    }

    /* something not right */
    RWRETURN((RwRaster *)NULL);
}

/**
 * \ingroup sky2unsupported
 * \ref skyUploadPixelData.
 *
 * Note that the pixel data must be flushed from the DCache before it can
 * be uploaded by the DMA hardware.
 *
 * \param pixelWidth  Width of the array of pixels to upload.
 * \param pixelHeight  Height of the array of pixels to upload.
 * \param srcAddress  Start address in memory of the pixel data. Note that this must be flushed
 * \param dstAddress  Address in GS memory to where data is written (word address divided by 64).
 * \param dstWidthBy64  Width of raster divided by 64 (1 if the raster width is less than 64).
 * \param bitsPerPixel  Bits per pixel.
 * \param pixelFormat  GS register pixel format as per TEX0 register.
 * \param srcStride  Distance in bytes from first pixel of row one to first pixel of row two.
 *
 * \return Returns the TRUE on success, FALSE if the function fails.
 *
 */
RwBool
skyUploadPixelData(RwUInt32 pixelWidth, RwUInt32 pixelHeight,
                   RwUInt8 *srcAddress, RwUInt32 dstAddress,
                   RwUInt32 dstWidthBy64, RwUInt32 bitsPerPixel,
                   RwUInt32 pixelFormat, RwUInt32 srcStride)
{
    RwBool   aligned = (!((RwUInt32)srcAddress & 0xf));

    RWFUNCTION(RWSTRING("skyUploadPixelData"));

    if (aligned)
    {
        _SkyUpLoadAlignedRectangle(pixelWidth, pixelHeight, srcAddress, dstAddress,
                               dstWidthBy64, bitsPerPixel, pixelFormat,
                               srcStride);
    }
    else
    {
        _SkyUpLoadRectangle(pixelWidth, pixelHeight, srcAddress, dstAddress,
                        dstWidthBy64, bitsPerPixel, pixelFormat, srcStride);
    }
    RWRETURN(TRUE);
}

/**
 * \ingroup driversky2
 * \ref RpSkyTextureCacheSetState controls the mode that the texture
 * cache operates in.  By default the mode is designed to get "best" cache
 * performance.  If you are intending to lock rasters in the GS memory then
 * switching to an alternative mode is better.  To absolutely guarantee that a
 * locked texture stays in GS memory and is never overwritten, you can change
 * the cache behaviour to RWSKYCACHESTATIC. This tunes the cache to work better
 * with textures that are intended to stay around.
 *
 * It is possible for the application to lock every texture into the
 * texture cache.  If RenderWare needs to evict a texture to upload a new
 * one, then the locked flag of one texture will be ignored.  It is best to
 * allow some free space in the cache for RenderWare to use for such
 * dynamic textures.  Note that the RWSKYCACHESTATIC mode should be selected
 * if the application performs rendering to a texture.  This allows the
 * texture to be locked and not overwritten.
 *
 * \param nFlags  mode to set the cache to, one of RWSKYCACHENORMAL which causes a
 * custom caching algorithm that uses LRU and MRU behaviours, and
 * RWSKYCACHESTATIC which selects a MRU algorithm which considers locked
 * textures only as a last resort.
 * \return TRUE on success, FALSE on failure.
 *
 *
 */
RwBool
RpSkyTextureCacheSetState(RwInt32 nFlags)
{
    RWAPIFUNCTION(RWSTRING("RpSkyTextureCacheSetState"));

    cGCache.nCacheControlFlags = nFlags;
    RWRETURN(TRUE);
}

#else /* !ASYNCTEXTURES */

/************************************************************************
 * Module  : texcache.c                                                 *
 *                                                                      *
 * Purpose : A simple round robin with LRU texture cache for PS2        *
 ************************************************************************/

/************************************************************************
 Includes
 */
#if 0
/* Included at top of file */
#include "baraster.h"
#include "rwstring.h"

#include "balibtyp.h"
#include "badebug.h"

#include "badma.h"
#include "gs.h"
#include "basky.h"

#include "texcache.h"
#endif

#define REDEBUGx

/************************************************************************
 RCS automatically updated string
 */
static const char rcsid1[] __RWUNUSED__ =
"@@(#)$Id:";

/************************************************************************
 Local Types
 */
/* *INDENT-OFF* */

typedef struct _SkyTexCache _SkyTexCache;
struct _SkyTexCache
{
    RwBool enabled __attribute__((aligned(64)));

    RwUInt32 ringStart;         /* Head. Texture in use */
    RwUInt32 ringEnd;		/* LRUd texture */
    RwUInt32 ringSize;          /* Total number of ring blocks */
    RwUInt32 lockedStart;       /* Locked rasters' blocks. Start at end */
    _SkyMemBlock *ring;

    RwInt32 startAddr;		/* Start address in GS words */
    RwInt32 totalSize;		/* Total size in GS words */

    SkyCacheCallBack uploadRaster;

    RwRaster *currentRaster;
    RwBool context;

    RwUInt32 context1Index;	/* Current ring loc of context 1 raster */
    RwUInt32 context2Index;	/* Current ring loc of context 2 raster */
} ;

/* Cut and paste from the old texture cache. */
typedef struct TexCacheState TexCacheState;

struct TexCacheState
{
     u_long128 texFlushGsCommand __attribute__((aligned(64)));
     u_long128 trxPos0GsCommand;
     u_long128 trxDir0GsCommand;
     u_long128 refUploadSetupGifTag5;
};
/* End cut and paste from the old texture cache. */
/************************************************************************
 Local Static Prototypes.
 Try to avoid using this section, as auto inlining often fails if the
 function dependencies are the wrong way up in the file.
 */

/************************************************************************
 Local Defines
 */

/* Number of textures to preserve in the texture queue under speculative
   upload. This must be at least 1 */
/* WARNING: Disabled in this build */
#define SPECPRESERVE (3)

#if (SPECPRESERVE < 1)
#error "SPECPRESERVE must be >= 1"
#endif /* (SPECPRESERVE < 1) */

#define ContiguousRectangleType \
 ( SWE_PKT_VU1 |                \
   SWE_PKT_CIRCALLOC |          \
   SWE_PKT_DMA_MODE_CHAIN_TTE | \
   SWE_LPS_NOFIXUP )

/************************************************************************
 Globals (across program)
 */

RwUInt32 skyGNumTexMemBlocks = 196;

/************************************************************************
 File scope globals.
 */

static _SkyTexCache skyTexCache =
{
    /* enabled      */ TRUE,

    /* ringStart;   */ 0,
    /* ringEnd;     */ 0,
    /* ringSize;    */ 0,
    /* lockedStart; */ 0,
    /* ring;        */ (_SkyMemBlock *)NULL,

    /* startAddr;   */ -1,
    /* totalSize;   */ -1,

    /* uploadRaster */ (SkyCacheCallBack)NULL,

    /* currentRaster*/ (RwRaster *)NULL,
    /* context      */ FALSE,

    /* context1Index*/ ~0U,
    /* context2Index*/ ~0U
};

static RwBool skyTexCacheLastWasSpec = FALSE;

/* Cut and paste from the old texture cache. */

static TexCacheState state;

/* End cut and paste from the old texture cache. */
/************************************************************************
 File scope functions.
 */

/* Cut and paste from the old texture cache. */

u_long128   *
skyFillInPktForUpLoadAlignedContiguousRectangle(u_long128 * pkt,
                                                RwUInt8 * src,
                                                RwUInt64 bytesInRectangle,
                                                RwUInt8 cachedPacket)
{
    RwUInt64            tmp, tmp1;
    u_long128           gifTagCmd;
    u_long128           dmaTag1Direct, ltmp;
    int                 i, k, l;
    u_long128          *ptr;

    RWFUNCTION(RWSTRING("skyFillInPktForUpLoadAlignedContiguousRectangle"));
    RWASSERT(pkt);
    RWASSERT(src);

    tmp = 1l | (1l << 28);
    tmp1 = ((0x50l << 24) | (1l)) << 32;
    MAKE128(dmaTag1Direct, tmp1, tmp);

    /* We attempt to shift via the vif.  We use tte */
    /* Build a ref chain, where the last one is a refe(i) */
    /* Note that here we can use 32750 as the data is not inlined */
    ptr = (u_long128 *) (((RwUInt32) pkt + 63) & ~63);

    /* Number of qwords */
    k = l = ((bytesInRectangle + 15) / 16);
    for (i = 0; i < (k + 32749) / 32750; i++)
    {
        u_long128           dmaRefCmd;
        RwUInt32            nloop, eop, dmaID;

        /* count */
        *ptr++ = dmaTag1Direct;

        if (l > 32750)
        {
            /* Not last ref (ref) */
            nloop = 32750;
            eop = 0;
            dmaID = 3;
        }
        else
        {
            /* Last ref (ref + int) */
            nloop = l;
            eop = 0;
            dmaID = 3;
        }
        /* gif tag */
        tmp = /* NLOOP */ (nloop) |
            /* EOP */ (eop << 15) |
            /* FLG */ (2l << 58);
        MAKE128(gifTagCmd, 0l, tmp);
        *ptr++ = gifTagCmd;

        /* ref(e)(i) */
        tmp =
            (nloop) | (dmaID << 28) | ((RwUInt64) (RwUInt32) src << 32);
        tmp1 = ((0x50l << 24) | (nloop)) << 32;
        MAKE128(dmaRefCmd, tmp1, tmp);
        *ptr++ = dmaRefCmd;
        src += 32750 * 16;
        l -= 32750;
    }

    if (cachedPacket)
    {
        tmp = (0x6l << 28) | 2;
    }
    else
    {
        tmp = (0xfl << 28) | 2;
    }
    tmp1 = ((0x50l << 24) | 2l) << 32;
    MAKE128(ltmp, tmp1, tmp);
    *ptr++ = ltmp;

    tmp = /* NLOOP */ 1 |
        /* EOP */ (1 << 15) |
        /* FLG */ (0l << 58) |
        /* NREG */ (1l << 60);
    tmp1 = 0xe;
    MAKE128(gifTagCmd, tmp1, tmp);
    *ptr++ = gifTagCmd;

    *ptr++ = state.texFlushGsCommand;

    /* Push it out of cache into memory */
    SyncDCache((void *) (((RwUInt32) pkt + 63) & ~63),
               SCESYNCDCACHEROUNDUP(ptr));

    RWRETURN(pkt);
}

static u_long128*
_SkyBuildPktForUpLoadAlignedContiguousRectangle(RwUInt32 pixelWidth,
                                            RwUInt32 pixelHeight,
                                            RwUInt8 *srcAddress,
                                            RwUInt32 bitsPerPixel,
                                            RwUInt8 cachedPacket)
{
    RwUInt8   *src = (RwUInt8 *)srcAddress;
    RwUInt64  bytesInRectangle = (pixelWidth * pixelHeight * bitsPerPixel) >> 3;
    u_long128 *pkt;

    RWFUNCTION(RWSTRING("_SkyBuildPktForUpLoadAlignedContiguousRectangle"));
    RWASSERT(pixelWidth);
    RWASSERT(pixelHeight);
    RWASSERT(srcAddress);

    /* Get the memory for it */
    if (cachedPacket)
    {
        pkt = (u_long128 *)RwMalloc((((bytesInRectangle+15)/16)
                                     /32750+1)*16 *3+3*16 +128);
    }
    else
    {
        pkt = (u_long128 *)circularMalloc(circAllocator,
                                          (((bytesInRectangle+15)/16)
                                           /32750 + 1)*16 *3+3*16 +128,
                                          calDCACHE_ALLOC);
    }

    /* Now fill it in */
    pkt = skyFillInPktForUpLoadAlignedContiguousRectangle(pkt, src, bytesInRectangle, cachedPacket);

    RWRETURN(pkt);
}

void
skyPrepareUploadRaster(RwRaster *raster)
{
    RwInt32       level;
    RwUInt32      bitsPerPixel, pixelFormat;
    RwBool        contiguous;
    _SkyRasterExt *rasExt = RASTEREXTFROMRASTER(raster);
    RwUInt8       *srcAddress = raster->cpPixels;

    RWFUNCTION(RWSTRING("skyPrepareUploadRaster"));
    RWASSERT(raster);

    bitsPerPixel = raster->depth;
    pixelFormat = (rasExt->lsb >> 20) & 0x3f;

    contiguous =
        (raster->stride == ((raster->width*(RwInt32)bitsPerPixel)>>3));

    /* Upload each of the mipmap levels */
    for (level = 0; level <= (rasExt->maxMipLevel>>2); level++)
    {
        RwUInt32 mipWidth = (raster->width>>level);
        RwUInt32 mipHeight = (raster->height>>level);
        RwUInt32 mipStride = (raster->stride>>level);
        RwBool   aligned = (!((RwUInt32)srcAddress & 0xf));

        if (aligned)
        {
            /* Aligned - choose whether to use ref per scanline or not */
            if (contiguous)
            {
                if (!rasExt->mipUploadPkts[level])
                {
                    rasExt->mipUploadPkts[level] =
                        _SkyBuildPktForUpLoadAlignedContiguousRectangle(mipWidth,
                                                                    mipHeight,
                                                                    srcAddress,
                                                                    bitsPerPixel,
                                                                    rasExt->cachePkts);
                }
                if (!rasExt->cachePkts)
                {
                    rasExt->mipUploadPkts[level] = (u_long128 *)NULL;
                }

            }
        }

        /* Breaks if we upload mipmapped subrastered texture
           - but we're not going to do that, are we... :-) */
        srcAddress += mipStride * mipHeight;

        /* Align for later mip levels */
        if((RwUInt32)srcAddress & 0xf)
        {
            srcAddress = (RwUInt8 *)(((RwUInt32)srcAddress + 15) & ~15);
        }
    }

    /* Upload a palette if applicable */
    if ((rasExt->sysMemPalSize) && (raster->depth <= 8))
    {
        static const RwUInt32 palWidths[8] = {8, 8, 8, 8, 16, 16, 16, 16};
        static const RwUInt32 palHeights[8] = {2, 2, 2, 2, 16, 16, 16, 16};
        RwInt32  palBPP;
        RwUInt32 palWidth, palHeight;
        RwUInt32 format = (rasExt->msb >> (51-32)) & 0xF;

        palWidth = palWidths[raster->depth-1];
        palHeight = palHeights[raster->depth-1];

        switch (format)
        {
            case (PSMCT32):
                {
                    palBPP = 32;
                    break;
                }
            case (PSMCT16):
            case (PSMCT16S):
            default:
                {
                    palBPP = 16;
                    break;
                }
        }

        if (!rasExt->palUploadPkt)
        {
            rasExt->palUploadPkt =
                _SkyBuildPktForUpLoadAlignedContiguousRectangle(palWidth,
                                                            palHeight,
                                                            raster->palette,
                                                            palBPP,
                                                            rasExt->cachePkts);
        }

        if (!rasExt->cachePkts)
        {
            rasExt->palUploadPkt = (u_long128 *)NULL;
        }
    }

    RWRETURNVOID();
}
static void
_SkyDdispatchUpLoadAlignedContiguousRectangle(RwUInt32 pixelWidth,
                                         RwUInt32 pixelHeight,
                                         RwUInt32 dstAddress,
                                         RwUInt32 dstWidthBy64,
                                         RwUInt32 pixelFormat,
                                         u_long128 *pkt,
                                         RwUInt8 cachedPacket)
{
    RwUInt64  tmp;
    u_long128 bitBltBufCmd, trxRegCmd, ltmp;

    RWFUNCTION(RWSTRING("_SkyDdispatchUpLoadAlignedContiguousRectangle"));
    RWASSERT(pixelWidth);
    RWASSERT(pixelHeight);
    RWASSERT(dstAddress);
    RWASSERT(dstWidthBy64);

    if (cachedPacket)
    {

        /* we now create a special ref(e)(i) dma pkt, but */
        /* marked as local so that it will be freed by */
        /* the pkt garbage collector */
        sweOpenLocalPkt(SWE_LPS_CONT, 0);
        /* Prepare the GS to accept the texture */
#if defined(GSB) && defined(GSPLUS)
        SWEADDCONTGIFFAST(state.refUploadSetupGifTag6, 6);
#else /* defined(GSB) && defined(GSPLUS) */
        SWEADDCONTGIFFAST(state.refUploadSetupGifTag5, 5);
#endif /* defined(GSB) && defined(GSPLUS) */

        /* Flush - It is uncertain that we need this */
        SWEADDCONTFAST(state.texFlushGsCommand);

        /* BitBltbuf */
        tmp = ((RwUInt64)pixelFormat << 56) |
            ((RwUInt64)dstWidthBy64 << 48) |
            ((RwUInt64)(dstAddress & 0x3fff)<< 32) |
            ((RwUInt64)pixelFormat << 24) |
            (10l << 16);  /* Doesn't matter - feeding from FIFO */
        MAKE128(bitBltBufCmd, GS_BITBLTBUF, tmp);
        SWEADDCONTFAST(bitBltBufCmd);

#if defined(GSB) && defined(GSPLUS)
        tmp = ((RwUInt64)(dstAddress & 0x1ffff)<< 32);
        MAKE128(bitBltBufCmd, GS_BITBLTBUF2, tmp);
        SWEADDCONTFAST(bitBltBufCmd);
#endif /* defined(GSB) && defined(GSPLUS) */

        /* TrxPos */
        SWEADDCONTFAST(state.trxPos0GsCommand);

        /* TrxReg */
        tmp = ((RwUInt64)pixelHeight << 32) | (RwUInt64)pixelWidth;
        MAKE128(trxRegCmd, GS_TRXREG, tmp);
        SWEADDCONTFAST(trxRegCmd);

        /* TrxDir */
        SWEADDCONTFAST(state.trxDir0GsCommand);

        /* This will also add the command packet first */

#if (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO))
        {
            RwBool status;
            int size = -4;

            sweFinaliseOpenLocalPktMacro(status,
                                         ContiguousRectangleType, size);
        }
#else /* (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO)) */
        sweFinaliseOpenLocalPkt(ContiguousRectangleType, -4);
#endif /* (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO)) */

        tmp = (5l<<28) | ((RwUInt64)(((RwUInt32)pkt + 63) & ~63)<<32);
        MAKE128(ltmp, 0l, tmp);
        SWEADDCONTFAST(ltmp);

        tmp = (0xfl<<28);
        MAKE128(ltmp, 0l, tmp);
        SWEADDCONTFAST(ltmp);

#if (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO))
        {
            RwBool status;
            int size = 0;

            sweFinaliseOpenLocalPktMacro(status, SWE_LPS_CONT, size);
        }
#else /* (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO)) */
        sweFinaliseOpenLocalPkt(SWE_LPS_CONT, 0);
#endif /* (defined(RWUSESWEFINALISEOPENLOCALPKTMACRO)) */
    }
    else
    {
        /* Use by ref */
        sweCloseLocalPkt();
        /* we now create a special ref(e)(i) dma pkt, but */
        /* marked as local so that it will be freed by */
        /* the pkt garbage collector */
        sweOpenLocalPkt(SWE_LPS_CONT, 0);
        /* Prepare the GS to accept the texture */
#if defined(GSB) && defined(GSPLUS)
        SWEADDCONTGIFFAST(state.refUploadSetupGifTag6, 6);
#else /* defined(GSB) && defined(GSPLUS) */
        SWEADDCONTGIFFAST(state.refUploadSetupGifTag5, 5);
#endif /* defined(GSB) && defined(GSPLUS) */

        /* Flush - It is uncertain that we need this */
        SWEADDCONTFAST(state.texFlushGsCommand);

        /* BitBltbuf */
        tmp = ((RwUInt64)pixelFormat << 56) |
            ((RwUInt64)dstWidthBy64 << 48) |
            ((RwUInt64)(dstAddress & 0x3fff)<< 32) |
            ((RwUInt64)pixelFormat << 24) |
            (10l << 16);  /* Doesn't matter - feeding from FIFO */
        MAKE128(bitBltBufCmd, GS_BITBLTBUF, tmp);
        SWEADDCONTFAST(bitBltBufCmd);

#if defined(GSB) && defined(GSPLUS)
        tmp = ((RwUInt64)(dstAddress & 0x1ffff)<< 32);
        MAKE128(bitBltBufCmd, GS_BITBLTBUF2, tmp);
        SWEADDCONTFAST(bitBltBufCmd);
#endif /* defined(GSB) && defined(GSPLUS) */

        /* TrxPos */
        SWEADDCONTFAST(state.trxPos0GsCommand);

        /* TrxReg */
        tmp = ((RwUInt64)pixelHeight << 32) | (RwUInt64)pixelWidth;
        MAKE128(trxRegCmd, GS_TRXREG, tmp);
        SWEADDCONTFAST(trxRegCmd);

        /* TrxDir */
        SWEADDCONTFAST(state.trxDir0GsCommand);

        /* This will also add the command packet first */
        _sweAddPkt((void *)(((RwUInt32)pkt + 63) & ~63),
                   SWE_PKT_GIF | SWE_PKT_LOCAL | SWE_PKT_CIRCALLOC
                   | SWE_PKT_DMA_MODE_CHAIN);

        _sweFlush();
    }

    RWRETURNVOID();
}

static void
_SkyUpLoadAlignedRectangle(RwUInt32 pixelWidth, RwUInt32 pixelHeight,
                       RwUInt8 *srcAddress, RwUInt32 dstAddress,
                       RwUInt32 dstWidthBy64, RwUInt32 bitsPerPixel,
                       RwUInt32 pixelFormat, RwUInt32 srcStride)
{
    RwUInt8   *src;
    RwUInt64  tmp, tmp1;
    u_long128 gifTagCmd, bitBltBufCmd, trxRegCmd;
    RwUInt32  i;
    RwUInt64  qWordsInLine;
    u_long128 *ptr, *pkt;
    u_long128 dmaTag1Direct, ltmp;

    RWFUNCTION(RWSTRING("_SkyUpLoadAlignedRectangle"));
    RWASSERT(pixelWidth);
    RWASSERT(pixelHeight);
    RWASSERT(srcAddress);
    RWASSERT(dstWidthBy64);

    tmp = 1l | (1l<<28);
    tmp1 = ((0x50l<<24) | (1l))<<32;
    MAKE128(dmaTag1Direct, tmp1, tmp);

    /* Make it a multiple of qWords for upload... :-) */
    qWordsInLine = (((pixelWidth * bitsPerPixel) >> 3) + 15) / 16;
    pixelWidth = (qWordsInLine*128)/bitsPerPixel;

    /* Use by ref */
    sweCloseLocalPkt();
    /* we now create a special ref(e)(i) dma pkt, but marked as local so that
     * it will be freed by the pkt garbage collector.  We also assume that
     * each scanline is less than 32750 qWords...!
     */
    sweOpenLocalPkt(SWE_LPS_CONT, 0);
    pkt = (u_long128 *)circularMalloc(circAllocator,
                                      pixelHeight * 16 * 3 +3*16,
                                      calDCACHE_ALLOC);

    /* Prepare the GS to accept the texture */
#if defined(GSB) && defined(GSPLUS)
    SWEADDCONTGIFFAST(state.refUploadSetupGifTag6, 6);
#else /* defined(GSB) && defined(GSPLUS) */
    SWEADDCONTGIFFAST(state.refUploadSetupGifTag5, 5);
#endif /* defined(GSB) && defined(GSPLUS) */

    /* Flush - It is uncertain that we need this */
    SWEADDCONTFAST(state.texFlushGsCommand);

    /* BitBltbuf */
    tmp = ((RwUInt64)pixelFormat << 56) |
        ((RwUInt64)dstWidthBy64 << 48) |
        ((RwUInt64)(dstAddress & 0x3fff)<< 32) |
        ((RwUInt64)pixelFormat << 24) |
        (10l << 16);  /* Doesn't matter - feeding from FIFO */
    MAKE128(bitBltBufCmd, GS_BITBLTBUF, tmp);
    SWEADDCONTFAST(bitBltBufCmd);

#if defined(GSB) && defined(GSPLUS)
    tmp = ((RwUInt64)(dstAddress & 0x1ffff)<< 32);
    MAKE128(bitBltBufCmd, GS_BITBLTBUF2, tmp);
    SWEADDCONTFAST(bitBltBufCmd);
#endif /* defined(GSB) && defined(GSPLUS) */

    /* TrxPos */
    SWEADDCONTFAST(state.trxPos0GsCommand);

    /* TrxReg */
    tmp = ((RwUInt64)pixelHeight << 32) | (RwUInt64)pixelWidth;
    MAKE128(trxRegCmd, GS_TRXREG, tmp);
    SWEADDCONTFAST(trxRegCmd);

    /* TrxDir */
    SWEADDCONTFAST(state.trxDir0GsCommand);

    /* Build a ref chain, where the last one is a refe(i) */
    ptr = pkt;
    src = srcAddress;
    for (i=0; i < pixelHeight; i++)
    {
        u_long128    dmaRefCmd;
        RwUInt32     eop, dmaID;

        /* count */
        *ptr++ = dmaTag1Direct;

        if (i < (pixelHeight-1))
        {
            /* Not the last line */
            eop = 0;
            dmaID = 3;

        }
        else
        {
            /* The last line */
            eop = 0;
            dmaID = 3;
        }

        /* DMAtag */
        tmp = /* NLOOP */ (qWordsInLine) |
            /* EOP */   (eop << 15) |
            /* FLG */   (2l << 58);
        MAKE128(gifTagCmd, 0l, tmp);
        *ptr++ = gifTagCmd;

        /* ref */
        tmp = (qWordsInLine) | (dmaID << 28) |
            ((RwUInt64)(RwUInt32)src << 32);
        tmp1 = (((0x50l<<24) | (qWordsInLine))<<32);
        MAKE128(dmaRefCmd, tmp1, tmp);
        *ptr++ = dmaRefCmd;
        src += srcStride;
    }
    tmp = (0xfl<<28) | 2;
    tmp1 = ((0x50l<<24) | 2l)<<32;
    MAKE128(ltmp, tmp1, tmp);
    *ptr++ = ltmp;

    tmp = /* NLOOP */ 1 |
        /* EOP */   (1<<15) |
        /* FLG */   (0l<<58) |
        /* NREG */ (1l<<60);
    tmp1 = 0xe;
    MAKE128(gifTagCmd, tmp1, tmp);
    *ptr++ = gifTagCmd;

    *ptr++ = state.texFlushGsCommand;

    /* Sync ref packet and source data out of DCache */
    SyncDCache(pkt, SCESYNCDCACHEROUNDUP(ptr));

    /* This will also add the command packet first */
    _sweAddPkt(pkt, SWE_PKT_GIF | SWE_PKT_LOCAL | SWE_PKT_CIRCALLOC |
               SWE_PKT_DMA_MODE_CHAIN);
    _sweFlush();

    RWRETURNVOID();
}

static void
_SkyUpLoadRectangle(RwUInt32 pixelWidth, RwUInt32 pixelHeight,
                RwUInt8 * srcAddress,
                RwUInt32 dstAddress, RwUInt32 dstWidthBy64,
                RwUInt32 bitsPerPixel, RwUInt32 pixelFormat,
                RwUInt32 srcStride)
{
    RwUInt8            *src8;
    RwUInt16           *src16;
    RwUInt32           *src32;
    RwUInt32            scanLineAdjust;
    RwUInt64            tmp, tmp1;
    u_long128           gifTagCmd, bitBltBufCmd, trxRegCmd;
    RwUInt64            qcount;
    RwUInt32            i, j, k, l;
    RwUInt64            bytesInRectangle =
        (pixelWidth * pixelHeight * bitsPerPixel) >> 3;

    RWFUNCTION(RWSTRING("_SkyUpLoadRectangle"));
    RWASSERT(pixelWidth);
    RWASSERT(pixelHeight);
    RWASSERT(srcAddress);
    RWASSERT(dstWidthBy64);

    /* If not qword aligned or not contiguous, create
     * a packet big enough to hold copy by value data.
     */
    /* Copy by value */
    sweCloseLocalPkt();
    /* Force creation of a packet of sufficient or max size */
    sweOpenLocalPkt(SWE_LPS_CONT, ((bytesInRectangle + 15) >> 4) + 7);

    /* Prepare the GS to accept the texture */
#if defined(GSB) && defined(GSPLUS)
    tmp = /* NLOOP */ 6l
        | /* EOP */ (0l << 15)
        | /* PRE */ (0l << 46)
        | /* FLG */ (0l << 58)
        | /* NREG */ (1l << 60);
    tmp1 = /* A+D */ (0xel << (64 - 64));
    MAKE128(gifTagCmd, tmp1, tmp);
    SWEADDCONTGIFFAST(gifTagCmd, 6);
#else /* defined(GSB) && defined(GSPLUS) */
    tmp = /* NLOOP */ 5l
        | /* EOP */ (0l << 15)
        | /* PRE */ (0l << 46)
        | /* FLG */ (0l << 58)
        | /* NREG */ (1l << 60);
    tmp1 = /* A+D */ (0xel << (64 - 64));
    MAKE128(gifTagCmd, tmp1, tmp);
    SWEADDCONTGIFFAST(gifTagCmd, 5);
#endif /* defined(GSB) && defined(GSPLUS) */

    /* Flush - It is uncertain that we need this */
    SWEADDCONTFAST(state.texFlushGsCommand);

    /* BitBltbuf */
    tmp = ((RwUInt64) pixelFormat << 56) |
        ((RwUInt64) dstWidthBy64 << 48) |
        ((RwUInt64) (dstAddress & 0x3fff) << 32) |
        ((RwUInt64) pixelFormat << 24) | (10l << 16); /* Doesn't matter - feeding from FIFO */
    MAKE128(bitBltBufCmd, GS_BITBLTBUF, tmp);
    SWEADDCONTFAST(bitBltBufCmd);

#if defined(GSB) && defined(GSPLUS)
    tmp = ((RwUInt64) (dstAddress & 0x1ffff) << 32);
    MAKE128(bitBltBufCmd, GS_BITBLTBUF2, tmp);
    SWEADDCONTFAST(bitBltBufCmd);
#endif /* defined(GSB) && defined(GSPLUS) */

    /* TrxPos */
    SWEADDCONTFAST(state.trxPos0GsCommand);

    /* TrxReg */
    tmp = ((RwUInt64) pixelHeight << 32) | (RwUInt64) pixelWidth;
    MAKE128(trxRegCmd, GS_TRXREG, tmp);
    SWEADDCONTFAST(trxRegCmd);

    /* TrxDir */
    SWEADDCONTFAST(state.trxDir0GsCommand);

    /* Now pack and write data to HWReg */
    /* We can't necessarily move the data in one chunk as NLOOP */
    /* is a 15 bit field, so max is 32767 words */
    /* However for efficiency we need to fit in a max size DMA */
    /* pkt so we back off the size a bit */

    /* How many quads do we need to write? */
    qcount = (bytesInRectangle + 15) >> 4;
    /* Set up a GifTag for writing raw to the GS Fifo */
    tmp =
        /* NLOOP */
        (qcount > SWE_LPS_MAX_IMG_QWC ? SWE_LPS_MAX_IMG_QWC : qcount)
#ifdef LESSEOPS
        | /* EOP */ (0 << 15)
#else /* LESSEOPS */
        | /* EOP */ ((qcount > SWE_LPS_MAX_IMG_QWC ? 1 : 1) << 15)
#endif /* LESSEOPS */
        | /* FLG */ (2l << 58);
    MAKE128(gifTagCmd, 0l, tmp);
    SWEADDCONTGIFFAST(gifTagCmd,
                      (qcount >
                       SWE_LPS_MAX_IMG_QWC ? SWE_LPS_MAX_IMG_QWC :
                       qcount));

    k = 0;                     /* Number of bytes accumulated */
    l = 0;
    tmp = 0;
    tmp1 = 0;

    src8 = (RwUInt8 *) srcAddress;
    src16 = (RwUInt16 *) srcAddress;
    src32 = (RwUInt32 *) srcAddress;
    scanLineAdjust = ((srcStride << 3) / bitsPerPixel) - pixelWidth;

#if (defined(RWVERBOSE))
    RWMESSAGE(("%d == bitsPerPixel", bitsPerPixel));
#endif /* (defined(RWVERBOSE)) */

    switch (bitsPerPixel)
    {
        case (4):
            for (i = 0; i < pixelHeight; i++)
            {
                for (j = 0; j < pixelWidth; j++)
                {
                    RwUInt64            pixel4;

                    /* Build two 64 bit words with 16 pixels each */
                    if (k & 4)
                    {
                        pixel4 = (((RwUInt64) * src8) >> 4) & 0xf;
                        src8++;
                    }
                    else
                    {
                        pixel4 = ((RwUInt64) * src8) & 0xf;
                    }

                    if (k < 64)
                    {
                        tmp |= pixel4 << k;
                    }
                    else
                    {
                        tmp1 |= pixel4 << (k - 64);
                    }
                    k += bitsPerPixel;

                    if (k >= 128)
                    {
                        u_long128           manyPixels;

                        /* Now combine into a 128 bit word with 8 pixels */
                        MAKE128(manyPixels, tmp1, tmp);
                        SWEADDCONTFAST(manyPixels);

                        l++;
                        if (l >= SWE_LPS_MAX_IMG_QWC)
                        {
                            qcount -= SWE_LPS_MAX_IMG_QWC;
                            if (qcount)
                            {
                                sweCloseLocalPkt();
                                sweOpenLocalPkt(SWE_LPS_CONT,
                                                qcount + 4);

                                tmp =
                                    /* NLOOP */
                                    (qcount
                                     >
                                     SWE_LPS_MAX_IMG_QWC ?
                                     SWE_LPS_MAX_IMG_QWC : qcount)
#ifdef LESSEOPS
                                    | /* EOP */ (0 << 15)
#else /* LESSEOPS */
                                    | /* EOP */
                                    ((qcount
                                      >
                                      SWE_LPS_MAX_IMG_QWC ? 1 : 1l) <<
                                     15)
#endif /* LESSEOPS */
                                    | /* FLG */ (2l << 58);
                                MAKE128(gifTagCmd, 0l, tmp);
                                SWEADDCONTGIFFAST(gifTagCmd,
                                                  (qcount >
                                                   SWE_LPS_MAX_IMG_QWC ?
                                                   SWE_LPS_MAX_IMG_QWC :
                                                   qcount));

                                l = 0;
                            }
                        }
                        k = 0;
                        tmp = 0;
                        tmp1 = 0;
                    }
                }              /*         for (j=0; j<pixelWidth; j++) */
                src8 += scanLineAdjust;
                src16 += scanLineAdjust;
                src32 += scanLineAdjust;
            }                  /*     for (i=0; i<pixelHeight; i++) */
            break;

        case (8):
            for (i = 0; i < pixelHeight; i++)
            {
                for (j = 0; j < pixelWidth; j++)
                {
                    /* Build two 64 bit words with 8 pixels each */
                    if (k < 64)
                    {
                        tmp |= ((RwUInt64) * src8) << k;
                    }
                    else
                    {
                        tmp1 |= ((RwUInt64) * src8) << (k - 64);
                    }
                    src8++;
                    k += bitsPerPixel;

                    if (k >= 128)
                    {
                        u_long128           manyPixels;

                        /* Now combine into a 128 bit word with 8 pixels */
                        MAKE128(manyPixels, tmp1, tmp);
                        SWEADDCONTFAST(manyPixels);

                        l++;
                        if (l >= SWE_LPS_MAX_IMG_QWC)
                        {
                            qcount -= SWE_LPS_MAX_IMG_QWC;
                            if (qcount)
                            {
                                sweCloseLocalPkt();
                                sweOpenLocalPkt(SWE_LPS_CONT,
                                                qcount + 4);

                                tmp =
                                    /* NLOOP */
                                    (qcount
                                     >
                                     SWE_LPS_MAX_IMG_QWC ?
                                     SWE_LPS_MAX_IMG_QWC : qcount)
#ifdef LESSEOPS
                                    | /* EOP */ (0 << 15)
#else /* LESSEOPS */
                                    | /* EOP */
                                    ((qcount
                                      >
                                      SWE_LPS_MAX_IMG_QWC ? 1 : 1l) <<
                                     15)
#endif /* LESSEOPS */
                                    | /* FLG */ (2l << 58);
                                MAKE128(gifTagCmd, 0l, tmp);
                                SWEADDCONTGIFFAST(gifTagCmd,
                                                  (qcount >
                                                   SWE_LPS_MAX_IMG_QWC ?
                                                   SWE_LPS_MAX_IMG_QWC :
                                                   qcount));

                                l = 0;
                            }
                        }
                        k = 0;
                        tmp = 0;
                        tmp1 = 0;
                    }
                }              /*         for (j=0; j<pixelWidth; j++) */
                src8 += scanLineAdjust;
                src16 += scanLineAdjust;
                src32 += scanLineAdjust;
            }                  /*     for (i=0; i<pixelHeight; i++) */
            break;

        case (16):
            for (i = 0; i < pixelHeight; i++)
            {
                for (j = 0; j < pixelWidth; j++)
                {
                    /* Build two 64 bit words with 4 pixels each */
                    if (k < 64)
                    {
                        tmp |= ((RwUInt64) * src16) << k;
                    }
                    else
                    {
                        tmp1 |= ((RwUInt64) * src16) << (k - 64);
                    }
                    src16++;
                    k += bitsPerPixel;

                    if (k >= 128)
                    {
                        u_long128           manyPixels;

                        /* Now combine into a 128 bit word with 8 pixels */
                        MAKE128(manyPixels, tmp1, tmp);
                        SWEADDCONTFAST(manyPixels);

                        l++;
                        if (l >= SWE_LPS_MAX_IMG_QWC)
                        {
                            qcount -= SWE_LPS_MAX_IMG_QWC;
                            if (qcount)
                            {
                                sweCloseLocalPkt();
                                sweOpenLocalPkt(SWE_LPS_CONT,
                                                qcount + 4);

                                tmp =
                                    /* NLOOP */
                                    (qcount
                                     >
                                     SWE_LPS_MAX_IMG_QWC ?
                                     SWE_LPS_MAX_IMG_QWC : qcount)
#ifdef LESSEOPS
                                    | /* EOP */ (0 << 15)
#else /* LESSEOPS */
                                    | /* EOP */
                                    ((qcount
                                      >
                                      SWE_LPS_MAX_IMG_QWC ? 1 : 1l) <<
                                     15)
#endif /* LESSEOPS */
                                    | /* FLG */ (2l << 58);
                                MAKE128(gifTagCmd, 0l, tmp);
                                SWEADDCONTGIFFAST(gifTagCmd,
                                                  (qcount >
                                                   SWE_LPS_MAX_IMG_QWC ?
                                                   SWE_LPS_MAX_IMG_QWC :
                                                   qcount));

                                l = 0;
                            }
                        }
                        k = 0;
                        tmp = 0;
                        tmp1 = 0;
                    }
                }              /*         for (j=0; j<pixelWidth; j++) */
                src8 += scanLineAdjust;
                src16 += scanLineAdjust;
                src32 += scanLineAdjust;
            }                  /*     for (i=0; i<pixelHeight; i++) */
            break;

        case (32):
        default:
            for (i = 0; i < pixelHeight; i++)
            {
                for (j = 0; j < pixelWidth; j++)
                {
                    /* Build two 64 bit words with 2 pixels each */
                    if (k < 64)
                    {
                        tmp |= ((RwUInt64) * src32) << k;
                    }
                    else
                    {
                        tmp1 |= ((RwUInt64) * src32) << (k - 64);
                    }
                    src32++;
                    k += bitsPerPixel;

                    if (k >= 128)
                    {
                        u_long128           manyPixels;

                        /* Now combine into a 128 bit word with 8 pixels */
                        MAKE128(manyPixels, tmp1, tmp);
                        SWEADDCONTFAST(manyPixels);

                        l++;
                        if (l >= SWE_LPS_MAX_IMG_QWC)
                        {
                            qcount -= SWE_LPS_MAX_IMG_QWC;
                            if (qcount)
                            {
                                sweCloseLocalPkt();
                                sweOpenLocalPkt(SWE_LPS_CONT,
                                                qcount + 4);

                                tmp =
                                    /* NLOOP */
                                    (qcount
                                     >
                                     SWE_LPS_MAX_IMG_QWC ?
                                     SWE_LPS_MAX_IMG_QWC : qcount)
#ifdef LESSEOPS
                                    | /* EOP */ (0 << 15)
#else /* LESSEOPS */
                                    | /* EOP */
                                    ((qcount
                                      >
                                      SWE_LPS_MAX_IMG_QWC ? 1 : 1l) <<
                                     15)
#endif /* LESSEOPS */
                                    | /* FLG */ (2l << 58);
                                MAKE128(gifTagCmd, 0l, tmp);
                                SWEADDCONTGIFFAST(gifTagCmd,
                                                  (qcount >
                                                   SWE_LPS_MAX_IMG_QWC ?
                                                   SWE_LPS_MAX_IMG_QWC :
                                                   qcount));

                                l = 0;
                            }
                        }
                        k = 0;
                        tmp = 0;
                        tmp1 = 0;
                    }
                }              /*         for (j=0; j<pixelWidth; j++) */
                src8 += scanLineAdjust;
                src16 += scanLineAdjust;
                src32 += scanLineAdjust;
            }                  /*     for (i=0; i<pixelHeight; i++) */
            break;
    }                          /*    switch (bitsPerPixel) */

    if (k != 0)
    {
        u_long128           manyPixels;

        /* Need to kick out remaining frag */
        MAKE128(manyPixels, tmp1, tmp);
        SWEADDCONTFAST(manyPixels);
    }

    /* Close potentially large packet to prevent realloc */
    sweCloseLocalPkt();

    /* We need to texflush here */
    sweOpenLocalPkt(SWE_LPS_CONT, 3);

    tmp = /* NLOOP */ 1 |
        /* EOP */ (1 << 15) |
        /* FLG */ (0l << 58) |
        /* NREG */ (1l << 60);
    tmp1 = 0xe;
    MAKE128(gifTagCmd, tmp1, tmp);
    SWEADDCONTGIFFAST(gifTagCmd, 1);

    SWEADDCONTFAST(state.texFlushGsCommand);

    sweCloseLocalPkt();

    _sweFlush();

    RWRETURNVOID();
}
/* End of cut and paste from the old texture cache. */

static void
_skyTexCacheNullUploadRaster(RwRaster * __RWUNUSED__ raster,
                             RwBool __RWUNUSED__ speculative)
{
    RWFUNCTION(RWSTRING("_skyTexCacheNullUploadRaster"));

    RWRETURNVOID();
}

/* Pulled out from main upload function as the raster lock function needs to
   be able to upload textures too. */
static void
skyTexCacheMemRasterToGs(RwRaster* raster, RwUInt32 address, RwBool speculative)
{
    _SkyRasterExt *rasExt;

    RWFUNCTION(RWSTRING("skyTexCacheMemRasterToGs"));
    rasExt = RASTEREXTFROMRASTER(raster);
    {
        /* We have to upload it */

#ifdef RWMETRICS
        RWSRCGLOBAL(metrics)->numTextureUploads++;
#endif /* RWMETRICS */

#ifdef REDEBUG
        printf("Not in cache. Uploading...\n");
#endif /* REDEBUG */

#ifdef ALTTEXFORM
        if (rasExt->flags & 1)
        {
            RwUInt32  pktSize;
            unsigned long tmp, tmp1;
            u_long128 ltmp;
            unsigned long txadj;
            u_long128 *data1;
            RwUInt32 i;
            RwUInt32 pktType = ContiguousRectangleType;

            /* Do upload directly here */
            pktSize = ((RwUInt32*)(raster->originalPixels))[0];
            data1 = (u_long128*)(raster->originalPixels) + 1;
            skyTexCacheLastWasSpec = speculative;
            if (speculative)
            {
#ifdef SWEASYNC
                sweDontFlushRefs = TRUE;
#endif /* SWEASYNC */
                pktType = SWE_PKT_AGIF | SWE_PKT_DMA_MODE_CHAIN
                          | SWE_PKT_CIRCALLOC | SWE_LPS_NOFIXUP;
            }
            else
            {
                pktType = SWE_PKT_GIF | SWE_PKT_DMA_MODE_CHAIN
                          | SWE_PKT_CIRCALLOC | SWE_LPS_NOFIXUP;
            }

            /* The actual pkt is 3 qwc longer; dma tag, gif EOP, end tag */
            sweFinaliseOpenLocalPkt(pktType, -((pktSize<<2)+4));
#ifdef SWEASYNC
            sweDontFlushRefs = FALSE;
#endif /* SWEASYNC */

            txadj = (unsigned long)(address & ~0x3f) << (32-6);

            /* Now copy the prebuilt dma packet, offsetting the blit register */
            for (i=0; i<pktSize; i++)
            {
                /* First the dma tag */
                SWEADDCONTFAST(*data1);
                data1++;

                /* Then the gif tag */
                SWEADDCONTFAST(*data1);
                data1++;

                /* Then the bitbltbuf reg */
                tmp = *(unsigned long *)data1;
                tmp1 = *((unsigned long *)data1+1);
                tmp += txadj;
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTFAST(ltmp);
                data1++;

                /* then the ref */
                SWEADDCONTFAST(*data1);
                data1++;
            }
            tmp = (1l<<28) | 2l;
            tmp1 = ((0x50l << 24) | (2l)) << 32;
            MAKE128(ltmp, tmp1, tmp);
            SWEADDCONTFAST(ltmp);

            tmp = /* NLOOP */ 1 |
                  /* EOP */  (1 << 15) |
                  /* FLG */  (0l << 58) |
                  /* NREG */ (1l << 60);
            tmp1 = 0xe;
            MAKE128(ltmp, tmp1, tmp);
            SWEADDCONTFAST(ltmp);

            if (speculative)
            {
                /* GS_NOP */
                MAKE128(ltmp, 0x7f, 0l);
            }
            else
            {
                MAKE128(ltmp, GS_TEXFLUSH, 0l);
            }
            SWEADDCONTFAST(ltmp);

            tmp = (0xfl<<28);
            MAKE128(ltmp, 0l, tmp);
            SWEADDCONTFAST(ltmp);

            sweFinaliseOpenLocalPkt(SWE_LPS_CONT, 0);

#ifdef RWMETRICS
            RWSRCGLOBAL(metrics)->sizeTextureUploads
                += rasExt->sysMemSize + rasExt->sysMemPalSize;
#endif /* RWMETRICS */
        }
        else
#endif /* ALTTEXFORM */
        {
            /* Old style. You can kind of tell why I added ALTEXFORM */
            RwUInt32 bitsPerPixel;
            RwUInt32 pixelFormat;
            RwBool contiguous;
            RwUInt64 miptbp1, miptbp2;
            RwUInt32 offsetBy64[7];
            RwUInt32 widthBy64[7];
            RwInt32 level;
            RwUInt8 *srcAddress = raster->cpPixels;

            skyTexCacheLastWasSpec = FALSE;

            bitsPerPixel = raster->depth;
            pixelFormat = (rasExt->lsb >> 20) & 0x3f;

            contiguous =
                (raster->stride == ((raster->width*(RwInt32)bitsPerPixel)>>3));

            switch (rasExt->maxMipLevel>>2)
            {
                default: /* Be safe */
                case (6): case (5): case (4):
                    miptbp2 = ( rasExt->miptbp2Lsb |
                                ((RwUInt64)rasExt->miptbp2Msb << 32) );
                    offsetBy64[4] = (miptbp2 >>  0) & 0x3fff;
                    offsetBy64[5] = (miptbp2 >> 20) & 0x3fff;
                    offsetBy64[6] = (miptbp2 >> 40) & 0x3fff;
                    widthBy64[4] = (miptbp2 >> 14) & 0x3f;
                    widthBy64[5] = (miptbp2 >> 34) & 0x3f;
                    widthBy64[6] = (miptbp2 >> 54) & 0x3f;
                    /* Drop through to next case */
                case (3): case (2): case (1):
                    miptbp1 = ( rasExt->miptbp1Lsb |
                                ((RwUInt64)rasExt->miptbp1Msb << 32) );
                    offsetBy64[1] = (miptbp1 >>  0) & 0x3fff;
                    offsetBy64[2] = (miptbp1 >> 20) & 0x3fff;
                    offsetBy64[3] = (miptbp1 >> 40) & 0x3fff;
                    widthBy64[1] = (miptbp1 >> 14) & 0x3f;
                    widthBy64[2] = (miptbp1 >> 34) & 0x3f;
                    widthBy64[3] = (miptbp1 >> 54) & 0x3f;
                    /* Drop through to next case */
                case (0):
                    offsetBy64[0] = 0;
                    widthBy64[0] = (rasExt->lsb >> 14) & 0x3f;
                    break;
            }

            /* Upload each of the mipmap levels */
            for (level = 0; level <= (rasExt->maxMipLevel>>2); level++)
            {
                RwUInt32 mipWidth = (raster->width>>level);
                RwUInt32 mipHeight = (raster->height>>level);
                RwUInt32 mipStride = (raster->stride>>level);
                RwUInt32 dstAddress = ((address>>6) + offsetBy64[level]);
                RwBool   aligned = (!((RwUInt32)srcAddress & 0xf));

#ifdef RWMETRICS
                RWSRCGLOBAL(metrics)->sizeTextureUploads
                    += (mipWidth * mipHeight * bitsPerPixel) >> 3;
#endif /* RWMETRICS */
                if (aligned)
                {
                    /* Aligned - choose whether to use ref per scanline */
                    if (contiguous)
                    {
                        if (!rasExt->mipUploadPkts[level])
                        {
                            rasExt->mipUploadPkts[level] =
                               _SkyBuildPktForUpLoadAlignedContiguousRectangle(
                                               mipWidth, mipHeight, srcAddress,
                                               bitsPerPixel, rasExt->cachePkts);
                        }
                        if ((rasExt->flags & 2)
                                && (((rasExt->lsb>>20)&0x3f) == SCE_GS_PSMT8))
                        {
                            _SkyDdispatchUpLoadAlignedContiguousRectangle(
                                               mipWidth>>1, mipHeight>>1,
                                               dstAddress, widthBy64[level] >>1,
                                               SCE_GS_PSMCT32,
                                               rasExt->mipUploadPkts[level],
                                               rasExt->cachePkts);
                        }
                        else if ((rasExt->flags & 4)
                                && (((rasExt->lsb>>20)&0x3f) == SCE_GS_PSMT4))
                        {
                            _SkyDdispatchUpLoadAlignedContiguousRectangle(
                                               mipWidth>>1, mipHeight>>1,
                                               dstAddress, widthBy64[level] >>1,
                                               SCE_GS_PSMCT16,
                                               rasExt->mipUploadPkts[level],
                                               rasExt->cachePkts);
                        }
                        else
                        {
                            _SkyDdispatchUpLoadAlignedContiguousRectangle(
                                                mipWidth, mipHeight, dstAddress,
                                                widthBy64[level], pixelFormat,
                                                rasExt->mipUploadPkts[level],
                                                rasExt->cachePkts);
                        }
                        if (!rasExt->cachePkts)
                        {
                            rasExt->mipUploadPkts[level] = (u_long128 *)NULL;
                        }

                    }
                    else
                    {
                        if ((rasExt->flags & 2)
                                && (((rasExt->lsb>>20)&0x3f) == SCE_GS_PSMT8))
                        {
                            _SkyUpLoadAlignedRectangle(mipWidth>>1,
                                                       mipHeight>>1,
                                                       srcAddress, dstAddress,
                                                       widthBy64[level]>>1,
                                                       32, SCE_GS_PSMCT32,
                                                       mipStride);
                        }
                        else if ((rasExt->flags & 4)
                                && (((rasExt->lsb>>20)&0x3f) == SCE_GS_PSMT4))
                        {
                            _SkyUpLoadAlignedRectangle(mipWidth>>1,
                                                       mipHeight>>1,
                                                       srcAddress, dstAddress,
                                                       widthBy64[level]>>1,
                                                       16, SCE_GS_PSMCT16,
                                                       mipStride);
                        }
                        else
                        {
                            /* Load aligned non contiguous (ref per scanline) */
                            _SkyUpLoadAlignedRectangle(mipWidth, mipHeight,
                                                       srcAddress, dstAddress,
                                                       widthBy64[level],
                                                       bitsPerPixel,
                                                       pixelFormat, mipStride);
                        }
                    }
                }
                else
                {
                    if ((rasExt->flags & 2)
                            && (((rasExt->lsb>>20)&0x3f) == SCE_GS_PSMT8))
                    {
                        _SkyUpLoadRectangle(mipWidth>>1, mipHeight>>1,
                                            srcAddress, dstAddress,
                                            widthBy64[level]>>1, 32,
                                            SCE_GS_PSMCT32, mipStride);
                    }
                    else if ((rasExt->flags & 4)
                            && (((rasExt->lsb>>20)&0x3f) == SCE_GS_PSMT4))
                    {
                        _SkyUpLoadRectangle(mipWidth>>1, mipHeight>>1,
                                            srcAddress, dstAddress,
                                            widthBy64[level]>>1, 16,
                                            SCE_GS_PSMCT16, mipStride);
                    }
                    else
                    {
                        /* Not aligned, load by value */
                        _SkyUpLoadRectangle(mipWidth, mipHeight, srcAddress,
                                            dstAddress, widthBy64[level],
                                            bitsPerPixel, pixelFormat,
                                            mipStride);
                    }
                }

                /* Breaks if we upload mipmapped subrastered texture
                   - but we're not going to do that, are we... :-) */
                srcAddress += mipStride * mipHeight;

                /* Align for later mip levels */
                if((RwUInt32)srcAddress & 0xf)
                {
                    srcAddress = (RwUInt8 *)(((RwUInt32)srcAddress + 15) & ~15);
                }
            }

            /* Upload a palette if applicable */
            if ((rasExt->sysMemPalSize) && (raster->depth <= 8))
            {
                static const RwUInt32 palWidths[8] = {8, 8, 8, 8,
                                                      16, 16, 16, 16};
                static const RwUInt32 palHeights[8] = {2, 2, 2, 2,
                                                       16, 16, 16, 16};
                RwInt32  palBPP;
                RwUInt32 dstAddress, palWidth, palHeight;
                RwUInt32 format = (rasExt->msb >> (51-32)) & 0xF;

                dstAddress = ((address>>6) + rasExt->palOffset);
                palWidth = palWidths[raster->depth-1];
                palHeight = palHeights[raster->depth-1];

                switch (format)
                {
                    case (PSMCT32):
                    {
                        palBPP = 32;
                        break;
                    }
                    case (PSMCT16):
                    case (PSMCT16S):
                    default:
                    {
                        palBPP = 16;
                        break;
                    }
                }

                if (!rasExt->palUploadPkt)
                {
                    rasExt->palUploadPkt =
                       _SkyBuildPktForUpLoadAlignedContiguousRectangle(palWidth,
                                                     palHeight, raster->palette,
                                                     palBPP, rasExt->cachePkts);
                }

                _SkyDdispatchUpLoadAlignedContiguousRectangle(palWidth,
                                                          palHeight, dstAddress,
                                                          1, format,
                                                          rasExt->palUploadPkt,
                                                          rasExt->cachePkts);

                if (!rasExt->cachePkts)
                {
                    rasExt->palUploadPkt = (u_long128 *)NULL;
                }
#ifdef RWMETRICS
                RWSRCGLOBAL(metrics)->sizeTextureUploads
                    += (palWidth * palHeight * palBPP) >> 3;
#endif /* RWMETRICS */
            }
            /* end of old style section */
        }
    }
    DI();

    RWASSERT(0<= rasExt->dmaRefCount);
    rasExt->dmaRefCount++;

    EI();

    rasExt->dmaClrCount += 1;
    if (rasExt->dmaClrCount == 1)
    {
        _sweProcrastinatedAddURef((unsigned int*)&(rasExt->dmaRefCount));
    }

    RWRETURNVOID();
}

static void
_skyTexCacheUploadRaster(RwRaster *raster, RwBool speculative)
{
    _SkyRasterExt *rasExt;
    RwUInt32 address;

    RWFUNCTION(RWSTRING("_skyTexCacheUploadRaster"));
    RWASSERT(raster);

#ifdef REDEBUG
    printf("Entered _skyTexCacheUploadRaster %p, %d\n", raster, speculative);
    printf("end = %d, start = %d\n", skyTexCache.ringEnd, skyTexCache.ringStart);
#endif /* REDEBUG */
    rasExt = RASTEREXTFROMRASTER(raster);
    if ((!(rasExt->flags & 1)) && (speculative))
    {
#ifdef REDEBUG
        printf("Can't speculative upload old raster\n\n");
#endif /* REDEBUG */
        RWRETURNVOID();
    }
    /* First off, is it already at the head of the list? */

    if ((rasExt->mpCacheEntry)
        && (skyTexCache.ringStart == (RwUInt32)(rasExt->mpCacheEntry
                                                - skyTexCache.ring)))
    {
        /* Quite why this isn't the current texture is abit odd, so
           raise an assert */
        RWASSERT((!speculative) && (raster != skyTexCache.currentRaster));

        if (!speculative)
        {
            DI();

            RWASSERT(0<= rasExt->dmaRefCount);
            rasExt->dmaRefCount++;

            EI();

            rasExt->dmaClrCount += 1;
            if (rasExt->dmaClrCount == 1)
            {
                _sweProcrastinatedAddURef((unsigned int*)&(rasExt->dmaRefCount));
            }

#ifdef REDEBUG
            printf("already at the head of the list\n\n");
#endif /* REDEBUG */
            RWASSERT(0<= rasExt->dmaRefCount);
            skyTexCache.currentRaster = raster;
            RWRETURNVOID();
        }

#ifdef REDEBUG
        printf("already at the head of the list\n\n");
#endif /* REDEBUG */

        RWRETURNVOID();
    }

    if (rasExt->bLocked)
    {
        /* Locked rasters must be in cache */
        RWASSERT(rasExt->mpCacheEntry);
#ifdef REDEBUG
        printf("locked in cache at %x\n\n", rasExt->mpCacheEntry->address);
#endif /* REDEBUG */
        RWRETURNVOID();
    }

    /* Ok, so we need to find a space to put it */
    {
        RwUInt32 topOfMem;

        /* Will it fit between "here" and the top of memory */
        if (skyTexCache.lockedStart != skyTexCache.ringSize)
        {
            topOfMem = skyTexCache.ring[skyTexCache.lockedStart].address;
        }
        else
        {
            topOfMem =  skyTexCache.startAddr + skyTexCache.totalSize;
        }
#ifdef REDEBUG
        printf("Top of memory is at %x\n", topOfMem);
#endif /* REDEBUG */
        if ((skyTexCache.ringStart != ~0U)
            && (topOfMem - (skyTexCache.ring[skyTexCache.ringStart].address
                            + skyTexCache.ring[skyTexCache.ringStart].size)
                >= rasExt->nTexCacheSize))
        {
            address = skyTexCache.ring[skyTexCache.ringStart].address
                      + skyTexCache.ring[skyTexCache.ringStart].size;
        }
        else
        {
            if (topOfMem - skyTexCache.startAddr
                < rasExt->nTexCacheSize)
            {
                /* Raster is too big given the current top of memory */
#ifdef REDEBUG
                printf("Raster won't fit\n\n");
#endif /* REDEBUG */
                RWRETURNVOID();
            }
            else
            {
                address = skyTexCache.startAddr;
            }
        }
    }
#ifdef REDEBUG
    printf("Address = %x, size = %x\n", address, rasExt->nTexCacheSize);
#endif /* REDEBUG */

    if ((speculative) && (skyTexCache.ringStart != ~0U))
    {
        RwUInt32 ringEnd;
        RwUInt32 entry;
        RwUInt32 size;

        ringEnd = skyTexCache.ringEnd;

        if ((skyTexCache.context1Index != ~0U)
            && (skyTexCache.ring[skyTexCache.context1Index].raster == NULL))
        {
            skyTexCache.context1Index = ~0U;
        }
        if ((skyTexCache.context2Index != ~0U)
            && (skyTexCache.ring[skyTexCache.context2Index].raster == NULL))
        {
            skyTexCache.context2Index = ~0U;
        }

        if (skyTexCache.ringStart != ~0U)
        {
            RwUInt32 startAddress;
            RwUInt32 startSize;
            _SkyMemBlock *blockEnd;


            entry = skyTexCache.ringStart+1;
            if (entry == skyTexCache.lockedStart)
            {
                entry = 0;
            }

            /* First, do we need to recover a ring entry? */
            if (entry == ringEnd)
            {
                if ((ringEnd == skyTexCache.context1Index)
                    || (ringEnd == skyTexCache.context2Index))
                {
                    /* We would overwrite a texture in use */
#ifdef REDEBUG
                    printf("We would overwrite ringEnd, which is in use\n\n");
#endif /* REDEBUG */
                    RWRETURNVOID();
                }
                ringEnd++;
                if (ringEnd == skyTexCache.lockedStart)
                {
                    ringEnd = 0;
                }
                /* We clearly haven't hit ringStart due to a restriction on
                   the number of locked textures */
                /* Under normal conditions, ringEnd and ringStart must point
                   to entries with point to a valid raster. However, I know
                   that I will fix up before I exit the function */
            }
            size = (rasExt->nTexCacheSize+2047)&~2047;


            startAddress = skyTexCache.ring[skyTexCache.ringStart].address;
            startSize = skyTexCache.ring[skyTexCache.ringStart].size;
            blockEnd = &skyTexCache.ring[ringEnd];

            while ( (entry != ringEnd) &&
                    ((!blockEnd->raster) ||
                     ((startAddress >= blockEnd->address && 
                       address + size > blockEnd->address &&
                       address <= blockEnd->address) ||
                      (startAddress < blockEnd->address && 
                       ((startAddress + startSize <= address &&
                         address + size > blockEnd->address) ||
                        address <= startAddress))
                      )
                     )
                    )
            {
                if (blockEnd->raster)
                {
                    if ((ringEnd == skyTexCache.context1Index) ||
                        (ringEnd == skyTexCache.context2Index))
                    {
                        /* We would overwrite a texture in use */
#ifdef REDEBUG
                        printf("We would overwrite %d, which is in use\n\n",
                               ringEnd);
#endif /* REDEBUG */
                        RWRETURNVOID();
                    }
                }
                ringEnd++;
                if (ringEnd == skyTexCache.lockedStart)
                {
                    ringEnd = 0;
                }

                blockEnd = &skyTexCache.ring[ringEnd];
            }
        }
    }

    if (rasExt->mpCacheEntry)
#if 0
    {
        /* if the texture is in cache, we copy it, freeing its ring entry */
        u_long128 ltmp;
        u_long128 bitbltbuf;
        u_long128 trxpos;
        u_long128 trxdir;
        RwUInt32 qwc;
        RwUInt32  pktType = ContiguousRectangleType;
        RwUInt32 i;

#ifdef REDEBUG
        printf("Already in cache. Copying...\n");
        printf("Old address %x\n", rasExt->mpCacheEntry->address);
        printf("size %x\n", rasExt->mpCacheEntry->size);
#endif /* REDEBUG */

        qwc = 4*((rasExt->nTexCacheSize+64*2048-1)/(64*2048)) + 3;

        /* Though it looks odd, we submit this via the async channel, as
           we need to prevent it possibly being overwritten */

        if (speculative)
        {
#ifdef SWEASYNC
            sweDontFlushRefs = TRUE;
#endif /* SWEASYNC */
            pktType = SWE_PKT_AGIF | SWE_PKT_DMA_MODE_CHAIN |SWE_PKT_CIRCALLOC
                      | SWE_LPS_NOFIXUP;
        }
        sweFinaliseOpenLocalPkt(pktType, -qwc-2);
#ifdef SWEASYNC
        sweDontFlushRefs = FALSE;
#endif /* SWEASYNC */

        MAKE128(ltmp, (((0x50l<<24)|(qwc)) << 32), ((1<<28) | (qwc)));
        SWEADDCONTFAST(ltmp);

        MAKE128(ltmp, 0x0el, ((1l<<60) | (1l<<15) | (qwc-1)));
        SWEADDCONTFAST(ltmp);

        if (rasExt->mpCacheEntry->address < address)
        {
            MAKE128(trxpos, GS_TRXPOS, (1l<<59));
        }
        else
        {
	    MAKE128(trxpos, GS_TRXPOS, 0l);
        }
        MAKE128(trxdir, GS_TRXDIR, 2l);

        MAKE128(ltmp, GS_TEXFLUSH, 0l);

        SWEADDCONTFAST(ltmp);

        /* We dispatch in 64 page chunks */
        for (i=0; i<rasExt->nTexCacheSize; i+= 64*2048)
        {
            MAKE128(bitbltbuf, GS_BITBLTBUF, (((0x10000l
                                                |(((RwUInt64)address+i)>>6))
                                               <<32)
                                              | 0x10000
                                              | ((rasExt->mpCacheEntry->address
                                                  + i)>>6)));
            SWEADDCONTFAST(bitbltbuf);
#ifdef REDEBUG
            printf("%8x %16lx\n", GS_BITBLTBUF, bitbltbuf);
#endif /* REDEBUG */

            SWEADDCONTFAST(trxpos);
#ifdef REDEBUG
            printf("%8x %16lx\n", GS_TRXPOS, trxpos);
#endif /* REDEBUG */

            MAKE128(ltmp, GS_TRXREG, ((rasExt->nTexCacheSize-i > 64*2048 ? 64
                                       : ((rasExt->nTexCacheSize-i+2047l)>>11))
                                      <<(32+5)) | 64);
            SWEADDCONTFAST(ltmp);
#ifdef REDEBUG
            printf("%8x %16lx\n", GS_TRXREG, ltmp);
#endif /* REDEBUG */

            SWEADDCONTFAST(trxdir);
#ifdef REDEBUG
            printf("%8x %16lx\n", GS_TRXDIR, trxdir);
#endif /* REDEBUG */
        }

        MAKE128(ltmp, GS_TEXFLUSH, 0l);

        SWEADDCONTFAST(ltmp);
#ifdef REDEBUG
            printf("%8x %16lx\n", GS_TEXFLUSH, ltmp);
#endif /* REDEBUG */

        MAKE128(ltmp, 0l, (0xfl<<28));

        SWEADDCONTFAST(ltmp);

        if (!speculative)
        {
            sweFinaliseOpenLocalPkt(SWE_LPS_CONT, 0);
        }

        /* Clear down old ring entry */
        i = rasExt->mpCacheEntry - skyTexCache.ring;

        rasExt->mpCacheEntry->raster = (RwRaster *)NULL;

        if (i == skyTexCache.ringStart)
        {
#ifdef REDEBUG
            printf("Old entry was ringStart\n");
#endif /* REDEBUG */
            while ((skyTexCache.ringStart != skyTexCache.ringEnd)
                   && (!skyTexCache.ring[skyTexCache.ringStart].raster))
            {
                if (0 == skyTexCache.ringStart)
                {
                    skyTexCache.ringStart = skyTexCache.lockedStart;
                }
                skyTexCache.ringStart--;
#ifdef REDEBUG
                printf("back to %d\n", skyTexCache.ringStart);
#endif /* REDEBUG */
            }
        }
        else if (i == skyTexCache.ringEnd)
        {
#ifdef REDEBUG
            printf("Old entry was ringEnd\n");
#endif /* REDEBUG */
            while ((skyTexCache.ringStart != skyTexCache.ringEnd)
                   && (!skyTexCache.ring[skyTexCache.ringEnd].raster))
            {
                skyTexCache.ringEnd++;
                if (skyTexCache.ringEnd == skyTexCache.lockedStart)
                {
                    skyTexCache.ringEnd = 0;
                }
#ifdef REDEBUG
                printf("forward to %d\n", skyTexCache.ringEnd);
#endif /* REDEBUG */
            }
        }
        if (NULL == skyTexCache.ring[skyTexCache.ringStart].raster)
        {
#ifdef REDEBUG
            printf("expelled all textures!\n");
#endif /* REDEBUG */
            skyTexCache.ringStart = ~0U;
            skyTexCache.ringEnd = ~0U;
        }
        rasExt->mpCacheEntry = (_SkyMemBlock*)NULL;
    }
    else
#else
    {
        RwUInt32 i;

        /* Simple zero out old reference */
        i = rasExt->mpCacheEntry - skyTexCache.ring;

        rasExt->mpCacheEntry->raster = (RwRaster *)NULL;
        rasExt->mpCacheEntry = (_SkyMemBlock *)NULL;
    }
#endif
    {
        skyTexCacheMemRasterToGs(raster, address, speculative);
    }

    /* We now need to add the raster to the head of the list and remove
       any that have been trampled */
    {
        RwUInt32 entry;
        RwUInt32 size;
        _SkyRasterExt *rasExt2;

        if (skyTexCache.ringStart != ~0U)
        {
            RwUInt32 startAddress;
            RwUInt32 startSize;
            _SkyMemBlock *blockEnd;

            entry = skyTexCache.ringStart+1;
            if (entry == skyTexCache.lockedStart)
            {
                entry = 0;
            }
#ifdef REDEBUG
            printf("Using entry %d\n", entry);
#endif /* REDEBUG */

            /* First, do we need to recover a ring entry? */
            if (entry == skyTexCache.ringEnd)
            {
#ifdef REDEBUG
                printf("Recovering ringEnd (%d)\n", entry);
#endif /* REDEBUG */
                if (skyTexCache.ring[entry].raster)
                {
                    rasExt2=RASTEREXTFROMRASTER(skyTexCache.ring[entry].raster);
                    rasExt2->mpCacheEntry->raster = (RwRaster*)NULL;
                    rasExt2->mpCacheEntry = (_SkyMemBlock*)NULL;
                }
                skyTexCache.ringEnd++;
                if (skyTexCache.ringEnd == skyTexCache.lockedStart)
                {
                    skyTexCache.ringEnd = 0;
                }
                /* We clearly haven't hit ringStart due to a restriction on
                   the number of locked textures */
                /* Under normal conditions, ringEnd and ringStart must point
                   to entries with point to a valid raster. However, I know
                   that I will fix up before I exit the function */
            }
            skyTexCache.ring[entry].raster = raster;
            rasExt->mpCacheEntry = &skyTexCache.ring[entry];
            skyTexCache.ring[entry].address = address;
            size = skyTexCache.ring[entry].size = (rasExt->nTexCacheSize+2047)&~2047;

            startAddress = skyTexCache.ring[skyTexCache.ringStart].address;
            startSize = skyTexCache.ring[skyTexCache.ringStart].size;
            blockEnd = &skyTexCache.ring[skyTexCache.ringEnd];

            while ((!blockEnd->raster) ||
                   ((entry != skyTexCache.ringEnd) &&
                    ((startAddress >= blockEnd->address &&
                      address + size > blockEnd->address &&
                      address <= blockEnd->address) ||
                     (startAddress < blockEnd->address &&
                      ( (startAddress + startSize <= address && 
                         address + size > blockEnd->address) ||
                       address  <= startAddress))
                     )
                    )
                   )
            {
                if (blockEnd->raster)
                {
#ifdef REDEBUG
                   printf("Ejecting %p from entry %d\n", 
                          blockEnd->raster, skyTexCache.ringEnd);
#endif /* REDEBUG */
                    rasExt2=RASTEREXTFROMRASTER(blockEnd->raster);
                    rasExt2->mpCacheEntry->raster = (RwRaster*)NULL;
                    rasExt2->mpCacheEntry = (_SkyMemBlock*)NULL;
                }
#ifdef REDEBUG
                else
                {
                    printf("Salvaging %d\n", skyTexCache.ringEnd);
                }
#endif /* REDEBUG */
                skyTexCache.ringEnd++;
                if (skyTexCache.ringEnd == skyTexCache.lockedStart)
                {
                    skyTexCache.ringEnd = 0;
                }
                blockEnd = &skyTexCache.ring[skyTexCache.ringEnd];
            }

            skyTexCache.ringStart = entry;
#ifdef REDEBUG
            printf("Installed at %d in the ring\n", entry);
#endif /* REDEBUG */
        }
        else
        {

#ifdef REDEBUG
            printf("Installed at the beginning of the ring\n");
#endif /* REDEBUG */

            skyTexCache.ringStart = 0;
            skyTexCache.ringEnd = 0;

            skyTexCache.ring[0].raster = raster;
            rasExt->mpCacheEntry = &skyTexCache.ring[0];
            skyTexCache.ring[0].address = address;
            skyTexCache.ring[0].size = (rasExt->nTexCacheSize+2047)&~2047;
        }
    }
#ifdef REDEBUG
    printf("Start = %3d, %p, %x, %x\n", skyTexCache.ringStart,
           skyTexCache.ring[skyTexCache.ringStart].raster,
           skyTexCache.ring[skyTexCache.ringStart].address,
           skyTexCache.ring[skyTexCache.ringStart].size);
    printf("End =   %3d, %p, %x, %x\n\n", skyTexCache.ringEnd,
           skyTexCache.ring[skyTexCache.ringEnd].raster,
           skyTexCache.ring[skyTexCache.ringEnd].address,
           skyTexCache.ring[skyTexCache.ringEnd].size);
#endif /* REDEBUG */
#ifdef REDEBUG
    {
        RwUInt32            j, ze = 0;
        volatile RwUInt32  *vol = &ze;

        /* Slow things down a bit to cope with buffer overrun */
        for (j = 0; j < 100000; j++)
        {
            if (*vol)
            {
                printf("Odd?\n");
            }
        }

    }
#endif /* REDEBUG */

    RWRETURNVOID();
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                              Internal API

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
/************************************************************************
 skyTexCacheOpenCache

 Start up the texture cache

 On Entry : Start of cache area in GS memory in words
            Size in words

 On Exit  : Success/failure
 */
RwBool
skyTexCacheOpenCache(RwInt32 start, RwInt32 size)
{
    RwUInt64 tmp, tmp1;

    RWFUNCTION(RWSTRING("skyTexCacheOpenCache"));

    skyTexCache.startAddr = start;
    skyTexCache.totalSize = size;

    skyTexCacheLastWasSpec = FALSE;

    if (skyTexCache.enabled)
    {
        skyTexCache.ring = (_SkyMemBlock *)RwMalloc(sizeof(_SkyMemBlock)
                                                    *skyGNumTexMemBlocks);
        if (!skyTexCache.ring)
        {
            RWRETURN(FALSE);
        }
        skyTexCache.ringStart     = ~0U;
        skyTexCache.ringEnd       = ~0U;
        skyTexCache.ringSize      = skyGNumTexMemBlocks;
        skyTexCache.lockedStart   = skyGNumTexMemBlocks;
        skyTexCache.uploadRaster  = _skyTexCacheUploadRaster;
        skyTexCache.currentRaster = (RwRaster*)NULL;
        skyTexCache.context1Index = ~0U;
        skyTexCache.context2Index = ~0U;
    }
    else
    {
        skyTexCache.uploadRaster  = _skyTexCacheNullUploadRaster;
    }

/* Cut and paste from the old texture cache. */

    tmp = /* NLOOP */ 5l
#ifdef LESSEOPS
        | /* EOP */ (0l<<15)
#else /* LESSEOPS */
        | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
        | /* PRE */ (0l<<46)
        | /* FLG */ (0l<<58)
        | /* NREG */(1l<<60);
    tmp1 = /* A+D */ (0xel<<(64-64));
    MAKE128(state.refUploadSetupGifTag5, tmp1, tmp);

    /* Gs command to flush texture memory accesses */
    MAKE128(state.texFlushGsCommand, GS_TEXFLUSH, 0);

    /* Gs command to set upload to top left of texture */
    MAKE128(state.trxPos0GsCommand, GS_TRXPOS, 0);

    /* Gs command to set direction of transmission */
    MAKE128(state.trxDir0GsCommand, GS_TRXDIR, 0);

/* End cut and paste from the old texture cache. */

    RWRETURN(TRUE);
}

/************************************************************************
 skyTexCacheCloseCache

 Close down the texture cache. Ensure that all items in cache are unused.
 Free any memory used by the system. We asume that we are called after the
 dma system has been shutdown.

 On Entry :
 On Exit  : Success/failure
 */

RwBool
skyTexCacheCloseCache(void)
{
    RWFUNCTION(RWSTRING("skyTexCacheCloseCache"));

    if (skyTexCache.enabled)
    {
        RwUInt32 i;

        /* Normal textures */
        for (i = skyTexCache.ringEnd; i != skyTexCache.ringStart;
             i = (i+1)%skyTexCache.lockedStart)
        {
            if (skyTexCache.ring[i].raster)
            {
                _SkyRasterExt *rasExt;

                rasExt = RASTEREXTFROMRASTER(skyTexCache.ring[i].raster);

                rasExt->mpCacheEntry = (_SkyMemBlock*)NULL;
            }
        }
        /* Locked textures */
        for (i = skyTexCache.lockedStart; i<skyTexCache.ringSize; i++)
        {
            if (skyTexCache.ring[i].raster)
            {
                _SkyRasterExt *rasExt;

                rasExt = RASTEREXTFROMRASTER(skyTexCache.ring[i].raster);
                rasExt->bLocked = FALSE;
                rasExt->mpCacheEntry = (_SkyMemBlock*)NULL;
            }
        }
        RwFree(skyTexCache.ring);
    }

    /* Reset the static vars */
    skyTexCache.enabled       = TRUE;
    skyTexCache.ringStart     = 0;
    skyTexCache.ringEnd       = 0;
    skyTexCache.ringSize      = 0;
    skyTexCache.lockedStart   = 0;
    skyTexCache.ring          = (_SkyMemBlock *)NULL;

    skyTexCache.startAddr     = -1;
    skyTexCache.totalSize     = -1;

    skyTexCache.uploadRaster  = (SkyCacheCallBack)NULL;

    skyTexCache.currentRaster = (RwRaster*)NULL;

    RWRETURN(TRUE);
}

/************************************************************************
 skyTexCacheEndFrame

 Do any once per-frame processing required by the cache. This function
 is now unused.

 On Entry :
 On Exit  :
 */
void
skyTexCacheEndFrame(void)
{
    RWFUNCTION(RWSTRING("skyTexCacheEndFrame"));

    RWRETURNVOID();
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                               External API

 Most of this lot are external only because people writing PP stuff need
 to be able to access them.
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/**
 * \ingroup driversky2
 * \ref RpSkyTexCacheAccessSpeculate
 *
 *  Attempt an asynscronous upload of the raster. This fails silently as
 *  it is not a fail to fail, so to speak. Even if it does upload the
 *  raster, it doesn't dispatch any state to the GS.
 *
 *  On Entry : The raster
 *  On Exit  :
 */
void
RpSkyTexCacheAccessSpeculate(RwRaster *raster)
{
    _SkyRasterExt *rasExt;
    RWAPIFUNCTION(RWSTRING("RpSkyTexCacheAccessSpeculate"));

    RWASSERT(raster);

    rasExt = RASTEREXTFROMRASTER(raster);
    if ((raster != skyTexCache.currentRaster)
        && !(raster->cType & rwRASTERDONTALLOCATE)
#if 1
         && (!rasExt->mpCacheEntry)
#endif
                                   )
    {
        (skyTexCache.uploadRaster)(raster, TRUE);

        /* recalc msb/lsb if it uploaded */
        if (rasExt->mpCacheEntry)
        {
            RwUInt32 destAddr;

            destAddr = rasExt->mpCacheEntry->address >> 6;

            rasExt->lsb &= ~0x3fffl;
            rasExt->lsb |= (destAddr&0x3fff);

            destAddr += rasExt->palOffset;
            destAddr &= 0x3fff;

            rasExt->msb &= ~(0x3fffl << (37-32));
            rasExt->msb |= ((destAddr&0x3fffl) << (37 - 32));
        }
    }

    RWRETURNVOID();
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexCacheAccessRaster
 *
 *  Upload the raster to the GS (if required) and set up the GS state to use
 *  it for texturing. This function can fail if the raster is too big to fit
 *  into cache. This may be because too many textures are locked into cache.
 *  This function will not evict textures from cache.
 *
 *  On Entry : The raster
 *             Bool to indicate if this is a context 2 access
 *  On Exit  : Success/failure
 */
RwBool
RpSkyTexCacheAccessRaster(RwRaster *raster, RwBool useContext2)
{
    _SkyRasterExt *rasExt;
    u_long128 ltmp;
    RwUInt64 tmp, tmp1;
    RwUInt64 texAddr;
    RwUInt64 mipAddr1;
    RwUInt32 destAddr;

    RWAPIFUNCTION(RWSTRING("RpSkyTexCacheAccessRaster"));

    RWASSERT(raster);

    rasExt = RASTEREXTFROMRASTER(raster);
    if ((raster != skyTexCache.currentRaster)
        && !(raster->cType & rwRASTERDONTALLOCATE)
#if 1
         && (!rasExt->mpCacheEntry)
#endif
                                   )
    {
        (skyTexCache.uploadRaster)(raster, FALSE);
    }
#ifdef REDEBUG
    else if (skyTexCache.currentRaster != raster)
    {
#if 0
        printf("Using %d: %p at %x size %x\n", rasExt->mpCacheEntry
                                               - skyTexCache.ring,
               raster, rasExt->mpCacheEntry->address,
               rasExt->mpCacheEntry->size);
#endif
    }
#endif
    if (rasExt->mpCacheEntry)
    {
        destAddr = rasExt->mpCacheEntry->address >> 6;

        rasExt->lsb &= ~0x3fffl;
        rasExt->lsb |= (destAddr&0x3fff);

        destAddr += rasExt->palOffset;
        destAddr &= 0x3fff;

        rasExt->msb &= ~(0x3fffl << (37-32));
        rasExt->msb |= ((destAddr&0x3fffl) << (37 - 32));
    }

    if (!((raster->cType&rwRASTERTYPEMASK)==rwRASTERTYPECAMERA)
        && (!rasExt->mpCacheEntry))
    {
        RWRETURN(FALSE);
    }
#ifdef REDEBUG
    /* Ensure chain */
    {
        RwUInt32 i;
        _SkyRasterExt *rasExt2;

        i = skyTexCache.ringEnd-1;
        do
        {
            i += 1;
            if (i == skyTexCache.lockedStart)
            {
                i = 0;
            }
            if (skyTexCache.ring[i].raster)
            {
                rasExt2 = RASTEREXTFROMRASTER(skyTexCache.ring[i].raster);

                if (skyTexCache.ring[i].raster != rasExt2->mpCacheEntry->raster)
                {
                    printf("FAILED %d: %p != %p\n", i, rasExt->mpCacheEntry->raster, raster);
                }
            }
        }
        while (i != skyTexCache.ringStart);
        /* and the locked textures */
        for (i = skyTexCache.lockedStart; i < skyTexCache.ringSize; i++)
        {
            rasExt2 = RASTEREXTFROMRASTER(skyTexCache.ring[i].raster);

            if (skyTexCache.ring[i].raster != rasExt2->mpCacheEntry->raster)
            {
                printf("FAILED %d: %p != %p\n", i, rasExt->mpCacheEntry->raster, raster);
            }
        }
        /* Do any overlap ? */
        {
            RwUInt32 address;
            RwUInt32 size;

            address = rasExt->mpCacheEntry->address;
            size = rasExt->mpCacheEntry->size;

            i = skyTexCache.ringEnd-1;
            do
            {
                i += 1;
                if (i == skyTexCache.lockedStart)
                {
                    i = 0;
                }
                if (((address < skyTexCache.ring[i].address)
                     && (address + size > skyTexCache.ring[i].address))
                    || ((address < skyTexCache.ring[i].address
                                    + skyTexCache.ring[i].size)
                        && (address + size > skyTexCache.ring[i].address
                                              + skyTexCache.ring[i].size)))
                {
                    printf("OVERLAP: %d: %p: %x, %x <-> %d: %p: %x, %x\n", i,
                           skyTexCache.ring[i].raster,
                           skyTexCache.ring[i].address,
                           skyTexCache.ring[i].size,
                           rasExt->mpCacheEntry - skyTexCache.ring,
                           raster, rasExt->mpCacheEntry->address,
                           rasExt->mpCacheEntry->size);
                }
            }
            while (i != skyTexCache.ringStart);
        }
    }
    if (rasExt->mpCacheEntry->raster != raster)
    {
        printf("FAILED: %p != %p\n", rasExt->mpCacheEntry->raster, raster);
    }
#endif

    if ((skyTexCache.currentRaster != raster)
        || (skyTexCache.context != useContext2))
    {
    skyTexCache.currentRaster = raster;
    skyTexCache.context = useContext2;

    if (rasExt->mpCacheEntry)
    {
        if (!useContext2)
        {
            skyTexCache.context1Index = rasExt->mpCacheEntry - skyTexCache.ring;
        }
        else
        {
            skyTexCache.context2Index = rasExt->mpCacheEntry - skyTexCache.ring;
        }
    }

    /* Set up the GS registers */

    if ((!(rasExt->bLocked)) && (!skyTexCacheLastWasSpec))
    {
        sweCloseLocalPkt();
    }
    sweOpenLocalPkt(SWE_LPS_CONT, 0);

    tmp = /* NLOOP */ (skyTexCacheLastWasSpec?5l:4l)
#ifdef LESSEOPS
        | /* EOP */ (0l<<15)
#else /* LESSEOPS */
        | /* EOP */ (1l<<15)
#endif /* LESSEOPS */
        | /* PRE */ (0l<<46)
        | /* FLG */ (0l<<58)
        | /* NREG */(1l<<60);
    tmp1 = /* A+D */ (0xel<<(64-64));
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTGIFFAST(ltmp, (skyTexCacheLastWasSpec?5l:4l));

    if (skyTexCacheLastWasSpec)
    {
        MAKE128(ltmp, GS_TEXFLUSH, 0l);
        SWEADDCONTFAST(ltmp);
    }

    tmp = rasExt->lsb | ((RwUInt64)rasExt->msb << 32);

    /* If this texture is actually the camera then setup the framebuffer
       pointer */
    if ((raster->cType&rwRASTERTYPEMASK)==rwRASTERTYPECAMERA)
    {
        long skyBackFrame;

        if (skyFrameBit & 0x1)
        {
            skyBackFrame = *(long*)&sweDb.draw11.frame1;
        }
        else
        {
            skyBackFrame = *(long*)&sweDb.draw01.frame1;
        }

        /* add frame buffer to texture base pointer -fbp is in 2048
           alignment, -tex in 64 align */
        tmp = (tmp & ~0x3fffl)
            | ((rasExt->lsb&0x3fff)+((skyBackFrame&0x1ff)<<5));
    }

    tmp1 = ((useContext2?GS_TEX0_2:GS_TEX0_1) << (64-64));
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

	/* mask out the K and L values, also the MXL and MTBA fields */
    skyTex1_1 &= ~0xfff0018021cl;
    skyTex1_1 |= ( ( rasExt->maxMipLevel ) |
                   ( (RwUInt64)(rasExt->mipmapKL &0xfff)<< 32 ) |
                   ( (RwUInt64)((rasExt->mipmapKL >> 12) &0x3)<< 19 ) );
    MAKE128(ltmp, (useContext2?GS_TEX1_2:GS_TEX1_1), skyTex1_1);
    SWEADDCONTFAST(ltmp);

    texAddr = rasExt->lsb & 0x3fff;
    mipAddr1 = texAddr | (texAddr<<20) | (texAddr<<40);

    if (skyTexCache.enabled)
    {
        /* the miptbp1 and 2 registers store just offset and width
         * so we have to compute the actual register values, knowing
         * the location in local memory where the raster starts...
         */
        tmp = ((RwUInt64)rasExt->miptbp1Lsb |
                     ((RwUInt64)rasExt->miptbp1Msb)<<32 );
        tmp += mipAddr1;
        MAKE128(ltmp, (useContext2?GS_MIPTBP1_2:GS_MIPTBP1_1), tmp);
        SWEADDCONTFAST(ltmp);

        tmp = ((RwUInt64)rasExt->miptbp2Lsb |
                ((RwUInt64)rasExt->miptbp2Msb)<<32);
        tmp += mipAddr1;
        MAKE128(ltmp, (useContext2?GS_MIPTBP2_2:GS_MIPTBP2_1), tmp);
        SWEADDCONTFAST(ltmp);
    }
    else /* disabled */
    {
        /* just write the data as stored in the extension */
        tmp = rasExt->miptbp1Lsb | ((RwUInt64)rasExt->miptbp1Msb << 32);
        MAKE128(ltmp, (useContext2?GS_MIPTBP1_2:GS_MIPTBP1_1), tmp);
        SWEADDCONTFAST(ltmp);

        tmp = rasExt->miptbp2Lsb | ((RwUInt64)rasExt->miptbp2Msb << 32);
        MAKE128(ltmp, (useContext2?GS_MIPTBP2_2:GS_MIPTBP2_1), tmp);
        SWEADDCONTFAST(ltmp);
    }
    skyTexCacheLastWasSpec = FALSE;
    }

    RWRETURN(TRUE);
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexCacheReleaseRaster
 *
 *  Remove the raster from cache. This does not check the reference count
 *  on the object, so this should be done from the parent if required.
 *
 *  On Entry : The raster
 *  On Exit  :
 */
void
RpSkyTexCacheReleaseRaster(RwRaster *raster)
{
    _SkyRasterExt *rasExt;
    RwUInt32 i;

    RWAPIFUNCTION(RWSTRING("RpSkyTexCacheReleaseRaster"));

    RWASSERT(raster);
    rasExt = RASTEREXTFROMRASTER(raster);

    if (rasExt->mpCacheEntry)
    {
        i = rasExt->mpCacheEntry - skyTexCache.ring;

#ifdef REDEBUG
        printf("Releasing %d: %p at %x\n", i, raster, rasExt->mpCacheEntry->size);
#endif
        rasExt->mpCacheEntry->raster = (RwRaster *)NULL;
        if (i == skyTexCache.context1Index)
        {
            skyTexCache.context1Index = ~0U;
        }
        if (i == skyTexCache.context2Index)
        {
            skyTexCache.context2Index = ~0U;
        }

        if (i == skyTexCache.ringStart)
        {
            while ((skyTexCache.ringStart != skyTexCache.ringEnd)
                   && (!skyTexCache.ring[skyTexCache.ringStart].raster))
            {
#ifdef REDEBUG
                printf("Salvaging %d from start\n", skyTexCache.ringStart);
#endif
                if (0 == skyTexCache.ringStart)
                {
                    skyTexCache.ringStart = skyTexCache.lockedStart;
                }
                skyTexCache.ringStart--;
            }
        }
        else
        if (i == skyTexCache.ringEnd)
        {
            while ((skyTexCache.ringStart != skyTexCache.ringEnd)
                   && (!skyTexCache.ring[skyTexCache.ringEnd].raster))
            {
#ifdef REDEBUG
                printf("Salvaging %d from end\n", skyTexCache.ringEnd);
#endif
                skyTexCache.ringEnd++;
                if (skyTexCache.ringEnd == skyTexCache.lockedStart)
                {
                    skyTexCache.ringEnd = 0;
                }
            }
        }
        if (NULL == skyTexCache.ring[skyTexCache.ringStart].raster)
        {
            skyTexCache.ringStart = ~0U;
            skyTexCache.ringEnd = ~0U;
#ifdef REDEBUG
                printf("Release emptied ring\n");
#endif
        }
        rasExt->mpCacheEntry = (_SkyMemBlock*)NULL;
    }
    if (raster == skyTexCache.currentRaster)
    {
        skyTexCache.currentRaster = (RwRaster *)NULL;
    }
#ifdef REDEBUG
    printf("\n");
#endif

    RWRETURNVOID();
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexCacheRasterLock
 *
 *  Lock/unlock a raster in cache. This locks the raster in GS memory. It
 *  attempts to pack the locked rasters at the top end on memory to avoid
 *  fragmentation, but locking several rasters then unlocking all bar the
 *  last locked, will not release the space, until the last one is finally
 *  unlocked.
 *
 *  On Entry : The raster
 *             Bool to indicate lock/unlock requested
 *  On Exit  : Success/failure
 */
RwBool
RpSkyTexCacheRasterLock(RwRaster *raster, RwBool lock)
{
    _SkyRasterExt *rasExt;
    RwUInt32 i;

    RWAPIFUNCTION(RWSTRING("RpSkyTexCacheRasterLock"));

    RWASSERT(raster);
    rasExt = RASTEREXTFROMRASTER(raster);

    if (skyTexCache.enabled)
    {
        if (lock && (!rasExt->bLocked))
        {

            /* We allow an extra few slots over the preserve number */
            if (skyTexCache.lockedStart <= (SPECPRESERVE+3))
            {
#ifdef REDEBUG
                printf("LOCK: failed due to lack of ring entries\n");
#endif
                RWRETURN(FALSE);
            }

            /* Will the texture fit? */
            if (skyTexCache.ring[skyTexCache.lockedStart].address
                - skyTexCache.startAddr < rasExt->nTexCacheSize)
            {
#ifdef REDEBUG
                printf("LOCK: failed due to lack of buffer\n");
#endif
                RWRETURN(FALSE);
            }

            /* For now we just flush all the textures from cache */
            /* We may later implement a more complicated version */
            /* This removes the raster we want to lock if it is in cache.
               if this code is changed, the raster we wish to lock must still
               be removed from cache */
            if (skyTexCache.ringStart != ~0U)
            {
                i = skyTexCache.ringEnd-1;
                do
                {
                    i += 1;
                    if (i == skyTexCache.lockedStart)
                    {
                        i = 0;
                    }
                    if (skyTexCache.ring[i].raster)
                    {
                        _SkyRasterExt *rasExt2;

                        rasExt2
                             = RASTEREXTFROMRASTER(skyTexCache.ring[i].raster);

#ifdef REDEBUG
                        printf("Lock expelling %p from %d\n",
                               skyTexCache.ring[i].raster, i);
                        if (rasExt2->mpCacheEntry != &skyTexCache.ring[i])
                        {
                            printf("FAILED: %p != %p\n", rasExt2->mpCacheEntry,
                                   &skyTexCache.ring[i]);
                        }
#endif
                        rasExt2->mpCacheEntry = (_SkyMemBlock*)NULL;
                        if (skyTexCache.ring[i].raster
                            == skyTexCache.currentRaster)
                        {
                            skyTexCache.currentRaster = (RwRaster *)NULL;
                        }
                        skyTexCache.ring[i].raster = (RwRaster*)NULL;
                    }
                }
                while (i != skyTexCache.ringStart);
            }

            /* Reset the ring pointers */
            skyTexCache.ringStart = ~0U;
            skyTexCache.ringEnd = ~0U;

            i = skyTexCache.lockedStart-1;
            skyTexCache.ring[i].raster = raster;
            skyTexCache.ring[i].address = ((skyTexCache.lockedStart
                                            == skyGNumTexMemBlocks)
                                           ? skyTexCache.startAddr
                                             + skyTexCache.totalSize
                          : skyTexCache.ring[skyTexCache.lockedStart].address)
                                          - rasExt->nTexCacheSize;
#ifdef REDEBUG
            printf("LOCK: %p stored at %x\n", raster,
                   skyTexCache.ring[i].address);
#endif
            skyTexCache.ring[i].size = rasExt->nTexCacheSize;
            rasExt->mpCacheEntry = &skyTexCache.ring[i];
            skyTexCache.lockedStart = i;
            rasExt->bLocked = TRUE;
            skyTexCacheMemRasterToGs(raster, skyTexCache.ring[i].address,
                                     FALSE);
            {
                RwUInt32 destAddr;

                destAddr = rasExt->mpCacheEntry->address >> 6;

                rasExt->lsb &= ~0x3fffl;
                rasExt->lsb |= (destAddr&0x3fff);

                destAddr += rasExt->palOffset;
                destAddr &= 0x3fff;

                rasExt->msb &= ~(0x3fffl << (37-32));
                rasExt->msb |= ((destAddr&0x3fffl) << (37 - 32));
            }
        }
        else if ((!lock) && (rasExt->bLocked))
        {
            /* We always expel the texture from cache on unlock as it
               is in the wrong list */
            rasExt->bLocked = FALSE;

            i = rasExt->mpCacheEntry - skyTexCache.ring;
#ifdef REDEBUG
            printf("UNLOCK %d: %p stored at %x\n", i, raster,
                   rasExt->mpCacheEntry->address);
#endif
            rasExt->mpCacheEntry->raster = (RwRaster *)NULL;
            rasExt->mpCacheEntry = (_SkyMemBlock*)NULL;
            if (skyTexCache.currentRaster == raster)
            {
                skyTexCache.currentRaster = (RwRaster *)NULL;
            }
            if (i == skyTexCache.lockedStart)
            {
                while ((skyTexCache.lockedStart < skyGNumTexMemBlocks)
                       && (!skyTexCache.ring[skyTexCache.lockedStart].raster))
                {
#ifdef REDEBUG
                    printf("UNLOCK: returning %d\n", skyTexCache.lockedStart);
#endif
                    skyTexCache.lockedStart++;
                }
            }
        }
        RWRETURN(TRUE);
    }
    else
    {
        RWRETURN(FALSE);
    }
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexCacheRasterUnlockAll
 *
 *  This function unlocks all rasters which have been locked into the cache
 *  and expels them.
 *
 *  On Entry :
 *  On Exit  :
 */
void
RpSkyTexCacheRasterUnlockAll(void)
{
    RwUInt32 i;
    RWAPIFUNCTION(RWSTRING("RpSkyTexCacheRasterUnlockAll"));

    if (skyTexCache.enabled)
    {
        for (i = skyTexCache.lockedStart; i<skyTexCache.ringSize; i++)
        {
            if (skyTexCache.ring[i].raster)
            {
                _SkyRasterExt *rasExt;

                rasExt = RASTEREXTFROMRASTER(skyTexCache.ring[i].raster);
                rasExt->bLocked = FALSE;
                rasExt->mpCacheEntry = (_SkyMemBlock*)NULL;
                if (skyTexCache.ring[i].raster == skyTexCache.currentRaster)
                {
                    skyTexCache.currentRaster = (RwRaster *)NULL;
                }
                skyTexCache.ring[i].raster = (RwRaster *)NULL;
            }
        }
        skyTexCache.lockedStart = skyTexCache.ringSize;
    }

    RWRETURNVOID();
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexCacheFlush
 *
 *  Remove all rasters from cache. This does not remove locked rasters from
 *  memory.
 *
 *  On Entry :
 *  On Exit  :
 */
void
RpSkyTexCacheFlush(void)
{
    RWAPIFUNCTION(RWSTRING("RpSkyTexCacheFlush"));

    if (skyTexCache.enabled)
    {
        RwUInt32 i;
        _SkyRasterExt *rasExt;

        if (skyTexCache.ringStart != ~0U)
        {
            i = skyTexCache.ringEnd-1;
            do
            {
                i += 1;
                if (i == skyTexCache.lockedStart)
                {
                    i = 0;
                }
                if (skyTexCache.ring[i].raster)
                {
                    rasExt = RASTEREXTFROMRASTER(skyTexCache.ring[i].raster);

#ifdef REDEBUG
                    printf("Flush expelling %p from %d\n",
                            skyTexCache.ring[i].raster, i);
                    if (rasExt->mpCacheEntry != &skyTexCache.ring[i])
                    {
                        printf("FAILED: %p != %p\n", rasExt->mpCacheEntry,
                               &skyTexCache.ring[i]);
                    }
#endif
                    rasExt->mpCacheEntry = (_SkyMemBlock*)NULL;
                    if (skyTexCache.ring[i].raster == skyTexCache.currentRaster)
                    {
                        skyTexCache.currentRaster = (RwRaster *)NULL;
                    }
                    skyTexCache.ring[i].raster = (RwRaster*)NULL;
                }
            }
            while (i != skyTexCache.ringStart);
        }

        /* Reset the ring pointers */
        skyTexCache.ringStart = ~0U;
        skyTexCache.ringEnd = ~0U;
    }

    RWRETURNVOID();
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexCacheRestore
 *
 *  Restore the current texture context. This is required as the FSAA modes
 *  which use blit destroy the GS texture reqisters. Note that this does not
 *  reupload any data. It assumes that contents on the cache is unchanged.
 *
 *  On Entry :
 *  On Exit  :
 */
void
RpSkyTexCacheRestore(void)
{
    RwRaster *rpRas;
    _SkyRasterExt *rasExt;
    RwBool useContext2;

    RWAPIFUNCTION(RWSTRING("RpSkyTexCacheRestore"));

    if (skyTexCache.currentRaster)
    {
        RwUInt64  mipAddr1, mipAddr2, texAddr;
        RwUInt64  tmp1, tmp;
        u_long128 gifTagCmd;
        u_long128 miptbp11Cmd, miptbp21Cmd;
        u_long128 tex01Cmd, tex11Cmd;

        rasExt = RASTEREXTFROMRASTER(skyTexCache.currentRaster);
        rpRas = skyTexCache.currentRaster;
        useContext2 = skyTexCache.context;

        sweOpenLocalPkt(SWE_LPS_CONT, 0);

        /* Select it as the texture source */
        tmp = /* NLOOP */ 4l
#ifdef LESSEOPS
            | /* EOP */ (0l<<15)
#else /* LESSEOPS */
            | /* EOP */ (1l<<15)
#endif /* LESSEOPS */
            | /* PRE */ (0l<<46)
            | /* FLG */ (0l<<58)
            | /* NREG */(1l<<60);
        tmp1 = /* A+D */ (0xel<<(64-64));
        MAKE128(gifTagCmd, tmp1, tmp);
        SWEADDCONTGIFFAST(gifTagCmd, 4);

        tmp = rasExt->lsb | (RwUInt64)rasExt->msb << 32;

        /* If this texture is actually the camera then setup the framebuffer
           pointer */
        if ((rpRas->cType&rwRASTERTYPEMASK)==rwRASTERTYPECAMERA)
        {
            long skyBackFrame;
            if (skyFrameBit & 0x1)
            {
                skyBackFrame = *(long*)&sweDb.draw11.frame1;
            }
            else
            {
                skyBackFrame = *(long*)&sweDb.draw01.frame1;
            }

            /* add frame buffer to texture base pointer -fbp is in 2048
               alignment, -tex in 64 align */
            tmp = (tmp & ~0x3fffl)
                | ((rasExt->lsb&0x3fff)+((skyBackFrame&0x1ff)<<5));
        }

        tmp1 = ((useContext2?GS_TEX0_2:GS_TEX0_1) << (64-64));
        MAKE128(tex01Cmd, tmp1, tmp);
        SWEADDCONTFAST(tex01Cmd);

		/* mask out the K and L values, also the MXL and MTBA fields */
        skyTex1_1 &= ~0xfff0018021cl;
        skyTex1_1 |= ( ( rasExt->maxMipLevel ) |
                       ( (RwUInt64)(rasExt->mipmapKL &0xfff)<< 32 ) |
                       ( (RwUInt64)((rasExt->mipmapKL >> 12) &0x3)<< 19 ) );
        MAKE128(tex11Cmd, (useContext2?GS_TEX1_2:GS_TEX1_1), skyTex1_1);
        SWEADDCONTFAST(tex11Cmd);

        texAddr = rasExt->lsb & 0x3fff;
        mipAddr1 = texAddr | (texAddr<<20) | (texAddr<<40);

        if (skyTexCache.enabled)
        {
            /* the miptbp1 and 2 registers store just offset and width
             * so we have to compute the actual register values, knowing
             * the location in local memory where the raster starts...
             */
            mipAddr2 = ( (RwUInt64)rasExt->miptbp1Lsb |
                         ((RwUInt64)rasExt->miptbp1Msb)<<32 );
            mipAddr2 += mipAddr1;
            MAKE128(miptbp11Cmd,
                    (useContext2?GS_MIPTBP1_2:GS_MIPTBP1_1), mipAddr2);
            SWEADDCONTFAST(miptbp11Cmd);

            mipAddr2 = ((RwUInt64)rasExt->miptbp2Lsb |
                        ((RwUInt64)rasExt->miptbp2Msb)<<32);
            mipAddr2 += mipAddr1;
            MAKE128(miptbp21Cmd,
                    (useContext2?GS_MIPTBP2_2:GS_MIPTBP2_1), mipAddr2);
            SWEADDCONTFAST(miptbp21Cmd);
        }
        else /* disabled */
        {
            /* just write the data as stored in the extension */
            tmp = rasExt->miptbp1Lsb | ((RwUInt64)rasExt->miptbp1Msb << 32);
            MAKE128(miptbp11Cmd,
                    (useContext2?GS_MIPTBP1_2:GS_MIPTBP1_1), tmp);
            SWEADDCONTFAST(miptbp11Cmd);

            tmp = rasExt->miptbp2Lsb | ((RwUInt64)rasExt->miptbp2Msb << 32);
            MAKE128(miptbp11Cmd,
                    (useContext2?GS_MIPTBP2_2:GS_MIPTBP2_1), tmp);
            SWEADDCONTFAST(miptbp11Cmd);
        }
    }

    RWRETURNVOID();
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                         Backward compatability

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/**
 * \ingroup driversky2
 * \ref RpSkyTextureCacheSetState
 *
 *  This used to change the texture cache from Auto LRU/MRU to MRU and back.
 *  This has no meaning and is now ignored. It may be resurected by a 25th
 *  level or above Mage.
 *
 *  On Entry : flags
 *  On Exit  : Success/failure
 */
RwBool
RpSkyTextureCacheSetState(RwInt32 flags __RWUNUSED__)
{
    RWAPIFUNCTION(RWSTRING("RpSkyTextureCacheSetState"));

    RWRETURN(TRUE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                           Cache overload API

 Provided applications which a special requirement to replace the RW
 texeture cache. We attempt to expose enough to make the task easy.
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/**
 * \ingroup driversky2
 * \ref RpSkyTexCacheDisable
 *
 *  Disable the texture cache. Must be called  before RwEngineStart().
 *
 *  On Entry : Bool to indicate disable/ensble
 *  On Exit  : Success/failure
 */
RwBool
RpSkyTexCacheDisable(RwBool disable)
{
    RWAPIFUNCTION(RWSTRING("RpSkyTexCacheDisable"));

    if (-1 == skyTexCache.startAddr)
    {
        skyTexCache.enabled = !disable;
        RWRETURN(TRUE);
    }
    else
    {
        RWRETURN(FALSE);
    }
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexGetStartAddress
 *
 *  Get the start word address of the texture cache
 *
 *  On Entry :
 *  On Exit  : Start address in words, or -1 if the cache has not been started
 */
RwUInt32
RpSkyTexGetStartAddress(void)
{
    RWAPIFUNCTION(RWSTRING("RpSkyTexGetStartAddress"));

    RWRETURN(skyTexCache.startAddr);
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexGetSize
 *
 *  Get the size of the texture cache in words
 *
 *  On Entry :
 *  On Exit  : Size in words, oe -1 if the cache has not been started yet.
 */
RwUInt32
RpSkyTexGetSize(void)
{
    RWAPIFUNCTION(RWSTRING("RpSkyTexGetSize"));

    RWRETURN(skyTexCache.totalSize);
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexCacheSetCallBack
 *
 *  Used to specify the callback invoked when a raster must be uploaded. Note
 *  that the callback's signature has changed since RW 3.10. This function
 *  will fail if the cache is enabled.
 *
 *  On Entry : callback
 *  On Exit  : Success/Failure
 */
RwBool
RpSkyTexCacheSetCallBack(SkyCacheCallBack fpCacheCB)
{
    RWAPIFUNCTION(RWSTRING("RpSkyTexCacheSetCallBack"));

    RWASSERT(fpCacheCB);

    if (!skyTexCache.enabled)
    {
        skyTexCache.uploadRaster = fpCacheCB;
        RWRETURN(TRUE);
    }
    else
    {
        RWRETURN(FALSE);
    }
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexCacheRasterGetAddr
 *
 *  Get the current address in GS memory of a particular raster. This is not
 *  zero'd when a raster is ejected from cache.
 *
 *  On Entry : raster
 *  On Exit  : address
 */
RwUInt32
RpSkyTexCacheRasterGetAddr(RwRaster *raster)
{
    _SkyRasterExt *rasExt;

    RWAPIFUNCTION(RWSTRING("RpSkyTexCacheRasterGetAddr"));

    RWASSERT(raster);
    rasExt = RASTEREXTFROMRASTER(raster);

    RWRETURN((rasExt->lsb) & 0x3fffl);
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexSetTex0
 *
 *  Set the value of tex0 in the raster extension on the raster. This will
 *  be set in the GS registers when the main library uses this raster as
 *  a texture
 *
 *  On Entry : The raster
 *             the most significant 32 bits of tex0
 *             the least significant 32 bits of tex0
 *  On Exit  : The raster
 */
RwRaster*
RpSkyTexSetTex0(RwRaster *raster, RwUInt32 msb, RwUInt32 lsb)
{
    _SkyRasterExt *rasExt;

    RWAPIFUNCTION(RWSTRING("RpSkyTexSetTex0"));

    RWASSERT(raster);
    rasExt = RASTEREXTFROMRASTER(raster);

    rasExt->lsb = lsb;
    rasExt->msb = msb;

    RWRETURN(raster);
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexGetTex0
 *
 *  Get the contents of the tex0 raster extension fields on a raster
 *
 *  On Entry : The raster
 *             Pointer to return for most significant 32 bits of tex0
 *             Pointer to return for the least significant 32 bits of tex0
 *  On Exit  : The raster
 */
RwRaster*
RpSkyTexGetTex0(RwRaster *raster, RwUInt32 *msb, RwUInt32 *lsb)
{
    _SkyRasterExt *rasExt;

    RWAPIFUNCTION(RWSTRING("RpSkyTexGetTex0"));

    RWASSERT(raster);
    rasExt = RASTEREXTFROMRASTER(raster);

    *lsb = rasExt->lsb;
    *msb = rasExt->msb;

    RWRETURN(raster);
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexSetMiptbp1
 *
 *  Set the value of miptbp1 in the raster extension on the raster. This will
 *  be set in the GS registers when the main library uses this raster as
 *  a texture
 *
 *  On Entry : The raster
 *             the most significant 32 bits of miptbp1
 *             the least significant 32 bits of miptbp1
 *  On Exit  : The raster
 */
RwRaster*
RpSkyTexSetMiptbp1(RwRaster *raster, RwUInt32 msb, RwUInt32 lsb)
{
    _SkyRasterExt *rasExt;

    RWAPIFUNCTION(RWSTRING("RpSkyTexSetMiptbp1"));

    RWASSERT(raster);
    rasExt = RASTEREXTFROMRASTER(raster);

    rasExt->miptbp1Lsb = lsb;
    rasExt->miptbp1Msb = msb;

    RWRETURN(raster);
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexGetMiptbp1
 *
 *  Get the contents of the miptbp1 raster extension fields on a raster
 *
 *  On Entry : The raster
 *             Pointer to return for most significant 32 bits of miptbp1
 *             Pointer to return for the least significant 32 bits of miptbp1
 *  On Exit  : The raster
 */
RwRaster*
RpSkyTexGetMiptbp1(RwRaster *raster, RwUInt32 *msb, RwUInt32 *lsb)
{
    _SkyRasterExt *rasExt;

    RWAPIFUNCTION(RWSTRING("RpSkyTexGetMiptbp1"));

    RWASSERT(raster);
    rasExt = RASTEREXTFROMRASTER(raster);

    *lsb = rasExt->miptbp1Lsb;
    *msb = rasExt->miptbp1Msb;

    RWRETURN(raster);
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexSetMiptbp12
 *
 *  Set the value of miptbp1 and miptbp2 in the extension on the raster.
 *  This will be set in the GS registers when the main library uses this
 *  raster as  a texture
 *
 *  On Entry : The raster
 *             the most significant 32 bits of miptbp1
 *             the least significant 32 bits of miptbp1
 *             the most significant 32 bits of miptbp2
 *             the least significant 32 bits of miptbp2
 *  On Exit  : The raster
 */
RwRaster*
RpSkyTexSetMiptbp12(RwRaster *raster, RwUInt32 msb1, RwUInt32 lsb1,
                    RwUInt32 msb2, RwUInt32 lsb2)
{
    _SkyRasterExt *rasExt;

    RWAPIFUNCTION(RWSTRING("RpSkyTexSetMiptbp12"));

    RWASSERT(raster);
    rasExt = RASTEREXTFROMRASTER(raster);

    rasExt->miptbp1Lsb = lsb1;
    rasExt->miptbp1Msb = msb1;
    rasExt->miptbp2Lsb = lsb2;
    rasExt->miptbp2Msb = msb2;

    RWRETURN(raster);
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexGetMiptbp12
 *
 *  Get the contents of the miptbp1 and miptbp2 extension fields on a raster
 *
 *  On Entry : The raster
 *             Pointer to return for most significant 32 bits of miptbp1
 *             Pointer to return for the least significant 32 bits of miptbp1
 *             Pointer to return for most significant 32 bits of miptbp2
 *             Pointer to return for the least significant 32 bits of miptbp2
 *  On Exit  : The raster
 */
RwRaster*
RpSkyTexGetMiptbp12(RwRaster *raster, RwUInt32 *msb1, RwUInt32 *lsb1,
                    RwUInt32 *msb2, RwUInt32 *lsb2)
{
    _SkyRasterExt *rasExt;

    RWAPIFUNCTION(RWSTRING("RpSkyTexGetMiptbp12"));

    RWASSERT(raster);
    rasExt = RASTEREXTFROMRASTER(raster);

    *lsb1 = rasExt->miptbp1Lsb;
    *msb1 = rasExt->miptbp1Msb;
    *lsb2 = rasExt->miptbp2Lsb;
    *msb2 = rasExt->miptbp2Msb;

    RWRETURN(raster);
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexCacheValidateRaster
 *
 *  Mark the raster as in cache/not in cache. The upload callback will be
 *
 *
 *  On Entry : The Raster
 *             Bool indicating uploaded/not uploaded
 *  On Exit  : The raster on success
 */
RwRaster*
RpSkyTexCacheValidateRaster(RwRaster *raster, RwBool valid)
{
    RWAPIFUNCTION(RWSTRING("RpSkyTexCacheValidateRaster"));

    if (!skyTexCache.enabled)
    {
        _SkyRasterExt *rasExt;

        rasExt = RASTEREXTFROMRASTER(raster);
        rasExt->mpCacheEntry = (valid? (_SkyMemBlock *)1
                                     : (_SkyMemBlock *) NULL);
        RWRETURN(raster);
    }
    else
    {
        RWRETURN((RwRaster *)NULL);
    }
}

/**
 * \ingroup driversky2
 * \ref RpSkyUploadPixelData
 *
 *  Upload some pixel data to GS memory. The pixels muct be flushed from
 *  DCache. Additionally the start address must be quad word aligned, and
 *  in the case where the data cannot be uploaded in a single dma transfer,
 *  each restart point must be quad word aligned (For example, if the stride
 *  is not equal to the pixel depth*width, each scan line must be aligned)
 *
 *  On Entry : width in pixels
 *             height in pixels
 *             src address
 *             GS memory destination memory address in words
 *             destination buffer width in pixels/64
 *             pixel format
 *             source stride
 *  On Exit  : Success/failure
 */
RwBool
RpSkyUploadPixelData(RwUInt32 pixelWidth __RWUNUSED__, 
                     RwUInt32 pixelHeight __RWUNUSED__,
                     RwUInt8 *srcAddress __RWUNUSED__,
                     RwUInt32 dstAddress __RWUNUSED__,
                     RwUInt32 dstWidthBy64 __RWUNUSED__,
                     RwUInt32 bitsPerPixel __RWUNUSED__,
                     RwUInt32 pixelFormat __RWUNUSED__, 
                     RwUInt32 srcStride __RWUNUSED__)
{
    RWAPIFUNCTION(RWSTRING("RpSkyUploadPixelData"));

    RWRETURN(TRUE);
}

/**
 * \ingroup driversky2
 * \ref RpSkyTexCacheUploadRaster
 *
 *  Upload an entire raster and all its miplevels and palette to the address
 *  specified. This will be faster than calling RpSkyUploadPixelData() on
 *  several mip levels and will take advantage of inlined dma data, if present,
 *  in the raster. It will update the tex0, miptbp1 and miptbp2 extension
 *  fields.
 *
 *  On Entry : The raster
 *             The GS memory word address
 *  On Exit  : The raster on success
 */
RwRaster*
RpSkyTexCacheUploadRaster(RwRaster *raster __RWUNUSED__, 
                          RwUInt32 dstAddress __RWUNUSED__)
{
    RWAPIFUNCTION(RWSTRING("RpSkyTexCacheUploadRaster"));

    RWRETURN((RwRaster *)NULL);
}

/************************************************************************/
/* Turn it back on so that lack of textual distruction will pass unknoticed */
/* *INDENT-ON* */
#endif /* !ASYNCTEXTURES */
