comment $
    BITPLANE.ASM
    Graphics program from Michael Abrash, Power Graphics Programming
    ported to 286|DOS-Extender

    masm bitplane;
    link bitplane,,,,protmode;
        (PROTMODE.DEF contains single line: PROTMODE)
    LINK : warning L4036 : no automatic data segment

    added IFNEW NEW to switch to DOSSEG
    
    masm -DNEW bitplane;
    link bitplane,,,,protmode;
        (LINK warning went away)
    run286 bitplane
        (GP fault error=A000)

    added IFNEW DOSX286 to get protected-mode selector with DosMapRealSeg

    masm -DNEW -DDOSX286 bitplane;
    link bitplane,,,phapi,protmode;
    run286 bitplane
        WORKS!
$

; Program to demonstrate bit-plane animation. Performs
; flicker-free animation with image transparency and
; image precedence across four distinct planes, with
; 13 32x32 images kept in motion at once.
;
; Updated 6/29/89.
;
; Set to higher values to slow down on faster computers.
; 0 is fine for a PC. 500 is a reasonable setting for an AT.
; Slowing animation further allows a good look at
; transparency and the lack of flicker and color effects
; when images cross.
;
IFNDEF SLOWDOWN
SLOWDOWN        equ     0
ENDIF
;
; Plane selects for the four colors we're using.
;
RED     equ     01h
GREEN   equ     02h
BLUE    equ     04h
WHITE   equ     08h
;
VGA_SEGMENT     equ     0a000h  ;mode 10h display memory
                                ; segment
SC_INDEX        equ     3c4h    ;Sequence Controller Index
                                ; register
MAP_MASK        equ     2       ;Map Mask register index in
                                ; Sequence Controller
SCREEN_WIDTH    equ     80      ;# of bytes across screen
SCREEN_HEIGHT   equ     350     ;# of scan lines on screen
WORD_OUTS_OK    equ     1       ;set to 0 to assemble for
                                ; computers that can't
                                ; handle word outs to
                                ; indexed VGA regs
;

        extrn   DOSMAPREALSEG:far
        extrn   DOSFREESEG:far
IFDEF NEW
dosseg
.model small
.stack
ELSE
stackseg        segment para stack 'STACK'
        db      512 dup (?)
stackseg        ends
ENDIF
;
; Complete info about one object that we're animating.
;
ObjectStructure struc
Delay           dw      ?       ;used to delay for n passes
                                ; through the loop to
                                ; control animation speed
BaseDelay       dw      ?       ;reset value for Delay
Image           dw      ?       ;pointer to drawing info
                                ; for object
XCoord          dw      ?       ;object X location in pixels
XInc            dw      ?       ;# of pixels to increment
                                ; location by in the X
                                ; direction on each move
XLeftLimit      dw      ?       ;left limit of X motion
XRightLimit     dw      ?       ;right limit of X motion
YCoord          dw      ?       ;object Y location in pixels
YInc            dw      ?       ;# of pixels to increment
                                ; location by in the Y
                                ; direction on each move
YTopLimit       dw      ?       ;top limit of Y motion
YBottomLimit    dw      ?       ;bottom limit of Y motion
PlaneSelect     db      ?       ;mask to select plane to
                                ; which object is drawn
                db      ?       ;to make an even # of words
                                ; long, for better 286
                                ; performance (keeps the
                                ; following structure
                                ; word-aligned)
ObjectStructure ends
;
IFDEF NEW
.data
IFDEF DOSX286
sel                     dw      0
ENDIF
ELSE
Data    segment word 'DATA'
ENDIF
;
; Palette settings to give plane 0 precedence, followed by
; planes 1, 2, and 3. Plane 3 has the lowest precedence (is
; obscured by any other plane), while plane 0 has the
; highest precedence (displays in front of any other plane).
;
Colors  db      000h ;background color=black
        db      03ch ;plane 0 only=red
        db      03ah ;plane 1 only=green
        db      03ch ;planes 0&1=red (plane 0 priority)
        db      039h ;plane 2 only=blue
        db      03ch ;planes 0&2=red (plane 0 priority)
        db      03ah ;planes 1&2=green (plane 1 priority)
        db      03ch ;planes 0&1&2=red (plane 0 priority)
        db      03fh ;plane 3 only=white
        db      03ch ;planes 0&3=red (plane 0 priority)
        db      03ah ;planes 1&3=green (plane 1 priority)
        db      03ch ;planes 0&1&3=red (plane 0 priority)
        db      039h ;planes 2&3=blue (plane 2 priority)
        db      03ch ;planes 0&2&3=red (plane 0 priority)
        db      03ah ;planes 1&2&3=green (plane 1 priority)
        db      03ch ;planes 0&1&2&3=red (plane 0 priority)
        db      000h ;border color=black
