1692 lines
49 KiB
C
1692 lines
49 KiB
C
/******************************Module*Header*******************************\
|
|
*
|
|
* *******************
|
|
* * GDI SAMPLE CODE *
|
|
* *******************
|
|
*
|
|
* Module Name: pointer.c
|
|
*
|
|
* This module contains the hardware pointer support for the display
|
|
* driver. This supports both the built-in S3 hardware pointer and
|
|
* some common DAC hardware pointers.
|
|
*
|
|
* Copyright (c) 1992-1998 Microsoft Corporation
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
|
|
typedef struct BT485_POINTER_DATA {
|
|
|
|
LONG xHot;
|
|
LONG yHot;
|
|
ULONG ulExtendedDacControl;
|
|
BYTE jCommandRegister0;
|
|
BYTE jCommandRegister1;
|
|
BYTE jCommandRegister2;
|
|
BYTE jCommandRegister3;
|
|
|
|
} BT485_POINTER_DATA;
|
|
|
|
typedef struct TI025_POINTER_DATA {
|
|
|
|
ULONG ulExtendedDacControl;
|
|
|
|
} TI025_POINTER_DATA;
|
|
|
|
ULONG NewMmIoSetPointerShape(
|
|
PDEV* ppdev,
|
|
SURFOBJ* psoMsk,
|
|
SURFOBJ* psoColor,
|
|
XLATEOBJ* pxlo,
|
|
LONG xHot,
|
|
LONG yHot,
|
|
LONG x,
|
|
LONG y,
|
|
RECTL* prcl,
|
|
FLONG fl,
|
|
BYTE* pBuf
|
|
);
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vShowPointerBt485
|
|
*
|
|
* Show or hide the Brooktree 485 hardware pointer.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vShowPointerBt485(
|
|
PDEV* ppdev,
|
|
BT485_POINTER_DATA* pbp,
|
|
BOOL bShow)
|
|
{
|
|
BYTE* pjIoBase = ppdev->pjIoBase;
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0200);
|
|
|
|
OUTP(pjIoBase, BT485_ADDR_CMD_REG2, (bShow) ?
|
|
(pbp->jCommandRegister2 | BT485_CURSOR_MODE2) :
|
|
pbp->jCommandRegister2);
|
|
|
|
if (!bShow)
|
|
{
|
|
// Move the hardware pointer off-screen so that it doesn't flash
|
|
// in the old position when we finally turn it back on:
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0300);
|
|
|
|
OUTP(pjIoBase, BT485_CURSOR_X_LOW, 0);
|
|
OUTP(pjIoBase, BT485_CURSOR_X_HIGH, 0);
|
|
|
|
// A 'y' value of 1600 should be enough...
|
|
|
|
OUTP(pjIoBase, BT485_CURSOR_Y_LOW, 1663);
|
|
OUTP(pjIoBase, BT485_CURSOR_Y_HIGH, (1663 >> 8));
|
|
}
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vMovePointerBt485
|
|
*
|
|
* Move the Brooktree 485 hardware pointer.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vMovePointerBt485(
|
|
PDEV* ppdev,
|
|
BT485_POINTER_DATA* pbp,
|
|
LONG x,
|
|
LONG y)
|
|
{
|
|
BYTE* pjIoBase = ppdev->pjIoBase;
|
|
|
|
x -= pbp->xHot;
|
|
y -= pbp->yHot;
|
|
|
|
x += 64;
|
|
y += 64;
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0300);
|
|
|
|
OUTP(pjIoBase, BT485_CURSOR_X_LOW, (x));
|
|
OUTP(pjIoBase, BT485_CURSOR_X_HIGH, (x >> 8));
|
|
|
|
OUTP(pjIoBase, BT485_CURSOR_Y_LOW, (y));
|
|
OUTP(pjIoBase, BT485_CURSOR_Y_HIGH, (y >> 8));
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL bSetPointerShapeBt485
|
|
*
|
|
* Set the Brooktree 485 hardware pointer shape.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bSetPointerShapeBt485(
|
|
PDEV* ppdev,
|
|
BT485_POINTER_DATA* pbp,
|
|
LONG x, // If -1, pointer should be created hidden
|
|
LONG y,
|
|
LONG xHot,
|
|
LONG yHot,
|
|
LONG cx,
|
|
LONG cy,
|
|
BYTE* pjShape)
|
|
{
|
|
BYTE* pjIoBase = ppdev->pjIoBase;
|
|
BYTE* pjSrc;
|
|
LONG i;
|
|
|
|
// Get access to command register 3:
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0100);
|
|
OUTP(pjIoBase, BT485_ADDR_CMD_REG0, pbp->jCommandRegister0 | BT485_CMD_REG_3_ACCESS);
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl);
|
|
OUTP(pjIoBase, BT485_ADDR_CMD_REG1, 0x01);
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0200);
|
|
OUTP(pjIoBase, BT485_ADDR_CMD_REG3, pbp->jCommandRegister3);
|
|
|
|
// Disable the pointer:
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0200);
|
|
OUTP(pjIoBase, BT485_ADDR_CMD_REG2, pbp->jCommandRegister2);
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl);
|
|
OUTP(pjIoBase, BT485_ADDR_CUR_RAM_WRITE, 0x0);
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0200);
|
|
|
|
// Point to first XOR word:
|
|
|
|
pjSrc = pjShape + 2;
|
|
|
|
// Download the XOR mask:
|
|
|
|
for (i = 256; i > 0; i--)
|
|
{
|
|
OUTP(pjIoBase, BT485_CUR_RAM_ARRAY_DATA, *(pjSrc));
|
|
OUTP(pjIoBase, BT485_CUR_RAM_ARRAY_DATA, *(pjSrc + 1));
|
|
|
|
// Skip over AND word:
|
|
|
|
pjSrc += 4;
|
|
}
|
|
|
|
// Pointer to first AND word:
|
|
|
|
pjSrc = pjShape;
|
|
|
|
// Download the AND mask:
|
|
|
|
for (i = 256; i > 0; i--)
|
|
{
|
|
OUTP(pjIoBase, BT485_CUR_RAM_ARRAY_DATA, *(pjSrc));
|
|
OUTP(pjIoBase, BT485_CUR_RAM_ARRAY_DATA, *(pjSrc + 1));
|
|
|
|
// Skip over XOR word:
|
|
|
|
pjSrc += 4;
|
|
}
|
|
|
|
pbp->xHot = xHot;
|
|
pbp->yHot = yHot;
|
|
|
|
// Set the position of the pointer:
|
|
|
|
if (x != -1)
|
|
{
|
|
x -= xHot;
|
|
y -= yHot;
|
|
|
|
x += 64;
|
|
y += 64;
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0300);
|
|
|
|
OUTP(pjIoBase, BT485_CURSOR_X_LOW, (x));
|
|
OUTP(pjIoBase, BT485_CURSOR_X_HIGH, (x >> 8));
|
|
|
|
OUTP(pjIoBase, BT485_CURSOR_Y_LOW, (y));
|
|
OUTP(pjIoBase, BT485_CURSOR_Y_HIGH, (y >> 8));
|
|
|
|
// Enable the pointer:
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0200);
|
|
OUTP(pjIoBase, BT485_ADDR_CMD_REG2, pbp->jCommandRegister2 | BT485_CURSOR_MODE2);
|
|
}
|
|
else
|
|
{
|
|
// Move the hardware pointer off-screen so that it doesn't flash
|
|
// in the old position when we finally turn it back on:
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0300);
|
|
|
|
OUTP(pjIoBase, BT485_CURSOR_X_LOW, 0);
|
|
OUTP(pjIoBase, BT485_CURSOR_X_HIGH, 0);
|
|
|
|
// A 'y' value of 1600 should be enough...
|
|
|
|
OUTP(pjIoBase, BT485_CURSOR_Y_LOW, 1663);
|
|
OUTP(pjIoBase, BT485_CURSOR_Y_HIGH, (1663 >> 8));
|
|
}
|
|
|
|
// Reset the DAC extended register:
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vEnablePointerBt485
|
|
*
|
|
* Get the hardware ready to use the Brooktree 485 hardware pointer.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vEnablePointerBt485(
|
|
PDEV* ppdev,
|
|
BT485_POINTER_DATA* pbp,
|
|
BOOL bFirst)
|
|
{
|
|
BYTE* pjIoBase = ppdev->pjIoBase;
|
|
|
|
if (bFirst)
|
|
{
|
|
// Make a copy of the extended DAC control register:
|
|
|
|
OUTP(pjIoBase, CRTC_INDEX, EX_DAC_CT);
|
|
|
|
pbp->ulExtendedDacControl = ((INP(pjIoBase, CRTC_DATA) << 8) | EX_DAC_CT) & ~0x0300;
|
|
|
|
// Make copies of command registers 1 and 2:
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0100);
|
|
pbp->jCommandRegister0 = INP(pjIoBase, BT485_ADDR_CMD_REG0);
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0200);
|
|
pbp->jCommandRegister1 = INP(pjIoBase, BT485_ADDR_CMD_REG1);
|
|
|
|
// Make a copy of command register 2 and mask off the pointer control bits:
|
|
|
|
pbp->jCommandRegister2 = INP(pjIoBase, BT485_ADDR_CMD_REG2) & BT485_CURSOR_DISABLE;
|
|
|
|
// Disable the pointer:
|
|
|
|
OUTP(pjIoBase, BT485_ADDR_CMD_REG2, pbp->jCommandRegister2);
|
|
|
|
// To access command register 3, we do the following:
|
|
|
|
// 1. Set the command register access bit in command register 0.
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0100);
|
|
OUTP(pjIoBase, BT485_ADDR_CMD_REG0, pbp->jCommandRegister0 | BT485_CMD_REG_3_ACCESS);
|
|
|
|
// 2. Set the index to 1.
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl);
|
|
OUTP(pjIoBase, BT485_ADDR_CMD_REG1, 0x01);
|
|
|
|
// 3. Now read command register 3.
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0200);
|
|
pbp->jCommandRegister3 = INP(pjIoBase, BT485_ADDR_CMD_REG3);
|
|
|
|
// Set command register 3 for a 64 X 64 pointer:
|
|
|
|
pbp->jCommandRegister3 |= BT485_64X64_CURSOR;
|
|
OUTP(pjIoBase, BT485_ADDR_CMD_REG3, pbp->jCommandRegister3);
|
|
|
|
// Disable access to command register 3:
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, (pbp->ulExtendedDacControl | 0x0100));
|
|
OUTP(pjIoBase, BT485_ADDR_CMD_REG0, pbp->jCommandRegister0);
|
|
|
|
// Set the colour 1 and colour 2 for the pointer. Select address
|
|
// register; pointer/overscan color write on the Bt485.
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl | 0x0100);
|
|
OUTP(pjIoBase, BT485_ADDR_CUR_COLOR_WRITE, BT485_CURSOR_COLOR_1);
|
|
|
|
// Output the RGB for pointer colour 1 (black):
|
|
|
|
OUTP(pjIoBase, BT485_CUR_COLOR_DATA, 0x00);
|
|
OUTP(pjIoBase, BT485_CUR_COLOR_DATA, 0x00);
|
|
OUTP(pjIoBase, BT485_CUR_COLOR_DATA, 0x00);
|
|
|
|
// Output the RGB for pointer colour 2 (white):
|
|
|
|
OUTP(pjIoBase, BT485_CUR_COLOR_DATA, 0xff);
|
|
OUTP(pjIoBase, BT485_CUR_COLOR_DATA, 0xff);
|
|
OUTP(pjIoBase, BT485_CUR_COLOR_DATA, 0xff);
|
|
|
|
// Reset the DAC control register:
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, pbp->ulExtendedDacControl);
|
|
}
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vShowPointerTi025
|
|
*
|
|
* Show or hide the TI 025 hardware pointer.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vShowPointerTi025(
|
|
PDEV* ppdev,
|
|
TI025_POINTER_DATA* ptp,
|
|
BOOL bShow)
|
|
{
|
|
BYTE* pjIoBase = ppdev->pjIoBase;
|
|
BYTE jDacControl;
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, ptp->ulExtendedDacControl | 0x0100);
|
|
|
|
OUTP(pjIoBase, 0x3c6, 6);
|
|
|
|
jDacControl = INP(pjIoBase, 0x3c7);
|
|
|
|
if (bShow)
|
|
jDacControl |= 0x40;
|
|
else
|
|
{
|
|
jDacControl &= ~0x40;
|
|
|
|
// Move the hardware pointer off-screen so that it doesn't flash
|
|
// in the old position when we finally turn it back on:
|
|
|
|
OUTP(pjIoBase, 0x3c6, 0);
|
|
OUTP(pjIoBase, 0x3c7, 0);
|
|
|
|
OUTP(pjIoBase, 0x3c6, 1);
|
|
OUTP(pjIoBase, 0x3c7, 0);
|
|
|
|
OUTP(pjIoBase, 0x3c6, 2);
|
|
OUTP(pjIoBase, 0x3c7, 1663); // A 'y' value of 1600 should be enough...
|
|
|
|
OUTP(pjIoBase, 0x3c6, 3);
|
|
OUTP(pjIoBase, 0x3c7, (1663 >> 8));
|
|
}
|
|
|
|
OUTP(pjIoBase, 0x3c6, 6);
|
|
OUTP(pjIoBase, 0x3c7, jDacControl);
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, ptp->ulExtendedDacControl);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vMovePointerTi025
|
|
*
|
|
* Move the TI 025 hardware pointer.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vMovePointerTi025(
|
|
PDEV* ppdev,
|
|
TI025_POINTER_DATA* ptp,
|
|
LONG x,
|
|
LONG y)
|
|
{
|
|
BYTE* pjIoBase = ppdev->pjIoBase;
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, ptp->ulExtendedDacControl | 0x0100);
|
|
|
|
OUTP(pjIoBase, 0x3c6, 0);
|
|
OUTP(pjIoBase, 0x3c7, (x));
|
|
|
|
OUTP(pjIoBase, 0x3c6, 1);
|
|
OUTP(pjIoBase, 0x3c7, (x >> 8));
|
|
|
|
OUTP(pjIoBase, 0x3c6, 2);
|
|
OUTP(pjIoBase, 0x3c7, (y));
|
|
|
|
OUTP(pjIoBase, 0x3c6, 3);
|
|
OUTP(pjIoBase, 0x3c7, (y >> 8));
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, ptp->ulExtendedDacControl);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL bSetPointerShapeTi025
|
|
*
|
|
* Set the TI 025 hardware pointer shape.
|
|
*
|
|
* Don't do word outs to the DAC because they may not be performed correctly
|
|
* on some ISA machines.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bSetPointerShapeTi025(
|
|
PDEV* ppdev,
|
|
TI025_POINTER_DATA* ptp,
|
|
LONG x, // If -1, pointer should be created hidden
|
|
LONG y,
|
|
LONG xHot,
|
|
LONG yHot,
|
|
LONG cx,
|
|
LONG cy,
|
|
BYTE* pjShape)
|
|
{
|
|
BYTE* pjIoBase = ppdev->pjIoBase;
|
|
LONG i;
|
|
DWORD dwShape;
|
|
LONG cShift;
|
|
WORD wMask;
|
|
WORD wAnd;
|
|
WORD wXor;
|
|
BYTE jDacControl;
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, ptp->ulExtendedDacControl | 0x0100);
|
|
|
|
// Hide the pointer, otherwise it will show random garbage when
|
|
// animating cursors on the TI 020 DAC.
|
|
|
|
OUTP(pjIoBase, 0x3c6, 6);
|
|
|
|
jDacControl = INP(pjIoBase, 0x3c7);
|
|
|
|
jDacControl &= ~0x40;
|
|
|
|
OUTP(pjIoBase, 0x3c7, jDacControl);
|
|
|
|
// Set the pointer hot-spot offset:
|
|
|
|
OUTP(pjIoBase, 0x3c6, 4);
|
|
OUTP(pjIoBase, 0x3c7, xHot);
|
|
OUTP(pjIoBase, 0x3c6, 5);
|
|
OUTP(pjIoBase, 0x3c7, yHot);
|
|
|
|
// Download the pointer shape. Do the OUTs for downloading the
|
|
// pointer data slowly -- don't use REP OUTSB.
|
|
|
|
OUTP(pjIoBase, 0x3c6, 8);
|
|
OUTP(pjIoBase, 0x3c7, 0);
|
|
OUTP(pjIoBase, 0x3c6, 9);
|
|
OUTP(pjIoBase, 0x3c7, 0); // Start with pixel 0 of the pointer
|
|
|
|
OUTP(pjIoBase, 0x3c6, 10); // Get ready for downloading
|
|
|
|
for (i = 256; i != 0; i--)
|
|
{
|
|
// Every time through this loop we'll handle one AND word and one
|
|
// XOR word of the pointer data (which is good because the S3
|
|
// display driver gives us the pointer shape in 'pjShape' such that
|
|
// it starts with the first AND word, followed by the first XOR
|
|
// word, followed by the second AND word, etc.)
|
|
|
|
dwShape = 0;
|
|
|
|
// The AND word is first. Don't forget about endianness...
|
|
|
|
wAnd = (*(pjShape) << 8) | *(pjShape + 1);
|
|
for (wMask = 0x8000, cShift = 16; wMask != 0; wMask >>= 1, cShift--)
|
|
{
|
|
dwShape |= ((wAnd & wMask) << cShift);
|
|
}
|
|
|
|
// The XOR word is next. Don't forget about endianness...
|
|
|
|
wXor = (*(pjShape + 2) << 8) | *(pjShape + 3);
|
|
for (wMask = 0x8000, cShift = 15; wMask != 0; wMask >>= 1, cShift--)
|
|
{
|
|
dwShape |= ((wXor & wMask) << cShift);
|
|
}
|
|
|
|
// We've now interleaved the AND and XOR words into a dword such
|
|
// that if the AND word bits are ABC... and the XOR word bits are
|
|
// 123..., the resulting dword will be A1B2C3...
|
|
|
|
OUTP(pjIoBase, 0x3c7, (dwShape >> 24));
|
|
OUTP(pjIoBase, 0x3c7, (dwShape >> 16));
|
|
OUTP(pjIoBase, 0x3c7, (dwShape >> 8));
|
|
OUTP(pjIoBase, 0x3c7, (dwShape));
|
|
|
|
// Advance to next AND/XOR word pair:
|
|
|
|
pjShape += 4;
|
|
}
|
|
|
|
if (x != -1)
|
|
{
|
|
// Set the position of the pointer:
|
|
|
|
OUTP(pjIoBase, 0x3c6, 0);
|
|
OUTP(pjIoBase, 0x3c7, (x));
|
|
|
|
OUTP(pjIoBase, 0x3c6, 1);
|
|
OUTP(pjIoBase, 0x3c7, (x >> 8));
|
|
|
|
OUTP(pjIoBase, 0x3c6, 2);
|
|
OUTP(pjIoBase, 0x3c7, (y));
|
|
|
|
OUTP(pjIoBase, 0x3c6, 3);
|
|
OUTP(pjIoBase, 0x3c7, (y >> 8));
|
|
|
|
// Show the pointer:
|
|
|
|
OUTP(pjIoBase, 0x3c6, 6);
|
|
|
|
OUTP(pjIoBase, 0x3c7, jDacControl | 0x40);
|
|
}
|
|
else
|
|
{
|
|
// Move the hardware pointer off-screen so that it doesn't flash
|
|
// in the old position when we finally turn it back on:
|
|
|
|
OUTP(pjIoBase, 0x3c6, 0);
|
|
OUTP(pjIoBase, 0x3c7, 0);
|
|
|
|
OUTP(pjIoBase, 0x3c6, 1);
|
|
OUTP(pjIoBase, 0x3c7, 0);
|
|
|
|
OUTP(pjIoBase, 0x3c6, 2);
|
|
OUTP(pjIoBase, 0x3c7, 1663); // A 'y' value of 1600 should be enough...
|
|
|
|
OUTP(pjIoBase, 0x3c6, 3);
|
|
OUTP(pjIoBase, 0x3c7, (1663 >> 8));
|
|
}
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, ptp->ulExtendedDacControl);
|
|
|
|
// Reset DAC read mask to 0xff:
|
|
|
|
OUTP(pjIoBase, 0x3c6, 0xff);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vEnablePointerTi025
|
|
*
|
|
* Get the hardware ready to use the TI 025 hardware pointer.
|
|
*
|
|
* Don't do word outs to the DAC because they may not be performed correctly
|
|
* on some ISA machines.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vEnablePointerTi025(
|
|
PDEV* ppdev,
|
|
TI025_POINTER_DATA* ptp,
|
|
BOOL bFirst)
|
|
{
|
|
BYTE* pjIoBase = ppdev->pjIoBase;
|
|
BYTE jMode;
|
|
BYTE jDacControl;
|
|
|
|
// Make a copy of the extended DAC control register:
|
|
|
|
OUTP(pjIoBase, CRTC_INDEX, EX_DAC_CT);
|
|
|
|
ptp->ulExtendedDacControl = ((INP(pjIoBase, CRTC_DATA) << 8) | EX_DAC_CT) & ~0x0300;
|
|
|
|
// Disable the DAC's Bt485 emulation so that we can use the TI hardware
|
|
// pointer.
|
|
|
|
OUTP(pjIoBase, CRTC_INDEX, 0x5C);
|
|
|
|
jMode = INP(pjIoBase, CRTC_DATA);
|
|
|
|
OUTP(pjIoBase, CRTC_DATA, jMode & ~0x20); // Select TI mode in the DAC
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, ptp->ulExtendedDacControl | 0x0100);
|
|
|
|
OUTP(pjIoBase, 0x3c6, 6);
|
|
|
|
jDacControl = INP(pjIoBase, 0x3c7);
|
|
|
|
OUTP(pjIoBase, 0x3c7, jDacControl & 0x7f); // Set to TI mode (non planar)
|
|
|
|
// Set the pointer colours to black and white.
|
|
|
|
OUTP(pjIoBase, 0x3c6, 0x26);
|
|
OUTP(pjIoBase, 0x3c7, 0xff); // Foreground red component
|
|
OUTP(pjIoBase, 0x3c6, 0x27);
|
|
OUTP(pjIoBase, 0x3c7, 0xff); // Foreground green component
|
|
OUTP(pjIoBase, 0x3c6, 0x28);
|
|
OUTP(pjIoBase, 0x3c7, 0xff); // Foreground blue component
|
|
|
|
OUTP(pjIoBase, 0x3c6, 0x23);
|
|
OUTP(pjIoBase, 0x3c7, 0x00); // Background red component
|
|
OUTP(pjIoBase, 0x3c6, 0x24);
|
|
OUTP(pjIoBase, 0x3c7, 0x00); // Background green component
|
|
OUTP(pjIoBase, 0x3c6, 0x25);
|
|
OUTP(pjIoBase, 0x3c7, 0x00); // Background blue component
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, ptp->ulExtendedDacControl);
|
|
|
|
OUTP(pjIoBase, 0x3c6, 0xff); // Reset DAC read mask to 0xff
|
|
|
|
// Note that we don't have to bother hiding the pointer, because
|
|
// vShowPointer will be called immediately...
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vShowPointerS3
|
|
*
|
|
* Show or hide the S3 hardware pointer.
|
|
*
|
|
* We hide the pointer by making it only one row high (we always reserve
|
|
* the bottom scan of the pointer shape to be invisible). We do it this
|
|
* way because we ran into problems doing it with any other method:
|
|
*
|
|
* 1. Disabling the hardware pointer via register CR45 will hang
|
|
* 80x/928/864 chips if it is done at exactly the wrong time during
|
|
* the horizontal retrace. It's is not safe to wait for vertical
|
|
* blank and do it then, because we're a user mode process and
|
|
* could get context switched after doing the wait but before setting
|
|
* the bit.
|
|
*
|
|
* 2. Simply changing the pointer position to move it off-screen works,
|
|
* but is not a good solution because the pointer position is latched
|
|
* by the hardware, and it usually takes a couple of frames for the
|
|
* new position to take effect (which causes the pointer to jump even
|
|
* more than it currently does).
|
|
*
|
|
* 3. Using registers CR4C and CR4D to switch to a pre-defined 'invisible'
|
|
* pointer also worked, but still caused machines to crash with the
|
|
* same symptoms as from solution 1 (although it was somewhat more
|
|
* rare).
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vShowPointerS3(
|
|
PDEV* ppdev,
|
|
BOOL bShow) // If TRUE, show the pointer. If FALSE, hide the pointer.
|
|
{
|
|
BYTE* pjIoBase = ppdev->pjIoBase;
|
|
LONG x;
|
|
LONG y;
|
|
LONG dx;
|
|
LONG dy;
|
|
|
|
// If we don't wait for vertical retrace here, the S3 sometimes ignores
|
|
// the setting of the new pointer position:
|
|
|
|
while (INP(pjIoBase, STATUS_1) & VBLANK_ACTIVE)
|
|
; // Wait for bit 3 to become 0
|
|
while (!(INP(pjIoBase, STATUS_1) & VBLANK_ACTIVE))
|
|
; // Wait for bit 3 to become 1
|
|
|
|
if (bShow)
|
|
{
|
|
// Make the hardware pointer visible:
|
|
|
|
x = ppdev->xPointer;
|
|
y = ppdev->yPointer;
|
|
dx = ppdev->dxPointer;
|
|
dy = ppdev->dyPointer;
|
|
}
|
|
else
|
|
{
|
|
// Move the hardware pointer off-screen so that it doesn't flash
|
|
// in the old position when we finally turn it back on:
|
|
|
|
x = ppdev->cxScreen + 64;
|
|
y = ppdev->cyScreen + 64;
|
|
dx = 0;
|
|
dy = HW_POINTER_HIDE;
|
|
}
|
|
|
|
// Note that due to register shadowing, these OUTs should be done
|
|
// in a specific order, otherwise you may get a flashing pointer:
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, HGC_ORGX_MSB | ((x >> 8) << 8));
|
|
OUTPW(pjIoBase, CRTC_INDEX, HGC_ORGX_LSB | ((x & 0xff) << 8));
|
|
OUTPW(pjIoBase, CRTC_INDEX, HGC_ORGY_LSB | ((y & 0xff) << 8));
|
|
OUTPW(pjIoBase, CRTC_INDEX, HGC_DX | ((dx) << 8));
|
|
OUTPW(pjIoBase, CRTC_INDEX, HGC_DY | ((dy) << 8));
|
|
OUTPW(pjIoBase, CRTC_INDEX, HGC_ORGY_MSB | ((y >> 8) << 8));
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vMovePointerS3
|
|
*
|
|
* Move the S3 hardware pointer.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vMovePointerS3(
|
|
PDEV* ppdev,
|
|
LONG x,
|
|
LONG y)
|
|
{
|
|
BYTE* pjIoBase = ppdev->pjIoBase;
|
|
LONG dx;
|
|
LONG dy;
|
|
|
|
// 'dx' and 'dy' are the offsets into the pointer bitmap at which
|
|
// the hardware is supposed to start drawing, when the pointer is
|
|
// along the left or top edge and needs to be clipped:
|
|
|
|
x -= ppdev->xPointerHot;
|
|
y -= ppdev->yPointerHot;
|
|
|
|
dx = 0;
|
|
dy = 0;
|
|
|
|
if (x <= 0)
|
|
{
|
|
dx = -x;
|
|
x = 0;
|
|
}
|
|
|
|
if (y <= 0)
|
|
{
|
|
dy = -y;
|
|
y = 0;
|
|
}
|
|
|
|
// Account for pointer position scaling in high-colour modes:
|
|
|
|
x <<= ppdev->cPointerShift;
|
|
|
|
ppdev->dxPointer = dx;
|
|
ppdev->dyPointer = dy;
|
|
ppdev->xPointer = x;
|
|
ppdev->yPointer = y;
|
|
|
|
// Note that due to register shadowing, these OUTs should be done
|
|
// in a specific order, otherwise you may get a flashing pointer:
|
|
|
|
OUTPW(pjIoBase, CRTC_INDEX, HGC_ORGX_MSB | ((x >> 8) << 8));
|
|
OUTPW(pjIoBase, CRTC_INDEX, HGC_ORGX_LSB | ((x & 0xff) << 8));
|
|
OUTPW(pjIoBase, CRTC_INDEX, HGC_ORGY_LSB | ((y & 0xff) << 8));
|
|
OUTPW(pjIoBase, CRTC_INDEX, HGC_DX | ((dx) << 8));
|
|
OUTPW(pjIoBase, CRTC_INDEX, HGC_DY | ((dy) << 8));
|
|
OUTPW(pjIoBase, CRTC_INDEX, HGC_ORGY_MSB | ((y >> 8) << 8));
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vSetPointerShapeS3
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vSetPointerShapeS3(
|
|
SURFOBJ* pso,
|
|
LONG x, // Relative coordinates
|
|
LONG y, // Relative coordinates
|
|
LONG xHot,
|
|
LONG yHot,
|
|
BYTE* pjShape,
|
|
FLONG fl)
|
|
{
|
|
BYTE* pjIoBase;
|
|
PDEV* ppdev;
|
|
ULONG* pulSrc;
|
|
ULONG* pulDst;
|
|
LONG i;
|
|
|
|
ppdev = (PDEV*) pso->dhpdev;
|
|
pjIoBase = ppdev->pjIoBase;
|
|
|
|
// 1. Hide the current pointer.
|
|
|
|
if (!(fl & SPS_ANIMATEUPDATE))
|
|
{
|
|
// Hide the pointer to try and lessen the jumpiness when the
|
|
// new shape has a different hot spot. We don't hide the
|
|
// pointer while animating, because that definitely causes
|
|
// flashing:
|
|
|
|
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
|
|
OUTPW(pjIoBase, CRTC_INDEX, HGC_DY | (HW_POINTER_HIDE << 8));
|
|
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
|
|
}
|
|
|
|
// 2. Wait until the vertical retrace is done.
|
|
// --
|
|
//
|
|
// If we don't wait for vertical retrace here, the S3 sometimes ignores
|
|
// the setting of the new pointer position:
|
|
|
|
while (INP(pjIoBase, STATUS_1) & VBLANK_ACTIVE)
|
|
; // Wait for bit 3 to become 0
|
|
while (!(INP(pjIoBase, STATUS_1) & VBLANK_ACTIVE))
|
|
; // Wait for bit 3 to become 1
|
|
|
|
// 3. Set the new pointer position.
|
|
// --
|
|
|
|
ppdev->xPointerHot = xHot;
|
|
ppdev->yPointerHot = yHot;
|
|
|
|
DrvMovePointer(pso, x, y, NULL); // Note: Must pass relative coordinates!
|
|
|
|
// 4. Download the new pointer shape.
|
|
|
|
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
|
|
|
|
ppdev->pfnBankSelectMode(ppdev, ppdev->pvBankData, BANK_ON);
|
|
ppdev->pfnBankMap(ppdev, ppdev->pvBankData, ppdev->iPointerBank);
|
|
|
|
pulSrc = (ULONG*) pjShape;
|
|
pulDst = (ULONG*) ppdev->pvPointerShape;
|
|
|
|
if (DIRECT_ACCESS(ppdev))
|
|
{
|
|
for (i = HW_POINTER_TOTAL_SIZE / sizeof(ULONG); i != 0; i--)
|
|
{
|
|
*pulDst++ = *pulSrc++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = HW_POINTER_TOTAL_SIZE / sizeof(ULONG); i != 0; i--)
|
|
{
|
|
WRITE_REGISTER_ULONG(pulDst, *pulSrc);
|
|
pulSrc++;
|
|
pulDst++;
|
|
}
|
|
}
|
|
|
|
ppdev->pfnBankSelectMode(ppdev, ppdev->pvBankData, BANK_OFF);
|
|
|
|
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID DrvMovePointer
|
|
*
|
|
* NOTE: Because we have set GCAPS_ASYNCMOVE, this call may occur at any
|
|
* time, even while we're executing another drawing call!
|
|
*
|
|
* Consequently, we have to explicitly synchronize any shared
|
|
* resources. In our case, since we touch the CRTC register here
|
|
* and in the banking code, we synchronize access using a critical
|
|
* section.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID DrvMovePointer(
|
|
SURFOBJ* pso,
|
|
LONG x,
|
|
LONG y,
|
|
RECTL* prcl)
|
|
{
|
|
PDEV* ppdev;
|
|
|
|
ppdev = (PDEV*) pso->dhpdev;
|
|
|
|
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
|
|
|
|
if (x != -1)
|
|
{
|
|
if (ppdev->flCaps & CAPS_DAC_POINTER)
|
|
{
|
|
ppdev->pfnMovePointer(ppdev, ppdev->pvPointerData, x, y);
|
|
}
|
|
else
|
|
{
|
|
vMovePointerS3(ppdev, x, y);
|
|
}
|
|
|
|
if (!ppdev->bHwPointerActive)
|
|
{
|
|
// We have to make the pointer visible:
|
|
|
|
ppdev->bHwPointerActive = TRUE;
|
|
|
|
if (ppdev->flCaps & CAPS_DAC_POINTER)
|
|
{
|
|
ppdev->pfnShowPointer(ppdev, ppdev->pvPointerData, TRUE);
|
|
}
|
|
else
|
|
{
|
|
vShowPointerS3(ppdev, TRUE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ppdev->bHwPointerActive)
|
|
{
|
|
// The pointer is visible, and we've been asked to hide it:
|
|
|
|
ppdev->bHwPointerActive = FALSE;
|
|
|
|
if (ppdev->flCaps & CAPS_DAC_POINTER)
|
|
{
|
|
ppdev->pfnShowPointer(ppdev, ppdev->pvPointerData, FALSE);
|
|
}
|
|
else
|
|
{
|
|
vShowPointerS3(ppdev, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
|
|
|
|
// Note that we don't have to modify 'prcl', since we have a
|
|
// NOEXCLUDE pointer...
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID DrvSetPointerShape
|
|
*
|
|
* Sets the new pointer shape.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
ULONG DrvSetPointerShape(
|
|
SURFOBJ* pso,
|
|
SURFOBJ* psoMsk,
|
|
SURFOBJ* psoColor,
|
|
XLATEOBJ* pxlo,
|
|
LONG xHot,
|
|
LONG yHot,
|
|
LONG x,
|
|
LONG y,
|
|
RECTL* prcl,
|
|
FLONG fl)
|
|
{
|
|
PDEV* ppdev;
|
|
DWORD* pul;
|
|
ULONG cx;
|
|
ULONG cy;
|
|
LONG i;
|
|
LONG j;
|
|
BYTE* pjSrcScan;
|
|
BYTE* pjDstScan;
|
|
LONG lSrcDelta;
|
|
LONG lDstDelta;
|
|
WORD* pwSrc;
|
|
WORD* pwDst;
|
|
LONG cwWhole;
|
|
BOOL bAccept;
|
|
BYTE ajBuf[HW_POINTER_TOTAL_SIZE];
|
|
|
|
ppdev = (PDEV*) pso->dhpdev;
|
|
|
|
// When CAPS_SW_POINTER is set, we have no hardware pointer available,
|
|
// so we always ask GDI to simulate the pointer for us, using
|
|
// DrvCopyBits calls:
|
|
|
|
if (ppdev->flCaps & CAPS_SW_POINTER)
|
|
return(SPS_DECLINE);
|
|
|
|
// We're not going to handle any colour pointers, pointers that
|
|
// are larger than our hardware allows, or flags that we don't
|
|
// understand.
|
|
//
|
|
// (Note that the spec says we should decline any flags we don't
|
|
// understand, but we'll actually be declining if we don't see
|
|
// the only flag we *do* understand...)
|
|
//
|
|
// Our old documentation says that 'psoMsk' may be NULL, which means
|
|
// that the pointer is transparent. Well, trust me, that's wrong.
|
|
// I've checked GDI's code, and it will never pass us a NULL psoMsk:
|
|
|
|
cx = psoMsk->sizlBitmap.cx; // Note that 'sizlBitmap.cy' accounts
|
|
cy = psoMsk->sizlBitmap.cy >> 1; // for the double height due to the
|
|
// inclusion of both the AND masks
|
|
// and the XOR masks. For now, we're
|
|
// only interested in the true
|
|
// pointer dimensions, so we divide
|
|
// by 2.
|
|
|
|
// We reserve the bottom scan of the pointer shape and keep it
|
|
// empty so that we can hide the pointer by changing the S3's
|
|
// display start y-pixel position register to show only the bottom
|
|
// scan of the pointer shape:
|
|
|
|
if ((cx > HW_POINTER_DIMENSION) ||
|
|
(cy > (HW_POINTER_DIMENSION - 1)) ||
|
|
(psoColor != NULL) ||
|
|
!(fl & SPS_CHANGE) ||
|
|
(cx & 0x7)) // make sure cx is a multiple of 8 (byte aligned).
|
|
{
|
|
goto HideAndDecline;
|
|
}
|
|
|
|
ASSERTDD(psoMsk != NULL, "GDI gave us a NULL psoMsk. It can't do that!");
|
|
//ASSERTDD(pso->iType == STYPE_DEVICE, "GDI gave us a weird surface");
|
|
|
|
if ((cx <= (HW_POINTER_DIMENSION / 2)) &&
|
|
!(ppdev->flCaps & CAPS_DAC_POINTER) &&
|
|
(ppdev->flCaps & CAPS_NEW_MMIO))
|
|
{
|
|
return( NewMmIoSetPointerShape(
|
|
ppdev,
|
|
psoMsk,
|
|
psoColor,
|
|
pxlo,
|
|
xHot,
|
|
yHot,
|
|
x,
|
|
y,
|
|
prcl,
|
|
fl,
|
|
ajBuf
|
|
));
|
|
}
|
|
|
|
pul = (ULONG*) &ajBuf[0];
|
|
for (i = HW_POINTER_TOTAL_SIZE / sizeof(ULONG); i != 0; i--)
|
|
{
|
|
// Here we initialize the entire pointer work buffer to be
|
|
// transparent (the S3 has no means of specifying a pointer size
|
|
// other than 64 x 64 -- so if we're asked to draw a 32 x 32
|
|
// pointer, we want the unused portion to be transparent).
|
|
//
|
|
// The S3's hardware pointer is defined by an interleaved pattern
|
|
// of AND words and XOR words. So a totally transparent pointer
|
|
// starts off with the word 0xffff, followed by the word 0x0000,
|
|
// followed by 0xffff, etc.. Since we're a little endian system,
|
|
// this is simply the repeating dword '0x0000ffff'.
|
|
//
|
|
// The compiler is nice enough to optimize this into a REP STOSD
|
|
// for us:
|
|
|
|
*pul++ = 0x0000ffff;
|
|
}
|
|
|
|
// Now we're going to take the requested pointer AND masks and XOR
|
|
// masks and combine them into our work buffer, being careful of
|
|
// the edges so that we don't disturb the transparency when the
|
|
// requested pointer size is not a multiple of 16.
|
|
//
|
|
// 'psoMsk' is actually cy * 2 scans high; the first 'cy' scans
|
|
// define the AND mask. So we start with that:
|
|
|
|
pjSrcScan = psoMsk->pvScan0;
|
|
lSrcDelta = psoMsk->lDelta;
|
|
pjDstScan = &ajBuf[0]; // Start with first AND word
|
|
lDstDelta = HW_POINTER_DIMENSION / 4;// Every 8 pels is one AND/XOR word
|
|
|
|
cwWhole = cx / 16; // Each word accounts for 16 pels
|
|
|
|
for (i = cy; i != 0; i--)
|
|
{
|
|
pwSrc = (WORD*) pjSrcScan;
|
|
pwDst = (WORD*) pjDstScan;
|
|
|
|
for (j = cwWhole; j != 0; j--)
|
|
{
|
|
*pwDst = *pwSrc;
|
|
pwSrc += 1; // Go to next word in source mask
|
|
pwDst += 2; // Skip over the XOR word in the dest mask
|
|
}
|
|
|
|
pjSrcScan += lSrcDelta;
|
|
pjDstScan += lDstDelta;
|
|
}
|
|
|
|
// Now handle the XOR mask:
|
|
|
|
pjDstScan = &ajBuf[2]; // Start with first XOR word
|
|
for (i = cy; i != 0; i--)
|
|
{
|
|
pwSrc = (WORD*) pjSrcScan;
|
|
pwDst = (WORD*) pjDstScan;
|
|
|
|
for (j = cwWhole; j != 0; j--)
|
|
{
|
|
*pwDst = *pwSrc;
|
|
pwSrc += 1; // Go to next word in source mask
|
|
pwDst += 2; // Skip over the AND word in the dest mask
|
|
}
|
|
|
|
pjSrcScan += lSrcDelta;
|
|
pjDstScan += lDstDelta;
|
|
}
|
|
|
|
// Okay, I admit it -- I'm wildly inconsistent here. I pass
|
|
// absolute (x, y) coordinates to pfnSetPointerShape, but pass
|
|
// relative (x, y) coordinates to vSetPointerShapeS3. I would
|
|
// clean this all up, but we're too close to shipping. LATER!
|
|
|
|
if (ppdev->flCaps & CAPS_DAC_POINTER)
|
|
{
|
|
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
|
|
|
|
bAccept = ppdev->pfnSetPointerShape(ppdev, ppdev->pvPointerData, x, y,
|
|
xHot, yHot, cx, cy, &ajBuf[0]);
|
|
|
|
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
|
|
|
|
ppdev->bHwPointerActive = (x != -1);
|
|
|
|
if (!bAccept)
|
|
goto HideAndDecline;
|
|
}
|
|
else
|
|
{
|
|
vSetPointerShapeS3(pso, x, y, xHot, yHot, &ajBuf[0], fl);
|
|
}
|
|
|
|
// Since it's a hardware pointer, GDI doesn't have to worry about
|
|
// overwriting the pointer on drawing operations (meaning that it
|
|
// doesn't have to exclude the pointer), so we return 'NOEXCLUDE'.
|
|
// Since we're returning 'NOEXCLUDE', we also don't have to update
|
|
// the 'prcl' that GDI passed us.
|
|
|
|
return(SPS_ACCEPT_NOEXCLUDE);
|
|
|
|
HideAndDecline:
|
|
|
|
// Since we're declining the new pointer, GDI will simulate it via
|
|
// DrvCopyBits calls. So we should really hide the old hardware
|
|
// pointer if it's visible. We can get DrvMovePointer to do this
|
|
// for us:
|
|
|
|
DrvMovePointer(pso, -1, -1, NULL);
|
|
|
|
return(SPS_DECLINE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vDisablePointer
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vDisablePointer(
|
|
PDEV* ppdev)
|
|
{
|
|
// Nothing to do, really
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vAssertModePointer
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vAssertModePointer(
|
|
PDEV* ppdev,
|
|
BOOL bEnable)
|
|
{
|
|
ULONG* pulDst;
|
|
LONG i;
|
|
LONG lPointerShape;
|
|
|
|
// We will turn any hardware pointer -- either in the S3 or in the
|
|
// DAC -- off to begin with:
|
|
|
|
ppdev->bHwPointerActive = FALSE;
|
|
|
|
if (ppdev->flCaps & CAPS_SW_POINTER)
|
|
{
|
|
// With a software pointer, we don't have to do anything.
|
|
}
|
|
else if (ppdev->flCaps & CAPS_DAC_POINTER)
|
|
{
|
|
// Hide the DAC pointer:
|
|
|
|
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
|
|
|
|
// hide the pointer by moving offscreen
|
|
|
|
ppdev->pfnShowPointer(ppdev, ppdev->pvPointerData, FALSE);
|
|
|
|
// but enable the pointer registers
|
|
|
|
ppdev->pfnEnablePointer(ppdev, ppdev->pvPointerData, TRUE);
|
|
|
|
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
|
|
}
|
|
else
|
|
{
|
|
// We're using the built-in hardware pointer:
|
|
|
|
if (bEnable)
|
|
{
|
|
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
|
|
|
|
ppdev->cPointerShift = 0;
|
|
|
|
if (ppdev->iBitmapFormat > BMF_8BPP)
|
|
{
|
|
// Initializing the pointer colours is a bit different
|
|
// for high-colour modes:
|
|
|
|
if (ppdev->flCaps & CAPS_SCALE_POINTER)
|
|
{
|
|
ppdev->cPointerShift = 1;
|
|
ppdev->ulHwGraphicsCursorModeRegister_45 |= (0x4 << 8);
|
|
}
|
|
}
|
|
|
|
// We download an invisible pointer shape because we're about
|
|
// to enable the hardware pointer, but we still want the
|
|
// pointer hidden until we get the first DrvSetPointerShape
|
|
// call:
|
|
|
|
ppdev->pfnBankSelectMode(ppdev, ppdev->pvBankData, BANK_ON);
|
|
|
|
ppdev->pfnBankMap(ppdev, ppdev->pvBankData, ppdev->iPointerBank);
|
|
|
|
pulDst = (ULONG*) ppdev->pvPointerShape;
|
|
|
|
if (DIRECT_ACCESS(ppdev))
|
|
{
|
|
for (i = HW_POINTER_TOTAL_SIZE / sizeof(ULONG); i != 0; i--)
|
|
{
|
|
*pulDst++ = 0x0000ffff;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = HW_POINTER_TOTAL_SIZE / sizeof(ULONG); i != 0; i--)
|
|
{
|
|
WRITE_REGISTER_ULONG(pulDst, 0x0000ffff);
|
|
pulDst++;
|
|
}
|
|
}
|
|
|
|
ppdev->pfnBankSelectMode(ppdev, ppdev->pvBankData, BANK_OFF);
|
|
|
|
// Point the S3 to where we're storing the pointer shape.
|
|
// The location is specified as a multiple of 1024:
|
|
|
|
lPointerShape = ppdev->cjPointerOffset / 1024;
|
|
|
|
OUTPW(ppdev->pjIoBase, CRTC_INDEX, CR4C | ((lPointerShape >> 8) << 8));
|
|
OUTPW(ppdev->pjIoBase, CRTC_INDEX, CR4D | ((lPointerShape & 0xff) << 8));
|
|
|
|
// Now hide it by moving it off-screen:
|
|
|
|
vShowPointerS3(ppdev, FALSE);
|
|
|
|
// Enable the hardware pointer. As per the 8/31/93 Design
|
|
// Alert from S3 Incorporated, there's a goofy bug in all
|
|
// S3 chips up to the 928 where writing to this register
|
|
// at the same time as a horizontal sync may cause the
|
|
// chip to crash. So we wait for the vertical sync to be safe.
|
|
//
|
|
// Note that since we're a preemptive multitasking
|
|
// operating system, the following code is not guaranteed
|
|
// to be safe. To do that, we would have to put this in
|
|
// the miniport, where we could disable all interrupts while
|
|
// we wait for the vertical sync.
|
|
//
|
|
// However, this is only ever executed once at initialization
|
|
// and every time full-screen is executed, so I would expect
|
|
// the chances of there still being a problem to be extremely
|
|
// small:
|
|
|
|
while (INP(ppdev->pjIoBase, STATUS_1) & VBLANK_ACTIVE)
|
|
; // Wait for bit 3 to become 0
|
|
while (!(INP(ppdev->pjIoBase, STATUS_1) & VBLANK_ACTIVE))
|
|
; // Wait for bit 3 to become 1
|
|
|
|
OUTPW(ppdev->pjIoBase, CRTC_INDEX,
|
|
ppdev->ulHwGraphicsCursorModeRegister_45 | (HGC_ENABLE << 8));
|
|
|
|
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL bEnablePointer
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bEnablePointer(
|
|
PDEV* ppdev)
|
|
{
|
|
RECTL rclDraw;
|
|
RECTL rclBank;
|
|
LONG iBank;
|
|
LONG cjOffset;
|
|
LONG cjOffsetInBank;
|
|
|
|
if (ppdev->flCaps & CAPS_SW_POINTER)
|
|
{
|
|
// With a software pointer, we don't have to do anything.
|
|
}
|
|
else if (ppdev->flCaps & CAPS_DAC_POINTER)
|
|
{
|
|
// Initialize the DAC pointer:
|
|
|
|
if (ppdev->flCaps & CAPS_BT485_POINTER)
|
|
{
|
|
ppdev->pfnShowPointer = vShowPointerBt485;
|
|
ppdev->pfnMovePointer = vMovePointerBt485;
|
|
ppdev->pfnSetPointerShape = bSetPointerShapeBt485;
|
|
ppdev->pfnEnablePointer = vEnablePointerBt485;
|
|
}
|
|
else
|
|
{
|
|
ASSERTDD(ppdev->flCaps & CAPS_TI025_POINTER,
|
|
"A new DAC type was added?");
|
|
|
|
ppdev->pfnShowPointer = vShowPointerTi025;
|
|
ppdev->pfnMovePointer = vMovePointerTi025;
|
|
ppdev->pfnSetPointerShape = bSetPointerShapeTi025;
|
|
ppdev->pfnEnablePointer = vEnablePointerTi025;
|
|
}
|
|
|
|
ppdev->pvPointerData = &ppdev->ajPointerData[0];
|
|
|
|
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
|
|
|
|
ppdev->pfnEnablePointer(ppdev, ppdev->pvPointerData, TRUE);
|
|
|
|
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
|
|
}
|
|
else
|
|
{
|
|
// Enable the S3 hardware pointer.
|
|
|
|
// We're going to assume that the pointer shape doesn't span
|
|
// more than one bank. We have to figure out what bank that
|
|
// will be, and so we call 'pfnBankCompute' with the start
|
|
// point:
|
|
|
|
rclDraw.left = rclDraw.right = ppdev->xPointerShape;
|
|
rclDraw.top = rclDraw.bottom = ppdev->yPointerShape;
|
|
|
|
ppdev->pfnBankCompute(ppdev, &rclDraw, &rclBank, &cjOffset, &iBank);
|
|
|
|
cjOffsetInBank = ppdev->cjPointerOffset - cjOffset;
|
|
|
|
ASSERTDD(cjOffsetInBank + HW_POINTER_TOTAL_SIZE <= ppdev->cjBank,
|
|
"SetPointerShape assumes pointer shape doesn't span banks");
|
|
|
|
// When bank 'iPointerBank' is mapped in, 'pvPointerShape' is the
|
|
// actual pointer to be the beginning of the pointer shape bits
|
|
// in off-screen memory:
|
|
|
|
ppdev->pvPointerShape = ppdev->pjScreen + cjOffsetInBank;
|
|
ppdev->iPointerBank = iBank;
|
|
|
|
// Get a copy of the current register '45' state, so that whenever
|
|
// we enable or disable the S3 hardware pointer, we don't have to
|
|
// do a read-modify-write on this register:
|
|
|
|
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
|
|
|
|
OUTP(ppdev->pjIoBase, CRTC_INDEX, HGC_MODE);
|
|
ppdev->ulHwGraphicsCursorModeRegister_45
|
|
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | HGC_MODE) & ~(HGC_ENABLE << 8);
|
|
|
|
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
|
|
}
|
|
|
|
// Actually turn on the pointer:
|
|
|
|
vAssertModePointer(ppdev, TRUE);
|
|
|
|
DISPDBG((5, "Passed bEnablePointer"));
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* Sets the new pointer shape.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
|
|
ULONG NewMmIoSetPointerShape(
|
|
PDEV* ppdev,
|
|
SURFOBJ* psoMsk,
|
|
SURFOBJ* psoColor,
|
|
XLATEOBJ* pxlo,
|
|
LONG xHot,
|
|
LONG yHot,
|
|
LONG x,
|
|
LONG y,
|
|
RECTL* prcl,
|
|
FLONG fl,
|
|
BYTE* pBuf)
|
|
{
|
|
ULONG cx;
|
|
ULONG cy;
|
|
LONG i;
|
|
LONG j;
|
|
BYTE* pjSrcScan;
|
|
BYTE* pjDstScan;
|
|
LONG lSrcDelta;
|
|
LONG lDstDelta;
|
|
WORD* pwSrc;
|
|
WORD* pwDst;
|
|
BYTE* pbSrc;
|
|
BYTE* pbDst;
|
|
|
|
ULONG* pulDst;
|
|
ULONG* pulSrc;
|
|
|
|
LONG cxWhole;
|
|
LONG xHotWordBnd;
|
|
|
|
ULONG ulTransp = 0xFFFF0000L;
|
|
ULONG ulData, ulPreviousData;
|
|
|
|
UCHAR ucTemp;
|
|
|
|
|
|
|
|
// We're not going to handle any colour pointers, pointers that
|
|
// are larger than our hardware allows, or flags that we don't
|
|
// understand.
|
|
//
|
|
// (Note that the spec says we should decline any flags we don't
|
|
// understand, but we'll actually be declining if we don't see
|
|
// the only flag we *do* understand...)
|
|
//
|
|
// Our old documentation says that 'psoMsk' may be NULL, which means
|
|
// that the pointer is transparent. Well, trust me, that's wrong.
|
|
// I've checked GDI's code, and it will never pass us a NULL psoMsk:
|
|
|
|
cx = psoMsk->sizlBitmap.cx; // Note that 'sizlBitmap.cy' accounts
|
|
cy = psoMsk->sizlBitmap.cy >> 1; // for the double height due to the
|
|
// inclusion of both the AND masks
|
|
// and the XOR masks. For now, we're
|
|
// only interested in the true
|
|
// pointer dimensions, so we divide
|
|
// by 2.
|
|
|
|
|
|
//
|
|
// 'psoMsk' is actually cy * 2 scans high; the first 'cy' scans
|
|
// define the AND mask. So we start with that:
|
|
|
|
pjSrcScan = psoMsk->pvScan0;
|
|
lSrcDelta = psoMsk->lDelta;
|
|
lDstDelta = HW_POINTER_DIMENSION / 4; // Every 8 pels is one AND/XOR word
|
|
|
|
cxWhole = cx / 16; // Each word accounts for 16 pels
|
|
|
|
// caculating pointer checksum whether update the pointer or not
|
|
pulSrc = (ULONG*) pjSrcScan;
|
|
ulData = 0L;
|
|
|
|
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
|
|
|
|
if (!(fl & SPS_ANIMATEUPDATE))
|
|
{
|
|
OUTPW(ppdev->pjIoBase, CRTC_INDEX, HGC_DY | (HW_POINTER_HIDE << 8));
|
|
}
|
|
|
|
if(x >= 0)
|
|
{
|
|
vMovePointerS3(ppdev, x, y);
|
|
}
|
|
|
|
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
|
|
|
|
|
|
// Now we're going to take the requested pointer AND masks and XOR
|
|
// masks and combine them into our work buffer, being careful of
|
|
// the edges so that we don't disturb the transparency when the
|
|
// requested pointer size is not a multiple of 16.
|
|
|
|
|
|
pulDst = (ULONG*) pBuf;
|
|
|
|
for (i = 0; i < HW_POINTER_TOTAL_SIZE / sizeof(ULONG); i++)
|
|
{
|
|
// Here we initialize the entire pointer work buffer to be
|
|
// transparent (the S3 has no means of specifying a pointer size
|
|
// other than 64 x 64 -- so if we're asked to draw a 32 x 32
|
|
// pointer, we want the unused portion to be transparent).
|
|
//
|
|
// The S3's hardware pointer is defined by an interleaved pattern
|
|
// of AND words and XOR words. So a totally transparent pointer
|
|
// starts off with the word 0xffff, followed by the word 0x0000,
|
|
// followed by 0xffff, etc.. Since we're a little endian system,
|
|
// this is simply the repeating dword '0x0000ffff'.
|
|
//
|
|
// The compiler is nice enough to optimize this into a REP STOSD
|
|
// for us:
|
|
|
|
*pulDst++ = 0x0000ffff;
|
|
}
|
|
|
|
// ekl - take care word bnd.
|
|
// Start with first AND word
|
|
pjDstScan = (BYTE *) pBuf;
|
|
|
|
pjDstScan += ((HW_POINTER_DIMENSION / 2 - yHot) * lDstDelta +
|
|
(HW_POINTER_DIMENSION / 2 - ((xHot+15) & 0xFFFFFFF0L)) / 4);
|
|
|
|
cxWhole = cx / 16; // Each word accounts for 16 pels
|
|
|
|
|
|
xHotWordBnd = xHot % 16;
|
|
|
|
if(xHotWordBnd)
|
|
{
|
|
ulTransp >>= (16 - xHotWordBnd);
|
|
cxWhole *= 2;
|
|
|
|
for (i = cy; i != 0; i--)
|
|
{
|
|
pbSrc = pjSrcScan;
|
|
pbDst = pjDstScan;
|
|
|
|
ulPreviousData = ulTransp << 16;
|
|
|
|
for (j = 0; j < cxWhole; j++, pbSrc++)
|
|
{
|
|
ulData = (ULONG) (*pbSrc);
|
|
ulData <<= (8 + xHotWordBnd);
|
|
ulData |= ulPreviousData;
|
|
|
|
ucTemp = (UCHAR)(ulData >> 24);
|
|
*pbDst = ucTemp;
|
|
|
|
pbDst += (j % 2 ? 3 : 1);
|
|
|
|
// next byte
|
|
ulData <<= 8;
|
|
ulPreviousData = ulData;
|
|
|
|
}
|
|
|
|
// last word
|
|
ulData |= ulTransp;
|
|
ucTemp = (UCHAR)(ulData >> 24);
|
|
*pbDst = ucTemp;
|
|
|
|
pbDst += (2*j + 1);
|
|
ucTemp = (UCHAR)(ulData >> 16);
|
|
*pbDst = ucTemp;
|
|
|
|
pjSrcScan += lSrcDelta;
|
|
pjDstScan += lDstDelta;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
for (i = cy; i != 0; i--)
|
|
{
|
|
pwSrc = (WORD*) pjSrcScan;
|
|
pwDst = (WORD*) pjDstScan;
|
|
|
|
for (j = cxWhole; j != 0; j--)
|
|
{
|
|
*pwDst = *pwSrc;
|
|
pwSrc += 1; // Go to next word in source mask
|
|
pwDst += 2; // Skip over the XOR word in the dest mask
|
|
}
|
|
|
|
pjSrcScan += lSrcDelta;
|
|
pjDstScan += lDstDelta;
|
|
}
|
|
}
|
|
|
|
|
|
// Now handle the XOR mask:
|
|
|
|
pjDstScan = (BYTE *) pBuf;
|
|
pjDstScan += (2 + (HW_POINTER_DIMENSION / 2 - yHot) * lDstDelta +
|
|
(HW_POINTER_DIMENSION / 2 - ((xHot+15) & 0xFFFFFFF0L)) / 4);
|
|
|
|
if(xHotWordBnd)
|
|
{
|
|
for (i = cy; i != 0; i--)
|
|
{
|
|
pbSrc = pjSrcScan;
|
|
pbDst = pjDstScan;
|
|
|
|
ulPreviousData = 0;
|
|
|
|
for (j = 0; j < cxWhole; j++, pbSrc++)
|
|
{
|
|
ulData = (ULONG) (*pbSrc);
|
|
ulData <<= (8 + xHotWordBnd);
|
|
ulData |= ulPreviousData;
|
|
|
|
ucTemp = (UCHAR)(ulData >> 24);
|
|
*pbDst = ucTemp;
|
|
|
|
pbDst += (j % 2 ? 3 : 1);
|
|
|
|
// Next byte
|
|
ulData <<= 8;
|
|
ulPreviousData = ulData;
|
|
|
|
}
|
|
|
|
ucTemp = (UCHAR)(ulData >> 24);
|
|
*pbDst = ucTemp;
|
|
|
|
pbDst += (2*j + 1);
|
|
ucTemp = (UCHAR)(ulData >> 16);
|
|
*pbDst = ucTemp;
|
|
|
|
pjSrcScan += lSrcDelta;
|
|
pjDstScan += lDstDelta;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
for (i = cy; i != 0; i--)
|
|
{
|
|
pwSrc = (WORD*) pjSrcScan;
|
|
pwDst = (WORD*) pjDstScan;
|
|
|
|
for (j = cxWhole; j != 0; j--)
|
|
{
|
|
*pwDst = *pwSrc;
|
|
pwSrc += 1; // Go to next word in source mask
|
|
pwDst += 2; // Skip over the AND word in the dest mask
|
|
}
|
|
|
|
pjSrcScan += lSrcDelta;
|
|
pjDstScan += lDstDelta;
|
|
}
|
|
}
|
|
|
|
|
|
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
|
|
|
|
pulSrc = (ULONG*) pBuf;
|
|
pulDst = (ULONG*) ppdev->pvPointerShape;
|
|
|
|
if (DIRECT_ACCESS(ppdev))
|
|
{
|
|
for (i = HW_POINTER_TOTAL_SIZE / sizeof(ULONG); i != 0; i--)
|
|
{
|
|
*pulDst++ = *pulSrc++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = HW_POINTER_TOTAL_SIZE / sizeof(ULONG); i != 0; i--)
|
|
{
|
|
WRITE_REGISTER_ULONG(pulDst, *pulSrc);
|
|
pulSrc++;
|
|
pulDst++;
|
|
}
|
|
}
|
|
|
|
if(x >= 0)
|
|
{
|
|
if (!ppdev->bHwPointerActive) {
|
|
ppdev->bHwPointerActive = TRUE;
|
|
vShowPointerS3(ppdev, TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ppdev->bHwPointerActive) {
|
|
ppdev->bHwPointerActive = FALSE;
|
|
vShowPointerS3(ppdev, FALSE);
|
|
}
|
|
}
|
|
|
|
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
|
|
|
|
// fix the hot spot at the center of the HW cursor
|
|
ppdev->xPointerHot = HW_POINTER_DIMENSION / 2;
|
|
ppdev->yPointerHot = HW_POINTER_DIMENSION / 2;
|
|
|
|
|
|
return(SPS_ACCEPT_NOEXCLUDE);
|
|
|
|
}
|