; mandel.asm - Don Yang (uguu.org) ; ; nasm mandel.asm -fbin -o mandel.com ; ; 01/13/01: v1.0 ; 01/18/01: v1.1 ;..................................................................... Header org 100h ; Output image size BMPWIDTH equ 1024 ; Must be multiple of 8 BMPHEIGHT equ (3 * BMPWIDTH) / 4 ; Keep this for square aspect ratio ; System constants (do not change) BMPHEADSIZE equ 54 VIDEO_SEG equ 0a000h MAX_ZOOM equ 16 WIDTH equ 320 HEIGHT equ 200 LIMIT equ 128 ; Offsets RMIN equ 0 RMAX equ 8 IMIN equ 16 IMAX equ 24 ;....................................................................... Code section .text ;----------------------------------------------------------------------- main main: cld finit ; Initialize mouse xor ax, ax int 33h or ax, ax jz main_nomouse mov ax, 2 int 33h mov ax, 4 xor cx, cx xor dx, dx int 33h ; Set VGA mode mov ax, 13h int 10h call InitPalette main_mainloop: call Render call DrawFrame main_keypress: call MouseInput jc main_mainloop mov ah, 1 int 16h jz main_keypress ; Clear keystroke xor ah, ah int 16h ; Restore screen mov ax, 3 int 10h mov ax, 500h int 10h mov ah, 1 mov cx, 607h int 10h ; Save screenshot call SaveBMP ; Return to DOS mov ax, 4c00h int 21h main_nomouse: mov ah, 9 mov dx, mouse_text int 21h mov ax, 4c00h int 21h ;------------------------------------------------------------------ DrawFrame DrawFrame: ; Do not draw frame at maximum zoom cmp word [zoomoffset], MAX_ZOOM * 4 * 8 jge DrawFrame_end ; Compute frame address mov ax, [mousey] mov bx, WIDTH mul bx mov si, [mousex] add si, ax mov di, si push ds mov ax, VIDEO_SEG mov es, ax mov ds, ax mov cx, (WIDTH / 4) / 4 DrawFrame_top: lodsd or eax, 80808080h stosd loop DrawFrame_top add di, WIDTH - (WIDTH / 4) mov cx, (HEIGHT / 4) - 2 DrawFrame_middle: or byte [di], 80h or byte [di + (WIDTH / 4) - 1], 80h add di, WIDTH loop DrawFrame_middle mov cx, (WIDTH / 4) / 4 mov si, di DrawFrame_bottom: lodsd or eax, 80808080h stosd loop DrawFrame_bottom pop ds DrawFrame_end: retn ;----------------------------------------------------------------- EraseFrame EraseFrame: push ds mov ax, VIDEO_SEG mov ds, ax mov es, ax xor esi, esi xor edi, edi mov cx, WIDTH * HEIGHT / 4 EraseFrame_loop: lodsd and eax, 7f7f7f7fh stosd loop EraseFrame_loop pop ds retn ;---------------------------------------------------------------- InitPalette InitPalette: mov ax, ds mov es, ax mov di, palette ; 0: (0, 0, 0), 1: (37, 39, 37) mov dword [di], 25000000h mov word [di+4], 2527h add di, byte 6 mov ax, 3f3fh mov bx, ax xor dx, dx mov cx, 62 InitPalette_loop1: ; 2 - 63: (63, 63, 63) -> (0, 63, 63) mov [di], al mov [di+1], bx dec ax add di, byte 3 loop InitPalette_loop1 mov al, bl mov cx, 64 InitPalette_loop2: ; 64 - 127: (0, 63, 63) -> (0, 0, 63) mov [di], dl mov [di+1], al mov [di+2], bl dec ax add di, byte 3 loop InitPalette_loop2 mov al, 3fh mov cx, 32 InitPalette_loop3: mov bx, 4 InitPalette_loop3b: ; 128 - 255 mov [di], al mov [di+1], dx add di, byte 3 dec bx jnz InitPalette_loop3b dec ax loop InitPalette_loop3 ; Update DAC registers mov ax, 1012h xor bx, bx mov cx, 256 mov dx, palette ; ES:DX points at palette int 10h retn ;----------------------------------------------------------------- MouseInput MouseInput: mov ax, 3 int 33h ; Check motion cmp cx, [mousex] jne MouseInput_savex cmp dx, [mousey] jne MouseInput_savey ; Check buttons clc cmp bx, [mouseb] je MouseInput_end or bx, bx jnz MouseInput_press mov ax, [mouseb] test ax, 1 jnz MouseInput_zoomin mov [mouseb], bx call ZoomOut retn MouseInput_zoomin: mov [mouseb], bx call ZoomIn retn MouseInput_press: ; Mouse press - does nothing, wait for mouse release mov [mouseb], bx retn MouseInput_clampx0: xor cx, cx jmp short MouseInput_savey MouseInput_clampx1: mov cx, WIDTH - (WIDTH / 4) jmp short MouseInput_savey MouseInput_clampy0: xor dx, dx jmp short MouseInput_redrawcursor MouseInput_clampy1: mov dx, HEIGHT - (HEIGHT / 4) jmp short MouseInput_redrawcursor MouseInput_savex: cmp cx, 0 jl MouseInput_clampx0 cmp cx, WIDTH - (WIDTH / 4) jge MouseInput_clampx1 MouseInput_savey: cmp dx, 0 jl MouseInput_clampy0 cmp dx, HEIGHT - (HEIGHT / 4) jge MouseInput_clampy1 MouseInput_redrawcursor: mov [mousex], cx mov [mousey], dx call EraseFrame call DrawFrame mov ax, 4 mov cx, [mousex] mov dx, [mousey] int 33h MouseInput_end: clc retn ;--------------------------------------------------------------------- Render Render: ; SI = window range mov si, param add si, [zoomoffset] mov ax, [sy] mov [y], ax Render_yloop: mov ax, ds mov es, ax mov di, buffer mov bx, 2 Render_bloop: ; Convert screen Y to imaginary position fld qword [si+IMAX] fsub qword [si+IMIN] fimul word [y] fidiv word [height2] fadd qword [si+IMIN] fstp qword [i0] mov word [x], 0 mov cx, [width2] Render_xloop: ; Convert screen X to real position fld qword [si+RMAX] fsub qword [si+RMIN] fimul word [x] fidiv word [width2] fadd qword [si+RMIN] fstp qword [r0] ; Render point mov eax, [i0] mov edx, [i0+4] mov [i], eax mov [i+4], edx mov eax, [r0] mov edx, [r0+4] mov [r], eax mov [r+4], edx mov ax, 127 Render_cloop: fld qword [i] fmul qword [i] fld qword [r] fmul qword [r] fld st0 ; ST0 = ST1 = r * r, ST2 = i * i fsub st2 fadd qword [r0] ; ST0 = r * r - i * i + r0 ; ST1 = r * r, ST2 = i * i fld qword [two] fmul qword [r] fmul qword [i] fadd qword [i0] ; ST0 = 2 * r * i + i0 ; ST1 = r * r - i * i + r0 ; ST2 = r * r, ST3 = i * i fstp qword [i] fstp qword [r] faddp st1 ; ST0 = r * r + i * i fistp dword [magn] cmp dword [magn], LIMIT jae Render_exceedbound dec ax jnz Render_cloop Render_exceedbound: ; Write pixel stosb inc word [x] dec cx jnz near Render_xloop mov ax, [dy] add [y], ax dec bx jnz near Render_bloop mov ax, [file] or ax, ax jnz Render_copytofile ; End of two scanlines, average and write to screen mov ax, VIDEO_SEG mov es, ax mov ax, word [y] ; DI = 2 * screeny sub ax, byte 2 mov di, ax shl di, 7 shl ax, 5 add di, ax ; DI = (256 + 64) * screeny mov cx, WIDTH mov bx, buffer Render_copytoscreenloop: mov ax, [bx] ; AL = 0..127, AH = 0..127 mov dx, [bx+WIDTH*2] add ax, dx shr ax, 2 and ax, 3f3fh add al, ah stosb add bx, byte 2 loop Render_copytoscreenloop jmp short Render_endyloop Render_copytofile: mov ah, 40h mov bx, [file] mov cx, BMPWIDTH * 2 mov dx, buffer int 21h jc Render_writeerr Render_endyloop: mov ax, [ey] cmp [y], ax jne near Render_yloop clc retn Render_writeerr: stc retn ;-------------------------------------------------------------------- SaveBMP SaveBMP: mov ah, 9 mov dx, end_text int 21h ; Create file mov ah, 3ch mov cx, 20h mov dx, bmpname ; DS:DX = name int 21h jc near SaveBMP_err0 mov [file], ax ; Write header mov bx, ax mov ah, 40h mov cx, BMPHEADSIZE mov dx, bmpheader int 21h jc SaveBMP_err1 ; Convert palette mov ax, ds mov es, ax mov si, palette mov di, buffer mov cx, 128 SaveBMP_cloop: xor eax, eax lodsb ; R rol eax, 8 lodsb ; G rol eax, 8 lodsb ; B shl eax, 2 stosd loop SaveBMP_cloop ; Write palette mov ah, 40h mov bx, [file] mov cx, 128 * 4 mov dx, buffer int 21h jc SaveBMP_err1 ; Write scanlines mov word [width2], BMPWIDTH mov word [height2], BMPHEIGHT mov word [sy], BMPHEIGHT - 1 mov word [ey], -1 mov word [dy], -1 call Render jc SaveBMP_err1 ; Close file mov ah, 3eh mov bx, [file] int 21h ; Success mov ah, 9 mov dx, end_text0 int 21h retn SaveBMP_err1: mov ah, 3eh mov bx, [file] int 21h SaveBMP_err0: ; Failure mov ah, 9 mov dx, end_text1 int 21h retn ;--------------------------------------------------------------------- ZoomIn ZoomIn: cmp word [zoomoffset], MAX_ZOOM * 4 * 8 jge near ZoomIn_end ; Draw frame call EraseFrame push ds mov ax, VIDEO_SEG mov ds, ax mov es, ax xor di, di mov cx, WIDTH / 4 ZoomIn_drawtop: lodsd or eax, 80808080h stosd loop ZoomIn_drawtop mov cx, HEIGHT - 2 ZoomIn_drawmiddle: or byte [di], 80h or byte [di+WIDTH-1], 80h add di, WIDTH loop ZoomIn_drawmiddle mov cx, WIDTH / 4 ZoomIn_drawbottom: lodsd or eax, 80808080h stosd loop ZoomIn_drawbottom pop ds ; Set window range mov si, param add si, [zoomoffset] ; Real fld qword [si+RMAX] fsub qword [si+RMIN] fst st1 fimul word [mousex] fidiv word [width] fadd qword [si+RMIN] fstp qword [si+RMIN+(8*4)] fdiv qword [four] fadd qword [si+RMIN+(8*4)] fstp qword [SI+RMAX+(8*4)] ; Imaginary fld qword [si+IMAX] fsub qword [si+IMIN] fst st1 fimul word [mousey] fidiv word [height] fadd qword [si+IMIN] fstp qword [si+IMIN+(8*4)] fdiv qword [four] fadd qword [si+IMIN+(8*4)] fstp qword [si+IMAX+(8*4)] ; Push stack mov bx, [zoomoffset] shr bx, 4 mov ax, [mousex] mov [mousexstack+bx], ax mov ax, [mousey] mov [mouseystack+bx], ax add word [zoomoffset], 8 * 4 stc ZoomIn_end: retn ;-------------------------------------------------------------------- ZoomOut ZoomOut: mov ax, [zoomoffset] or ax, ax jz ZoomOut_end ; Pop window range sub ax, 8 * 4 mov [zoomoffset], ax ; Set mouse position shr ax, 4 mov bx, ax mov cx, [mousexstack+bx] mov dx, [mouseystack+bx] mov [mousex], cx mov [mousey], dx mov ax, 4 int 33h stc ZoomOut_end: retn ;....................................................................... Data section .data align=1 end_text db 'Mandelbrot 1.1 (1/18/01) - ' db 'Don Yang (uguu.org)', 13, 10, 10, db 'Saving mandel.bmp... ', 36 end_text0 db 'done.', 13, 10, 36 end_text1 db 'failed.', 13, 10, 36 mouse_text db 'Mouse not detected', 13, 10, 36 bmpname db 'mandel.bmp', 0 bmpheader db 'BM' dd BMPHEADSIZE + 128 * 4 + BMPWIDTH * BMPHEIGHT, 0 dd BMPHEADSIZE + 128 * 4, 40, dd BMPWIDTH, BMPHEIGHT dw 1, 8 dd 0 dd BMPWIDTH * BMPHEIGHT dd 2834, 2834, 128, 0 file dw 0 width dw WIDTH height dw HEIGHT width2 dw WIDTH * 2 height2 dw HEIGHT * 2 two dq 2.0 four dq 4.0 zoomoffset dw 0 mousex dw 0 mousey dw 0 mouseb dw 0 sy dw 0 dy dw 1 ey dw HEIGHT * 2 param dq -2.5, 1.5, -1.5, 1.5 section .bss align=1 paramstack resq 4 * MAX_ZOOM mousexstack resw MAX_ZOOM mouseystack resw MAX_ZOOM x resw 1 y resw 1 r0 resq 1 i0 resq 1 r resq 1 i resq 1 magn resd 1 palette resb 256 * 3 buffer resb BMPWIDTH * 4