Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1161 lines
42 KiB

  1. /******************************Module*Header*******************************\
  2. *
  3. * *******************
  4. * * GDI SAMPLE CODE *
  5. * *******************
  6. *
  7. * Module Name: bltmm.c
  8. *
  9. * Contains the low-level memory-mapped IO blt functions. This module
  10. * mirrors 'bltio.c'.
  11. *
  12. * Hopefully, if you're basing your display driver on this code, to
  13. * support all of DrvBitBlt and DrvCopyBits, you'll only have to implement
  14. * the following routines. You shouldn't have to modify much in
  15. * 'bitblt.c'. I've tried to make these routines as few, modular, simple,
  16. * and efficient as I could, while still accelerating as many calls as
  17. * possible that would be cost-effective in terms of performance wins
  18. * versus size and effort.
  19. *
  20. * Note: In the following, 'relative' coordinates refers to coordinates
  21. * that haven't yet had the offscreen bitmap (DFB) offset applied.
  22. * 'Absolute' coordinates have had the offset applied. For example,
  23. * we may be told to blt to (1, 1) of the bitmap, but the bitmap may
  24. * be sitting in offscreen memory starting at coordinate (0, 768) --
  25. * (1, 1) would be the 'relative' start coordinate, and (1, 769)
  26. * would be the 'absolute' start coordinate'.
  27. *
  28. * Copyright (c) 1992-1998 Microsoft Corporation
  29. *
  30. \**************************************************************************/
  31. #include "precomp.h"
  32. /******************************Public*Routine******************************\
  33. * VOID vMmImageTransferMm16
  34. *
  35. * Low-level routine for transferring a bitmap image via the data transfer
  36. * register using 16 bit writes and entirely memory-mapped I/O.
  37. *
  38. * NOTE: Upon entry, there must be 1 guaranteed free empty FIFO!
  39. *
  40. \**************************************************************************/
  41. VOID vMmImageTransferMm16( // Type FNIMAGETRANSFER
  42. PDEV* ppdev,
  43. BYTE* pjSrc, // Source pointer
  44. LONG lDelta, // Delta from start of scan to start of next
  45. LONG cjSrc, // Number of bytes to be output on every scan
  46. LONG cScans, // Number of scans
  47. ULONG ulCmd) // Accelerator command - shouldn't include bus size
  48. {
  49. BYTE* pjMmBase;
  50. LONG cwSrc;
  51. ASSERTDD(cScans > 0, "Can't handle non-positive count of scans");
  52. ASSERTDD((ulCmd & (BUS_SIZE_8 | BUS_SIZE_16 | BUS_SIZE_32)) == 0,
  53. "Shouldn't specify bus size in command -- we handle that");
  54. IO_GP_WAIT(ppdev);
  55. pjMmBase = ppdev->pjMmBase;
  56. MM_CMD(ppdev, pjMmBase, ulCmd | BUS_SIZE_16);
  57. CHECK_DATA_READY(ppdev);
  58. cwSrc = (cjSrc) >> 1; // Floor
  59. if (cjSrc & 1)
  60. {
  61. do {
  62. if (cwSrc > 0)
  63. {
  64. MM_TRANSFER_WORD(ppdev, pjMmBase, pjSrc, cwSrc);
  65. }
  66. // Make sure we do only a byte read of the last odd byte
  67. // in the scan so that we'll never read past the end of
  68. // the bitmap:
  69. MM_PIX_TRANS(ppdev, pjMmBase, *(pjSrc + cjSrc - 1));
  70. pjSrc += lDelta;
  71. } while (--cScans != 0);
  72. }
  73. else
  74. {
  75. do {
  76. MM_TRANSFER_WORD(ppdev, pjMmBase, pjSrc, cwSrc);
  77. pjSrc += lDelta;
  78. } while (--cScans != 0);
  79. }
  80. CHECK_DATA_COMPLETE(ppdev);
  81. }
  82. /******************************Public*Routine******************************\
  83. * VOID vMmImageTransferMm32
  84. *
  85. * Low-level routine for transferring a bitmap image via the data transfer
  86. * register using 32 bit writes and entirely memory-mapped I/O.
  87. *
  88. * NOTE: Upon entry, there must be 1 guaranteed free empty FIFO!
  89. *
  90. \**************************************************************************/
  91. VOID vMmImageTransferMm32( // Type FNIMAGETRANSFER
  92. PDEV* ppdev,
  93. BYTE* pjSrc, // Source pointer
  94. LONG lDelta, // Delta from start of scan to start of next
  95. LONG cjSrc, // Number of bytes to be output on every scan
  96. LONG cScans, // Number of scans
  97. ULONG ulCmd) // Accelerator command - shouldn't include bus size
  98. {
  99. BYTE* pjMmBase;
  100. LONG cdSrc;
  101. LONG cjEnd;
  102. ULONG d;
  103. ASSERTDD(cScans > 0, "Can't handle non-positive count of scans");
  104. ASSERTDD((ulCmd & (BUS_SIZE_8 | BUS_SIZE_16 | BUS_SIZE_32)) == 0,
  105. "Shouldn't specify bus size in command -- we handle that");
  106. IO_GP_WAIT(ppdev);
  107. pjMmBase = ppdev->pjMmBase;
  108. MM_CMD(ppdev, pjMmBase, ulCmd | BUS_SIZE_32);
  109. CHECK_DATA_READY(ppdev);
  110. cdSrc = cjSrc >> 2;
  111. cjEnd = cdSrc << 2;
  112. switch (cjSrc & 3)
  113. {
  114. case 3:
  115. do {
  116. if (cdSrc > 0)
  117. MM_TRANSFER_DWORD(ppdev, pjMmBase, pjSrc, cdSrc);
  118. d = (ULONG) (*(pjSrc + cjEnd)) |
  119. (*(pjSrc + cjEnd + 1) << 8) |
  120. (*(pjSrc + cjEnd + 2) << 16);
  121. MM_TRANSFER_DWORD(ppdev, pjMmBase, &d, 1);
  122. pjSrc += lDelta;
  123. } while (--cScans != 0);
  124. break;
  125. case 2:
  126. do {
  127. if (cdSrc > 0)
  128. MM_TRANSFER_DWORD(ppdev, pjMmBase, pjSrc, cdSrc);
  129. d = (ULONG) (*(pjSrc + cjEnd)) |
  130. (*(pjSrc + cjEnd + 1) << 8);
  131. MM_TRANSFER_DWORD(ppdev, pjMmBase, &d, 1);
  132. pjSrc += lDelta;
  133. } while (--cScans != 0);
  134. break;
  135. case 1:
  136. do {
  137. if (cdSrc > 0)
  138. MM_TRANSFER_DWORD(ppdev, pjMmBase, pjSrc, cdSrc);
  139. d = (ULONG) (*(pjSrc + cjEnd));
  140. MM_TRANSFER_DWORD(ppdev, pjMmBase, &d, 1);
  141. pjSrc += lDelta;
  142. } while (--cScans != 0);
  143. break;
  144. case 0:
  145. do {
  146. MM_TRANSFER_DWORD(ppdev, pjMmBase, pjSrc, cdSrc);
  147. pjSrc += lDelta;
  148. } while (--cScans != 0);
  149. break;
  150. }
  151. CHECK_DATA_COMPLETE(ppdev);
  152. }
  153. /******************************Public*Routine******************************\
  154. * VOID vMmFillSolid
  155. *
  156. * Fills a list of rectangles with a solid colour.
  157. *
  158. \**************************************************************************/
  159. VOID vMmFillSolid( // Type FNFILL
  160. PDEV* ppdev,
  161. LONG c, // Can't be zero
  162. RECTL* prcl, // List of rectangles to be filled, in relative
  163. // coordinates
  164. ULONG rop4, // rop4
  165. RBRUSH_COLOR rbc, // Drawing colour is rbc.iSolidColor
  166. POINTL* pptlBrush) // Not used
  167. {
  168. BYTE* pjMmBase = ppdev->pjMmBase;
  169. ULONG ulHwForeMix;
  170. ASSERTDD(c > 0, "Can't handle zero rectangles");
  171. ulHwForeMix = gaulHwMixFromRop2[(rop4 >> 2) & 0xf];
  172. // It's quite likely that we've just been called from GDI, so it's
  173. // even more likely that the accelerator's graphics engine has been
  174. // sitting around idle. Rather than doing a FIFO_WAIT(3) here and
  175. // then a FIFO_WAIT(5) before outputing the actual rectangle,
  176. // we can avoid an 'in' (which can be quite expensive, depending on
  177. // the card) by doing a single FIFO_WAIT(8) right off the bat:
  178. IO_FIFO_WAIT(ppdev, 8);
  179. MM_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
  180. MM_FRGD_MIX(ppdev, pjMmBase, FOREGROUND_COLOR | ulHwForeMix);
  181. MM_FRGD_COLOR(ppdev, pjMmBase, rbc.iSolidColor);
  182. while(TRUE)
  183. {
  184. MM_CUR_X(ppdev, pjMmBase, prcl->left);
  185. MM_CUR_Y(ppdev, pjMmBase, prcl->top);
  186. MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, prcl->right - prcl->left - 1);
  187. MM_MIN_AXIS_PCNT(ppdev, pjMmBase, prcl->bottom - prcl->top - 1);
  188. MM_CMD(ppdev, pjMmBase, RECTANGLE_FILL | DRAWING_DIR_TBLRXM |
  189. DRAW | DIR_TYPE_XY |
  190. LAST_PIXEL_ON | MULTIPLE_PIXELS |
  191. WRITE);
  192. if (--c == 0)
  193. return;
  194. prcl++;
  195. IO_FIFO_WAIT(ppdev, 5);
  196. }
  197. }
  198. /******************************Public*Routine******************************\
  199. * VOID vMmFastPatRealize
  200. *
  201. * This routine transfers an 8x8 pattern to off-screen display memory,
  202. * so that it can be used by the S3 pattern hardware.
  203. *
  204. \**************************************************************************/
  205. VOID vMmFastPatRealize( // Type FNFASTPATREALIZE
  206. PDEV* ppdev,
  207. RBRUSH* prb, // Points to brush realization structure
  208. POINTL* pptlBrush, // Brush origin for aligning realization
  209. BOOL bTransparent) // FALSE for normal patterns; TRUE for
  210. // patterns with a mask when the background
  211. // mix is LEAVE_ALONE.
  212. {
  213. BRUSHENTRY* pbe;
  214. LONG iBrushCache;
  215. LONG x;
  216. LONG y;
  217. LONG i;
  218. LONG xShift;
  219. LONG yShift;
  220. BYTE* pjSrc;
  221. BYTE* pjDst;
  222. LONG cjLeft;
  223. LONG cjRight;
  224. BYTE* pjPattern;
  225. LONG cwPattern;
  226. ULONG aulBrush[TOTAL_BRUSH_SIZE];
  227. // Temporary buffer for aligning brush. Declared
  228. // as an array of ULONGs to get proper dword
  229. // alignment. Also leaves room for brushes that
  230. // are up to 32bpp. Note: this takes up 1/4k!
  231. BYTE* pjMmBase = ppdev->pjMmBase;
  232. ASSERTDD(!(bTransparent && ppdev->iBitmapFormat == BMF_24BPP),
  233. "s3 diamond 968 at 24bpp doesn't support transparent FastPatRealize");
  234. pbe = prb->pbe;
  235. if ((pbe == NULL) || (pbe->prbVerify != prb))
  236. {
  237. // We have to allocate a new off-screen cache brush entry for
  238. // the brush:
  239. iBrushCache = ppdev->iBrushCache;
  240. pbe = &ppdev->abe[iBrushCache];
  241. iBrushCache++;
  242. if (iBrushCache >= ppdev->cBrushCache)
  243. iBrushCache = 0;
  244. ppdev->iBrushCache = iBrushCache;
  245. // Update our links:
  246. pbe->prbVerify = prb;
  247. prb->pbe = pbe;
  248. }
  249. // Load some variables onto the stack, so that we don't have to keep
  250. // dereferencing their pointers:
  251. x = pbe->x;
  252. y = pbe->y;
  253. // Because we handle only 8x8 brushes, it is easy to compute the
  254. // number of pels by which we have to rotate the brush pattern
  255. // right and down. Note that if we were to handle arbitrary sized
  256. // patterns, this calculation would require a modulus operation.
  257. //
  258. // The brush is aligned in absolute coordinates, so we have to add
  259. // in the surface offset:
  260. xShift = pptlBrush->x + ppdev->xOffset;
  261. yShift = pptlBrush->y + ppdev->yOffset;
  262. prb->ptlBrushOrg.x = xShift; // We have to remember the alignment
  263. prb->ptlBrushOrg.y = yShift; // that we used for caching (we check
  264. // this when we go to see if a brush's
  265. // cache entry is still valid)
  266. xShift &= 7; // Rotate pattern 'xShift' pels right
  267. yShift &= 7; // Rotate pattern 'yShift' pels down
  268. prb->bTransparent = bTransparent;
  269. // I considered doing the colour expansion for 1bpp brushes in
  270. // software, but by letting the hardware do it, we don't have
  271. // to do as many OUTs to transfer the pattern.
  272. if (prb->fl & RBRUSH_2COLOR)
  273. {
  274. // We're going to do a colour-expansion ('across the plane')
  275. // bitblt of the 1bpp 8x8 pattern to the screen. But first
  276. // we'll align it properly by copying it to a temporary buffer
  277. // (which we'll conveniently pack word aligned so that we can do a
  278. // REP OUTSW...)
  279. pjSrc = (BYTE*) &prb->aulPattern[0]; // Copy from the start of the
  280. // brush buffer
  281. pjDst = (BYTE*) &aulBrush[0]; // Copy to our temp buffer
  282. pjDst += yShift * sizeof(WORD); // starting yShift rows down
  283. i = 8 - yShift; // for 8 - yShift rows
  284. do {
  285. *pjDst = (*pjSrc >> xShift) | (*pjSrc << (8 - xShift));
  286. pjDst += sizeof(WORD); // Destination is word packed
  287. pjSrc += sizeof(WORD); // Source is word aligned too
  288. } while (--i != 0);
  289. pjDst -= 8 * sizeof(WORD); // Move to the beginning of the source
  290. ASSERTDD(pjDst == (BYTE*) &aulBrush[0], "pjDst not back at start");
  291. for (; yShift != 0; yShift--)
  292. {
  293. *pjDst = (*pjSrc >> xShift) | (*pjSrc << (8 - xShift));
  294. pjDst += sizeof(WORD); // Destination is word packed
  295. pjSrc += sizeof(WORD); // Source is word aligned too
  296. }
  297. if (bTransparent)
  298. {
  299. IO_FIFO_WAIT(ppdev, 3);
  300. MM_PIX_CNTL(ppdev, pjMmBase, CPU_DATA);
  301. MM_FRGD_MIX(ppdev, pjMmBase, LOGICAL_1);
  302. MM_BKGD_MIX(ppdev, pjMmBase, LOGICAL_0);
  303. }
  304. else
  305. {
  306. IO_FIFO_WAIT(ppdev, 5);
  307. MM_PIX_CNTL(ppdev, pjMmBase, CPU_DATA);
  308. MM_FRGD_MIX(ppdev, pjMmBase, FOREGROUND_COLOR | OVERPAINT);
  309. MM_BKGD_MIX(ppdev, pjMmBase, BACKGROUND_COLOR | OVERPAINT);
  310. MM_FRGD_COLOR(ppdev, pjMmBase, prb->ulForeColor);
  311. MM_BKGD_COLOR(ppdev, pjMmBase, prb->ulBackColor);
  312. }
  313. IO_FIFO_WAIT(ppdev, 4);
  314. MM_ABS_CUR_X(ppdev, pjMmBase, x);
  315. MM_ABS_CUR_Y(ppdev, pjMmBase, y);
  316. MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, 7); // Brush is 8 wide
  317. MM_MIN_AXIS_PCNT(ppdev, pjMmBase, 7); // Brush is 8 high
  318. IO_GP_WAIT(ppdev);
  319. MM_CMD(ppdev, pjMmBase, RECTANGLE_FILL | BUS_SIZE_16 | WAIT |
  320. DRAWING_DIR_TBLRXM | DRAW | LAST_PIXEL_ON |
  321. MULTIPLE_PIXELS | WRITE | BYTE_SWAP);
  322. CHECK_DATA_READY(ppdev);
  323. pjPattern = (BYTE*) &aulBrush[0];
  324. MM_TRANSFER_WORD_ALIGNED(ppdev, pjMmBase, pjPattern, 8);
  325. // Each word transferred
  326. // comprises one row of the
  327. // pattern, and there are
  328. // 8 rows in the pattern
  329. CHECK_DATA_COMPLETE(ppdev);
  330. }
  331. else
  332. {
  333. ASSERTDD(!bTransparent,
  334. "Shouldn't have been asked for transparency with a non-1bpp brush");
  335. // We're going to do a straight ('through the plane') bitblt
  336. // of the Xbpp 8x8 pattern to the screen. But first we'll align
  337. // it properly by copying it to a temporary buffer:
  338. cjLeft = CONVERT_TO_BYTES(xShift, ppdev); // Number of bytes pattern
  339. // is shifted to the right
  340. cjRight = CONVERT_TO_BYTES(8, ppdev) - // Number of bytes pattern
  341. cjLeft; // is shifted to the left
  342. pjSrc = (BYTE*) &prb->aulPattern[0]; // Copy from brush buffer
  343. pjDst = (BYTE*) &aulBrush[0]; // Copy to our temp buffer
  344. pjDst += yShift * CONVERT_TO_BYTES(8, ppdev); // starting yShift rows
  345. i = 8 - yShift; // down for 8 - yShift rows
  346. do {
  347. RtlCopyMemory(pjDst + cjLeft, pjSrc, cjRight);
  348. RtlCopyMemory(pjDst, pjSrc + cjRight, cjLeft);
  349. pjDst += cjLeft + cjRight;
  350. pjSrc += cjLeft + cjRight;
  351. } while (--i != 0);
  352. pjDst = (BYTE*) &aulBrush[0]; // Move to the beginning of destination
  353. for (; yShift != 0; yShift--)
  354. {
  355. RtlCopyMemory(pjDst + cjLeft, pjSrc, cjRight);
  356. RtlCopyMemory(pjDst, pjSrc + cjRight, cjLeft);
  357. pjDst += cjLeft + cjRight;
  358. pjSrc += cjLeft + cjRight;
  359. }
  360. IO_FIFO_WAIT(ppdev, 6);
  361. MM_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
  362. MM_FRGD_MIX(ppdev, pjMmBase, SRC_CPU_DATA | OVERPAINT);
  363. MM_ABS_CUR_X(ppdev, pjMmBase, x);
  364. MM_ABS_CUR_Y(ppdev, pjMmBase, y);
  365. MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, 7); // Brush is 8 wide
  366. MM_MIN_AXIS_PCNT(ppdev, pjMmBase, 7); // Brush is 8 high
  367. IO_GP_WAIT(ppdev);
  368. MM_CMD(ppdev, pjMmBase, RECTANGLE_FILL | BUS_SIZE_16| WAIT |
  369. DRAWING_DIR_TBLRXM | DRAW | LAST_PIXEL_ON |
  370. SINGLE_PIXEL | WRITE | BYTE_SWAP);
  371. CHECK_DATA_READY(ppdev);
  372. pjPattern = (BYTE*) &aulBrush[0];
  373. cwPattern = CONVERT_TO_BYTES((TOTAL_BRUSH_SIZE / 2), ppdev);
  374. MM_TRANSFER_WORD_ALIGNED(ppdev, pjMmBase, pjPattern, cwPattern);
  375. CHECK_DATA_COMPLETE(ppdev);
  376. }
  377. }
  378. /******************************Public*Routine******************************\
  379. * VOID vMmFillPatFast
  380. *
  381. * This routine uses the S3 pattern hardware to draw a patterned list of
  382. * rectangles.
  383. *
  384. \**************************************************************************/
  385. VOID vMmFillPatFast( // Type FNFILL
  386. PDEV* ppdev,
  387. LONG c, // Can't be zero
  388. RECTL* prcl, // List of rectangles to be filled, in relative
  389. // coordinates
  390. ULONG rop4, // rop4
  391. RBRUSH_COLOR rbc, // rbc.prb points to brush realization structure
  392. POINTL* pptlBrush) // Pattern alignment
  393. {
  394. BOOL bTransparent;
  395. ULONG ulHwForeMix;
  396. BRUSHENTRY* pbe; // Pointer to brush entry data, which is used
  397. // for keeping track of the location and status
  398. // of the pattern bits cached in off-screen
  399. // memory
  400. BYTE* pjMmBase = ppdev->pjMmBase;
  401. ASSERTDD(c > 0, "Can't handle zero rectangles");
  402. ASSERTDD(ppdev->flCaps & CAPS_HW_PATTERNS,
  403. "Shouldn't use fast patterns when can't do hw patterns");
  404. bTransparent = (((rop4 >> 8) & 0xff) != (rop4 & 0xff));
  405. // The S3's pattern hardware requires that we keep an aligned copy
  406. // of the brush in off-screen memory. We have to update this
  407. // realization if any of the following are true:
  408. //
  409. // 1) The brush alignment has changed;
  410. // 2) The off-screen location we thought we had reserved for our
  411. // realization got overwritten by a different pattern;
  412. // 3) We had realized the pattern to do transparent hatches, but
  413. // we're now being asked to do an opaque pattern, or vice
  414. // versa (since we use different realizations for transparent
  415. // vs. opaque patterns).
  416. //
  417. // To handle the initial realization of a pattern, we're a little
  418. // tricky in order to save an 'if' in the following expression. In
  419. // DrvRealizeBrush, we set 'prb->ptlBrushOrg.x' to be 0x80000000 (a
  420. // very negative number), which is guaranteed not to equal 'pptlBrush->x
  421. // + ppdev->xOffset'. So our check for brush alignment will also
  422. // handle the initialization case (note that this check must occur
  423. // *before* dereferencing 'prb->pbe' because that pointer will be
  424. // NULL for a new pattern).
  425. if ((rbc.prb->ptlBrushOrg.x != pptlBrush->x + ppdev->xOffset) ||
  426. (rbc.prb->ptlBrushOrg.y != pptlBrush->y + ppdev->yOffset) ||
  427. (rbc.prb->pbe->prbVerify != rbc.prb) ||
  428. (rbc.prb->bTransparent != bTransparent))
  429. {
  430. vMmFastPatRealize(ppdev, rbc.prb, pptlBrush, bTransparent);
  431. }
  432. else if (ppdev->flCaps & CAPS_RE_REALIZE_PATTERN)
  433. {
  434. // The initial revs of the Vision chips have a bug where, if
  435. // we have not just drawn the pattern to off-screen memory,
  436. // we have to draw some sort of 1x8 rectangle before using
  437. // the pattern hardware (note that a LEAVE_ALONE rop will not
  438. // work).
  439. IO_FIFO_WAIT(ppdev, 7);
  440. MM_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
  441. MM_FRGD_MIX(ppdev, pjMmBase, SRC_DISPLAY_MEMORY | OVERPAINT);
  442. MM_ABS_CUR_X(ppdev, pjMmBase, ppdev->ptlReRealize.x);
  443. MM_ABS_CUR_Y(ppdev, pjMmBase, ppdev->ptlReRealize.y);
  444. MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, 0);
  445. MM_MIN_AXIS_PCNT(ppdev, pjMmBase, 7);
  446. MM_CMD(ppdev, pjMmBase, RECTANGLE_FILL | DRAWING_DIR_TBLRXM |
  447. DRAW | DIR_TYPE_XY |
  448. LAST_PIXEL_ON | MULTIPLE_PIXELS |
  449. WRITE);
  450. }
  451. ASSERTDD(rbc.prb->bTransparent == bTransparent,
  452. "Not realized with correct transparency");
  453. pbe = rbc.prb->pbe;
  454. ulHwForeMix = gaulHwMixFromRop2[(rop4 >> 2) & 0xf];
  455. if (!bTransparent)
  456. {
  457. IO_FIFO_WAIT(ppdev, 4);
  458. MM_ABS_CUR_X(ppdev, pjMmBase, pbe->x);
  459. MM_ABS_CUR_Y(ppdev, pjMmBase, pbe->y);
  460. MM_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
  461. MM_FRGD_MIX(ppdev, pjMmBase, SRC_DISPLAY_MEMORY | ulHwForeMix);
  462. }
  463. else
  464. {
  465. IO_FIFO_WAIT(ppdev, 7);
  466. MM_FRGD_COLOR(ppdev, pjMmBase, rbc.prb->ulForeColor);
  467. MM_RD_MASK(ppdev, pjMmBase, 1); // Pick a plane, any plane
  468. MM_ABS_CUR_X(ppdev, pjMmBase, pbe->x);
  469. MM_ABS_CUR_Y(ppdev, pjMmBase, pbe->y);
  470. MM_PIX_CNTL(ppdev, pjMmBase, DISPLAY_MEMORY);
  471. MM_FRGD_MIX(ppdev, pjMmBase, FOREGROUND_COLOR | ulHwForeMix);
  472. MM_BKGD_MIX(ppdev, pjMmBase, BACKGROUND_COLOR | LEAVE_ALONE);
  473. }
  474. do {
  475. IO_FIFO_WAIT(ppdev, 5);
  476. MM_DEST_X(ppdev, pjMmBase, prcl->left);
  477. MM_DEST_Y(ppdev, pjMmBase, prcl->top);
  478. MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, prcl->right - prcl->left - 1);
  479. MM_MIN_AXIS_PCNT(ppdev, pjMmBase, prcl->bottom - prcl->top - 1);
  480. MM_CMD(ppdev, pjMmBase, PATTERN_FILL | BYTE_SWAP | DRAWING_DIR_TBLRXM |
  481. DRAW | WRITE);
  482. prcl++;
  483. } while (--c != 0);
  484. }
  485. /******************************Public*Routine******************************\
  486. * VOID vMmXfer1bpp
  487. *
  488. * This routine colour expands a monochrome bitmap, possibly with different
  489. * Rop2's for the foreground and background. It will be called in the
  490. * following cases:
  491. *
  492. * 1) To colour-expand the monochrome text buffer for the vFastText routine.
  493. * 2) To blt a 1bpp source with a simple Rop2 between the source and
  494. * destination.
  495. * 3) To blt a true Rop3 when the source is a 1bpp bitmap that expands to
  496. * white and black, and the pattern is a solid colour.
  497. * 4) To handle a true Rop4 that works out to be two Rop2's between the
  498. * pattern and destination.
  499. *
  500. * Needless to say, making this routine fast can leverage a lot of
  501. * performance.
  502. *
  503. \**************************************************************************/
  504. VOID vMmXfer1bpp( // Type FNXFER
  505. PDEV* ppdev,
  506. LONG c, // Count of rectangles, can't be zero
  507. RECTL* prcl, // List of destination rectangles, in relative
  508. // coordinates
  509. ROP4 rop4, // rop4
  510. SURFOBJ* psoSrc, // Source surface
  511. POINTL* pptlSrc, // Original unclipped source point
  512. RECTL* prclDst, // Original unclipped destination rectangle
  513. XLATEOBJ* pxlo) // Translate that provides colour-expansion information
  514. {
  515. ULONG ulHwForeMix;
  516. ULONG ulHwBackMix;
  517. LONG dxSrc;
  518. LONG dySrc;
  519. LONG cx;
  520. LONG cy;
  521. LONG lSrcDelta;
  522. BYTE* pjSrcScan0;
  523. BYTE* pjSrc;
  524. LONG cjSrc;
  525. LONG xLeft;
  526. LONG yTop;
  527. LONG xBias;
  528. BYTE* pjMmBase = ppdev->pjMmBase;
  529. ASSERTDD(c > 0, "Can't handle zero rectangles");
  530. ASSERTDD(pptlSrc != NULL && psoSrc != NULL, "Can't have NULL sources");
  531. ASSERTDD(((((rop4 & 0xff00) >> 8) == (rop4 & 0xff)) || (rop4 == 0xaacc)),
  532. "Expect weird rops only when opaquing");
  533. // Note that only our text routine calls us with a '0xaacc' rop:
  534. ulHwForeMix = gaulHwMixFromRop2[rop4 & 0xf];
  535. ulHwBackMix = (rop4 != 0xaacc) ? ulHwForeMix : LEAVE_ALONE;
  536. IO_FIFO_WAIT(ppdev, 5);
  537. MM_PIX_CNTL(ppdev, pjMmBase, CPU_DATA);
  538. MM_FRGD_MIX(ppdev, pjMmBase, FOREGROUND_COLOR | ulHwForeMix);
  539. MM_BKGD_MIX(ppdev, pjMmBase, BACKGROUND_COLOR | ulHwBackMix);
  540. MM_FRGD_COLOR(ppdev, pjMmBase, pxlo->pulXlate[1]);
  541. MM_BKGD_COLOR(ppdev, pjMmBase, pxlo->pulXlate[0]);
  542. dxSrc = pptlSrc->x - prclDst->left;
  543. dySrc = pptlSrc->y - prclDst->top; // Add to destination to get source
  544. lSrcDelta = psoSrc->lDelta;
  545. pjSrcScan0 = psoSrc->pvScan0;
  546. do {
  547. IO_FIFO_WAIT(ppdev, 5);
  548. // We'll byte align to the source, but do word transfers
  549. // (implying that we may be doing unaligned reads from the
  550. // source). We do this because it may reduce the total
  551. // number of word outs/writes that we'll have to do to the
  552. // display:
  553. yTop = prcl->top;
  554. xLeft = prcl->left;
  555. xBias = (xLeft + dxSrc) & 7; // This is the byte-align bias
  556. if (xBias != 0)
  557. {
  558. // We could either align in software or use the hardware to do
  559. // it. We'll use the hardware; the cost we pay is the time spent
  560. // setting and resetting one scissors register:
  561. MM_SCISSORS_L(ppdev, pjMmBase, xLeft);
  562. xLeft -= xBias;
  563. }
  564. cx = prcl->right - xLeft;
  565. cy = prcl->bottom - yTop;
  566. MM_CUR_X(ppdev, pjMmBase, xLeft);
  567. MM_CUR_Y(ppdev, pjMmBase, yTop);
  568. MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, cx - 1);
  569. MM_MIN_AXIS_PCNT(ppdev, pjMmBase, cy - 1);
  570. cjSrc = (cx + 7) >> 3; // # bytes to transfer
  571. pjSrc = pjSrcScan0 + (yTop + dySrc) * lSrcDelta
  572. + ((xLeft + dxSrc) >> 3);
  573. // Start is byte aligned (note
  574. // that we don't have to add
  575. // xBias)
  576. ppdev->pfnImageTransfer(ppdev, pjSrc, lSrcDelta, cjSrc, cy,
  577. (RECTANGLE_FILL | WAIT | DRAWING_DIR_TBLRXM |
  578. DRAW | LAST_PIXEL_ON | MULTIPLE_PIXELS |
  579. WRITE | BYTE_SWAP));
  580. if (xBias != 0)
  581. {
  582. IO_FIFO_WAIT(ppdev, 1); // Reset the clipping if we used it
  583. MM_ABS_SCISSORS_L(ppdev, pjMmBase, 0);
  584. }
  585. prcl++;
  586. } while (--c != 0);
  587. }
  588. /******************************Public*Routine******************************\
  589. * VOID vMmXfer4bpp
  590. *
  591. * Does a 4bpp transfer from a bitmap to the screen.
  592. *
  593. * NOTE: The screen must be 8bpp for this function to be called!
  594. *
  595. * The reason we implement this is that a lot of resources are kept as 4bpp,
  596. * and used to initialize DFBs, some of which we of course keep off-screen.
  597. *
  598. \**************************************************************************/
  599. // XLATE_BUFFER_SIZE defines the size of the stack-based buffer we use
  600. // for doing the translate. Note that in general stack buffers should
  601. // be kept as small as possible. The OS guarantees us only 8k for stack
  602. // from GDI down to the display driver in low memory situations; if we
  603. // ask for more, we'll access violate. Note also that at any time the
  604. // stack buffer cannot be larger than a page (4k) -- otherwise we may
  605. // miss touching the 'guard page' and access violate then too.
  606. #define XLATE_BUFFER_SIZE 256
  607. VOID vMmXfer4bpp( // Type FNXFER
  608. PDEV* ppdev,
  609. LONG c, // Count of rectangles, can't be zero
  610. RECTL* prcl, // List of destination rectangles, in relative
  611. // coordinates
  612. ULONG rop4, // rop4
  613. SURFOBJ* psoSrc, // Source surface
  614. POINTL* pptlSrc, // Original unclipped source point
  615. RECTL* prclDst, // Original unclipped destination rectangle
  616. XLATEOBJ* pxlo) // Translate that provides colour-expansion information
  617. {
  618. LONG dx;
  619. LONG dy;
  620. LONG cx;
  621. LONG cy;
  622. LONG lSrcDelta;
  623. BYTE* pjSrcScan0;
  624. BYTE* pjScan;
  625. BYTE* pjSrc;
  626. BYTE* pjDst;
  627. LONG cxThis;
  628. LONG cxToGo;
  629. LONG xSrc;
  630. LONG iLoop;
  631. BYTE jSrc;
  632. ULONG* pulXlate;
  633. LONG cwThis;
  634. BYTE* pjBuf;
  635. BYTE ajBuf[XLATE_BUFFER_SIZE];
  636. BYTE* pjMmBase = ppdev->pjMmBase;
  637. ASSERTDD(ppdev->iBitmapFormat == BMF_8BPP, "Screen must be 8bpp");
  638. ASSERTDD(psoSrc->iBitmapFormat == BMF_4BPP, "Source must be 4bpp");
  639. ASSERTDD(c > 0, "Can't handle zero rectangles");
  640. ASSERTDD(((rop4 & 0xff00) >> 8) == (rop4 & 0xff),
  641. "Expect only a rop2");
  642. dx = pptlSrc->x - prclDst->left;
  643. dy = pptlSrc->y - prclDst->top; // Add to destination to get source
  644. lSrcDelta = psoSrc->lDelta;
  645. pjSrcScan0 = psoSrc->pvScan0;
  646. IO_FIFO_WAIT(ppdev, 6);
  647. MM_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
  648. MM_FRGD_MIX(ppdev, pjMmBase, SRC_CPU_DATA | gaulHwMixFromRop2[rop4 & 0xf]);
  649. while(TRUE)
  650. {
  651. cx = prcl->right - prcl->left;
  652. cy = prcl->bottom - prcl->top;
  653. MM_CUR_X(ppdev, pjMmBase, prcl->left);
  654. MM_CUR_Y(ppdev, pjMmBase, prcl->top);
  655. MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, cx - 1);
  656. MM_MIN_AXIS_PCNT(ppdev, pjMmBase, cy - 1);
  657. pulXlate = pxlo->pulXlate;
  658. xSrc = prcl->left + dx;
  659. pjScan = pjSrcScan0 + (prcl->top + dy) * lSrcDelta + (xSrc >> 1);
  660. IO_GP_WAIT(ppdev);
  661. MM_CMD(ppdev, pjMmBase, RECTANGLE_FILL | BUS_SIZE_16| WAIT |
  662. DRAWING_DIR_TBLRXM | DRAW | LAST_PIXEL_ON |
  663. SINGLE_PIXEL | WRITE | BYTE_SWAP);
  664. CHECK_DATA_READY(ppdev);
  665. do {
  666. pjSrc = pjScan;
  667. cxToGo = cx; // # of pels per scan in 4bpp source
  668. do {
  669. cxThis = XLATE_BUFFER_SIZE;
  670. // We can handle XLATE_BUFFER_SIZE number
  671. // of pels in this xlate batch
  672. cxToGo -= cxThis; // cxThis will be the actual number of
  673. // pels we'll do in this xlate batch
  674. if (cxToGo < 0)
  675. cxThis += cxToGo;
  676. pjDst = ajBuf; // Points to our temporary batch buffer
  677. // We handle alignment ourselves because it's easy to
  678. // do, rather than pay the cost of setting/resetting
  679. // the scissors register:
  680. if (xSrc & 1)
  681. {
  682. // When unaligned, we have to be careful not to read
  683. // past the end of the 4bpp bitmap (that could
  684. // potentially cause us to access violate):
  685. iLoop = cxThis >> 1; // Each loop handles 2 pels;
  686. // we'll handle odd pel
  687. // separately
  688. jSrc = *pjSrc;
  689. while (iLoop-- != 0)
  690. {
  691. *pjDst++ = (BYTE) pulXlate[jSrc & 0xf];
  692. jSrc = *(++pjSrc);
  693. *pjDst++ = (BYTE) pulXlate[jSrc >> 4];
  694. }
  695. if (cxThis & 1)
  696. {
  697. *pjDst++ = (BYTE) pulXlate[jSrc & 0xf];
  698. *pjDst = 0;
  699. }
  700. }
  701. else
  702. {
  703. iLoop = (cxThis + 1) >> 1; // Each loop handles 2 pels
  704. do {
  705. jSrc = *pjSrc++;
  706. *pjDst++ = (BYTE) pulXlate[jSrc >> 4];
  707. *pjDst++ = (BYTE) pulXlate[jSrc & 0xf];
  708. } while (--iLoop != 0);
  709. }
  710. // The number of bytes we'll transfer is equal to the number
  711. // of pels we've processed in the batch. Since we're
  712. // transferring words, we have to round up to get the word
  713. // count:
  714. cwThis = (cxThis + 1) >> 1;
  715. pjBuf = ajBuf;
  716. MM_TRANSFER_WORD_ALIGNED(ppdev, pjMmBase, pjBuf, cwThis);
  717. } while (cxToGo > 0);
  718. pjScan += lSrcDelta; // Advance to next source scan. Note
  719. // that we could have computed the
  720. // value to advance 'pjSrc' directly,
  721. // but this method is less
  722. // error-prone.
  723. } while (--cy != 0);
  724. CHECK_DATA_COMPLETE(ppdev);
  725. if (--c == 0)
  726. return;
  727. prcl++;
  728. IO_FIFO_WAIT(ppdev, 4);
  729. }
  730. }
  731. /******************************Public*Routine******************************\
  732. * VOID vMmXferNative
  733. *
  734. * Transfers a bitmap that is the same colour depth as the display to
  735. * the screen via the data transfer register, with no translation.
  736. *
  737. \**************************************************************************/
  738. VOID vMmXferNative( // Type FNXFER
  739. PDEV* ppdev,
  740. LONG c, // Count of rectangles, can't be zero
  741. RECTL* prcl, // Array of relative coordinates destination rectangles
  742. ROP4 rop4, // rop4
  743. SURFOBJ* psoSrc, // Source surface
  744. POINTL* pptlSrc, // Original unclipped source point
  745. RECTL* prclDst, // Original unclipped destination rectangle
  746. XLATEOBJ* pxlo) // Not used
  747. {
  748. LONG dx;
  749. LONG dy;
  750. LONG cx;
  751. LONG cy;
  752. LONG lSrcDelta;
  753. BYTE* pjSrcScan0;
  754. BYTE* pjSrc;
  755. LONG cjSrc;
  756. BYTE* pjMmBase = ppdev->pjMmBase;
  757. ASSERTDD((pxlo == NULL) || (pxlo->flXlate & XO_TRIVIAL),
  758. "Can handle trivial xlate only");
  759. ASSERTDD(psoSrc->iBitmapFormat == ppdev->iBitmapFormat,
  760. "Source must be same colour depth as screen");
  761. ASSERTDD(c > 0, "Can't handle zero rectangles");
  762. ASSERTDD(((rop4 & 0xff00) >> 8) == (rop4 & 0xff),
  763. "Expect only a rop2");
  764. dx = pptlSrc->x - prclDst->left;
  765. dy = pptlSrc->y - prclDst->top; // Add to destination to get source
  766. lSrcDelta = psoSrc->lDelta;
  767. pjSrcScan0 = psoSrc->pvScan0;
  768. IO_FIFO_WAIT(ppdev, 6);
  769. MM_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
  770. MM_FRGD_MIX(ppdev, pjMmBase, SRC_CPU_DATA | gaulHwMixFromRop2[rop4 & 0xf]);
  771. while(TRUE)
  772. {
  773. MM_CUR_X(ppdev, pjMmBase, prcl->left);
  774. MM_CUR_Y(ppdev, pjMmBase, prcl->top);
  775. cx = prcl->right - prcl->left;
  776. MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, cx - 1);
  777. cy = prcl->bottom - prcl->top;
  778. MM_MIN_AXIS_PCNT(ppdev, pjMmBase, cy - 1);
  779. cjSrc = CONVERT_TO_BYTES(cx, ppdev);
  780. pjSrc = pjSrcScan0 + (prcl->top + dy) * lSrcDelta
  781. + CONVERT_TO_BYTES((prcl->left + dx), ppdev);
  782. ppdev->pfnImageTransfer(ppdev, pjSrc, lSrcDelta, cjSrc, cy,
  783. (RECTANGLE_FILL | WAIT | DRAWING_DIR_TBLRXM |
  784. DRAW | LAST_PIXEL_ON | SINGLE_PIXEL |
  785. WRITE | BYTE_SWAP));
  786. if (--c == 0)
  787. return;
  788. prcl++;
  789. IO_FIFO_WAIT(ppdev, 4);
  790. }
  791. }
  792. /******************************Public*Routine******************************\
  793. * VOID vMmCopyBlt
  794. *
  795. * Does a screen-to-screen blt of a list of rectangles.
  796. *
  797. \**************************************************************************/
  798. VOID vMmCopyBlt( // Type FNCOPY
  799. PDEV* ppdev,
  800. LONG c, // Can't be zero
  801. RECTL* prcl, // Array of relative coordinates destination rectangles
  802. ULONG rop4, // rop4
  803. POINTL* pptlSrc, // Original unclipped source point
  804. RECTL* prclDst) // Original unclipped destination rectangle
  805. {
  806. LONG dx;
  807. LONG dy; // Add delta to destination to get source
  808. LONG cx;
  809. LONG cy; // Size of current rectangle - 1
  810. BYTE* pjMmBase = ppdev->pjMmBase;
  811. ASSERTDD(c > 0, "Can't handle zero rectangles");
  812. ASSERTDD(((rop4 & 0xff00) >> 8) == (rop4 & 0xff),
  813. "Expect only a rop2");
  814. IO_FIFO_WAIT(ppdev, 2);
  815. MM_FRGD_MIX(ppdev, pjMmBase, SRC_DISPLAY_MEMORY | gaulHwMixFromRop2[rop4 & 0xf]);
  816. MM_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
  817. dx = pptlSrc->x - prclDst->left;
  818. dy = pptlSrc->y - prclDst->top;
  819. // The accelerator may not be as fast at doing right-to-left copies, so
  820. // only do them when the rectangles truly overlap:
  821. if (!OVERLAP(prclDst, pptlSrc))
  822. goto Top_Down_Left_To_Right;
  823. if (prclDst->top <= pptlSrc->y)
  824. {
  825. if (prclDst->left <= pptlSrc->x)
  826. {
  827. Top_Down_Left_To_Right:
  828. do {
  829. IO_FIFO_WAIT(ppdev, 7);
  830. cx = prcl->right - prcl->left - 1;
  831. MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, cx);
  832. MM_DEST_X(ppdev, pjMmBase, prcl->left);
  833. MM_CUR_X(ppdev, pjMmBase, prcl->left + dx);
  834. cy = prcl->bottom - prcl->top - 1;
  835. MM_MIN_AXIS_PCNT(ppdev, pjMmBase, cy);
  836. MM_DEST_Y(ppdev, pjMmBase, prcl->top);
  837. MM_CUR_Y(ppdev, pjMmBase, prcl->top + dy);
  838. MM_CMD(ppdev, pjMmBase, BITBLT | DRAW | DIR_TYPE_XY | WRITE |
  839. DRAWING_DIR_TBLRXM);
  840. prcl++;
  841. } while (--c != 0);
  842. }
  843. else
  844. {
  845. do {
  846. IO_FIFO_WAIT(ppdev, 7);
  847. cx = prcl->right - prcl->left - 1;
  848. MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, cx);
  849. MM_DEST_X(ppdev, pjMmBase, prcl->left + cx);
  850. MM_CUR_X(ppdev, pjMmBase, prcl->left + cx + dx);
  851. cy = prcl->bottom - prcl->top - 1;
  852. MM_MIN_AXIS_PCNT(ppdev, pjMmBase, cy);
  853. MM_DEST_Y(ppdev, pjMmBase, prcl->top);
  854. MM_CUR_Y(ppdev, pjMmBase, prcl->top + dy);
  855. MM_CMD(ppdev, pjMmBase, BITBLT | DRAW | DIR_TYPE_XY | WRITE |
  856. DRAWING_DIR_TBRLXM);
  857. prcl++;
  858. } while (--c != 0);
  859. }
  860. }
  861. else
  862. {
  863. if (prclDst->left <= pptlSrc->x)
  864. {
  865. do {
  866. IO_FIFO_WAIT(ppdev, 7);
  867. cx = prcl->right - prcl->left - 1;
  868. MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, cx);
  869. MM_DEST_X(ppdev, pjMmBase, prcl->left);
  870. MM_CUR_X(ppdev, pjMmBase, prcl->left + dx);
  871. cy = prcl->bottom - prcl->top - 1;
  872. MM_MIN_AXIS_PCNT(ppdev, pjMmBase, cy);
  873. MM_DEST_Y(ppdev, pjMmBase, prcl->top + cy);
  874. MM_CUR_Y(ppdev, pjMmBase, prcl->top + cy + dy);
  875. MM_CMD(ppdev, pjMmBase, BITBLT | DRAW | DIR_TYPE_XY | WRITE |
  876. DRAWING_DIR_BTLRXM);
  877. prcl++;
  878. } while (--c != 0);
  879. }
  880. else
  881. {
  882. do {
  883. IO_FIFO_WAIT(ppdev, 7);
  884. cx = prcl->right - prcl->left - 1;
  885. MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, cx);
  886. MM_DEST_X(ppdev, pjMmBase, prcl->left + cx);
  887. MM_CUR_X(ppdev, pjMmBase, prcl->left + cx + dx);
  888. cy = prcl->bottom - prcl->top - 1;
  889. MM_MIN_AXIS_PCNT(ppdev, pjMmBase, cy);
  890. MM_DEST_Y(ppdev, pjMmBase, prcl->top + cy);
  891. MM_CUR_Y(ppdev, pjMmBase, prcl->top + cy + dy);
  892. MM_CMD(ppdev, pjMmBase, BITBLT | DRAW | DIR_TYPE_XY | WRITE |
  893. DRAWING_DIR_BTRLXM);
  894. prcl++;
  895. } while (--c != 0);
  896. }
  897. }
  898. }
  899. /******************************Public*Routine******************************\
  900. * VOID vMmCopyTransparent
  901. *
  902. * Does a screen-to-screen blt of a list of rectangles using a source
  903. * colorkey for transparency.
  904. *
  905. \**************************************************************************/
  906. VOID vMmCopyTransparent( // Type FNCOPYTRANSPARENT
  907. PDEV* ppdev,
  908. LONG c, // Can't be zero
  909. RECTL* prcl, // Array of relative coordinates destination rectangles
  910. POINTL* pptlSrc, // Original unclipped source point
  911. RECTL* prclDst, // Original unclipped destination rectangle
  912. ULONG iColor)
  913. {
  914. LONG dx;
  915. LONG dy; // Add delta to destination to get source
  916. BYTE* pjMmBase = ppdev->pjMmBase;
  917. ASSERTDD(c > 0, "Can't handle zero rectangles");
  918. // Note that we don't have to worry about overlapping blts, since GDI
  919. // will never allow those down to us.
  920. dx = pptlSrc->x - prclDst->left;
  921. dy = pptlSrc->y - prclDst->top;
  922. IO_FIFO_WAIT(ppdev, 4);
  923. MM_COLOR_CMP(ppdev, pjMmBase, iColor);
  924. MM_MULTIFUNC_CNTL(ppdev, pjMmBase, ppdev->ulMiscState
  925. | MULT_MISC_COLOR_COMPARE);
  926. MM_FRGD_MIX(ppdev, pjMmBase, SRC_DISPLAY_MEMORY | OVERPAINT);
  927. MM_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
  928. while (TRUE)
  929. {
  930. IO_FIFO_WAIT(ppdev, 7);
  931. MM_CUR_X(ppdev, pjMmBase, prcl->left + dx);
  932. MM_CUR_Y(ppdev, pjMmBase, prcl->top + dy);
  933. MM_DEST_X(ppdev, pjMmBase, prcl->left);
  934. MM_DEST_Y(ppdev, pjMmBase, prcl->top);
  935. MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, prcl->right - prcl->left - 1);
  936. MM_MIN_AXIS_PCNT(ppdev, pjMmBase, prcl->bottom - prcl->top - 1);
  937. MM_CMD(ppdev, pjMmBase, BITBLT | DRAW | DIR_TYPE_XY |
  938. WRITE | DRAWING_DIR_TBLRXM);
  939. if (--c == 0)
  940. {
  941. IO_FIFO_WAIT(ppdev, 1);
  942. MM_MULTIFUNC_CNTL(ppdev, pjMmBase, ppdev->ulMiscState);
  943. return;
  944. }
  945. prcl++;
  946. }
  947. }