home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / viscobv6.zip / vac22os2 / ibmcobol / samples / toolkit / mm / beehive / blitgen.c < prev    next >
Text File  |  1996-11-19  |  12KB  |  378 lines

  1.  
  2. /***************************************************************************
  3. *
  4. * File name        : BLITGEN.C
  5. *
  6. * Description      : This module contains the procedure that will process
  7. *                    a sprite and generate a specialized blitter specific
  8. *                    to that sprite ( compiled sprites ).
  9. *
  10. * Copyright        : COPYRIGHT IBM CORPORATION, 1991, 1992, 1993, 1994, 1995
  11. *
  12. *        DISCLAIMER OF WARRANTIES.  The following [enclosed] code is
  13. *        sample code created by IBM Corporation. This sample code is not
  14. *        part of any standard or IBM product and is provided to you solely
  15. *        for  the purpose of assisting you in the development of your
  16. *        applications.  The code is provided "AS IS", without
  17. *        warranty of any kind.  IBM shall not be liable for any damages
  18. *        arising out of your use of the sample code, even if they have been
  19. *        advised of the possibility of such damages.
  20. *
  21. ****************************************************************************/
  22.  
  23.  
  24. #define  INCL_DOS
  25.  
  26. #include <os2.h>
  27. #include "beehive.h"
  28.  
  29. //**************************************************************************
  30. //
  31. // This routine will generate another routine that will copy a given
  32. // image from it's source buffer to an arbitrary location in given
  33. // destination image buffer. The generated routine will be optimized
  34. // specifically for the image data that is passed to this routine and
  35. // will only work with images that have identical properties.
  36. //
  37. // The gererated routine will be of the type:
  38. //   VOID   ( * _System pfnBlitSprite )( PBYTE pSourceBuffer,
  39. //                                        PBYTE pDestinationBuffer,
  40. //                                        ULONG ulPositionX,
  41. //                                        ULONG ulPositionY );
  42. //
  43. // When using the generated routine, care must be taken to insure that
  44. // the target location is valid. This generated routine will not check
  45. // for boundary conditions.
  46. //
  47. // Passing a transparent color value that is higher than 255 will have the
  48. // affect of producing a completely opaque blit.
  49. //
  50. //***************************************************************************
  51.  
  52.  
  53. ULONG GenBlitSprite( PPVOID ppfnCode,  // Pointer to generated code pointer
  54.                      PBYTE pbSource,   // Pointer to sprite color data
  55.                      ULONG ulSizeX,    // Width of sprite
  56.                      ULONG ulSizeY,    // Height of sprite
  57.                      ULONG ulTargetWidth,   // Width of target video buffer
  58.                      ULONG ulTransparentColor )
  59. {
  60.    PBYTE    pbBuffer,
  61.             pfnCode;
  62.  
  63.    ULONG    ulCodeSize = 0,            // Total size of generated code
  64.             ulDoubleWordMoves = 0,     // Number of double word moves
  65.             ulAdjustments = 0,         // Number of pointer adjustments
  66.             ulBlanks = 0,
  67.             ulScanLineDelta = 0,
  68.             ulBytes = 0,
  69.             i,
  70.             j,
  71.             ulrc;
  72.  
  73.  
  74.    pbBuffer = pbSource;    // copy source buffer pointer
  75.  
  76.    // First we calculate the required buffer size to contain the generated
  77.    // code. This is done by counting the number of byte and double word move
  78.    // instructions as well as the number of transparent byte runs.
  79.  
  80.    for(  j = 0; j < ulSizeY; j++ )
  81.    {
  82.       for( i = 0; i < ulSizeX; i++ )
  83.       {
  84.          if( (ULONG)*(pbBuffer + i) != ulTransparentColor )
  85.          {
  86.             // If previous bytes were transparent then adjust pointers
  87.             // This will require 2, 6 byte ADD instructions to adjust
  88.             // the EDI and ESI registers.
  89.  
  90.             if( ulBlanks )
  91.             {
  92.                ulCodeSize += 6;
  93.                ulBlanks = 0;
  94.             }
  95.  
  96.             if( ulBlanks || ulScanLineDelta )
  97.             {
  98.                ulCodeSize += 6;
  99.                ulScanLineDelta = 0;
  100.             }
  101.  
  102.             ulBytes++;
  103.  
  104.             // If possible, use the faster, double word move
  105.  
  106.             if( ulBytes == 4 )
  107.             {
  108.                ulDoubleWordMoves++;
  109.                ulBytes = 0;
  110.             }
  111.          }
  112.          else
  113.          {
  114.             ulBlanks++;
  115.  
  116.             // First use the REP instruction to copy all of the required
  117.             // double words. This is a 7 byte instruction because we first
  118.             // have to set the value of the ECX register for counting.
  119.  
  120.             if( ulDoubleWordMoves > 1 )
  121.                ulCodeSize += 7;
  122.  
  123.             if( ulDoubleWordMoves == 1 )
  124.                ulCodeSize++;
  125.  
  126.             ulDoubleWordMoves = 0;
  127.  
  128.  
  129.             // If there are any bytes left to move then we need a
  130.             // series of single byte move instructions.
  131.  
  132.             for( ; ulBytes > 0; ulBytes-- )
  133.             {
  134.                ulCodeSize++;
  135.             }
  136.  
  137.          }  // End else
  138.  
  139.       }  // End for loop
  140.  
  141.       // This is the end of a scan line. If the target and destination
  142.       // widths are not the same, then we have to generate a blit for
  143.       // each scan line. Otherwise, we wait until the end of the source
  144.       // image.
  145.       //
  146.       // First use the REP instruction to copy all of the required
  147.       // double words. This is a 7 byte instruction because we first
  148.       // have to set the value of the ECX register.
  149.  
  150.       if( ( ulSizeX != ulTargetWidth ) || ( j == ( ulSizeY - 1 ) ) )
  151.       {
  152.          if( ulDoubleWordMoves > 1 )
  153.             ulCodeSize += 7;
  154.  
  155.          if( ulDoubleWordMoves == 1 )
  156.             ulCodeSize++;
  157.  
  158.          ulDoubleWordMoves = 0;
  159.  
  160.          // If there are any bytes left to move then we need a
  161.          // series of single byte move instructions.
  162.  
  163.          for( ; ulBytes > 0; ulBytes-- )
  164.          {
  165.             ulCodeSize++;
  166.          }
  167.  
  168.          ulScanLineDelta++;
  169.       }
  170.       // Go to next line in source buffer
  171.       //
  172.       pbBuffer += ulSizeX;
  173.  
  174.    }  // End for loop
  175.  
  176.    // Account for initialization code in the calculated code size
  177.  
  178.    ulCodeSize += 36;
  179.  
  180.    pbBuffer = pbSource;    // copy source buffer pointer
  181.  
  182.    // Allocate space for generated blit routine
  183.    //
  184.    ulrc = DosAllocMem ( ppfnCode, ulCodeSize, MY_ALLOC_FLAGS );
  185.    if( ulrc != 0 ) return ( ulrc );
  186.  
  187.    pfnCode = *ppfnCode;
  188.  
  189.    // Generate initiallization code. This code will push the required
  190.    // registers to the stack and then extract the passed parameters
  191.    // from the stack.
  192.  
  193.    *(PULONG)(pfnCode) = 0x53525150;       // PUSH EAX, ECX, EDX, EBX
  194.  
  195.    *(PULONG)(pfnCode + 4) = 0x00005756;   // PUSH ESI, EDI
  196.    pfnCode += 6;
  197.  
  198.    // MOV ESI, pbSourceBuffer
  199.    *(PULONG)(pfnCode) = 0x0024748b + ( ( SAVED_REGS * 4 + 4 ) << 24 );
  200.  
  201.    // MOV EDI, pbDestinationBuffer
  202.    *(PULONG)(pfnCode + 4) = 0x00247c8b + ( ( SAVED_REGS * 4 + 8 ) << 24 );
  203.    pfnCode += 8;
  204.  
  205.    // Calculate the offset into the target buffer
  206.    //
  207.    // MOV EAX, ulTargetWidth
  208.    *(pfnCode) = 0xb8;
  209.    *(PULONG)(pfnCode + 1) = ulTargetWidth;
  210.    pfnCode += 5;
  211.  
  212.    // MUL EAX, ulPositionY
  213.    *(PULONG)(pfnCode) = 0x002464f7 + ( ( SAVED_REGS * 4 + 16 ) << 24 );
  214.  
  215.    // ADD EAX, ulPositionX
  216.    *(PULONG)(pfnCode + 4) = 0x00244403 + ( ( SAVED_REGS * 4 + 12 ) << 24 );
  217.  
  218.    // ADD EDI, EAX
  219.    *(PULONG)(pfnCode + 8) = 0x0000f803;
  220.    pfnCode += 10;
  221.  
  222.    // Now we make another pass at the color data while generating the code.
  223.    //
  224.    ulBlanks = 0;
  225.    ulBytes = 0;
  226.    ulDoubleWordMoves = 0;
  227.    ulScanLineDelta = 0;
  228.  
  229.    for(  j = 0; j < ulSizeY; j++ )
  230.    {
  231.       for( i = 0; i < ulSizeX; i++ )
  232.       {
  233.          if( (ULONG)*(pbBuffer + i) != ulTransparentColor )
  234.          {
  235.             // If previous bytes were transparent then adjust pointers
  236.  
  237.             if( ulBlanks )
  238.             {
  239.                // ADD ESI, ulBlanks
  240.                *(PULONG)(pfnCode) = 0x0000c681;
  241.                *(PULONG)(pfnCode + 2) = ulBlanks;
  242.                pfnCode += 6;
  243.             }
  244.  
  245.             if( ulBlanks || ulScanLineDelta )
  246.             {
  247.                // ADD EDI, ulBlanks
  248.                *(PULONG)(pfnCode) = 0x0000c781;
  249.                *(PULONG)(pfnCode + 2) = ulBlanks + ulScanLineDelta *
  250.                                          ( ulTargetWidth - ulSizeX );
  251.                pfnCode += 6;
  252.                ulScanLineDelta = 0;
  253.             }
  254.  
  255.             ulBlanks = 0;
  256.  
  257.             ulBytes++;
  258.  
  259.             // If possible, use the faster, double word move
  260.  
  261.             if( ulBytes == 4 )
  262.             {
  263.                ulDoubleWordMoves++;
  264.                ulBytes = 0;
  265.             }
  266.          }
  267.          else
  268.          {
  269.             ulBlanks++;
  270.  
  271.             // If we have more than 1 double move then use a REP MOVSD to
  272.             // copy all of the counted double words. To do this, we have
  273.             // to first set the value of the CX register which is used
  274.             // as a loop counter.
  275.  
  276.             if( ulDoubleWordMoves > 1 )
  277.             {
  278.                // MOV CX, ulDoubleWordMoves
  279.                *(pfnCode) = 0xb9;
  280.                *(PULONG)(pfnCode + 1) = ulDoubleWordMoves;
  281.                pfnCode += 5;
  282.  
  283.                // REP MOVS
  284.                *(PULONG)(pfnCode) = 0x0000a5f3;
  285.                pfnCode += 2;
  286.  
  287.             }
  288.  
  289.             // If there is only one double move than we can use a single
  290.             // MOVD. The result is less code than the REP MOVSD and it will
  291.             // save a couple of clock cycles.
  292.  
  293.             if( ulDoubleWordMoves == 1 )
  294.             {
  295.                //MOVSD
  296.                *(pfnCode++) = 0xa7;
  297.             }
  298.  
  299.             ulDoubleWordMoves = 0;
  300.  
  301.             // If there are any bytes left to move then we need a
  302.             // series of single byte MOVS instructions.
  303.  
  304.             for( ; ulBytes > 0; ulBytes-- )
  305.             {
  306.                *(pfnCode++) = 0xa4;
  307.             }
  308.  
  309.          }  // End else
  310.  
  311.       }  // End for loop
  312.  
  313.       // The inner loop has fallen through but there may still be some
  314.       // image data left on this scan line to blit. If there is then we
  315.       // generate code to blit it before going to the next line.
  316.       //
  317.  
  318.       // If we have more than 1 double move then use a REP MOVSD to
  319.       // copy all of the counted double words. To do this, we have
  320.       // to first set the value of the CX register which is used
  321.       // as a loop counter.
  322.  
  323.       if( ( ulSizeX != ulTargetWidth ) || ( j == ( ulSizeY - 1 ) ) )
  324.       {
  325.  
  326.          if( ulDoubleWordMoves > 1 )
  327.          {
  328.  
  329.             // MOV CX, ulDoubleWordMoves
  330.             *(pfnCode) = 0xb9;
  331.             *(PULONG)(pfnCode + 1) = ulDoubleWordMoves;
  332.             pfnCode += 5;
  333.  
  334.             // REP MOVS
  335.             *(PULONG)(pfnCode) = 0x0000a5f3;
  336.             pfnCode += 2;
  337.  
  338.          }
  339.  
  340.          // If there is only one double move than we can use a single
  341.          // MOVD. The result is less code than the REP MOVSD and it will
  342.          // save a couple of clock cycles.
  343.  
  344.          if( ulDoubleWordMoves == 1 )
  345.          {
  346.             //MOVSD
  347.             *(pfnCode++) = 0xa7;
  348.          }
  349.  
  350.          ulDoubleWordMoves = 0;
  351.  
  352.          // If there are any bytes left to move then we need a
  353.          // series of single byte move instructions.
  354.  
  355.          for( ; ulBytes > 0; ulBytes-- )
  356.          {
  357.             *(pfnCode++) = 0xa4;
  358.          }
  359.  
  360.          if( ulSizeX != ulTargetWidth )
  361.             ulScanLineDelta++;
  362.       }
  363.  
  364.       pbBuffer += ulSizeX;
  365.  
  366.    }  // End for loop
  367.  
  368.    *(PULONG)(pfnCode) = 0x5a5b5e5f;       // POP EDI, ESI, EBX, EDX
  369.    *(PULONG)(pfnCode + 4) = 0x00005859;   // POP ECX, EAX
  370.    pfnCode += 6;
  371.  
  372.    // RET
  373.    *(pfnCode) = 0xc3;
  374.  
  375.    return( 0 );
  376.  
  377. }
  378.