;**LISTING 31.1 L31-1.ASM**

; 311L311.ASM


;```nasm

; Program to demonstrate pixel drawing in 320x400 256-color
; mode on the VGA. Draws 8 lines to form an octagon, a pixel
; at a time. Draws 8 octagons in all, one on top of the other,
; each in a different color set. Although it's not used, a
; pixel read function is also provided.
;
VGA_SEGMENT              equ        0a000h
SC_INDEX                 equ        3c4h        ;Sequence Controller Index register
GC_INDEX                 equ        3ceh        ;Graphics Controller Index register
CRTC_INDEX               equ        3d4h        ;CRT Controller Index register
MAP_MASK                 equ        2           ;Map Mask register index in SC
MEMORY_MODE              equ        4           ;Memory Mode register index in SC
MAX_SCAN_LINE            equ        9           ;Maximum Scan Line reg index in CRTC
START_ADDRESS_HIGH       equ        0ch         ;Start Address High reg index in CRTC
UNDERLINE                equ        14h         ;Underline Location reg index in CRTC
MODE_CONTROL             equ        17h         ;Mode Control register index in CRTC
READ_MAP                 equ        4           ;Read Map register index in GC
GRAPHICS_MODE            equ        5           ;Graphics Mode register index in GC
MISCELLANEOUS            equ        6           ;Miscellaneous register index in GC
SCREEN_WIDTH             equ        320         ;# of pixels across screen
SCREEN_HEIGHT            equ        400         ;# 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 registers
;
stack        segment     para stack 'STACK'
                  db     512 dup (?)
stack        ends
;
Data         segment     word 'DATA'
;
BaseColor         db     0
;
; Structure used to control drawing of a line.
;
LineControl struc
StartX           dw   ?
StartY           dw   ?
LineXInc         dw   ?
LineYInc         dw   ?
BaseLength       dw   ?
LineColor        db   ?
LineControl      ends
;
; List of descriptors for lines to draw.
;
LineList      label    LineControl
      LineControl <130,110,1,0,60,0>
      LineControl <190,110,1,1,60,1>
      LineControl <250,170,0,1,60,2>
      LineControl <250,230,-1,1,60,3>
      LineControl <190,290,-1,0,60,4>
      LineControl <130,290,-1,-1,60,5>
      LineControl <70,230,0,-1,60,6>
      LineControl <70,170,1,-1,60,7>
      LineControl <-1,0,0,0,0,0>
Data ends
;
; 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
;
Code    segment
        assume        cs:Code, ds:Data
Start   proc  near
        mov   ax,Data
        mov   ds,ax
;



; Set 320x400 256-color mode.
;
     call  Set320By400Mode
;
; We're in 320x400 256-color mode. Draw each line in turn.
;
ColorLoop:
     mov   si,offset LineList     ;point to the start of the
                                  ; line descriptor list
LineLoop:
     mov   cx,[si+StartX]         ;set the initial X coordinate
     cmp   cx,-1
     jz    LinesDone              ;a descriptor with a -1 X
                                  ; coordinate marks the end
                                  ; of the list
     mov   dx,[si+StartY]         ;set the initial Y coordinate,
     mov   bl,[si+LineColor]      ; line color,
     mov   bp,[si+BaseLength]     ; and pixel count
     add   bl,[BaseColor]         ;adjust the line color according
                                  ; to BaseColor
PixelLoop:
     push  cx                     ;save the coordinates
     push  dx
     call  WritePixel             ;draw this pixel
     pop   dx                     ;retrieve the coordinates
     pop   cx
     add   cx,[si+LineXInc]       ;set the coordinates of the
     add   dx,[si+LineYInc]       ; next point of the line
     dec   bp                     ;any more points?
     jnz   PixelLoop              ;yes, draw the next
     add   si,size LineControl    ;point to the next line descriptor
     jmp   LineLoop               ; and draw the next line
LinesDone:
     call  GetNextKey             ;wait for a key, then
     inc   [BaseColor]            ; bump the color selection and
     cmp   [BaseColor],8          ; see if we're done
     jb    ColorLoop              ;not done yet
;
; Wait for a key and return to text mode and end when
; one is pressed.
;
     call   GetNextKey
     mov    ax,0003h
     int    10h                    ;text mode
     mov    ah,4ch
     int    21h   ;done
;
Start endp
;
; Sets up 320x400 256-color modes.
;
; Input: none
;
; Output: none
;
Set320By400Mode   proc   near
;
; First, go to normal 320x200 256-color mode, which is really a
; 320x400 256-color mode with each line scanned twice.
;
        mov        ax,0013h        ;AH = 0 means mode set, AL = 13h selects
                                   ; 256-color graphics mode
        int        10h             ;BIOS video interrupt
;
; Change CPU addressing of video memory to linear (not odd/even,
; chain, or chain 4), to allow us to access all 256K of display
; memory. When this is done, VGA memory will look just like memory
; in modes 10h and 12h, except that each byte of display memory will
; control one 256-color pixel, with 4 adjacent pixels at any given
; address, one pixel per plane.
;
     mov    dx,SC_INDEX
     mov    al,MEMORY_MODE
     out    dx,al
     inc    dx
     in     al,dx
     and    al,not 08h                   ;turn off chain 4
     or     al,04h                       ;turn off odd/even
     out    dx,al
     mov    dx,GC_INDEX
     mov    al,GRAPHICS_MODE
     out    dx,al
     inc    dx
     in     al,dx
     and    al,not 10h                   ;turn off odd/even
     out    dx,al
     dec    dx
     mov    al,MISCELLANEOUS
     out    dx,al
     inc    dx
     in     al,dx
     and    al,not 02h                   ;turn off chain
     out    dx,al
;
; Now clear the whole screen, since the mode 13h mode set only
; cleared 64K out of the 256K of display memory. Do this before
; we switch the CRTC out of mode 13h, so we don't see garbage
; on the screen when we make the switch.
;
CONSTANT_TO_INDEXED_REGISTER SC_INDEX,MAP_MASK,0fh
                                     ;enable writes to all planes, so
                                     ; we can clear 4 pixels at a time
        mov    ax,VGA_SEGMENT
        mov    es,ax
        sub    di,di
        mov    ax,di
        mov    cx,8000h   ;# of words in 64K
        cld    
        rep    stosw      ;clear all of display memory
;
; Tweak the mode to 320x400 256-color mode by not scanning each
; line twice.
;
        mov   dx,CRTC_INDEX
        mov   al,MAX_SCAN_LINE
        out   dx,al
        inc   dx
        in    al,dx
        and   al,not 1fh         ;set maximum scan line = 0
        out   dx,al
        dec   dx
;
; Change CRTC scanning from doubleword mode to byte mode, allowing
; the CRTC to scan more than 64K of video data.
;
        mov    al,UNDERLINE
        out    dx,al
        inc    dx
        in     al,dx
        and    al,not 40h        ;turn off doubleword
        out    dx,al
        dec    dx
        mov    al,MODE_CONTROL
        out    dx,al
        inc    dx
        in    al,dx
        or    al,40h             ;turn on the byte mode bit, so memory is
                                 ; scanned for video data in a purely
                                 ; linear way, just as in modes 10h and 12h
        out    dx,al
        ret 
Set320By400Mode        endp
;
; Draws a pixel in the specified color at the specified
; location in 320x400 256-color mode.
;
; Input:
;    CX = X coordinate of pixel
;    DX = Y coordinate of pixel
;    BL = pixel color
;
; Output: none
;
; Registers altered: AX, CX, DX, DI, ES
;
WritePixel proc near
        mov    ax,VGA_SEGMENT
        mov    es,ax                    ;point to display memory
        mov    ax,SCREEN_WIDTH/4
                                        ;there are 4 pixels at each address, so
                                        ; each 320-pixel row is 80 bytes wide
                                        ; in each plane
        mul   dx                        ;point to start of desired row
        push  cx                        ;set aside the X coordinate
        shr   cx,1                      ;there are 4 pixels at each address
        shr   cx,1                      ; so divide the X coordinate by 4
        add   ax,cx                     ;point to the pixel's address
        mov   di,ax
        pop   cx                        ;get back the X coordinate
        and   cl,3                      ;get the plane # of the pixel
        mov   ah,1
        shl   ah,cl                     ;set the bit corresponding to the plane
                                        ; the pixel is in
        mov   al,MAP_MASK
        mov   dx,SC_INDEX
        OUT_WORD                        ;set to write to the proper plane for
                                        ; the pixel
        mov  es:[di],bl                 ;draw the pixel
        ret
WritePixel endp
;
; Reads the color of the pixel at the specified location in 320x400
; 256-color mode.
;
; Input:
;     CX = X coordinate of pixel to read
;     DX = Y coordinate of pixel to read
;
; Output:
;     AL = pixel color
;
; Registers altered: AX, CX, DX, SI, ES
;
ReadPixel proc near
        mov   ax,VGA_SEGMENT
        mov   es,ax                        ;point to display memory
        mov   ax,SCREEN_WIDTH/4
                                           ;there are 4 pixels at each address, so
                                           ; each 320-pixel row is 80 bytes wide
                                           ; in each plane
        mul    dx                          ;point to start of desired row
        push   cx                          ;set aside the X coordinate
        shr    cx,1                        ;there are 4 pixels at each address
        shr    cx,1                        ; so divide the X coordinate by 4
        add    ax,cx                       ;point to the pixel's address
        mov    si,ax
        pop    ax                          ;get back the X coordinate
        and    al,3                        ;get the plane # of the pixel
        mov    ah,al
        mov    al,READ_MAP
        mov    dx,GC_INDEX
        OUT_WORD                           ;set to read from the proper plane for
                                           ; the pixel
        lods byte ptr es:[si]              ;read the pixel
        ret
ReadPixel endp
;
; Waits for the next key and returns it in AX.
;
; Input: none
;
; Output:
;     AX = full 16-bit code for key pressed
;
GetNextKey   proc   near
WaitKey:
        mov   ah,1
        int   16h
        jz    WaitKey                        ;wait for a key to become available
        sub   ah,ah
        int   16h                            ;read the key
        ret
GetNextKey   endp
;
Code   ends
;
end    Start

;```

