Cogs and Levers A blog full of technical stuff

Snow flakes keep falling on my... screen?

A very simple effect this time around. It’s snow flakes. The operating premise for the effect is very simple and goes like this:

  • Generate 1 new snow flake at the top of the screen at every frame
  • A snow flake has an absolute floor of the last line in video memory
  • A snow flake should come to rest if it lands on top of another

Snowflakes

That’s it! So, immediately we need a way to get random numbers. We’re using a 320x200 screen here and my dodgy routine for getting random numbers only returns us 8 bit numbers (which gets us to 255). We need to add some more width to these numbers if we expect to be able to randomize across the whole 320 column positions. Calling the random port twice and adjusting the resolution of the second number should do it for us, such that:

8 bits (256) and 6 bits (64) will give us 320 - or the ability to add using 14 bits worth of numbers, which in this configuration takes us up to 320. Perfect.

Here’s the code!

get_random:
    ; start out with ax and bx = 0
	xor	ax, ax
	xor     bx, bx

    ; get the first random number and
    ; store it off in bl
	mov	dx, 40h
	in	al, dx
	mov	bl, al

    ; get the second random number and
    ; store it off in al, but we only 
    ; want 6 bits of this number
	mov	dx, 40h
	in	al, dx
	and     al, 63

    ; add the two numbers to produce a
    ; random digit in range
	add	ax, bx
	
	ret

Excellent. We can span the breadth of our screen with random flakes. Now it’s time to progress them down the screen. Here’s the main frame routine to do so.

no_kbhit:

    ; put a new snowflake at the top 
    ; of the screen
	call	get_random
	mov	di, ax
	mov	byte ptr es:[di], 15

decend:

    ; we can't move snowflakes any further
    ; than the bottom of the screen so we
    ; process all other lines
	mov	di, 63680
	mov	cx, 63680
	
next_pixel:

    ; test if there is a snowflake at the 
    ; current location
	mov	al, es:[di]
	cmp	al, 0
	je	no_flake
	
    ; test if there is a snowflake beneath
    ; us at the moment
	mov	al, es:[di+320]
	cmp	al, 0
	jne	no_flake
	
    ; move the snowflake from where we are 
    ; at the moment to one line below us
	xor	al, al
	mov	byte ptr es:[di], al
	mov	al, 15
	mov	byte ptr es:[di+320], 15
	
no_flake:

    ; move our way through video memory
	dec	di
	dec	cx
	jnz	next_pixel

    ; check for a keypress
	mov	ah, 01h
	int	16h
	jz	no_kbhit

The code itself above is pretty well commented, you shouldn’t need me to add much more here. There are a couple too many labels in the code, but they should help to add readability. I’ll leave it as an exercise to the reader to implement different speeds, colours and maybe even some horizontal movement (wind). Cool stuff.