home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1991 / 09 / gr_prog.lst < prev    next >
File List  |  1991-08-21  |  32KB  |  707 lines

  1. _GRAPHICS PROGRAMMING COLUMN_
  2. by Michael Abrash
  3.  
  4. [LISTING ONE]
  5.  
  6. ; Mode X (320x240, 256 colors) system memory-to-display memory masked copy 
  7. ; routine. Not particularly fast; images for which performance is critical 
  8. ; should be stored in off-screen memory and copied to screen via latches. Works
  9. ; on all VGAs. Copies up to but not including column at SourceEndX and row at 
  10. ; SourceEndY. No clipping is performed. Mask and source image are both byte-
  11. ; per-pixel, and must be of same widths and reside at same coordinates in their
  12. ; respective bitmaps. Assembly code tested with TASM 2.0. C near-callable as:
  13. ;    void CopySystemToScreenMaskedX(int SourceStartX,
  14. ;       int SourceStartY, int SourceEndX, int SourceEndY,
  15. ;       int DestStartX, int DestStartY, char * SourcePtr,
  16. ;       unsigned int DestPageBase, int SourceBitmapWidth,
  17. ;       int DestBitmapWidth, char * MaskPtr);
  18.  
  19. SC_INDEX equ    03c4h   ;Sequence Controller Index register port
  20. MAP_MASK equ    02h     ;index in SC of Map Mask register
  21. SCREEN_SEG equ  0a000h  ;segment of display memory in mode X
  22.  
  23. parms   struc
  24.         dw      2 dup (?) ;pushed BP and return address
  25. SourceStartX dw ?       ;X coordinate of upper left corner of source
  26.                         ; (source is in system memory)
  27. SourceStartY dw ?       ;Y coordinate of upper left corner of source
  28. SourceEndX   dw ?       ;X coordinate of lower right corner of source
  29.                         ; (the column at EndX is not copied)
  30. SourceEndY   dw ?       ;Y coordinate of lower right corner of source
  31.                         ; (the row at EndY is not copied)
  32. DestStartX   dw ?       ;X coordinate of upper left corner of dest
  33.                         ; (destination is in display memory)
  34. DestStartY   dw ?       ;Y coordinate of upper left corner of dest
  35. SourcePtr    dw ?       ;pointer in DS to start of bitmap which source resides
  36. DestPageBase dw ?       ;base offset in display memory of page in
  37.                         ; which dest resides
  38. SourceBitmapWidth dw ?  ;# of pixels across source bitmap (also must
  39.                         ; be width across the mask)
  40. DestBitmapWidth   dw ?  ;# of pixels across dest bitmap (must be multiple of 4)
  41. MaskPtr      dw ?       ;pointer in DS to start of bitmap in which mask 
  42.                         ; resides (byte-per-pixel format, just like the source 
  43.                         ; image; 0-bytes mean don't copy corresponding source 
  44.                         ; pixel, 1-bytes mean do copy)
  45. parms   ends
  46.  
  47. RectWidth equ   -2      ;local storage for width of rectangle
  48. RectHeight equ  -4      ;local storage for height of rectangle
  49. LeftMask equ    -6      ;local storage for left rect edge plane mask
  50. STACK_FRAME_SIZE equ 6
  51.         .model  small
  52.         .code
  53.         public  _CopySystemToScreenMaskedX
  54. _CopySystemToScreenMaskedX proc    near
  55.         push    bp      ;preserve caller's stack frame
  56.         mov     bp,sp   ;point to local stack frame
  57.         sub     sp,STACK_FRAME_SIZE ;allocate space for local vars
  58.         push    si      ;preserve caller's register variables
  59.         push    di
  60.  
  61.         mov     ax,SCREEN_SEG   ;point ES to display memory
  62.         mov     es,ax
  63.         mov     ax,[bp+SourceBitmapWidth]
  64.         mul     [bp+SourceStartY] ;top source rect scan line
  65.         add     ax,[bp+SourceStartX]
  66.         mov     bx,ax
  67.         add     ax,[bp+SourcePtr] ;offset of first source rect pixel
  68.         mov     si,ax             ; in DS
  69.         add     bx,[bp+MaskPtr] ;offset of first mask pixel in DS
  70.         
  71.         mov     ax,[bp+DestBitmapWidth]
  72.         shr     ax,1            ;convert to width in addresses
  73.         shr     ax,1
  74.         mov     [bp+DestBitmapWidth],ax ;remember address width
  75.         mul     [bp+DestStartY] ;top dest rect scan line
  76.         mov     di,[bp+DestStartX]
  77.         mov     cx,di
  78.         shr     di,1    ;X/4 = offset of first dest rect pixel in
  79.         shr     di,1    ; scan line
  80.         add     di,ax   ;offset of first dest rect pixel in page
  81.         add     di,[bp+DestPageBase] ;offset of first dest rect pixel
  82.                         ; in display memory
  83.         and     cl,011b ;CL = first dest pixel's plane
  84.         mov     al,11h  ;upper nibble comes into play when plane wraps
  85.                         ; from 3 back to 0
  86.         shl     al,cl   ;set the bit for the first dest pixel's plane
  87.         mov     [bp+LeftMask],al ; in each nibble to 1
  88.  
  89.         mov     ax,[bp+SourceEndX]   ;calculate # of pixels across
  90.         sub     ax,[bp+SourceStartX] ; rect
  91.         jle     CopyDone        ;skip if 0 or negative width
  92.         mov     [bp+RectWidth],ax
  93.         sub     word ptr [bp+SourceBitmapWidth],ax
  94.                     ;distance from end of one source scan line to start of next
  95.         mov     ax,[bp+SourceEndY]
  96.         sub     ax,[bp+SourceStartY] ;height of rectangle
  97.         jle     CopyDone        ;skip if 0 or negative height
  98.         mov     [bp+RectHeight],ax
  99.         mov     dx,SC_INDEX     ;point to SC Index register
  100.         mov     al,MAP_MASK
  101.         out     dx,al           ;point SC Index reg to the Map Mask
  102.         inc     dx              ;point DX to SC Data reg
  103. CopyRowsLoop:
  104.         mov     al,[bp+LeftMask]
  105.         mov     cx,[bp+RectWidth]
  106.         push    di      ;remember the start offset in the dest
  107. CopyScanLineLoop:
  108.         cmp     byte ptr [bx],0 ;is this pixel mask-enabled?
  109.         jz      MaskOff         ;no, so don't draw it
  110.                                 ;yes, draw the pixel
  111.         out     dx,al           ;set the plane for this pixel
  112.         mov     ah,[si]         ;get the pixel from the source
  113.         mov     es:[di],ah      ;copy the pixel to the screen
  114. MaskOff:
  115.         inc     bx              ;advance the mask pointer
  116.         inc     si              ;advance the source pointer
  117.         rol     al,1            ;set mask for next pixel's plane
  118.         adc     di,0            ;advance destination address only when
  119.                                 ; wrapping from plane 3 to plane 0
  120.         loop    CopyScanLineLoop
  121.         pop     di              ;retrieve the dest start offset
  122.         add     di,[bp+DestBitmapWidth] ;point to the start of the
  123.                                         ; next scan line of the dest
  124.         add     si,[bp+SourceBitmapWidth] ;point to the start of the
  125.                                         ; next scan line of the source
  126.         add     bx,[bp+SourceBitmapWidth] ;point to the start of the
  127.                                         ; next scan line of the mask
  128.         dec     word ptr [bp+RectHeight] ;count down scan lines
  129.         jnz     CopyRowsLoop
  130. CopyDone:
  131.         pop     di      ;restore caller's register variables
  132.         pop     si
  133.         mov     sp,bp   ;discard storage for local variables
  134.         pop     bp      ;restore caller's stack frame
  135.         ret
  136. _CopySystemToScreenMaskedX endp
  137.         end
  138.  
  139.  
  140.  
  141.  
  142. [LISTING TWO]
  143.  
  144. ; Mode X (320x240, 256 colors) display memory to display memory masked copy 
  145. ; routine. Works on all VGAs. Uses approach of reading 4 pixels at a time from 
  146. ; source into latches, then writing latches to destination, using Map Mask 
  147. ; register to perform masking. Copies up to but not including column at 
  148. ; SourceEndX and row at SourceEndY. No clipping is performed. Results are not 
  149. ; guaranteed if source and destination overlap. C near-callable as:
  150. ;    void CopyScreenToScreenMaskedX(int SourceStartX,
  151. ;       int SourceStartY, int SourceEndX, int SourceEndY,
  152. ;       int DestStartX, int DestStartY, MaskedImage * Source,
  153. ;       unsigned int DestPageBase, int DestBitmapWidth);
  154.  
  155. SC_INDEX equ    03c4h   ;Sequence Controller Index register port
  156. MAP_MASK equ    02h     ;index in SC of Map Mask register
  157. GC_INDEX equ    03ceh   ;Graphics Controller Index register port
  158. BIT_MASK equ    08h     ;index in GC of Bit Mask register
  159. SCREEN_SEG equ  0a000h  ;segment of display memory in mode X
  160.  
  161. parms   struc
  162.         dw      2 dup (?) ;pushed BP and return address
  163. SourceStartX dw ?       ;X coordinate of upper left corner of source
  164. SourceStartY dw ?       ;Y coordinate of upper left corner of source
  165. SourceEndX   dw ?       ;X coordinate of lower right corner of source
  166.                         ; (the column at SourceEndX is not copied)
  167. SourceEndY   dw ?       ;Y coordinate of lower right corner of source
  168.                         ; (the row at SourceEndY is not copied)
  169. DestStartX   dw ?       ;X coordinate of upper left corner of dest
  170. DestStartY   dw ?       ;Y coordinate of upper left corner of dest
  171. Source       dw ?       ;pointer to MaskedImage struct for source
  172.                         ; which source resides
  173. DestPageBase dw ?       ;base offset in display memory of page in 
  174.                         ; which dest resides
  175. DestBitmapWidth   dw ?  ;# of pixels across dest bitmap (must be multiple of 4)
  176. parms   ends
  177.  
  178. SourceNextScanOffset equ -2   ;local storage for distance from end of
  179.                               ; one source scan line to start of next
  180. DestNextScanOffset equ -4    ;local storage for distance from end of
  181.                               ; one dest scan line to start of next
  182. RectAddrWidth equ -6    ;local storage for address width of rectangle
  183. RectHeight    equ -8    ;local storage for height of rectangle
  184. SourceBitmapWidth equ -10 ;local storage for width of source bitmap
  185.                         ; (in addresses)
  186. STACK_FRAME_SIZE equ 10
  187. MaskedImage     struc
  188.  Alignments     dw  4 dup(?) ;pointers to AlignedMaskedImages for the
  189.                              ; 4 possible destination image alignments
  190. MaskedImage     ends
  191. AlignedMaskedImage      struc
  192.  ImageWidth     dw      ? ;image width in addresses (also mask width in bytes)
  193.  ImagePtr       dw      ? ;offset of image bitmap in display memory
  194.  MaskPtr        dw      ? ;pointer to mask bitmap in DS
  195. AlignedMaskedImage      ends
  196.         .model  small
  197.         .code
  198.         public  _CopyScreenToScreenMaskedX
  199. _CopyScreenToScreenMaskedX proc    near
  200.         push    bp      ;preserve caller's stack frame
  201.         mov     bp,sp   ;point to local stack frame
  202.         sub     sp,STACK_FRAME_SIZE ;allocate space for local vars
  203.         push    si      ;preserve caller's register variables
  204.         push    di
  205.  
  206.         cld
  207.         mov     dx,GC_INDEX     ;set the bit mask to select all bits
  208.         mov     ax,00000h+BIT_MASK ; from the latches and none from
  209.         out     dx,ax           ; the CPU, so that we can write the
  210.                                 ; latch contents directly to memory
  211.         mov     ax,SCREEN_SEG   ;point ES to display memory
  212.         mov     es,ax
  213.         mov     ax,[bp+DestBitmapWidth]
  214.         shr     ax,1            ;convert to width in addresses
  215.         shr     ax,1
  216.         mul     [bp+DestStartY] ;top dest rect scan line
  217.         mov     di,[bp+DestStartX]
  218.         mov     si,di
  219.         shr     di,1    ;X/4 = offset of first dest rect pixel in
  220.         shr     di,1    ; scan line
  221.         add     di,ax   ;offset of first dest rect pixel in page
  222.         add     di,[bp+DestPageBase] ;offset of first dest rect pixel
  223.                         ; in display memory. now look up the image that's 
  224.                         ; aligned to match left-edge alignment of destination
  225.         and     si,3    ;DestStartX modulo 4
  226.         mov     cx,si   ;set aside alignment for later
  227.         shl     si,1    ;prepare for word look-up
  228.         mov     bx,[bp+Source] ;point to source MaskedImage structure
  229.         mov     bx,[bx+Alignments+si] ;point to AlignedMaskedImage
  230.                         ; struc for current left edge alignment
  231.         mov     ax,[bx+ImageWidth] ;image width in addresses
  232.         mov     [bp+SourceBitmapWidth],ax ;remember image width in
  233.                                           ; addresses
  234.         mul     [bp+SourceStartY] ;top source rect scan line
  235.         mov     si,[bp+SourceStartX]
  236.         shr     si,1    ;X/4 = address of first source rect pixel in
  237.         shr     si,1    ; scan line
  238.         add     si,ax   ;offset of first source rect pixel in image
  239.         mov     ax,si
  240.         add     si,[bx+MaskPtr] ;point to mask offset of first mask pixel in DS
  241.         mov     bx,[bx+ImagePtr] ;offset of first source rect pixel
  242.         add     bx,ax            ; in display memory
  243.  
  244.         mov     ax,[bp+SourceStartX] ;calculate # of addresses across
  245.         add     ax,cx                ; rect, shifting if necessary to
  246.         add     cx,[bp+SourceEndX]   ; account for alignment
  247.         cmp     cx,ax
  248.         jle     CopyDone        ;skip if 0 or negative width
  249.         add     cx,3
  250.         and     ax,not 011b
  251.         sub     cx,ax
  252.         shr     cx,1
  253.         shr     cx,1    ;# of addresses across rectangle to copy
  254.         mov     ax,[bp+SourceEndY]
  255.         sub     ax,[bp+SourceStartY]  ;AX = height of rectangle
  256.         jle     CopyDone        ;skip if 0 or negative height
  257.         mov     [bp+RectHeight],ax
  258.         mov     ax,[bp+DestBitmapWidth]
  259.         shr     ax,1            ;convert to width in addresses
  260.         shr     ax,1
  261.         sub     ax,cx ;distance from end of one dest scan line to start of next
  262.         mov     [bp+DestNextScanOffset],ax
  263.         mov     ax,[bp+SourceBitmapWidth] ;width in addresses
  264.         sub     ax,cx ;distance from end of source scan line to start of next
  265.         mov     [bp+SourceNextScanOffset],ax
  266.         mov     [bp+RectAddrWidth],cx ;remember width in addresses
  267.  
  268.         mov     dx,SC_INDEX
  269.         mov     al,MAP_MASK
  270.         out     dx,al           ;point SC Index register to Map Mask
  271.         inc     dx              ;point to SC Data register
  272. CopyRowsLoop:
  273.         mov     cx,[bp+RectAddrWidth] ;width across
  274. CopyScanLineLoop:
  275.         lodsb                   ;get the mask for this four-pixel set
  276.                                 ; and advance the mask pointer
  277.         out     dx,al           ;set the mask
  278.         mov     al,es:[bx]      ;load the latches with 4-pixel set from source
  279.         mov     es:[di],al      ;copy the four-pixel set to the dest
  280.         inc     bx              ;advance the source pointer
  281.         inc     di              ;advance the destination pointer
  282.         dec     cx              ;count off four-pixel sets
  283.         jnz     CopyScanLineLoop
  284.  
  285.         mov     ax,[bp+SourceNextScanOffset]
  286.         add     si,ax                      ;point to the start of
  287.         add     bx,ax                      ; the next source, mask,
  288.         add     di,[bp+DestNextScanOffset] ; and dest lines
  289.         dec     word ptr [bp+RectHeight] ;count down scan lines
  290.         jnz     CopyRowsLoop
  291. CopyDone:
  292.         mov     dx,GC_INDEX+1   ;restore the bit mask to its default,
  293.         mov     al,0ffh         ; which selects all bits from the CPU
  294.         out     dx,al           ; and none from the latches (the GC
  295.                                 ; Index still points to Bit Mask)
  296.         pop     di      ;restore caller's register variables
  297.         pop     si
  298.         mov     sp,bp   ;discard storage for local variables
  299.         pop     bp      ;restore caller's stack frame
  300.         ret
  301. _CopyScreenToScreenMaskedX endp
  302.         end
  303.  
  304.  
  305.  
  306. [LISTING THREE]
  307.  
  308. /* Generates all four possible mode X image/mask alignments, stores image 
  309. alignments in display memory, allocates memory for and generates mask 
  310. alignments, and fills out an AlignedMaskedImage structure. Image and mask must 
  311. both be in byte-per-pixel form, and must both be of width ImageWidth. Mask 
  312. maps isomorphically (one to one) onto image, with each 0-byte in mask masking
  313. off corresponding image pixel (causing it not to be drawn), and each non-0-byte
  314. allowing corresponding image pixel to be drawn. Returns 0 if failure, or # of 
  315. display memory addresses (4-pixel sets) used if success. For simplicity, 
  316. allocated memory is not deallocated in case of failure. Compiled with 
  317. Borland C++ 2.0 in C compilation mode. */
  318.  
  319. #include <stdio.h>
  320. #include <stdlib.h>
  321. #include "maskim.h"
  322.  
  323. extern void CopySystemToScreenX(int, int, int, int, int, int, char *,
  324.    unsigned int, int, int);
  325. unsigned int CreateAlignedMaskedImage(MaskedImage * ImageToSet,
  326.    unsigned int DispMemStart, char * Image, int ImageWidth,
  327.    int ImageHeight, char * Mask)
  328. {
  329.    int Align, ScanLine, BitNum, Size, TempImageWidth;
  330.    unsigned char MaskTemp;
  331.    unsigned int DispMemOffset = DispMemStart;
  332.    AlignedMaskedImage *WorkingAMImage;
  333.    char *NewMaskPtr, *OldMaskPtr;
  334.    /* Generate each of the four alignments in turn */
  335.    for (Align = 0; Align < 4; Align++) {
  336.       /* Allocate space for the AlignedMaskedImage struct for this
  337.          alignment */
  338.       if ((WorkingAMImage = ImageToSet->Alignments[Align] =
  339.             malloc(sizeof(AlignedMaskedImage))) == NULL)
  340.          return 0;
  341.       WorkingAMImage->ImageWidth =
  342.             (ImageWidth + Align + 3) / 4; /* width in 4-pixel sets */
  343.       WorkingAMImage->ImagePtr = DispMemOffset; /* image dest */
  344.       /* Download this alignment of the image */
  345.       CopySystemToScreenX(0, 0, ImageWidth, ImageHeight, Align, 0,
  346.             Image, DispMemOffset, ImageWidth,
  347.             WorkingAMImage->ImageWidth * 4);
  348.       /* Calculate the number of bytes needed to store the mask in
  349.          nibble (Map Mask-ready) form, then allocate that space */
  350.       Size = WorkingAMImage->ImageWidth * ImageHeight;
  351.       if ((WorkingAMImage->MaskPtr = malloc(Size)) == NULL)
  352.          return 0;
  353.       /* Generate this nibble oriented (Map Mask-ready) alignment of
  354.          the mask, one scan line at a time */
  355.       OldMaskPtr = Mask;
  356.       NewMaskPtr = WorkingAMImage->MaskPtr;
  357.       for (ScanLine = 0; ScanLine < ImageHeight; ScanLine++) {
  358.          BitNum = Align;
  359.          MaskTemp = 0;
  360.          TempImageWidth = ImageWidth;
  361.          do {
  362.             /* Set the mask bit for next pixel according to its alignment */
  363.             MaskTemp |= (*OldMaskPtr++ != 0) << BitNum;
  364.             if (++BitNum > 3) {
  365.                *NewMaskPtr++ = MaskTemp;
  366.                MaskTemp = BitNum = 0;
  367.             }
  368.          } while (--TempImageWidth);
  369.          /* Set any partial final mask on this scan line */
  370.          if (BitNum != 0) *NewMaskPtr++ = MaskTemp;
  371.       }
  372.       DispMemOffset += Size; /* mark off the space we just used */
  373.    }
  374.    return DispMemOffset - DispMemStart;
  375. }
  376.  
  377.  
  378.  
  379. [LISTING FOUR]
  380.  
  381. /* MASKIM.H: structures used for storing and manipulating masked
  382.    images */
  383. /* Describes one alignment of a mask-image pair */
  384. typedef struct {
  385.    int ImageWidth; /* image width in addresses in display memory (also
  386.                       mask width in bytes) */
  387.    unsigned int ImagePtr; /* offset of image bitmap in display mem */
  388.    char *MaskPtr;  /* pointer to mask bitmap */
  389. } AlignedMaskedImage;
  390. /* Describes all four alignments of a mask-image pair */
  391. typedef struct {
  392.    AlignedMaskedImage *Alignments[4]; /* ptrs to AlignedMaskedImage
  393.                                       structs for four possible destination 
  394.                                       image alignments */
  395. } MaskedImage;
  396.  
  397.  
  398.  
  399.  
  400. [LISTING FIVE]
  401.  
  402. /* Sample mode X VGA animation program. Portions of this code first appeared 
  403. in PC Techniques. Compiled with Borland C++ 2.0 in C compilation mode. */
  404.  
  405. #include <stdio.h>
  406. #include <conio.h>
  407. #include <dos.h>
  408. #include <math.h>
  409. #include "maskim.h"
  410.  
  411. #define SCREEN_SEG         0xA000
  412. #define SCREEN_WIDTH       320
  413. #define SCREEN_HEIGHT      240
  414. #define PAGE0_START_OFFSET 0
  415. #define PAGE1_START_OFFSET (((long)SCREEN_HEIGHT*SCREEN_WIDTH)/4)
  416. #define BG_START_OFFSET    (((long)SCREEN_HEIGHT*SCREEN_WIDTH*2)/4)
  417. #define DOWNLOAD_START_OFFSET (((long)SCREEN_HEIGHT*SCREEN_WIDTH*3)/4)
  418.  
  419. static unsigned int PageStartOffsets[2] =
  420.    {PAGE0_START_OFFSET,PAGE1_START_OFFSET};
  421. static char GreenAndBrownPattern[] =
  422.    {2,6,2,6, 6,2,6,2, 2,6,2,6, 6,2,6,2};
  423. static char PineTreePattern[] = {2,2,2,2, 2,6,2,6, 2,2,6,2, 2,2,2,2};
  424. static char BrickPattern[] = {6,6,7,6, 7,7,7,7, 7,6,6,6, 7,7,7,7,};
  425. static char RoofPattern[] = {8,8,8,7, 7,7,7,7, 8,8,8,7, 8,8,8,7};
  426.  
  427. #define SMOKE_WIDTH  7
  428. #define SMOKE_HEIGHT 7
  429. static char SmokePixels[] = {
  430.    0, 0,15,15,15, 0, 0,
  431.    0, 7, 7,15,15,15, 0,
  432.    8, 7, 7, 7,15,15,15,
  433.    8, 7, 7, 7, 7,15,15,
  434.    0, 8, 7, 7, 7, 7,15,
  435.    0, 0, 8, 7, 7, 7, 0,
  436.    0, 0, 0, 8, 8, 0, 0};
  437. static char SmokeMask[] = {
  438.    0, 0, 1, 1, 1, 0, 0,
  439.    0, 1, 1, 1, 1, 1, 0,
  440.    1, 1, 1, 1, 1, 1, 1,
  441.    1, 1, 1, 1, 1, 1, 1,
  442.    1, 1, 1, 1, 1, 1, 1,
  443.    0, 1, 1, 1, 1, 1, 0,
  444.    0, 0, 1, 1, 1, 0, 0};
  445. #define KITE_WIDTH  10
  446. #define KITE_HEIGHT 16
  447. static char KitePixels[] = {
  448.    0, 0, 0, 0,45, 0, 0, 0, 0, 0,
  449.    0, 0, 0,46,46,46, 0, 0, 0, 0,
  450.    0, 0,47,47,47,47,47, 0, 0, 0,
  451.    0,48,48,48,48,48,48,48, 0, 0,
  452.   49,49,49,49,49,49,49,49,49, 0,
  453.    0,50,50,50,50,50,50,50, 0, 0,
  454.    0,51,51,51,51,51,51,51, 0, 0,
  455.    0, 0,52,52,52,52,52, 0, 0, 0,
  456.    0, 0,53,53,53,53,53, 0, 0, 0,
  457.    0, 0, 0,54,54,54, 0, 0, 0, 0,
  458.    0, 0, 0,55,55,55, 0, 0, 0, 0,
  459.    0, 0, 0, 0,58, 0, 0, 0, 0, 0,
  460.    0, 0, 0, 0,59, 0, 0, 0, 0,66,
  461.    0, 0, 0, 0,60, 0, 0,64, 0,65,
  462.    0, 0, 0, 0, 0,61, 0, 0,64, 0,
  463.    0, 0, 0, 0, 0, 0,62,63, 0,64};
  464. static char KiteMask[] = {
  465.    0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
  466.    0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
  467.    0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
  468.    0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  469.    1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
  470.    0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  471.    0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
  472.    0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
  473.    0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
  474.    0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
  475.    0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
  476.    0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
  477.    0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
  478.    0, 0, 0, 0, 1, 0, 0, 1, 0, 1,
  479.    0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
  480.    0, 0, 0, 0, 0, 0, 1, 1, 0, 1};
  481. static MaskedImage KiteImage;
  482.  
  483. #define NUM_OBJECTS  20
  484. typedef struct {
  485.    int X,Y,Width,Height,XDir,YDir,XOtherPage,YOtherPage;
  486.    MaskedImage *Image;
  487. } AnimatedObject;
  488. AnimatedObject AnimatedObjects[] = {
  489.    {  0,  0,KITE_WIDTH,KITE_HEIGHT, 1, 1,  0,  0,&KiteImage},
  490.    { 10, 10,KITE_WIDTH,KITE_HEIGHT, 0, 1, 10, 10,&KiteImage},
  491.    { 20, 20,KITE_WIDTH,KITE_HEIGHT,-1, 1, 20, 20,&KiteImage},
  492.    { 30, 30,KITE_WIDTH,KITE_HEIGHT,-1,-1, 30, 30,&KiteImage},
  493.    { 40, 40,KITE_WIDTH,KITE_HEIGHT, 1,-1, 40, 40,&KiteImage},
  494.    { 50, 50,KITE_WIDTH,KITE_HEIGHT, 0,-1, 50, 50,&KiteImage},
  495.    { 60, 60,KITE_WIDTH,KITE_HEIGHT, 1, 0, 60, 60,&KiteImage},
  496.    { 70, 70,KITE_WIDTH,KITE_HEIGHT,-1, 0, 70, 70,&KiteImage},
  497.    { 80, 80,KITE_WIDTH,KITE_HEIGHT, 1, 2, 80, 80,&KiteImage},
  498.    { 90, 90,KITE_WIDTH,KITE_HEIGHT, 0, 2, 90, 90,&KiteImage},
  499.    {100,100,KITE_WIDTH,KITE_HEIGHT,-1, 2,100,100,&KiteImage},
  500.    {110,110,KITE_WIDTH,KITE_HEIGHT,-1,-2,110,110,&KiteImage},
  501.    {120,120,KITE_WIDTH,KITE_HEIGHT, 1,-2,120,120,&KiteImage},
  502.    {130,130,KITE_WIDTH,KITE_HEIGHT, 0,-2,130,130,&KiteImage},
  503.    {140,140,KITE_WIDTH,KITE_HEIGHT, 2, 0,140,140,&KiteImage},
  504.    {150,150,KITE_WIDTH,KITE_HEIGHT,-2, 0,150,150,&KiteImage},
  505.    {160,160,KITE_WIDTH,KITE_HEIGHT, 2, 2,160,160,&KiteImage},
  506.    {170,170,KITE_WIDTH,KITE_HEIGHT,-2, 2,170,170,&KiteImage},
  507.    {180,180,KITE_WIDTH,KITE_HEIGHT,-2,-2,180,180,&KiteImage},
  508.    {190,190,KITE_WIDTH,KITE_HEIGHT, 2,-2,190,190,&KiteImage},
  509. };
  510. void main(void);
  511. void DrawBackground(unsigned int);
  512. void MoveObject(AnimatedObject *);
  513. extern void Set320x240Mode(void);
  514. extern void FillRectangleX(int, int, int, int, unsigned int, int);
  515. extern void FillPatternX(int, int, int, int, unsigned int, char*);
  516. extern void CopySystemToScreenMaskedX(int, int, int, int, int, int,
  517.    char *, unsigned int, int, int, char *);
  518. extern void CopyScreenToScreenX(int, int, int, int, int, int,
  519.    unsigned int, unsigned int, int, int);
  520. extern unsigned int CreateAlignedMaskedImage(MaskedImage *,
  521.    unsigned int, char *, int, int, char *);
  522. extern void CopyScreenToScreenMaskedX(int, int, int, int, int, int,
  523.    MaskedImage *, unsigned int, int);
  524. extern void ShowPage(unsigned int);
  525.  
  526. void main()
  527. {
  528.    int DisplayedPage, NonDisplayedPage, Done, i;
  529.    union REGS regset;
  530.    Set320x240Mode();
  531.    /* Download the kite image for fast copying later */
  532.    if (CreateAlignedMaskedImage(&KiteImage, DOWNLOAD_START_OFFSET,
  533.          KitePixels, KITE_WIDTH, KITE_HEIGHT, KiteMask) == 0) {
  534.       regset.x.ax = 0x0003; int86(0x10, ®set, ®set);
  535.       printf("Couldn't get memory\n"); exit();
  536.    }
  537.    /* Draw the background to the background page */
  538.    DrawBackground(BG_START_OFFSET);
  539.    /* Copy the background to both displayable pages */
  540.    CopyScreenToScreenX(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0,
  541.          BG_START_OFFSET, PAGE0_START_OFFSET, SCREEN_WIDTH,
  542.          SCREEN_WIDTH);
  543.    CopyScreenToScreenX(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0,
  544.          BG_START_OFFSET, PAGE1_START_OFFSET, SCREEN_WIDTH,
  545.          SCREEN_WIDTH);
  546.    /* Move the objects and update their images in the nondisplayed
  547.       page, then flip the page, until Esc is pressed */
  548.    Done = DisplayedPage = 0;
  549.    do {
  550.       NonDisplayedPage = DisplayedPage ^ 1;
  551.       /* Erase each object in nondisplayed page by copying block from
  552.             background page at last location in that page */
  553.       for (i=0; i<NUM_OBJECTS; i++) {
  554.          CopyScreenToScreenX(AnimatedObjects[i].XOtherPage,
  555.                AnimatedObjects[i].YOtherPage,
  556.                AnimatedObjects[i].XOtherPage +
  557.                AnimatedObjects[i].Width,
  558.                AnimatedObjects[i].YOtherPage +
  559.                AnimatedObjects[i].Height,
  560.                AnimatedObjects[i].XOtherPage,
  561.                AnimatedObjects[i].YOtherPage, BG_START_OFFSET,
  562.                PageStartOffsets[NonDisplayedPage], SCREEN_WIDTH,
  563.                SCREEN_WIDTH);
  564.       }
  565.       /* Move and draw each object in the nondisplayed page */
  566.       for (i=0; i<NUM_OBJECTS; i++) {
  567.          MoveObject(&AnimatedObjects[i]);
  568.          /* Draw object into nondisplayed page at new location */
  569.          CopyScreenToScreenMaskedX(0, 0, AnimatedObjects[i].Width,
  570.                AnimatedObjects[i].Height, AnimatedObjects[i].X,
  571.                AnimatedObjects[i].Y, AnimatedObjects[i].Image,
  572.                PageStartOffsets[NonDisplayedPage], SCREEN_WIDTH);
  573.       }
  574.       /* Flip to the page into which we just drew */
  575.       ShowPage(PageStartOffsets[DisplayedPage = NonDisplayedPage]);
  576.       /* See if it's time to end */
  577.       if (kbhit()) {
  578.          if (getch() == 0x1B) Done = 1;   /* Esc to end */
  579.       }
  580.    } while (!Done);   
  581.    /* Restore text mode and done */
  582.    regset.x.ax = 0x0003; int86(0x10, ®set, ®set);
  583. }
  584. void DrawBackground(unsigned int PageStart)
  585. {
  586.    int i,j,Temp;
  587.    /* Fill the screen with cyan */
  588.    FillRectangleX(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, PageStart, 11);
  589.    /* Draw a green and brown rectangle to create a flat plain */
  590.    FillPatternX(0, 160, SCREEN_WIDTH, SCREEN_HEIGHT, PageStart,
  591.                                                       GreenAndBrownPattern);
  592.    /* Draw blue water at the bottom of the screen */
  593.    FillRectangleX(0, SCREEN_HEIGHT-30, SCREEN_WIDTH, SCREEN_HEIGHT,
  594.                                                                PageStart, 1);
  595.    /* Draw a brown mountain rising out of the plain */
  596.    for (i=0; i<120; i++)
  597.       FillRectangleX(SCREEN_WIDTH/2-30-i, 51+i, SCREEN_WIDTH/2-30+i+1,
  598.                                                       51+i+1, PageStart, 6);
  599.    /* Draw a yellow sun by overlapping rects of various shapes */
  600.    for (i=0; i<=20; i++) {
  601.       Temp = (int)(sqrt(20.0*20.0 - (float)i*(float)i) + 0.5);
  602.       FillRectangleX(SCREEN_WIDTH-25-i, 30-Temp, SCREEN_WIDTH-25+i+1,
  603.                                                   30+Temp+1, PageStart, 14);
  604.    }
  605.    /* Draw green trees down the side of the mountain */
  606.    for (i=10; i<90; i += 15)
  607.       for (j=0; j<20; j++)
  608.        FillPatternX(SCREEN_WIDTH/2+i-j/3-15, i+j+51,SCREEN_WIDTH/2+i+j/3-15+1, 
  609.                                        i+j+51+1, PageStart, PineTreePattern);
  610.    /* Draw a house on the plain */
  611.    FillPatternX(265, 150, 295, 170, PageStart, BrickPattern);
  612.    FillPatternX(265, 130, 270, 150, PageStart, BrickPattern);
  613.    for (i=0; i<12; i++)
  614.       FillPatternX(280-i*2, 138+i, 280+i*2+1, 138+i+1, PageStart, RoofPattern);
  615.    /* Finally, draw puffs of smoke rising from the chimney */
  616.    for (i=0; i<4; i++)
  617.       CopySystemToScreenMaskedX(0, 0, SMOKE_WIDTH, SMOKE_HEIGHT, 264,
  618.         110-i*20, SmokePixels, PageStart, SMOKE_WIDTH,SCREEN_WIDTH, SmokeMask);
  619. }
  620. /* Move the specified object, bouncing at the edges of the screen and
  621.    remembering where the object was before the move for erasing next time */
  622. void MoveObject(AnimatedObject * ObjectToMove) {
  623.    int X, Y;
  624.    X = ObjectToMove->X + ObjectToMove->XDir;
  625.    Y = ObjectToMove->Y + ObjectToMove->YDir;
  626.    if ((X < 0) || (X > (SCREEN_WIDTH - ObjectToMove->Width))) {
  627.       ObjectToMove->XDir = -ObjectToMove->XDir;
  628.       X = ObjectToMove->X + ObjectToMove->XDir;
  629.    }
  630.    if ((Y < 0) || (Y > (SCREEN_HEIGHT - ObjectToMove->Height))) {
  631.       ObjectToMove->YDir = -ObjectToMove->YDir;
  632.       Y = ObjectToMove->Y + ObjectToMove->YDir;
  633.    }
  634.    /* Remember previous location for erasing purposes */
  635.    ObjectToMove->XOtherPage = ObjectToMove->X;
  636.    ObjectToMove->YOtherPage = ObjectToMove->Y;
  637.    ObjectToMove->X = X; /* set new location */
  638.    ObjectToMove->Y = Y;
  639. }
  640.  
  641.  
  642.  
  643. [LISTING SIX]
  644.  
  645. ; Shows the page at the specified offset in the bitmap. Page is displayed when 
  646. ; this routine returns. This code first appeared in PC Techniques.
  647. ; C near-callable as: void ShowPage(unsigned int StartOffset);
  648.  
  649. INPUT_STATUS_1  equ     03dah   ;Input Status 1 register
  650. CRTC_INDEX      equ     03d4h   ;CRT Controller Index reg
  651. START_ADDRESS_HIGH equ  0ch     ;bitmap start address high byte
  652. START_ADDRESS_LOW equ   0dh     ;bitmap start address low byte
  653.  
  654. ShowPageParms   struc
  655.         dw      2 dup (?) ;pushed BP and return address
  656. StartOffset dw  ?       ;offset in bitmap of page to display
  657. ShowPageParms   ends
  658.         .model  small
  659.         .code
  660.         public  _ShowPage
  661. _ShowPage       proc    near
  662.         push    bp      ;preserve caller's stack frame
  663.         mov     bp,sp   ;point to local stack frame
  664. ; Wait for display enable to be active (status is active low), to be
  665. ; sure both halves of the start address will take in the same frame.
  666.         mov     bl,START_ADDRESS_LOW        ;preload for fastest
  667.         mov     bh,byte ptr StartOffset[bp] ; flipping once display
  668.         mov     cl,START_ADDRESS_HIGH       ; enable is detected
  669.         mov     ch,byte ptr StartOffset+1[bp]
  670.         mov     dx,INPUT_STATUS_1
  671. WaitDE:
  672.         in      al,dx
  673.         test    al,01h
  674.         jnz     WaitDE  ;display enable is active low (0 = active)
  675. ; Set the start offset in display memory of the page to display.
  676.         mov     dx,CRTC_INDEX
  677.         mov     ax,bx
  678.         out     dx,ax   ;start address low
  679.         mov     ax,cx
  680.         out     dx,ax   ;start address high
  681. ; Now wait for vertical sync, so the other page will be invisible when
  682. ; we start drawing to it.
  683.         mov     dx,INPUT_STATUS_1
  684. WaitVS:
  685.         in      al,dx
  686.         test    al,08h
  687.         jz      WaitVS  ;vertical sync is active high (1 = active)
  688.         pop     bp      ;restore caller's stack frame
  689.         ret
  690. _ShowPage       endp
  691.         end
  692.  
  693.  
  694.  
  695.  
  696. [LISTING SEVEN]
  697.  
  698. ;;;<change this in Listing 3 from the August, 1991 column...>
  699. ;;;     mov     dx,SC_INDEX+1   ;point to Sequence Controller Data reg
  700. ;;;                             ; (SC Index still points to Map Mask)
  701. ;;;<...to this>
  702.         mov     dx,SC_INDEX
  703.         mov     al,MAP_MASK
  704.         out     dx,al           ;point SC Index reg to Map Mask
  705.         inc     dx              ;point to SC Data reg
  706.  
  707.