;
; Image of a hollow square.
; There's an 8-pixel-wide blank border around all edges
; so that the image erases the old version of itself as
; it's moved and redrawn.
;
Square  label   byte
        dw      48,6    ;height in pixels, width in bytes
        rept    8
        db      0,0,0,0,0,0     ;top blank border
        endm
        .radix  2
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,00000000,00000000,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        db      0,11111111,11111111,11111111,11111111,0
        .radix  10
        rept    8
        db      0,0,0,0,0,0     ;bottom blank border
        endm
;
; Image of a hollow diamond with a smaller diamond in the
; middle.
; There's an 8-pixel-wide blank border around all edges
; so that the image erases the old version of itself as
; it's moved and redrawn.
;
Diamond label   byte
        dw      48,6    ;height in pixels, width in bytes
        rept    8
        db      0,0,0,0,0,0     ;top blank border
        endm
        .radix  2
        db      0,11110000,00000001,10000000,00001111,0
        db      0,11110000,00000011,11000000,00001111,0
        db      0,11110000,00000111,11100000,00001111,0
        db      0,11110000,00001111,11110000,00001111,0
        db      0,00000000,00011111,11111000,00000000,0
        db      0,00000000,00111110,01111100,00000000,0
        db      0,00000000,01111100,00111110,00000000,0
        db      0,00000000,11111000,00011111,00000000,0
        db      0,00000001,11110000,00001111,10000000,0
        db      0,00000011,11100000,00000111,11000000,0
        db      0,00000111,11000000,00000011,11100000,0
        db      0,00001111,10000001,10000001,11110000,0
        db      0,00011111,00000011,11000000,11111000,0
        db      0,00111110,00000111,11100000,01111100,0
        db      0,01111100,00001111,11110000,00111110,0
        db      0,11111000,00011111,11111000,00011111,0
        db      0,11111000,00011111,11111000,00011111,0
        db      0,01111100,00001111,11110000,00111110,0
        db      0,00111110,00000111,11100000,01111100,0
        db      0,00011111,00000011,11000000,11111000,0
        db      0,00001111,10000001,10000001,11110000,0
        db      0,00000111,11000000,00000011,11100000,0
        db      0,00000011,11100000,00000111,11000000,0
        db      0,00000001,11110000,00001111,10000000,0
        db      0,00000000,11111000,00011111,00000000,0
        db      0,00000000,01111100,00111110,00000000,0
        db      0,00000000,00111110,01111100,00000000,0
        db      0,00000000,00011111,11111000,00000000,0
        db      0,11110000,00001111,11110000,00001111,0
        db      0,11110000,00000111,11100000,00001111,0
        db      0,11110000,00000011,11000000,00001111,0
        db      0,11110000,00000001,10000000,00001111,0
        .radix  10
        rept    8
        db      0,0,0,0,0,0     ;bottom blank border
        endm
;
; List of objects to animate.
;
        even    ;word-align for better 286 performance
;
ObjectList      label   ObjectStructure
 ObjectStructure <1,21,Diamond,88,8,80,512,16,0,0,350,RED>
 ObjectStructure <1,15,Square,296,8,112,480,144,0,0,350,RED>
 ObjectStructure <1,23,Diamond,88,8,80,512,256,0,0,350,RED>
 ObjectStructure <1,13,Square,120,0,0,640,144,4,0,280,BLUE>
 ObjectStructure <1,11,Diamond,208,0,0,640,144,4,0,280,BLUE>
 ObjectStructure <1,8,Square,296,0,0,640,144,4,0,288,BLUE>
 ObjectStructure <1,9,Diamond,384,0,0,640,144,4,0,288,BLUE>
 ObjectStructure <1,14,Square,472,0,0,640,144,4,0,280,BLUE>
 ObjectStructure <1,8,Diamond,200,8,0,576,48,6,0,280,GREEN>
 ObjectStructure <1,8,Square,248,8,0,576,96,6,0,280,GREEN>
 ObjectStructure <1,8,Diamond,296,8,0,576,144,6,0,280,GREEN>
 ObjectStructure <1,8,Square,344,8,0,576,192,6,0,280,GREEN>
 ObjectStructure <1,8,Diamond,392,8,0,576,240,6,0,280,GREEN>
ObjectListEnd   label   ObjectStructure
;
IFNDEF NEW
Data    ends
ENDIF
;
; Macro to output a word value to a port.
;
OUT_WORD        macro
if WORD_OUTS_OK
        out     dx,ax
