;******************************************************;
;* PLASMA.ASM -- Simple plasma display, with rotating *;
;* palette.  Requires VGA, 186.                       *;
;******************************************************;

Ideal
Jumps

Macro       GETPIX x, y             ;Getpixel macro

            mov bx,x
            mov cx,y
            add bh,cl
            shl cx,6
            add bx,cx
            mov al,[es:bx]

EndM

Macro       SETPIX x, y             ;Setpixel macro

            mov bx,x
            mov cx,y
            add bh,cl
            shl cx,6
            add bx,cx
            mov [es:bx],al

EndM

Model Tiny
P186
CodeSeg
Org 100h

Proc        Prog

            mov ax,13h              ;Set video mode 13h
            int 10h

            mov di,offset Palette   ;Zero the palette
            xor al,al
            mov cx,576
            rep stosw

            mov ax,3F00h            ;AL = 0, AH = 63, BX = 0
            xor bx,bx

PalLoop:    mov [Palette+bx],al     ;Set palette values
            mov [Palette+bx+2],ah
            mov [Palette+bx+192],ah
            mov [Palette+bx+193],al
            mov [Palette+bx+385],ah
            mov [Palette+bx+386],al

            add bx,3                ;Advance pointers
            inc al
            dec ah                  ;Loop back
            jns PalLoop

            mov dx,03C8h            ;Set register 1
            mov al,1
            out dx,al
            inc dx                  ;Send palette
            mov si,offset Palette
            mov cx,576
            rep outsb

            push 0                  ;ES = 0
            pop es
            mov ax,[es:046Ch]       ;Seed RNG
            mov bx,[es:046Eh]
            mov [RandNum],ax
            mov [RandNum+2],bx

            push 0A000h             ;ES = 0A000h
            pop es

            call rand192            ;Set corner pixels to random colors
            mov [es:0],al
            call rand192
            mov [es:319],al
            call rand192
            mov [es:63360],al
            call rand192
            mov [es:63999],al

            push 199 319 0 0        ;Draw plasma
            call Plasma

            push cs                 ;ES = CS
            pop es

            mov si,offset Palette   ;Double palette for rotation
            mov di,offset Palette+576
            mov cx,576
            rep movsb

            xor bx,bx               ;BX = 0

Rotate:     in al,60h               ;Check for Escape key
            dec al
            jz Done

            mov dx,03DAh            ;Status port

VRTloop1:   in al,dx                ;Wait for next retrace
            test al,8
            jnz VRTloop1
VRTloop2:   in al,dx
            test al,8
            jz VRTloop2

            mov dx,03C8h            ;Index port
            mov al,1                ;Set register 1
            out dx,al
            inc dx                  ;Data port
            mov si,offset Palette   ;Send palette
            add si,bx
            mov cx,576
            rep outsb

            add bx,3                ;Advance palette
            cmp bx,576              ;Check for wrap
            jne Rotate              ;Loop back...
            xor bx,bx
            jmp Rotate

Done:       xor ah,ah               ;Eat the key
            int 16h

            mov ax,3                ;Set text mode
            int 10h
            ret                     ;Return

rand192:    mov ax,192              ;Get random number 1-192
            call Rand
            inc ax
            ret

EndP        Prog

;**************************** Plasma -- Recursive plasma procedure

