; Author unknown - 360x480x256 vid mode
;
; *** Listing 1 ***
;
; 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

