; DOS or bootable sierpinski triangle hack ; ; Author: John Tsiombikas ; 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/ 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: