;       VGAMOIRE.COM

; Structured pattern generator to show off the undocumented
; 320x400 256 color mode of the IBM VGA as revealed by
; Michael Abrash in his "On Graphics" column in the
; January/February 1989 issue of Programmer's Journal, Volume 7.1


VGA_SEGMENT             equ     0A000h
SC_INDEX                equ     3C4h
GC_INDEX                equ     3CEh
CRTC_INDEX              equ     3D4h
MAP_MASK                equ     2
MEMORY_MODE             equ     4
MAX_SCAN_LINE           equ     9
START_ADDRESS_HIGH      equ     0Ch
UNDERLINE               equ     14h
MODE_CONTROL            equ     17h
READ_MAP                equ     4
GRAPHICS_MODE           equ     5
MISCELLANEOUS           equ     6
SCREEN_WIDTH            equ     320
SCREEN_HEIGHT           equ     400

tab                     equ     9
lf                      equ     10
cr                      equ     13


CONSTANT_TO_INDEXED_REGISTER    macro   ADDRESS, INDEX, VALUE
        mov     dx, ADDRESS
        mov     ax, (VALUE shl 8) + INDEX
        out     dx, ax
                                endm

code    segment
        org     100h
        assume  cs:code, ds:code


Begin:

        push    cs
        pop     ds

        mov     ax, VGA_SEGMENT
        mov     es, ax

        call    Set320x400Mode

        mov     ah, 2Ch
        int     21h
        mov     Random, dx

        mov     si, 256         ; ColorCount

        call    Randy
        mov     bp, ax
        call    DoCircles

MainLoop:
        call    MakeRandomPalette

        cmp     PageNo, 0
        je      SetPage1
        mov     ax, 0A800h
        mov     es, ax
        CONSTANT_TO_INDEXED_REGISTER CRTC_INDEX,START_ADDRESS_HIGH,0
        mov     PageNo, 0
        jmp     Start
SetPage1:
        mov     ax, 0A000h
        mov     es, ax
        CONSTANT_TO_INDEXED_REGISTER CRTC_INDEX,START_ADDRESS_HIGH,80h
        mov     PageNo, 1

Start:
        call    Randy
        and     ax, 0003h
        cmp     al, 0
        je      Circ
        cmp     al, 1
        je      Sqar
        cmp     al, 2
        je      TiltCirc
        jmp     TiltSqar

Circ:
        call    Randy
        inc     ax
        mov     bp, ax
        call    DoCircles
        jmp     CheckKey

Sqar:
        call    Randy
        inc     ax
        mov     bp, ax
        call    DoSquares
        jmp     CheckKey

TiltCirc:
        call    Randy
        inc     ax
        mov     bp, ax
        call    DoTiltCircles
        jmp     CheckKey

TiltSqar:
        call    Randy
        inc     ax
        mov     bp, ax
        call    DoTiltSquares

CheckKey:
        mov     ah, 0Bh
        int     21h
        cmp     al, 0
        jne     GetOut
        jmp     MainLoop

GetOut:
        mov     ax, 0003h       ; Back to Text Mode
        int     10h

        mov     ax, 4C00h
        int     21h

PageNo          dw      1


DoCircles               proc    near

        mov     bl, 00010001b   ; Plane Mask bit
        mov     di, 0           ; Video memory pointer

        mov     cx, 0           ; For Y := 0 to 399 do
ForYCirc:
        push    cx
        mov     cx, 0           ; For X := 0 to 319 do
ForXCirc:
        mov     ax, bp          ; Get IterateCnt
        mul     cx              ; DX:AX := IterateCnt * X
        add     si, ax          ; ColorCount := ColorCount + (IterateCnt * X)

        mov     dx, SC_INDEX    ; Select the correct plane
        mov     ah, bl
        mov     al, MAP_MASK
        out     dx, ax

        mov     ax, si          ; Poke Color
        mov     byte ptr es:[di], al

        rol     bl, 1           ; Rotate for next plane
        cmp     bl, 00010001b   ; Did we rotate four times?
        jne     DoNextXCirc     ; If not then do the next pixel
        inc     di              ; Otherwise bump the Video MemPtr