else
        out     dx,al
        inc     dx
        xchg    ah,al
        out     dx,al
        dec     dx
        xchg    ah,al
endif
        endm
;
; Macro to output a constant value to an indexed VGA
; register.
;
CONSTANT_TO_INDEXED_REGISTER    macro ADDRESS, INDEX, VALUE
        mov     dx,ADDRESS
        mov     ax,(VALUE shl 8) + INDEX
        OUT_WORD
        endm
;
IFDEF NEW
.code
start   proc    near
        cld
        mov     ax, DGROUP
        mov     ds, ax
ELSE
Code    segment
        assume  cs:Code, ds:Data
Start   proc    near
        cld
        mov     ax,Data
        mov     ds,ax
ENDIF

;
; Set 640x350 16-color mode.
;
        mov     ax,0010h        ;AH=0 means select mode
                                ;AL=10h means select
                                ; mode 10h
        int     10h             ;BIOS video interrupt
;
; Set the palette up to provide bit-plane precedence. If
; planes 0 & 1 overlap, the plane 0 color will be shown;
; if planes 1 & 2 overlap, the plane 1 color will be
; shown; and so on.
;
        mov     ax,(10h shl 8) + 2      ;AH = 10h means
                                        ; set palette
                                        ; registers fn
                                        ;AL = 2 means set
                                        ; all palette
                                        ; registers
        push    ds                      ;ES:DX points to
        pop     es                      ; the palette
        mov     dx,offset Colors        ; settings
        int     10h                     ;call the BIOS to
                                        ; set the palette
;
; Draw the static backdrop in plane 3. All the moving images
; will appear to be in front of this backdrop, since plane 3
; has the lowest precedence the way the palette is set up.
;
        CONSTANT_TO_INDEXED_REGISTER SC_INDEX, MAP_MASK, 08h
                                ;allow data to go to
                                ; plane 3 only
;
; Point ES to display memory for the rest of the program.
;
IFDEF DOSX286
.286
;USHORT APIENTRY DosMapRealSeg(USHORT rm_para, ULONG size, PSEL selp);
        push    VGA_SEGMENT
        push    4       ; 256k
        push    0
        push    ds
        push    offset sel
        call    far ptr DOSMAPREALSEG
        or      ax, ax
        jnz     Done
        mov     ax, sel
ELSE
        mov     ax,VGA_SEGMENT
ENDIF
        mov     es,ax
;
        sub     di,di
        mov     bp,SCREEN_HEIGHT/16     ;fill in the screen
                                        ; 16 lines at a time
BackdropBlockLoop:
        call    DrawGridCross           ;draw a cross piece
        call    DrawGridVert            ;draw the rest of a
                                        ; 15-high block
        dec     bp
        jnz     BackdropBlockLoop
        call    DrawGridCross           ;bottom line of grid
;
; Start animating!
;
AnimationLoop:
        mov     bx,offset ObjectList    ;point to the first
                                        ; object in the list
;
; For each object, see if it's time to move and draw that
; object.
;
ObjectLoop:
;
; See if it's time to move this object.
;
        dec     [bx+Delay]      ;count down delay
        jnz     DoNextObject    ;still delaying--don't move
        mov     ax,[bx+BaseDelay]
        mov     [bx+Delay],ax   ;reset delay for next time
;
; Select the plane that this object will be drawn in.
;
        mov     dx,SC_INDEX
        mov     ah,[bx+PlaneSelect]
        mov     al,MAP_MASK
        OUT_WORD
;
; Advance the X coordinate, reversing direction if either
; of the X margins has been reached.
;
        mov     cx,[bx+XCoord]          ;current X location
        cmp     cx,[bx+XLeftLimit]      ;at left limit?
        ja      CheckXRightLimit        ;no
        neg     [bx+XInc]               ;yes-reverse
CheckXRightLimit:
        cmp     cx,[bx+XRightLimit]     ;at right limit?
        jb      SetNewX                 ;no
        neg     [bx+XInc]               ;yes-reverse
SetNewX:
        add     cx,[bx+XInc]            ;move the X coord
        mov     [bx+XCoord],cx          ; & save it
;
; Advance the Y coordinate, reversing direction if either
; of the Y margins has been reached.
;
        mov     dx,[bx+YCoord]          ;current Y location
        cmp     dx,[bx+YTopLimit]       ;at top limit?
        ja      CheckYBottomLimit       ;no
        neg     [bx+YInc]               ;yes-reverse
CheckYBottomLimit:
        cmp     dx,[bx+YBottomLimit]    ;at bottom limit?
        jb      SetNewY                 ;no
        neg     [bx+YInc]               ;yes-reverse
