        org 0x100       ; For .com file.
        ;org 0x7c00      ; For MBR.

section .text
start:
        ; Enter mode 13h: 320x200, 1 byte (256 colors) per pixel.
        mov ax, 0x13
        int 0x10

        ; Make sure es and ds point to our segment (cs).
        push cs
        push cs
        pop ds
        pop es

        ; Write string.
        mov ax, 0x1300          ; ah=13h, al=write mode
        mov bx, 0xf             ; bh=page number (0), bl=attribute (white)
        mov cx, (msg_end - msg) ; cx=length
        mov dx, ((10 << 8) + (40 / 2 - (msg_end - msg) / 2)) ; dh=row, cl=column
        mov bp, msg             ; es:bp=string address
        int 0x10

        ; Set up the palette.
        ; Jare's original FirePal:
        cli             ; No interrupts while we do this, please.
        mov dx, 0x3c8   ; DAC Address Write Mode Register
        xor al, al
        out dx, al      ; Start setting DAC register 0
        inc dx          ; DAC Data Register
        mov cx, (firepal_end - firepal)
        mov si, firepal
setpal1:
        lodsb
        out dx, al      ; Set DAC register (3 byte writes per register)
        loop setpal1
        mov al, 63
        mov cx, (256 * 3 - (firepal_end - firepal))
setpal2:
        out dx, al      ; Set remaining registers to "white heat".
        loop setpal2
        sti             ; Re-enable interrupts.

        ; A buffer at offset 0x1000 from our segment will be used for preparing
        ; the frames. Copy the current framebuffer (the text) there.
        push 0xa000
        pop ds
        push cs
        pop ax
        add ax, 0x1000
        mov es, ax
        xor si, si
        xor di, di
        mov cx, (320 * 200 / 2)
        cld
        rep movsw       ; Copy two bytes at a time.

        push es
        pop ds
mainloop:
        ; On entry to the loop, es and ds should point to the scratch buffer.

        ; Since we'll be working "backwards" through the framebuffer, set the
        ; direction flag, meaning stosb etc. will decrement the index registers.
        std

        ; Let di point to the pixel to be written.
        mov di, (320 * 200 - 1)

        ; Write random values to the bottom row.
        ; For random numbers, use "x = 181 * x + 359" from
        ; Tom Dickens "Random Number Generator for Microcontrollers"
        ; http://home.earthlink.net/~tdickens/68hc11/random/68hc11random.html
        mov cx, 320
        xchg bp, ax     ; Fetch the seed from bp.
bottomrow:
        imul ax, 181
        add ax, 359
        xchg al, ah     ; It's the high 8 bits that are random.
        stosb
        xchg ah, al
        loop bottomrow
        xchg ax, bp     ; Store the seed in bp for next time.

        ; For the next 50 rows, propagate the fire upwards.
        mov cx, (320 * 50)
        mov si, di
        add si, 320     ; si points at the pixel below di.
propagate:
        ; Add the pixel below, below-left, below-right and two steps below.
        xor ax, ax
        mov al, [si]
        add al, [si - 1]
        adc ah, 0
        add al, [si + 1]
        adc ah, 0
        add al, [si + 320]
        adc ah, 0
        imul ax, 15
        shr ax, 6       ; Compute floor(sum * 15 / 64), averaging and cooling.
        stosb
        dec si
        loop propagate

        ; Mirror some of the fire onto the text.
        mov dx, 15              ; Loop count, decrementing.
        mov di, (90 * 320)      ; Destination pixel.
        mov si, (178 * 320)     ; Source pixel.
mirrorouter:
        mov cx, 320     ; Loop over each pixel in the row.
mirrorinner:
        mov al, [di]    ; Load destination pixel.
        test al, al     ; Check if its zero.
        lodsb           ; Load the source pixel into al.
        jnz mirrorwrite ; For non-zero destination pixel, don't zero al.
        xor al, al
mirrorwrite:
        stosb           ; Write al to the destination pixel.
        loop mirrorinner
        add si, 640     ; Bump si to the row below the one just processed.
        dec dx
        jnz mirrorouter

        ; Sleep for one system clock tick (about 1/18.2 s).
        xor ax, ax
        int 0x1a        ; Returns nbr of clock ticks in cx:dx.
        mov bx, dx
sleeploop:
        xor ax, ax
        int 0x1a
        cmp dx, bx
        je sleeploop

        ; Copy from the scratch buffer to the framebuffer.
        cld
        push 0xa000
        pop es
        mov cx, (320 * (200 - 3) / 2)
        xor si, si
        mov di, (320 * 3)       ; Scroll down three rows to avoid noisy pixels.
        rep movsw

        ; Restore es to point to the scratch buffer.
        push ds
        pop es

        ; Check for key press.
        mov ah, 1
        int 0x16
        jz mainloop

done:
        ; Fetch key from buffer.
        xor ah, ah
        int 0x16

        ; Return to mode 3.
        mov ax, 0x3
        int 0x10

        ; Exit with code 0.
        mov ax, 0x4c00
        int 0x21

; Data.
msg: db 'www.hanshq.net/fire.html'
msg_end:

firepal:
        db     0,   0,   0,   0,   1,   1,   0,   4,   5,   0,   7,   9
        db     0,   8,  11,   0,   9,  12,  15,   6,   8,  25,   4,   4
        db    33,   3,   3,  40,   2,   2,  48,   2,   2,  55,   1,   1
        db    63,   0,   0,  63,   0,   0,  63,   3,   0,  63,   7,   0
        db    63,  10,   0,  63,  13,   0,  63,  16,   0,  63,  20,   0
        db    63,  23,   0,  63,  26,   0,  63,  29,   0,  63,  33,   0
        db    63,  36,   0,  63,  39,   0,  63,  39,   0,  63,  40,   0
        db    63,  40,   0,  63,  41,   0,  63,  42,   0,  63,  42,   0
        db    63,  43,   0,  63,  44,   0,  63,  44,   0,  63,  45,   0
        db    63,  45,   0,  63,  46,   0,  63,  47,   0,  63,  47,   0
        db    63,  48,   0,  63,  49,   0,  63,  49,   0,  63,  50,   0
        db    63,  51,   0,  63,  51,   0,  63,  52,   0,  63,  53,   0
        db    63,  53,   0,  63,  54,   0,  63,  55,   0,  63,  55,   0
        db    63,  56,   0,  63,  57,   0,  63,  57,   0,  63,  58,   0
        db    63,  58,   0,  63,  59,   0,  63,  60,   0,  63,  60,   0
        db    63,  61,   0,  63,  62,   0,  63,  62,   0,  63,  63,   0
firepal_end:

        ; For MBR:
        ;times (510 - ($ - $$)) db 0      ; Pad to 510 bytes
        ;db 0x55                          ; MBR boot signature.
        ;db 0xaa
