;**************************** LIFE.ASM -- Life simulation, VERY FAST!
;                                         This is the enhanced version.

Ideal

Model Tiny
CodeSeg
P386
Org 100h

Proc        Prog

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

            mov dx,03C8h            ;Change color 78h
            mov al,78h
            out dx,al
            inc dx
            mov al,10h              ;Set to dark gray
            out dx,al
            out dx,al
            out dx,al

            mov [CurX],160          ;Cursor at center
            mov [CurY],100

            push 0                  ;ES = 0
            pop es
            mov eax,[es:046Ch]      ;Seed RNG
            mov [RandNum],eax
                                    
            mov ax,cs               ;DS, ES = virtual video
            add ax,1000h
            mov ds,ax
            mov es,ax

            xor di,di               ;Zero DI
            mov cx,32768            ;32K dwords
            xor ax,ax               ;Clear memory
            rep stosw

            push 0A000h             ;ES = video memory
            pop fs

            mov di,321              ;DI = 321
            mov cx,200              ;200 rows

RandOLoop:  mov si,318              ;318 pixels/row
RandLoop:   call Rand               ;Get random number
            stosb                   ;Store byte
            imul ax,28h             ;Write to screen
            mov [fs:di-321],al
            dec si                  ;Loop on pixel loop
            jnz RandLoop
            add di,2                ;Loop on row loop
            loop RandOLoop

            mov di,320              ;DI = 320
            mov cx,64000            ;64000 bytes

InitLoop:   xor ah,ah               ;Get boundary count
            mov al,[di+1]
            and al,1
            add ah,al
            mov al,[di-1]
            and al,1
            add ah,al
            mov al,[di+319]
            and al,1
            add ah,al
            mov al,[di-319]
            and al,1
            add ah,al
            mov al,[di+320]
            and al,1
            add ah,al
            mov al,[di-320]
            and al,1
            add ah,al
            mov al,[di+321]
            and al,1
            add ah,al
            mov al,[di-321]
            and al,1
            add ah,al

            shl ah,1                ;Shift left 1
            and ah,0Fh
            or [di],ah              ;Set count

            inc di                  ;Loop back
            loop InitLoop

            mov di,320              ;Set up for loop
            mov cx,64000

SetupLoop:  mov al,[di]             ;Copy to high bits
            shl al,4
            or [di],al
            
            inc di                  ;Loop back
            loop SetupLoop

            xor bp,bp               ;Zero BP

MainLoop:   mov di,321              ;Set up for LifeLoop
            mov cx,200

LifeOLoop:  mov dx,318              ;318 pixels/row
LifeLoop:   mov al,[di]             ;Get cell value
            and al,0Fh              ;AL = boundary count
            jz LifeLB               ;Empty cell?
            shr al,1
            jnc LifeDead            ;Test current cell

            sub al,2                ;Alive, check boundary
            cmp al,2
            jb DoScreen

            mov al,-20h             ;Set to flip cell
            jmp LifeFlip

LifeDead:   cmp al,3                ;Dead, check boundary
            jne LifeLB

            mov al,20h              ;Set to flip cell

LifeFlip:   add [di+1],al           ;Change boundary counts
            add [di-1],al
            add [di+319],al
            add [di-319],al
            add [di+320],al
            add [di-320],al
            add [di+321],al
            add [di-321],al

            mov al,[di]             ;Flip the cell
            xor al,10h
            mov [di],al

            and al,1                ;Check new cell
            cmp al,1                ;Off, set to 0
            sbb al,al               ;On, set to 28h
            and al,28h
            jmp Scr01               ;Go set pixel

DoScreen:   mov al,[fs:di-320]      ;Get current color
            cmp al,37h              ;Check to maximum
            jae LifeLB

            inc ax                  ;Increment color
Scr01:      mov [fs:di-320],al      ;Write color

LifeLB:     inc di                  ;Loop on pixels
            dec dx
            jnz LifeLoop

            xor ax,ax               ;Loop on rows
            stosw
            loop LifeOLoop

            mov di,320              ;Set up for CleanLoop
            mov cx,16000

CleanLoop:  mov ax,[di]             ;Get values
            mov dx,[di+2]
            and ax,0F0F0h
            and dx,0F0F0h

            mov bx,ax               ;Copy to both digits
            shr bx,4
            mov si,dx
            shr si,4
            or ax,bx
            or dx,si

            mov [di],ax             ;Store values
            mov [di+2],dx

            add di,4                ;Loop back
            dec cx
            jnz CleanLoop

            test bp,bp              ;Check wait flag
            jz MainLB

            mov ah,86h              ;Delay 1/5 second
            mov cx,0003h
            mov dx,0D40h
            int 15h

MainLB:     mov ah,1                ;Check for key
            int 16h
            jz MainLoop             ;Loop if no key

GetKey:     xor ah,ah               ;Get the key
            int 16h

            cmp ah,01h              ; Esc = quit
            je Quit
            cmp ah,10h              ; Q = quit
            je Quit
            cmp ah,19h              ; P = pause
            je GetKey
            cmp ah,1Fh              ; S = slow
            jne $+4
            not bp
            cmp ah,12h              ; E = edit
            jne MainLoop

            call DrawCur            ;Draw cursor