SetNewY:
        add     dx,[bx+YInc]            ;move the Y coord
        mov     [bx+YCoord],dx          ; & save it
;
; Draw at the new location. Because of the plane select
; above, only one plane will be affected.
;
        mov     si,[bx+Image]           ;point to the
                                        ; object's image
                                        ; info
        call    DrawObject
;
; Point to the next object in the list until we run out of
; objects.
;
DoNextObject:
        add     bx,size ObjectStructure
        cmp     bx,offset ObjectListEnd
        jb      ObjectLoop
;
; Delay as specified to slow things down.
;
if SLOWDOWN
        mov     cx,SLOWDOWN
DelayLoop:
        loop    DelayLoop
endif
;
; If a key's been pressed, we're done, otherwise animate
; again.
;
CheckKey:
        mov     ah,1
        int     16h             ;is a key waiting?
        jz      AnimationLoop   ;no
        sub     ah,ah
        int     16h             ;yes-clear the key & done

IFDEF DOSX286
        push    sel
        call    far ptr DOSFREESEG
ENDIF

Done:
;
; Back to text mode.
;
        mov     ax,0003h        ;AH=0 means select mode
                                ;AL=03h means select
                                ; mode 03h
        int     10h

;
; Back to DOS.
;
        mov     ah,4ch          ;DOS terminate function
        int     21h             ;done
;
Start   endp
;
; Draws a single grid cross-element at the display memory
; location pointed to by ES:DI. 1 horizontal line is drawn
; across the screen.
;
; Input: ES:DI points to the address at which to draw
;
; Output: ES:DI points to the address following the
;               line drawn
;
; Registers altered: AX, CX, DI
;
DrawGridCross   proc    near
        mov     ax,0ffffh       ;draw a solid line
        mov     cx,SCREEN_WIDTH/2-1
        rep     stosw           ;draw all but the rightmost
                                ; edge
        mov     ax,0080h
        stosw                   ;draw the right edge of the
                                ; grid
        ret
DrawGridCross   endp
;
; Draws the noncross part of the grid at the display-memory
; location pointed to by ES:DI. 15 scan lines are filled.
;
; Input: ES:DI points to the address at which to draw
;
; Output: ES:DI points to the address following the
;               part of the grid drawn
;
; Registers altered: AX, CX, DX, DI
;
DrawGridVert    proc    near
        mov     ax,0080h        ;pattern for a vertical line
        mov     dx,15           ;draw 15 scan lines (all of
                                ; a grid block except the
                                ; solid cross line)
BackdropRowLoop:
        mov     cx,SCREEN_WIDTH/2
        rep     stosw           ;draw this scan line's bit
                                ; of all the vertical lines
                                ; on the screen
        dec     dx
        jnz     BackdropRowLoop
        ret
DrawGridVert    endp
;
; Draws the specified image at the specified location.
; Images are drawn on byte boundaries horizontally, pixel
; boundaries vertically.
; The Map Mask register must already have been set to enable
; access to the desired plane.
;
; Input:
;       CX - X coordinate of upper left corner
;       DX - Y coordinate of upper left corner
;       DS:SI - pointer to draw info for image
;       ES - display memory segment
;
; Output: none
;
; Registers altered: AX, CX, DX, SI, DI, BP
;
DrawObject      proc    near
        mov     ax,SCREEN_WIDTH
        mul     dx      ;calculate the start offset in
                        ; display memory of the row the
                        ; image will be drawn at
IFDEF DOSX286
.286
        shr     cx,3
ELSE
        shr     cx,1
        shr     cx,1
        shr     cx,1    ;divide the X coordinate in pixels
                        ; by 8 to get the X coordinate in
                        ; bytes
ENDIF
        add     ax,cx   ;destination offset in display
                        ; memory for the image
        mov     di,ax   ;point ES:DI to the address to
                        ; which the image will be copied
                        ; in display memory
        lodsw
        mov     dx,ax   ;# of lines in the image
        lodsw           ;# of bytes across the image
        mov     bp,SCREEN_WIDTH
        sub     bp,ax   ;# of bytes to add to the display
                        ; memory offset after copying a line
                        ; of the image to display memory in
                        ; order to point to the address
                        ; where the next line of the image
                        ; will go in display memory
DrawLoop:
        mov     cx,ax   ;width of the image
        rep     movsb   ;copy the next line of the image
                        ; into display memory
        add     di,bp   ;point to the address at which the
                        ; next line will go in display
                        ; memory
        dec     dx      ;count down the lines of the image
        jnz     DrawLoop
        ret
DrawObject      endp
;
IFNDEF NEW
Code    ends
ENDIF
        end     Start
