;**LISTING 28.1 L28-1.ASM**

; 281L281.ASM

;```nasm

; Program to illustrate the use of the Read Map register in read mode 0.
; Animates by copying a 16-color image from VGA memory to system memory,
; one plane at a time, then copying the image back to a new location
; in VGA memory.
;
; By Michael Abrash
;
stack segment word stack 'STACK'
 db 512 dup (?)
stack ends
;
data segment    word 'DATA'
IMAGE_WIDTH    EQU   4           ;in bytes
IMAGE_HEIGHT   EQU   32          ;in pixels
LEFT_BOUND     EQU   10          ;in bytes
RIGHT_BOUND    EQU   66          ;in bytes
VGA_SEGMENT    EQU   0a000h
SCREEN_WIDTH   EQU   80          ;in bytes
SC_INDEX       EQU   3c4h        ;Sequence Controller Index register
GC_INDEX       EQU   3ceh        ;Graphics Controller Index register
MAP_MASK       EQU   2           ;Map Mask register index in SC
READ_MAP       EQU   4           ;Read Map register index in GC
;
                                 ; Base pattern for 16-color image.
;
PatternPlane0    label byte
     db    32 dup (0ffh,0ffh,0,0)
PatternPlane1    label byte
     db    32 dup (0ffh,0,0ffh,0)
PatternPlane2    label byte
     db    32 dup (0f0h,0f0h,0f0h,0f0h)
PatternPlane3    label byte
     db    32 dup (0cch,0cch,0cch,0cch)
;
; Temporary storage for 16-color image during animation.
;
ImagePlane0 db   32*4 dup (?)
ImagePlane1 db   32*4 dup (?)
ImagePlane2 db   32*4 dup (?)
ImagePlane3 db   32*4 dup (?)
;
; Current image location & direction.
;
ImageX          dw  40           ;in bytes
ImageY          dw  100          ;in pixels
ImageXDirection dw  1            ;in bytes
data ends
;
code segment    word 'CODE'
     assume     cs:code,ds:data
Start proc  near
     cld
     mov  ax,data
     mov  ds,ax
;
; Select graphics mode 10h.
;
     mov  ax,10h
     int  10h
;
; Draw the initial image.
;
     mov  si,offset PatternPlane0
     call DrawImage
;
; Loop to animate by copying the image from VGA memory to system memory,
; erasing the image, and copying the image from system memory to a new
; location in VGA memory. Ends when a key is hit.
;
AnimateLoop:
;
; Copy the image from VGA memory to system memory.
;
     mov  di,offset ImagePlane0
     call GetImage
;
; Clear the image from VGA memory.
;
     call EraseImage
;
; Advance the image X coordinate, reversing direction if either edge
; of the screen has been reached.
;
     mov  ax,[ImageX]
     cmp  ax,LEFT_BOUND
     jz   ReverseDirection
     cmp  ax,RIGHT_BOUND
     jnz  SetNewX
ReverseDirection:
     neg  [ImageXDirection]
SetNewX:
     add  ax,[ImageXDirection]
     mov  [ImageX],ax
;
; Draw the image by copying it from system memory to VGA memory.
;
     mov  si,offset ImagePlane0
     call DrawImage
;
; Slow things down a bit for visibility (adjust as needed).
;
     mov  cx,0
DelayLoop:
     loop DelayLoop
;
; See if a key has been hit, ending the program.
;
     mov  ah,1
     int  16h
     jz   AnimateLoop
;
; Clear the key, return to text mode, and return to DOS.
;
     sub  ah,ah
     int  16h
     mov  ax,3
     int  10h
     mov  ah,4ch
     int  21h
Start endp
;
; Draws the image at offset DS:SI to the current image location in
; VGA memory.
;
DrawImage proc near
     mov  ax,VGA_SEGMENT
     mov  es,ax
     call GetImageOffset   ;ES:DI is the destination address for the
                      ; image in VGA memory
     mov  dx,SC_INDEX
     mov  al,1        ;do plane 0 first
DrawImagePlaneLoop:
     push di          ;image is drawn at the same offset in
                      ; each plane
     push ax          ;preserve plane select
     mov  al,MAP_MASK ;Map Mask index
     out  dx,al       ;point SC Index to the Map Mask register
     pop  ax          ;get back plane select
     inc  dx          ;point to SC index register
     out  dx,al       ;set up the Map Mask to allow writes to
                      ; the plane of interest
     dec  dx          ;point back to SC Data register
     mov  bx,IMAGE_HEIGHT  ;# of scan lines in image
DrawImageLoop:
     mov  cx,IMAGE_WIDTH   ;# of bytes across image
     rep  movsb
     add  di,SCREEN_WIDTH-IMAGE_WIDTH
                           ;point to next scan line of image
     dec  bx               ;any more scan lines?
     jnz  DrawImageLoop
     pop  di               ;get back image start offset in VGA memory
     shl  al,1             ;Map Mask setting for next plane
     cmp  al,10h           ;have we done all four planes?
     jnz  DrawImagePlaneLoop
     ret
DrawImage endp
;
; Copies the image from its current location in VGA memory into the
; buffer at DS:DI.
;
GetImage  proc near
     mov  si,di             ;move destination offset into SI
     call GetImageOffset    ;DI is offset of image in VGA memory
     xchg si,di             ;SI is offset of image, DI is destination offset
     push ds
     pop  es                ;ES:DI is destination
     mov  ax,VGA_SEGMENT
     mov  ds,ax             ;DS:SI is source
;
     mov  dx,GC_INDEX
     sub  al,al;do plane 0 first
GetImagePlaneLoop:
     push si                ;image comes from same offset in each plane
     push ax                ;preserve plane select
     mov  al,READ_MAP       ;Read Map index
     out  dx,al             ;point GC Index to Read Map register
     pop  ax                ;get back plane select
     inc  dx                ;point to GC Index register
     out  dx,al             ;set up the Read Map to select reads from
                            ; the plane of interest
     dec  dx                ;point back to GC data register
     mov  bx,IMAGE_HEIGHT   ;# of scan lines in image
GetImageLoop:
     mov  cx,IMAGE_WIDTH    ;# of bytes across image
     rep  movsb
     add  si,SCREEN_WIDTH-IMAGE_WIDTH
                            ;point to next scan line of image
     dec  bx                ;any more scan lines?
     jnz  GetImageLoop
     pop  si                ;get back image start offset
     inc  al                ;Read Map setting for next plane
     cmp  al,4              ;have we done all four planes?
     jnz  GetImagePlaneLoop
     push es
     pop  ds                ;restore original DS
     ret
GetImage endp
;
; Erases the image at its current location.
;
EraseImage proc near
     mov  dx,SC_INDEX
     mov  al,MAP_MASK
     out  dx,al             ;point SC Index to the Map Mask register
     inc  dx                ;point to SC Data register
     mov  al,0fh
     out  dx,al             ;set up the Map Mask to allow writes to go to
                            ; all 4 planes
     mov  ax,VGA_SEGMENT
     mov  es,ax
     call GetImageOffset    ;ES:DI points to the start address
                            ; of the image
     sub  al,al             ;erase with zeros
     mov  bx,IMAGE_HEIGHT   ;# of scan lines in image
EraseImageLoop:
     mov  cx,IMAGE_WIDTH    ;# of bytes across image
     rep  stosb
     add  di,SCREEN_WIDTH-IMAGE_WIDTH
                            ;point to next scan line of image
     dec  bx                ;any more scan lines?
     jnz  EraseImageLoop
     ret
EraseImage endp
;
; Returns the current offset of the image in the VGA segment in DI.
;
GetImageOffset proc near
     mov  ax,SCREEN_WIDTH
     mul  [ImageY]
     add  ax,[ImageX]
     mov  di,ax
     ret
GetImageOffset endp
code  ends
     end  Start
```