EditLoop:   xor ah,ah               ;Get a key
            int 16h

            cmp ah,2Eh              ; 'C'?
            je ClearField
            cmp ah,48h              ; Up?
            je MoveUp
            cmp ah,50h              ; Down?
            je MoveDown
            cmp ah,4Bh              ; Left?
            je MoveLeft
            cmp ah,4Dh              ; Right?
            je MoveRight
            cmp ah,1Ch              ; Enter?
            je EditDone
            cmp ah,39h              ; Space?
            jne EditLoop

FlipCell:   imul bx,[cs:CurY],320   ;BX = buffer offset
            add bx,[cs:CurX]
            add bx,320

            mov al,[bx]             ;Flip the cell
            xor al,11h
            mov [bx],al

            mov ah,20h              ;AH = 20h if on
            test al,1               ;     -20h if off
            jnz $+4
            neg ah

            add [bx+1],ah           ;Change boundary counts
            add [bx-1],ah
            add [bx+319],ah
            add [bx-319],ah
            add [bx+320],ah
            add [bx-320],ah
            add [bx+321],ah
            add [bx-321],ah

            shr al,1                ;Check new cell
            sbb al,al               ;Off, set to 0
            and al,28h              ;On, set to 28h

            mov [fs:bx-320],al      ;Set screen pixel
            jmp EditLoop

ClearField: mov di,320              ;Clear life buffer
            mov cx,32000
            xor ax,ax
            rep stosw

            push fs                 ;ES = video memory
            pop es

            xor di,di               ;Clear the screen
            mov cx,32000
            xor ax,ax
            rep stosw

            push ds                 ;ES = data segment
            pop es

            call DrawCur            ;Redraw cursor
            jmp EditLoop

MoveUp:     mov ax,[cs:CurY]        ;AX = Y
            dec ax                  ;Move up
            jl EditLoop

            call EraseCur           ;Redraw cursor
            mov [cs:CurY],ax
            call DrawCur
            jmp EditLoop

MoveDown:   mov ax,[cs:CurY]        ;AX = Y
            inc ax                  ;Move down
            cmp ax,200
            jae EditLoop

            call EraseCur           ;Redraw cursor
            mov [cs:CurY],ax
            call DrawCur
            jmp EditLoop

MoveLeft:   mov ax,[cs:CurX]        ;AX = X
            dec ax                  ;Move left
            jle EditLoop

            call EraseCur           ;Redraw cursor
            mov [cs:CurX],ax
            call DrawCur
            jmp EditLoop

MoveRight:  mov ax,[cs:CurX]        ;AX = offset
            inc ax                  ;Move right
            cmp ax,318
            jae EditLoop

            call EraseCur           ;Redraw cursor
            mov [cs:CurX],ax
            call DrawCur
            jmp EditLoop

EditDone:   call EraseCur           ;Erase cursor

            mov di,320              ;Set up for CleanLoop
            mov cx,64000

ECleanLoop: mov al,[di]             ;Get value
            mov ah,al
            shr al,4                ;Copy to both digits
            and ah,0F0h
            or al,ah
            mov [di],al             ;Store value

            inc di                  ;Loop back
            loop ECleanLoop

            jmp MainLoop            ;Start main loop

Quit:       mov ax,3                ;Set text mode
            int 10h

            ret                     ;Return

EndP        Prog

;**************************** DrawCur -- Draw editing cursor

Proc        DrawCur

            pusha                   ;Save all registers

            imul bx,[cs:CurY],320   ;BX = offset
            add bx,[cs:CurX]

            mov ax,7878h            ;Draw cursor
            add [fs:bx-640],al
            add [fs:bx-320],al
            add [fs:bx+320],al
            add [fs:bx+640],al
            add [fs:bx-2],ax
            add [fs:bx+1],ax

            popa                    ;Restore registers
            ret                     ;Return

EndP        DrawCur

;**************************** EraseCur -- Erase the cursor

Proc        EraseCur

            pusha                   ;Save all registers

            imul bx,[cs:CurY],320   ;BX = offset
            add bx,[cs:CurX]

            mov ax,7878h            ;Erase cursor
            sub [fs:bx-640],al
            sub [fs:bx-320],al
            sub [fs:bx+320],al
            sub [fs:bx+640],al
            sub [fs:bx-2],ax
            sub [fs:bx+1],ax

            popa                    ;Restore registers
            ret                     ;Return

EndP        EraseCur

;**************************** Rand -- Generate random number 0 or 1

Proc        Rand

            imul eax,[cs:RandNum],015A4E35h
            inc eax
            mov [cs:RandNum],eax
            xchg dx,ax
            ror eax,16
            xor ax,dx
            ror ax,11
            and ax,1
            ret

EndP        Rand

RandNum     dd ?                    ;Random number

CurX        dw ?                    ;Current position
CurY        dw ?

End Prog