Proc        Plasma

            push bp                 ;Set up stack frame
            mov bp,sp

            pusha                   ;Save all registers

            mov ax,[bp+8]           ;x2 - x1 < 2, done
            sub ax,[bp+4]
            cmp ax,2
            jb P_Done

            mov ax,[bp+4]           ;Get values
            mov bx,[bp+6]
            mov cx,[bp+8]
            mov dx,[bp+10]

            mov si,ax               ;x = (x1 + x2) / 2
            add si,cx
            sar si,1
            mov di,bx               ;y = (y1 + y2) / 2
            add di,dx
            sar di,1

            pusha                   ;Save registers

            push bx si bx cx bx ax  ;Adjust top side
            call Adjust
            push dx si dx cx dx ax  ;Adjust bottom side
            call Adjust
            push di ax dx ax bx ax  ;Adjust left side
            call Adjust
            push di cx dx cx bx cx  ;Adjust right side
            call Adjust

            GETPIX si,di            ;Center pixel on, recurse
            test al,al
            jnz P_Recurse

            xor dx,dx               ;Zero DX, AH
            xor ah,ah
            GETPIX [bp+4],[bp+6]    ;DX = sum of corners
            add dx,ax
            GETPIX [bp+8],[bp+6]
            add dx,ax
            GETPIX [bp+4],[bp+10]
            add dx,ax
            GETPIX [bp+8],[bp+10]
            add dx,ax

            shr dx,2                ;DX = average of corners
            mov al,dl
            SETPIX si,di            ;Set center pixel

P_Recurse:  popa                    ;Restore registers
            push di si bx ax        ;Plasma x1, y1, x, y
            call Plasma
            push di cx bx si        ;Plasma x, y1, x2, y
            call Plasma
            push dx cx di si        ;Plasma x, y, x2, y2
            call Plasma
            push dx si di ax        ;Plasma x1, y, x, y2
            call Plasma

P_Done:     popa                    ;Restore registers
            pop bp                  ;Delete stack frame
            ret 8                   ;Return, pop args

EndP        Plasma

;**************************** Adjust -- Random adjust pixel midpoint

Proc        Adjust

            push bp                 ;Set up stack frame
            mov bp,sp

            pusha                   ;Save all registers

            GETPIX [bp+12],[bp+14]  ;Check pixel
            test al,al              ;Already on, done
            jnz A_Done

            mov ax,[bp+8]           ;BX = |x2 - x1| + |y2 - y1|
            sub ax,[bp+4]
            mov bx,[bp+10]
            sub bx,[bp+6]
            test ax,ax              ;get absolute values...
            jge $+4
            neg ax
            test bx,bx
            jge $+4
            neg bx
            add bx,ax

            mov ax,bx               ;AX = (BX * 3 / 2) + 1
            add bx,bx
            add ax,bx
            sar ax,1
            inc ax

            mov bx,ax               ;Get random number
            call Rand               ;positive and negative
            sar bx,1
            sub ax,bx
            mov dx,ax               ;in DX

            GETPIX [bp+4],[bp+6]    ;AX = average of ends + DX
            push ax
            GETPIX [bp+8],[bp+10]
            pop bx
            xor ah,ah
            add al,bl
            adc ah,0
            sar ax,1
            add ax,dx

            test ax,ax              ;Make sure it's in range
            jg $+5
            mov ax,1
            cmp ax,192
            jle $+5
            mov ax,192

            SETPIX [bp+12],[bp+14]  ;Set center pixel

A_Done:     popa                    ;Restore registers
            pop bp                  ;Delete stack frame
            ret 12                  ;Return, pop args

EndP        Adjust

;**************************** Rand -- Random number generator

Proc        Rand

            push bx cx dx ax       ; This RNG routine was
            mov ax,[RandNum]       ;taken from the TurboC
            mov dx,4E35h           ;runtime library, but
            mul dx                 ;it is a big kludge.
            mov cx,dx              ; I do not know how 
            mov bx,ax              ;it really works.
            imul ax,[RandNum],015Ah
            add cx,ax
            imul ax,[RandNum+2],4E35h
            add ax,cx
            inc bx
            mov [RandNum],bx
            mov [RandNum+2],ax
            add ax,ax
            and bx,1
            or ax,bx
            pop bx
            xor dx,dx
            div bx
            mov ax,dx
            pop dx cx bx
            ret

EndP        Rand

RandNum     dw ?,?

Palette     db 1152 dup(?)

End Prog