DoNextXCirc:
        inc     cx              ; Next X
        cmp     cx, 320
        jl      ForXCirc

        pop     cx

        mov     ax, bp          ; Get IterateCnt
        mul     cx              ; DX:AX := IterateCnt * Y
        add     si, ax          ; ColorCount := ColorCount + (IterateCnt * Y)

        inc     cx              ; Next Y
        cmp     cx, 400
        jl      ForYCirc

        ret

DoCircles               endp


DoTiltCircles           proc    near

        mov     bl, 00010001b   ; Plane Mask bit
        mov     di, 0           ; Video memory pointer

        mov     cx, 0           ; For Y := 0 to 399 do
ForYTiltCirc:
        push    cx
        mov     cx, 0           ; For X := 0 to 319 do
ForXTiltCirc:
        mov     ax, bp          ; Get IterateCnt
        mul     cx              ; DX:AX := IterateCnt * X
        add     si, ax          ; ColorCount := ColorCount + (IterateCnt * X)

        mov     dx, SC_INDEX    ; Select the correct plane
        mov     ah, bl
        mov     al, MAP_MASK
        out     dx, ax

        mov     ax, si          ; Poke Color
        mov     byte ptr es:[di], al

        rol     bl, 1           ; Rotate for next plane
        cmp     bl, 00010001b   ; Did we rotate four times?
        jne     DoNextXTiltCirc ; If not then do the next pixel
        inc     di              ; Otherwise bump the Video MemPtr

DoNextXTiltCirc:
        inc     cx              ; Next X
        cmp     cx, 320
        jl      ForXTiltCirc

        pop     cx

        mov     ax, bp          ; Get IterateCnt
        mul     cx              ; DX:AX := IterateCnt * Y
        sub     si, ax          ; ColorCount := ColorCount - (IterateCnt * Y)

        inc     cx              ; Next Y
        cmp     cx, 400
        jl      ForYTiltCirc

        ret

DoTiltCircles           endp


DoSquares               proc    near

        mov     bl, 00010001b   ; Plane Mask bit
        mov     di, 0           ; Video memory pointer

        mov     cx, 0           ; For Y := 0 to 399 do
ForYSqr:
        mov     CurrY, cx
        push    cx
        mov     cx, 0           ; For X := 0 to 319 do
ForXSqr:
        mov     CurrX, cx
        mov     ax, bp          ; Get IterateCnt
        mov     dx, CurrY       ; Get current Y
        mul     dx              ; DX:AX := IterateCnt * Y
        add     si, ax          ; ColorCount := ColorCount + (IterateCnt * Y)

        mov     dx, SC_INDEX    ; Select the correct plane
        mov     ah, bl
        mov     al, MAP_MASK
        out     dx, ax

        mov     ax, si          ; Poke Color
        mov     byte ptr es:[di], al

        rol     bl, 1           ; Rotate for next plane
        cmp     bl, 00010001b   ; Did we rotate four times?
        jne     DoNextXSqr      ; If not then do the next pixel
        inc     di              ; Otherwise bump the Video MemPtr

DoNextXSqr:
        inc     cx              ; Next X
        cmp     cx, 320
        jl      ForXSqr

        pop     cx

        mov     ax, bp          ; Get IterateCnt
        mov     dx, CurrX       ; Get current X
        mul     dx              ; DX:AX := IterateCnt * X
        add     si, ax          ; ColorCount := ColorCount + (IterateCnt * X)

        inc     cx              ; Next Y
        cmp     cx, 400
        jl      ForYSqr

        ret

CurrX           dw      (?)
CurrY           dw      (?)

DoSquares               endp


DoTiltSquares           proc    near

        mov     bl, 00010001b   ; Plane Mask bit
        mov     di, 0           ; Video memory pointer

        mov     cx, 0           ; For Y := 0 to 399 do
ForYTiltSqr:
        mov     CurrTiltY, cx
        push    cx
        mov     cx, 0           ; For X := 0 to 319 do
