;--------------------------------------------------------------------
; Hi-res mode Life simulator (640x480)  - Tenie Remmel
;--------------------------------------------------------------------

Ideal
Model Tiny
P386
CodeSeg
Org 100h

Start:      jmp Main

;----------------------------------------------------------

NoMem$      db 'Out of memory (need 256K)',13,10,'$'
Table$      db 'Generating tables . . .',13,10,'$'

VID_MODE    = 12h
X_SIZE      = 640
Y_SIZE      = 480

X_BYTES     = X_SIZE / 8
SEG_START   = X_BYTES * 2

;----------------------------------------------------------

DataSeg1    dw ?
DataSeg2    dw ?
TableSeg    dw ?

;----------------------------------------------------------

Proc        Main

            mov ah,4Ah              ;Resize memory block
            mov bx,1000h
            int 21h
            jc m_nomem

            mov ah,48h              ;Allocate 3 segments
            mov bx,3000h
            int 21h
            jnc m_cont1

m_nomem:    mov ah,9                ;Out of memory...
            mov dx,offset NoMem$
            int 21h
            mov ax,4C01h
            int 21h

m_cont1:    mov [DataSeg1],ax       ;Save segments
            add ax,1000h
            mov [DataSeg2],ax
            add ax,1000h
            mov [TableSeg],ax

            mov ah,9                ;'Generating...'
            mov dx,offset Table$
            int 21h

            mov es,[TableSeg]       ;Generate the table
            call GenTable

            mov ax,VID_MODE         ;Set video mode
            int 10h

;----------------------------------------------------------

            push 0                  ;Seed RNG with clock
            pop es
            mov ebx,[es:046Ch]

            mov es,[cs:DataSeg2]    ;ES = second data seg.
            xor di,di
            xor eax,eax             ;Clear the segment
            mov cx,16384
            rep stosd
            mov es,[cs:DataSeg1]    ;ES = first data seg.
            mov cx,16384            ;Clear the segment
            rep stosd

            mov di,SEG_START        ;DI = start segment
            mov cx,X_BYTES * Y_SIZE / 2 ;CX = word count

m_rloop:    imul ebx,015A4E35h      ;Generate random number
            inc ebx
            mov eax,ebx             ;AX = 16 bit number
            shr eax,8
            stosw                   ;Write value
            dec cx                  ;Loop back
            jnz m_rloop

;----------------------------------------------------------

            mov fs,[cs:TableSeg]    ;FS = table seg.

m_loop:     mov ds,[cs:DataSeg1]    ;Calculate 1 --> 2
            mov es,[cs:DataSeg2]
            call CalcLife

            mov ds,[cs:DataSeg2]    ;Display buffer 2
            call DispScreen
            
            mov ds,[cs:DataSeg2]    ;Calculate 2 --> 1
            mov es,[cs:DataSeg1]
            call CalcLife

            mov ds,[cs:DataSeg1]    ;Display buffer 1
            call DispScreen

            mov ah,1                ;Loop if no key
            int 16h
            jz m_loop

;----------------------------------------------------------

            mov ax,3                ;Set normal video mode
            int 10h

            mov ah,49h              ;Free the memory
            mov es,[cs:DataSeg1]
            int 21h

            mov ax,4C00h            ;Return
            int 21h

EndP        Main

;----------------------------------------------------------

Proc        GenTable                ;ES = segment

            pusha                   ;Save all registers

            xor di,di               ;Zero counter

gt_loop:    xor ax,ax               ;Zero AX
            mov bx,di               ;BX = UL bits
            and bx,0EAE0h
            call gt_count           ;Count bits
            cmp cl,3
            sete ah
            cmp cl,2
            jne gt_test2
            test di,0400h
            setnz ah

gt_test2:   shl ah,3                ;Add in bit
            or al,ah

            mov bx,di               ;BX = UR bits
            and bx,07570h
            call gt_count           ;Count bits
            cmp cl,3
            sete ah
            cmp cl,2
            jne gt_test3
            test di,0200h
            setnz ah

gt_test3:   shl ah,2                ;Add in bit
            or al,ah

            mov bx,di               ;BX = LL bits
            and bx,00EAEh
            call gt_count           ;Count bits
            cmp cl,3
            sete ah
            cmp cl,2
            jne gt_test4
            test di,0040h
            setnz ah

