initial commit, bumming the sierpinski
[vtuts] / dos1 / b.asm
1 ; DOS or bootable sierpinski triangle hack
2 ;
3 ; Author: John Tsiombikas <nuclear@member.fsf.org>
4 ; License: public domain or CC0
5 ;
6 ; Build for DOS: nasm -o sierp.com -f bin sierp.asm
7 ; Build to boot: nasm -o sierp.img -f bin -DBOOTSECT sierp.asm
8 ;       (then dd if=sierp.img of=/dev/<usbstick> bs=512)
9
10         bits 16
11 %ifdef BOOTSECT
12         org 7c00h
13 %else
14         org 100h
15 %endif
16
17 NUM_POINTS equ 8192
18 SHADOW_OFFS equ 5
19
20 %macro SETPAL 4
21         push word %4
22         push word %3
23         push word %2
24         push word %1
25         call setpal
26         add sp, 8
27 %endmacro
28
29 start:
30 %ifdef BOOTSECT
31         xor ax, ax
32         mov ds, ax
33         mov es, ax
34         mov ss, ax
35         jmp 00:.setcs
36 .setcs:
37         mov sp, 0
38 %endif
39         ; call video bios (10h) to set video mode (call 00h), to mode
40         ; 13h (320x200 8bpp).
41         mov ax, 13h     ; call number in ah (0), call argument in al (13h)
42         int 10h         ; call video bios
43
44         ; "allocate" the next segment for the framebuffer
45         mov ax, es
46         add ax, 1000h
47         mov es, ax
48
49         ; setup palette for the effect
50         ; - bg color (80h)
51         ; - shadow color (81h)
52         ; - fractal color (82h)
53         SETPAL 80h, 40, 40, 80
54         SETPAL 81h, 16, 16, 32
55         SETPAL 82h, 96, 192, 96
56
57 .mainloop:
58         call animate
59
60         ; clear the framebuffer
61         mov eax, 80808080h      ; bg color
62         mov ecx, 16000          ; framebuffer size in 32bit units (320x200/4 = 16000)
63         xor di, di              ; start from offset 0
64         rep stosd               ; repeat (while ecx != 0) the "store string dword"
65
66         ; draw shadow
67         push word SHADOW_OFFS   ; offset for the shadow
68         push word 81h           ; shadow color
69         call drawsierp
70         ; draw fractal
71         mov bp, sp
72         mov word [bp + 2], 0    ; reset offset
73         mov word [bp], 82h      ; fractal color
74         call drawsierp
75         add sp, 4               ; clean up arguments from the stack
76
77         ; copy framebuffer to video ram
78         push ds
79         push es
80         ; point ds:si (source) to es:0
81         mov ax, es
82         mov ds, ax
83         xor si, si
84         ; point es:di (dest) to a000:0
85         mov ax, 0a000h
86         mov es, ax
87         xor di, di
88         mov ecx, 16000
89         call wait_vblank
90         rep movsd
91         pop es
92         pop ds
93
94 %ifdef BOOTSECT
95         jmp .mainloop
96 %else
97         in al, 60h      ; read pending scancode from the keyboard port (if any)
98         dec al          ; ESC is 1, so decrement ...
99         jnz .mainloop   ; ... and loop as long as the result was not 0
100
101         ; switch back to text mode (mode 3)
102         mov ax, 3
103         int 10h
104         ; return to dos
105         ret
106 %endif
107
108         ; VBLANK is bit 3 of the input status #1 register (port 3dah).
109         ; It's 0 while we're in the visible area, and 1 if we're in the
110         ; vertical blank period.
111 wait_vblank:
112         mov dx, 3dah
113         ; in case we're already in vblank, wait until we get out of it
114 .inblank:
115         in al, dx
116         and al, 8
117         jnz .inblank    ; loop while vblank bit is 1 (in vblank)
118         ; wait until vblank starts
119 .notblank:
120         in al, dx
121         and al, 8
122         jz .notblank    ; loop while vblank bit is 0 (visible area)
123         ret
124
125         ; draw sierpinski triangle
126 drawsierp:
127         mov bp, sp
128         ; start from one of the vertices
129         mov ax, [sierp_verts]
130         mov bx, [sierp_verts + 2]
131         mov [sierp_pt], ax
132         mov [sierp_pt + 2], bx
133         ; number of iterations in cx
134         mov cx, NUM_POINTS
135 .loop:  ; pick a vertex at random and move half-way there
136         call rand
137         mov bx, 3
138         xor dx, dx
139         div bx  ; dx is now rand % 3
140         ; put the vertex address in ebx
141         mov edi, sierp_verts
142         movzx ebx, dx
143         lea ebx, [ebx * 4 + edi]
144         ; add to sierp_pt and divide by 2 to move half-way there
145         mov ax, [bx]
146         add ax, [sierp_pt]
147         shr ax, 1
148         mov [sierp_pt], ax      ; store the resulting X back to [sierp_pt]
149         mov di, ax              ; save X coordinate in di
150         mov ax, [bx + 2]
151         add ax, [sierp_pt + 2]
152         shr ax, 1
153         mov [sierp_pt + 2], ax  ; store the reuslting Y back to [sierp_pt + 2]
154         add ax, [bp + 4]        ; add offset
155         mov bx, ax
156         shl ax, 8
157         shl bx, 6
158         add bx, ax              ; bx = Y * 320
159         add bx, [bp + 4]        ; add offset
160         mov al, [bp + 2]        ; color to set
161         mov byte [es:bx + di], al
162
163         dec cx
164         jnz .loop
165         ret
166
167         align 2
168 sierp_verts:
169         dw 160, 40
170         dw 240, 160
171         dw 80, 160
172 sierp_pt dw 0, 0
173 sierp_vel:
174         dw 1, 1
175         dw -1, 1
176         dw -1, -1
177
178 animate:
179         mov cx, 3
180         mov di, sierp_verts
181         mov si, sierp_vel
182 .loop:
183         mov ax, [di]            ; grab vertex X
184         add ax, [si]            ; add velocity X
185         jl .xout
186         cmp ax, 320-SHADOW_OFFS
187         jge .xout
188         jmp .skip_xflip
189 .xout:
190         sub ax, [si]            ; revert to previous X
191         neg word [si]           ; negate velocity X
192 .skip_xflip:
193         mov [di], ax            ; update vertex X
194
195         ; to do the same for Y increment edi and esi by 2
196         add di, 2
197         add si, 2
198         mov ax, [di]            ; grab vertex Y
199         add ax, [si]            ; add velocity Y
200         jl .yout
201         cmp ax, 200-SHADOW_OFFS
202         jge .yout
203         jmp .skip_yflip
204 .yout:
205         sub ax, [si]            ; revert to previous Y
206         neg word [si]           ; negate velocity Y
207 .skip_yflip:
208         mov [di], ax            ; update vertex Y
209
210         add di, 2
211         add si, 2
212         dec cx
213         jnz .loop
214         ret
215
216         ; set palette entry (idx, r, g, b)
217 setpal:
218         mov bp, sp
219         mov dx, 3c8h            ; DAC index port
220         mov al, [bp + 2]
221         out dx, al
222         inc dx                  ; DAC data port
223         mov al, [bp + 4]
224         shr al, 2
225         out dx, al
226         mov al, [bp + 6]
227         shr al, 2
228         out dx, al
229         mov al, [bp + 8]
230         shr al, 2
231         out dx, al
232         ret
233
234         ; random number generator
235 rand:
236         mov eax, [randval]
237         mul dword [randmul]
238         add eax, 12345
239         and eax, 0x7fffffff
240         mov [randval], eax
241         shr eax, 16
242         ret
243
244         align 4
245 randval dd 0
246 randmul dd 1103515245
247
248 %ifdef BOOTSECT
249         times 510-($-$$) db 0
250         db 0x55,0xaa
251 %endif
252 ; vi:ft=nasm: