189 lines
5.3 KiB
Text
189 lines
5.3 KiB
Text
;;; Simulation settings (probabilities are P/65536)
|
|
probF: equ 7 ; P(spontaneous combustion) ~= 0.0001
|
|
probP: equ 655 ; P(spontaneous growth) ~= 0.01
|
|
HSIZE: equ 320 ; Field width (320x200 fills CGA screen)
|
|
VSIZE: equ 200 ; Field height
|
|
FSIZE: equ HSIZE*VSIZE ; Field size
|
|
FPARA: equ FSIZE/16+1 ; Field size in paragraphs
|
|
;;; Field values
|
|
EMPTY: equ 0 ; Empty cell (also CGA black)
|
|
TREE: equ 1 ; Tree cell (also CGA green)
|
|
FIRE: equ 2 ; Burning cell (also CGA red)
|
|
;;; MS-DOS system calls and values
|
|
TOPSEG: equ 2 ; First unavailable segment
|
|
puts: equ 9 ; Print a string
|
|
time: equ 2Ch ; Get system time
|
|
exit: equ 4Ch ; Exit to DOS
|
|
;;; BIOS calls and values
|
|
palet: equ 0Bh ; Set CGA color pallette
|
|
vmode: equ 0Fh ; Get current video mode
|
|
keyb: equ 1 ; Get keyboard status
|
|
CGALO: equ 4 ; Low-res (4-color) CGA graphics mode
|
|
MDA: equ 7 ; MDA monochrome text mode
|
|
CGASEG: equ 0B800h ; CGA memory segment
|
|
cpu 8086
|
|
org 100h
|
|
section .text
|
|
;;; Program set-up (check memory size and set video mode)
|
|
mov sp,stack.top ; Move stack inwards
|
|
mov bp,sp ; Set BP to first available paragraph
|
|
mov cl,4
|
|
shr bp,cl
|
|
inc bp
|
|
mov dx,cs
|
|
add bp,dx
|
|
mov bx,[TOPSEG] ; Get first unavailable segment
|
|
sub bx,bp ; Get amount of available memory
|
|
cmp bx,FPARA*2 ; Enough to fit two fields?
|
|
ja mem_ok
|
|
mov dx,errmem ; If not, print error message
|
|
err: mov ah,puts
|
|
int 21h
|
|
mov ah,exit ; And stop
|
|
int 21h
|
|
mem_ok: mov ah,vmode ; Get current video mode
|
|
int 10h
|
|
push ax ; Keep on stack for later retrieval
|
|
cmp al,MDA ; MDA card does not support CGA graphics,
|
|
mov dx,errcga ; so print an error and quit.
|
|
je err
|
|
mov ax,CGALO ; Otherwise, switch to 320x200 CGA mode
|
|
int 10h
|
|
mov ah,palet ; And set the black/green/red/brown palette
|
|
mov bx,0100h
|
|
int 10h
|
|
mov ah,time ; Get the system time
|
|
int 21h
|
|
mov [rnddat],cx ; Use it as the RNG seed
|
|
mov [rnddat+2],dx
|
|
;;; Initialize the field (place trees randomly)
|
|
mov es,bp ; ES = field segment
|
|
xor di,di ; Start at first field
|
|
mov cx,FSIZE ; CX = how many cells to initialize
|
|
mov ah,TREE
|
|
ptrees: call random ; Get random byte
|
|
and al,ah ; Place a tree 50% of the time
|
|
stosb
|
|
loop ptrees
|
|
mov ds,bp ; DS = field segment
|
|
;;; Write field to CGA display
|
|
disp: xor si,si ; Start at beginning
|
|
mov dx,CGASEG ; ES = CGA memory segment
|
|
.scrn: mov es,dx
|
|
xor di,di ; Start of segment
|
|
.line: mov cx,HSIZE/8 ; 8 pixels per word
|
|
.word: xor bx,bx ; BX will hold CGA word
|
|
xor ah,ah ; Set high byte to zero
|
|
%rep 7 ; Unroll this loop for speed
|
|
lodsb ; Get cell
|
|
or bx,ax ; Put it in low 2 bits of BX
|
|
shl bx,1 ; Shift BX to make room for next field
|
|
shl bx,1
|
|
%endrep
|
|
lodsb ; No shift needed for final cell
|
|
or ax,bx
|
|
stosw ; Store word in CGA memory
|
|
loop .word ; Do next byte of line
|
|
add si,HSIZE ; Even and odd lines stored separately
|
|
cmp si,FSIZE ; Done yet?
|
|
jb .line ; If not, do next line
|
|
add dx,200h ; Move to next segment
|
|
cmp dx,CGASEG+200h ; If we still need to do the odd lines,
|
|
mov si,HSIZE ; then do them
|
|
jbe .scrn
|
|
;;; Stop the program if a key is pressed
|
|
mov ah,1 ; Check if a key is pressed
|
|
int 16h
|
|
jz calc ; If not, calculate next field state
|
|
pop ax ; Otherwise, restore the old video mode,
|
|
cbw
|
|
int 10h
|
|
mov ah,exit ; and exit to DOS.
|
|
int 21h
|
|
;;; Calculate next field state
|
|
calc: mov ax,ds ; Set ES = new field segment
|
|
add ax,FPARA
|
|
mov es,ax
|
|
xor di,di ; Start at beginning
|
|
xor si,si
|
|
.cell: lodsb ; Get cell
|
|
dec al ; A=1 = tree
|
|
jz .tree
|
|
dec al ; A=2 = fire
|
|
jz .fire
|
|
call rand16 ; An empty space fills with a tree
|
|
cmp ax,probP ; with probability P.
|
|
jc .mtree ; Otherwise it stays empty
|
|
.fire: xor al,al ; A burning tree turns into an empty cell
|
|
stosb
|
|
jmp .cnext
|
|
.mtree: mov al,TREE
|
|
stosb
|
|
.cnext: cmp si,FSIZE ; Are we there yet?
|
|
jne .cell ; If not, do next cell
|
|
push es ; Done - set ES=old field, DS=new field,
|
|
push ds
|
|
pop es
|
|
pop ds
|
|
mov cx,FSIZE/2
|
|
xor si,si
|
|
xor di,di
|
|
rep movsw ; copy the new field to the old field,
|
|
push es ; set DS to be the field to draw,
|
|
pop ds
|
|
xor di,di ; Instead of doing edge case handling in the
|
|
xor ax,ax ; Moore neighbourhood calculation, just zero
|
|
mov cx,HSIZE/2 ; out the borders for a slightly smaller image
|
|
rep stosw ; Upper border,
|
|
mov di,FSIZE-HSIZE
|
|
mov cx,HSIZE/2
|
|
rep stosw ; lower border,
|
|
mov di,HSIZE-5 ; right border.
|
|
mov cx,VSIZE-1
|
|
.bordr: stosb
|
|
add di,HSIZE-1
|
|
loop .bordr
|
|
jmp disp ; and update the display.
|
|
.tree: mov ax,[si-HSIZE-2] ; Load Moore neighbourhood
|
|
or al,[si-HSIZE]
|
|
or ax,[si-2]
|
|
or al,[si]
|
|
or ax,[si+HSIZE-2]
|
|
or al,[si+HSIZE]
|
|
or al,ah
|
|
test al,FIRE ; Are any of the trees on fire?
|
|
jnz .tburn ; Then set this tree on fire too
|
|
call rand16 ; Otherwise, spontaneous combustion?
|
|
cmp ax,probF
|
|
jc .tburn
|
|
mov al,TREE ; If not, the tree remains a tree
|
|
stosb
|
|
jmp .cnext
|
|
.tburn: mov al,FIRE ; Set the tree on fire
|
|
stosb
|
|
jmp .cnext
|
|
;;; Get a random word in AX
|
|
rand16: call random
|
|
xchg al,ah
|
|
;;; Get a random byte in AL. BX and DX destroyed.
|
|
random: mov bx,[cs:rnddat] ; BL=X BH=A
|
|
mov dx,[cs:rnddat+2] ; DL=B DH=C
|
|
inc bl ; X++
|
|
xor bh,dh ; A ^= C
|
|
xor bh,bl ; A ^= X
|
|
add dl,bh ; B += A
|
|
mov al,dl ; C' = B
|
|
shr al,1 ; C' >>= 1
|
|
add al,dh ; C' += C
|
|
xor al,bh ; C' ^= A
|
|
mov dh,al ; C = C'
|
|
mov [cs:rnddat+2],dx ; Update RNG state
|
|
mov [cs:rnddat],bx
|
|
ret
|
|
section .data
|
|
errcga: db 'CGA mode not supported.$'
|
|
errmem: db 'Not enough memory.$'
|
|
section .bss
|
|
rnddat: resb 4 ; RNG state
|
|
stack: resw 128 ; Stack space
|
|
.top: equ $
|