gt_test4:   add ah,ah               ;Add in bit
            or al,ah

            mov bx,di               ;BX = LR bits
            and bx,00757h
            call gt_count           ;Count bits
            cmp cl,3
            sete ah
            cmp cl,2
            jne gt_lback
            test di,0020h
            setnz ah

gt_lback:   or al,ah                ;Add in bit
            stosb                   ;Store value
            test di,di              ;Loop back
            jnz gt_loop

            popa                    ;Restore registers
            ret                     ;Return

gt_count:   mov cx,bx               ;Collect bit pairs
            shr bx,1
            and cx,5555h
            and bx,5555h
            add bx,cx

            mov cx,bx               ;Collect nibbles
            shr bx,2
            and cx,3333h
            and bx,3333h
            add bx,cx

            mov cx,bx               ;Collect bytes
            shr bx,4
            and cx,0F0Fh
            and bx,0F0Fh
            add cx,bx

            add cl,ch               ;CL = result
            ret                     ;Return

EndP        GenTable

;----------------------------------------------------------

Proc        CalcLife                ;DS = source, ES = dest.

            pusha                   ;Save all registers

            mov si,SEG_START        ;SI = start of data
            mov bp,Y_SIZE / 2       ;BP = Y counter

cl_oloop:   mov cx,X_BYTES          ;CX = X bytes

cl_loop:    push cx bp              ;Save registers

            mov bx,[si-1-X_BYTES]   ;Get values for all lines
            mov cx,[si-1]
            mov dx,[si-1+X_BYTES]
            mov di,[si-1+X_BYTES*2]
            and bx,0E001h           ;Mask and shift them
            ror bx,1
            and cx,0E001h
            ror cx,5
            and dx,0E001h
            rol dx,7
            and di,0E001h
            rol di,3
            or bx,cx                ;OR them into BX and DI
            or di,dx

            mov al,[fs:bx+di]       ;Look up new value
            mov ah,al               ;Shift and mask
            and ax,030Ch            
            shl al,4
            shl ah,6
            mov cx,ax               ;CX = AX

            mov bh,[si-X_BYTES]     ;Get values for all lines
            mov dh,[si]
            mov bl,[si+X_BYTES]
            mov dl,[si+X_BYTES*2]
            push bx dx              ;Save values
            and bx,7878h            ;Add in all four lines
            add bx,bx
            and dx,7878h
            shr dx,3
            or bx,dx

            mov al,[fs:bx]          ;Look up new value
            mov ah,al               ;Shift and mask
            and ax,030Ch            
            shl al,2
            shl ah,4
            or cx,ax                ;OR in values     

            pop dx bx               ;Get values (same as last time)
            and bx,1E1Eh            ;Add in all four lines
            shl bx,3
            and dx,1E1Eh
            shr dx,1
            or bx,dx

            mov ah,[fs:bx]          ;Look up new value
            mov al,ah               ;Shift and mask
            and ax,030Ch            
            shl ah,2
            or cx,ax                ;OR in values     

            mov ax,[si-X_BYTES]     ;Get values for all lines
            mov bx,[si]
            mov dx,[si+X_BYTES]
            mov di,[si+X_BYTES*2]
            and ax,8007h            ;Mask and shift them
            ror ax,3
            and bx,8007h
            ror bx,7
            and dx,8007h
            rol dx,5
            and di,8007h
            rol di,1
            or bx,ax                ;OR them into BX and DI
            or di,dx

            mov al,[fs:bx+di]       ;Look up new value
            mov ah,al               ;Shift and mask
            and ax,030Ch            
            shr al,2
            or ax,cx                ;AX = result
            mov [es:si],al          ;Write new data
            mov [es:si+X_BYTES],ah

            pop bp cx               ;Restore registers
            inc si                  ;Next byte
            dec cx                  ;Loop back
            jnz cl_loop

            add si,X_BYTES          ;Next 2 rows
            dec bp                  ;Loop back
            jnz cl_oloop

            popa                    ;Restore registers
            ret                     ;Return

EndP        CalcLife

;----------------------------------------------------------

Proc        DispScreen              ;DS = source

            pusha                   ;Save all registers
            push es

            push 0A000h             ;ES = video memory
            pop es

            mov si,SEG_START        ;Copy to screen
            xor di,di
            mov cx,X_BYTES * Y_SIZE / 4
            rep movsd

            pop es                  ;Restore registers
            popa
            ret                     ;Return

EndP        DispScreen

;----------------------------------------------------------

End Start