ForXTiltSqr:
        mov     CurrTiltX, cx
        mov     ax, bp          ; Get IterateCnt
        mov     dx, CurrTiltY   ; Get current Y
        mul     dx              ; DX:AX := IterateCnt * Y
        add     si, ax          ; ColorCount := ColorCount + (IterateCnt * Y)

        mov     dx, SC_INDEX    ; Select the correct plane
        mov     ah, bl
        mov     al, MAP_MASK
        out     dx, ax

        mov     ax, si          ; Poke Color
        mov     byte ptr es:[di], al

        rol     bl, 1           ; Rotate for next plane
        cmp     bl, 00010001b   ; Did we rotate four times?
        jne     DoNextXTiltSqr  ; If not then do the next pixel
        inc     di              ; Otherwise bump the Video MemPtr

DoNextXTiltSqr:
        inc     cx              ; Next X
        cmp     cx, 320
        jl      ForXTiltSqr

        pop     cx

        mov     ax, bp          ; Get IterateCnt
        mov     dx, CurrTiltX   ; Get current X
        mul     dx              ; DX:AX := IterateCnt * X
        add     si, ax          ; ColorCount := ColorCount + (IterateCnt * X)

        inc     cx              ; Next Y
        cmp     cx, 400
        jl      ForYTiltSqr

        ret

CurrTiltX       dw      (?)
CurrTiltY       dw      (?)

DoTiltSquares           endp


Randy                   proc    near

;       Returns a pseudo random integer in AX

GetTime:
        mov     ah, 2Ch
        int     21h

        mov     ax, Random
        add     ax, 13
        mul     dx
        add     dx, 23
        mul     dx
        shr     dx, 1
        shr     dx, 1
        shr     dx, 1
        add     ax, dx
        and     ax, 7FFFh
        mov     Random, ax

        ret

Random  dw      (?)

Randy                   endp


MakeRandomPalette       proc    near

        call    Randy
        mov     byte ptr RED, al
        call    Randy
        mov     byte ptr GREEN, al
        call    Randy
        mov     byte ptr BLUE, al

        mov     cx, 0
NextPal:
        mov     al, cl
        mov     dx, 03C8h       ; Set Write PEL Address
        out     dx, al
        inc     dx

        mov     al, byte ptr RED        ; Get RED
        and     al, 3Fh
        out     dx, al

        mov     al, byte ptr GREEN      ; Get GREEN
        and     al, 3Fh
        out     dx, al

        mov     al, byte ptr BLUE       ; Get BLUE
        and     al, 3Fh
        out     dx, al

        inc     byte ptr RED
        inc     byte ptr GREEN
        inc     byte ptr BLUE
        inc     cx
        cmp     cx, 256
        jl      NextPal

        ret

RED     db      (?)
GREEN   db      (?)
BLUE    db      (?)

MakeRandomPalette               endp


Set320x400Mode          proc    near

        mov     ax, 0013h
        int     10h

        mov     dx, SC_INDEX
        mov     al, MEMORY_MODE
        out     dx, al
        inc     dx
        in      al, dx
        and     al, NOT 08h
        or      al, 04h
        out     dx, al
        mov     dx, GC_INDEX
        mov     al, GRAPHICS_MODE
        out     dx, al
        inc     dx
        in      al, dx
        and     al, NOT 10h
        out     dx, al
        dec     dx
        mov     al, MISCELLANEOUS
        out     dx, al
        inc     dx
        in      al, dx
        and     al, NOT 02h
        out     dx, al

        CONSTANT_TO_INDEXED_REGISTER  SC_INDEX, MAP_MASK, 0Fh

        mov     ax, VGA_SEGMENT
        mov     es, ax
        sub     di, di
        mov     ax, di
        mov     cx, 8000h
        rep     stosw

        mov     dx, CRTC_INDEX
        mov     al, MAX_SCAN_LINE
        out     dx, al
        inc     dx
        in      al, dx
        and     al, NOT 1Fh
        out     dx, al
        dec     dx

        mov     al, UNDERLINE
        out     dx, al
        inc     dx
        in      al, dx
        and     al, NOT 40h
        out     dx, al
        dec     dx
        mov     al, MODE_CONTROL
        out     dx, al
        inc     dx
        in      al, dx
        or      al, 40h
        out     dx, al

        ret

Set320x400Mode          endp


code    ends

End     Begin
