--- /dev/null
+ bits 16
+ org 100h
+
+ ; call video bios (10h) to set video mode (call 00h), to mode
+ ; 13h (320x200 8bpp).
+ mov ax, 13h ; call number in ah (0), call argument in al (13h)
+ int 10h ; call video bios
+
+ ; setup es to access the framebuffer segment (a000h)
+ push word 0a000h
+ pop es
+
+ ; prepare to fill the framebuffer with red (default palette color 4)
+ mov eax, 04040404h ; value to store each time
+ mov ecx, 16000 ; framebuffer size in 32bit units (320x200/4 = 16000)
+ xor di, di ; start from offset 0
+ rep stosd ; repeat (while ecx != 0) the "store string dword" instr.
+
+.mainloop:
+ in al, 60h ; read pending scancode from the keyboard port (if any)
+ dec al ; ESC is 1, so decrement ...
+ jnz .mainloop ; ... and loop as long as the result was not 0
+
+ ; switch back to text mode (mode 3)
+ mov ax, 3
+ int 10h
+
+ ; return to dos
+ ret
--- /dev/null
+; DOS or bootable sierpinski triangle hack
+;
+; Author: John Tsiombikas <nuclear@member.fsf.org>
+; License: public domain or CC0
+;
+; Build for DOS: nasm -o sierp.com -f bin sierp.asm
+; Build to boot: nasm -o sierp.img -f bin -DBOOTSECT sierp.asm
+; (then dd if=sierp.img of=/dev/<usbstick> bs=512)
+
+ bits 16
+%ifdef BOOTSECT
+ org 7c00h
+%else
+ org 100h
+%endif
+
+NUM_POINTS equ 8192
+SHADOW_OFFS equ 5
+
+%macro SETPAL 4
+ push word %4
+ push word %3
+ push word %2
+ push word %1
+ call setpal
+ add sp, 8
+%endmacro
+
+start:
+%ifdef BOOTSECT
+ xor ax, ax
+ mov ds, ax
+ mov es, ax
+ mov ss, ax
+ jmp 00:.setcs
+.setcs:
+ mov sp, 0
+%endif
+ ; call video bios (10h) to set video mode (call 00h), to mode
+ ; 13h (320x200 8bpp).
+ mov ax, 13h ; call number in ah (0), call argument in al (13h)
+ int 10h ; call video bios
+
+ ; "allocate" the next segment for the framebuffer
+ mov ax, es
+ add ax, 1000h
+ mov es, ax
+
+ ; setup palette for the effect
+ ; - bg color (80h)
+ ; - shadow color (81h)
+ ; - fractal color (82h)
+ SETPAL 80h, 40, 40, 80
+ SETPAL 81h, 16, 16, 32
+ SETPAL 82h, 96, 192, 96
+
+.mainloop:
+ call animate
+
+ ; clear the framebuffer
+ mov eax, 80808080h ; bg color
+ mov ecx, 16000 ; framebuffer size in 32bit units (320x200/4 = 16000)
+ xor di, di ; start from offset 0
+ rep stosd ; repeat (while ecx != 0) the "store string dword"
+
+ ; draw shadow
+ push word SHADOW_OFFS ; offset for the shadow
+ push word 81h ; shadow color
+ call drawsierp
+ ; draw fractal
+ mov bp, sp
+ mov word [bp + 2], 0 ; reset offset
+ mov word [bp], 82h ; fractal color
+ call drawsierp
+ add sp, 4 ; clean up arguments from the stack
+
+ ; copy framebuffer to video ram
+ push ds
+ push es
+ ; point ds:si (source) to es:0
+ mov ax, es
+ mov ds, ax
+ xor si, si
+ ; point es:di (dest) to a000:0
+ mov ax, 0a000h
+ mov es, ax
+ xor di, di
+ mov ecx, 16000
+ call wait_vblank
+ rep movsd
+ pop es
+ pop ds
+
+%ifdef BOOTSECT
+ jmp .mainloop
+%else
+ in al, 60h ; read pending scancode from the keyboard port (if any)
+ dec al ; ESC is 1, so decrement ...
+ jnz .mainloop ; ... and loop as long as the result was not 0
+
+ ; switch back to text mode (mode 3)
+ mov ax, 3
+ int 10h
+ ; return to dos
+ ret
+%endif
+
+ ; VBLANK is bit 3 of the input status #1 register (port 3dah).
+ ; It's 0 while we're in the visible area, and 1 if we're in the
+ ; vertical blank period.
+wait_vblank:
+ mov dx, 3dah
+ ; in case we're already in vblank, wait until we get out of it
+.inblank:
+ in al, dx
+ and al, 8
+ jnz .inblank ; loop while vblank bit is 1 (in vblank)
+ ; wait until vblank starts
+.notblank:
+ in al, dx
+ and al, 8
+ jz .notblank ; loop while vblank bit is 0 (visible area)
+ ret
+
+ ; draw sierpinski triangle
+drawsierp:
+ mov bp, sp
+ ; start from one of the vertices
+ mov ax, [sierp_verts]
+ mov bx, [sierp_verts + 2]
+ mov [sierp_pt], ax
+ mov [sierp_pt + 2], bx
+ ; number of iterations in cx
+ mov cx, NUM_POINTS
+.loop: ; pick a vertex at random and move half-way there
+ call rand
+ mov bx, 3
+ xor dx, dx
+ div bx ; dx is now rand % 3
+ ; put the vertex address in ebx
+ mov edi, sierp_verts
+ movzx ebx, dx
+ lea ebx, [ebx * 4 + edi]
+ ; add to sierp_pt and divide by 2 to move half-way there
+ mov ax, [bx]
+ add ax, [sierp_pt]
+ shr ax, 1
+ mov [sierp_pt], ax ; store the resulting X back to [sierp_pt]
+ mov di, ax ; save X coordinate in di
+ mov ax, [bx + 2]
+ add ax, [sierp_pt + 2]
+ shr ax, 1
+ mov [sierp_pt + 2], ax ; store the reuslting Y back to [sierp_pt + 2]
+ add ax, [bp + 4] ; add offset
+ mov bx, ax
+ shl ax, 8
+ shl bx, 6
+ add bx, ax ; bx = Y * 320
+ add bx, [bp + 4] ; add offset
+ mov al, [bp + 2] ; color to set
+ mov byte [es:bx + di], al
+
+ dec cx
+ jnz .loop
+ ret
+
+ align 2
+sierp_verts:
+ dw 160, 40
+ dw 240, 160
+ dw 80, 160
+sierp_pt dw 0, 0
+sierp_vel:
+ dw 1, 1
+ dw -1, 1
+ dw -1, -1
+
+animate:
+ mov cx, 3
+ mov di, sierp_verts
+ mov si, sierp_vel
+.loop:
+ mov ax, [di] ; grab vertex X
+ add ax, [si] ; add velocity X
+ jl .xout
+ cmp ax, 320-SHADOW_OFFS
+ jge .xout
+ jmp .skip_xflip
+.xout:
+ sub ax, [si] ; revert to previous X
+ neg word [si] ; negate velocity X
+.skip_xflip:
+ mov [di], ax ; update vertex X
+
+ ; to do the same for Y increment edi and esi by 2
+ add di, 2
+ add si, 2
+ mov ax, [di] ; grab vertex Y
+ add ax, [si] ; add velocity Y
+ jl .yout
+ cmp ax, 200-SHADOW_OFFS
+ jge .yout
+ jmp .skip_yflip
+.yout:
+ sub ax, [si] ; revert to previous Y
+ neg word [si] ; negate velocity Y
+.skip_yflip:
+ mov [di], ax ; update vertex Y
+
+ add di, 2
+ add si, 2
+ dec cx
+ jnz .loop
+ ret
+
+ ; set palette entry (idx, r, g, b)
+setpal:
+ mov bp, sp
+ mov dx, 3c8h ; DAC index port
+ mov al, [bp + 2]
+ out dx, al
+ inc dx ; DAC data port
+ mov al, [bp + 4]
+ shr al, 2
+ out dx, al
+ mov al, [bp + 6]
+ shr al, 2
+ out dx, al
+ mov al, [bp + 8]
+ shr al, 2
+ out dx, al
+ ret
+
+ ; random number generator
+rand:
+ mov eax, [randval]
+ mul dword [randmul]
+ add eax, 12345
+ and eax, 0x7fffffff
+ mov [randval], eax
+ shr eax, 16
+ ret
+
+ align 4
+randval dd 0
+randmul dd 1103515245
+
+%ifdef BOOTSECT
+ times 510-($-$$) db 0
+ db 0x55,0xaa
+%endif
+; vi:ft=nasm:
--- /dev/null
+; Lowest yet: 312
+
+ bits 16
+%ifdef BOOTSECT
+ org 7c00h
+%else
+ org 100h
+%endif
+
+NUM_POINTS equ 8192
+SHADOW_OFFS equ 5
+
+SIERP_PT equ code_end
+RANDVAL equ code_end + 4
+
+
+%macro WAIT_VBLANK 0
+ mov dx, 3dah
+.inblank:
+ in al, dx
+ and al, 8
+ jnz .inblank ; loop while vblank bit is 1 (in vblank)
+.notblank:
+ in al, dx
+ and al, 8
+ jz .notblank ; loop while vblank bit is 0 (visible area)
+%endmacro
+
+
+start:
+%ifdef BOOTSECT
+ xor ax, ax
+ mov ds, ax
+ mov es, ax
+ mov ss, ax
+ jmp 00:.setcs
+.setcs:
+ mov sp, 0
+%endif
+ mov al, 13h
+ int 10h
+
+ ; "allocate" the next segment for the framebuffer
+ mov ax, es
+ add ax, 1000h
+ mov es, ax
+
+ ; setup palette for the effect
+ ; - bg color (0)
+ ; - shadow color (1)
+ ; - fractal color (2)
+ mov dx, 0x3c8
+ xor al, al
+ out dx, al
+ inc dx
+ mov al, 10 ; 0: 40, 40, 80
+ out dx, al
+ out dx, al
+ shl al, 1
+ out dx, al
+ mov al, 4 ; 1: 16, 16, 32
+ out dx, al
+ out dx, al
+ shl al, 1
+ out dx, al
+ mov al, 18h ; 2: 96, 192, 96
+ out dx, al
+ mov al, 48
+ out dx, al
+ mov al, 18h
+ out dx, al
+
+mainloop:
+ call animate
+
+ ; clear the framebuffer
+ xor al, al
+ mov cx, 64000
+ xor di, di
+ rep stosb
+
+ ; draw shadow
+ mov si, 5
+ mov byte [setcol + 1], 1 ; shadow color
+ call drawsierp
+ ; draw fractal
+ xor si, si
+ mov byte [setcol + 1], 2 ; fractal color
+ call drawsierp
+
+ ; copy framebuffer to video ram
+ push ds
+ push es
+ ; point ds:si (source) to es:0
+ mov ax, es
+ mov ds, ax
+ xor si, si
+ ; point es:di (dest) to a000:0
+ mov ax, 0a000h
+ mov es, ax
+ xor di, di
+ mov cx, 32000
+ WAIT_VBLANK
+ rep movsw
+ pop es
+ pop ds
+
+%ifdef BOOTSECT
+ jmp mainloop
+%else
+ in al, 60h ; read pending scancode from the keyboard port (if any)
+ dec al ; ESC is 1, so decrement ...
+ jnz mainloop ; ... and loop as long as the result was not 0
+
+ ; switch back to text mode (mode 3)
+ mov ax, 3
+ int 10h
+ ; return to dos
+ ret
+%endif
+
+ ; draw sierpinski triangle
+drawsierp:
+ ; start from one of the vertices
+ mov ax, [sierp_verts]
+ mov bx, [sierp_verts + 2]
+ mov [SIERP_PT], ax
+ mov [SIERP_PT + 2], bx
+ ; number of iterations in cx
+ mov cx, NUM_POINTS
+dsloop: ; pick a vertex at random and move half-way there
+ call rand
+ mov bx, 3
+ xor dx, dx
+ div bx ; dx is now rand % 3
+ ; put the vertex address in bx
+ mov bx, sierp_verts
+ shl dx, 2
+ add bx, dx
+ ; add to SIERP_PT and divide by 2 to move half-way there
+ mov ax, [bx]
+ add ax, [SIERP_PT]
+ shr ax, 1
+ mov [SIERP_PT], ax ; store the resulting X back to [SIERP_PT]
+ mov di, ax ; save X coordinate in di
+ mov ax, [bx + 2]
+ add ax, [SIERP_PT + 2]
+ shr ax, 1
+ mov [SIERP_PT + 2], ax ; store the reuslting Y back to [SIERP_PT + 2]
+ add ax, si ; add offset
+ mov bx, ax
+ shl ax, 8
+ shl bx, 6
+ add bx, ax ; bx = Y * 320
+setcol: mov al, 1
+ mov byte [es:bx + di], al
+
+ dec cx
+ jnz dsloop
+ ret
+
+sierp_verts:
+ dw 160, 40
+ dw 240, 160
+ dw 80, 160
+sierp_vel:
+ dw 1, 1
+ dw -1, 1
+ dw -1, -1
+
+animate:
+ mov cx, 3
+ mov di, sierp_verts
+ mov si, sierp_vel
+.loop:
+ mov ax, [di] ; grab vertex X
+ add ax, [si] ; add velocity X
+ jl .xout
+ cmp ax, 320-SHADOW_OFFS
+ jge .xout
+ jmp .skip_xflip
+.xout:
+ sub ax, [si] ; revert to previous X
+ neg word [si] ; negate velocity X
+.skip_xflip:
+ mov [di], ax ; update vertex X
+
+ ; to do the same for Y increment edi and esi by 2
+ add di, 2
+ add si, 2
+ mov ax, [di] ; grab vertex Y
+ add ax, [si] ; add velocity Y
+ jl .yout
+ cmp ax, 200-SHADOW_OFFS
+ jge .yout
+ jmp .skip_yflip
+.yout:
+ sub ax, [si] ; revert to previous Y
+ neg word [si] ; negate velocity Y
+.skip_yflip:
+ mov [di], ax ; update vertex Y
+
+ add di, 2
+ add si, 2
+ dec cx
+ jnz .loop
+ ret
+
+ ; random number generator
+rand:
+ mov ax, [randval]
+ mov bx, ax
+ shr bx, 7
+ xor ax, bx
+ mov bx, ax
+ shl bx, 9
+ xor ax, bx
+ mov [randval], ax
+ ret
+; mov eax, [RANDVAL]
+; mul dword [randmul]
+; add eax, 12345
+; and eax, 0x7fffffff
+; mov [RANDVAL], eax
+; shr eax, 16
+; ret
+
+;randmul dd 1103515245
+randval dw 0ace1h
+code_end:
+
+%ifdef BOOTSECT
+ times 510-($-$$) db 0
+ db 0x55,0xaa
+%endif
+; vi:ft=nasm: