; Shaded donut in 256 bytes ; 22 Nov 1999, Mikael Kalms ; ; A fellow named Fysx has apparently made a 256byte donut back in 1997. ; Cheers to him! ; Idea to clear & copy buffers simultaneously using a normal loop, ; rather than doing one operation at a time using REP was ; 'borrowed' from his implementation (saved 4 bytes). ; ; The donut is generated at runtime using FPU sines/cosines. Rotation is ; also performed using FPU ops, and each pixel is plotted as a 2x2 block. ; 8bit Zbuffer is used for the depth sorting. ; ; The donut rotates around 2 axes (same rotation speed around both axes); ; having individual 3-axis rotation would take lots of space. ; ; Assembles with TASM. ; Various useful floating-point constants F_65536 EQU 4f800000h F_256 EQU 48800000h F_1 EQU 3f800000h F_OO256 EQU 37800000h F_OO65536 EQU 2f800000h F_PI EQU 40490fdbh F_TWOPI EQU 40c90fdbh F_EXPFACTOR EQU 00800000h DONUTRESA EQU 6 ; log2 of "number of vertices per ring" DONUTRESB EQU 8 ; log2 of "number of rings" DONUTSTEPSA EQU (1 SHL DONUTRESA) DONUTSTEPSB EQU (1 SHL DONUTRESB) b EQU byte ptr w EQU word ptr d EQU dword ptr LOCALS @@ .MODEL TINY .CODE .386 ORG 100h start: ; Set graphics mode (320x200x256) mov al,13h int 10h ; Set palette (greyscale, 64 entries) xchg ax,bx mov dx,3c8h out dx,al inc dx @@col: out dx,al out dx,al out dx,al inc al jnz @@col @@main: mov dx,cs ; Setup extrasegs add dh,10h mov es,dx ; 1) Screenbuffer add dh,10h mov fs,dx ; 2) Zbuffer mov bx,offset bss ; ax ? ; bx bss ; cx 0 ; dx ? ; si ? ; di ? ; bp 0 ; Update rotation fld d [bx+rot-bss] ; oldrot fadd d [bx+rot+4-bss] ; rot fst d [bx+rot-bss] ; rot fsincos ; rotcos rotsin fstp d [bx+rotcos-bss] ; rotsin fst d [bx+rotsin-bss] ; rotsin ; Draw mr. Donut ; rotsin is used as initial value for b, as b's starting value ; doesn't matter as long as it goes from x to x+2*PI ; b mov dx,DONUTSTEPSB @@b: fld st(0) ; b b fsincos ; cosb sinb b fstp d [bx+cosb-bss] ; sinb b fst d [bx+sinb-bss] ; sinb b ; sinb is used as initial value for a, as a's starting value ; doesn't matter as long as it goes from x to x+2*PI ; a b mov cl,DONUTSTEPSA @@a: call calcpoint ; Calculate one vertex/normal pair lodsw ; Fetch y xchg di,ax imul di,320 lodsw ; Fetch x add di,ax add di,100*320+160 ; Center of screen please dec si lodsw ; Fetch z * 256 + junk mov al,b [donutnormal+5] ; Fetch colour add ax,0800ah ; Apply bias to z & colour call dot2x2 ; Buffer/draw 2x2 pixel block fadd d [bx+donutinca-bss] ; a b loop @@a fstp st(0) ; b fadd d [bx+donutincb-bss] ; b dec dx jnz @@b fstp st(0) ; ; ax ? ; bx bss ; cx 0 ; dx 0 ; si ? ; di ? ; bp 0 ; Copy screenbuffer to gfxmem, and clear screenbuffer & zbuffer push ds push es pop ds push 0a000h pop es mov di,si ; Go through whole segment, no @@copyclr: ; matter where we begin movsb mov [si-1],dl mov fs:[si],dl loop @@copyclr pop ds ; Check for keypress mov ah,1 int 16h jz @@main ; Restore textmode mov ax,3 int 10h ; Exit program ret ; Draw 2x2 zbuffered pixels ; in al colour ; ah zbuffer value ; es:di pixel dot2x2: call @@y ; Two lines add di,319 @@y: call @@x ; Two pixels per line inc di @@x: cmp ah,fs:[di] ; Zcompare jb @@npixel1 ; Hidden? if not, draw to buffers mov fs:[di],ah stosb dec di @@npixel1: ret ; Calculate one vertex + corresponding normal ; in bx bss ; out si donutvertex calcpoint: mov si,offset donutscalevertex call @@calcvtx ; First gen vertex, then gen normal @@calcvtx: fld st(0) ; a a b fsincos ; cosa sina a b fmul d [si] ; x0 sina a b fadd d [si+4] ; x1 sina a b fld st(0) ; x1 x1 sina a b fmul d [bx+sinb-bss] ; z2 x1 sina a b fxch st(1) ; x1 z2 sina a b fmul d [bx+cosb-bss] ; x2 z2 sina a b fxch st(2) ; sina z2 x2 a b fmul d [si] ; y0 z2 x2 a b call rot2d ; z3 y3 x2 a b fxch st(2) ; x2 y3 z3 a b call rot2d ; y4 x4 z3 a b fistp w [si+16+2] ; x4 z3 a b fistp w [si+16] ; z3 a b fistp w [si+16+4] ; a b add si,8 ret ; Standard 2d rotation ; in bx bss ; st0 x0 ; st1 y0 ; out st0 y ; st1 x rot2d: ; x0 y0 fld st(1) ; y0 x0 y0 fmul d [bx+rotcos-bss] ; cos*y0 x0 y0 fld st(1) ; x0 cos*y0 x0 y0 fmul d [bx+rotcos-bss] ; cos*x0 cos*y0 x0 y0 fld d [bx+rotsin-bss] ; sin cos*x0 cos*y0 x0 y0 fmul st(3),st(0) ; sin cos*x0 cos*y0 sin*x0 y0 fmulp st(4),st(0) ; cos*x0 cos*y0 sin*x0 sin*y0 fsubrp st(3),st(0) ; cos*y0 sin*x0 cos*x0-sin*y0 faddp st(1),st(0) ; sin*x0+cos*y0 cos*x0-sin*y0 ret donutinca dd F_TWOPI - (F_EXPFACTOR * DONUTRESA) rot dd 0 ; Rotation angle rotinc dd F_TWOPI - (F_EXPFACTOR * 6) ; Rotation speed donutscalevertex dd F_1 + (F_EXPFACTOR * 4) ; Donut ring radius dd F_1 + (F_EXPFACTOR * 5) ; Donut radius donutscalenormal dd F_1 + F_EXPFACTOR * 14 - 300000h ; Normal-length donutincb dd F_TWOPI - (F_EXPFACTOR * DONUTRESB) ; Almost 0.0 donutvertex dw ?,?,?,? ; Vertex/normal pair generated donutnormal dw ?,?,?,? ; by calcpoint bss: rotcos dd ? ; Temporaries used in rotsin dd ? ; donut generation cosb dd ? sinb dd ? END start