;MANDZOOM.ASM   Mandelbrot generator written by Julian H. Schmidt.
;Requires 80x87 math-coprocessor.

;equates:
NumOfRows equ 199
NumOfCols equ 319
;----------------------------------------------------------------------

    Data_Seg        segment

ST8087  dw  0   ;This will allow me to access the 8087 status word.
M Record BY:1,C3:1,TOP:3,C2:1,C1:1,C0:1,IT:1,X:1,P:1,U:1,O:1,Z:1,D:1,I:1
;This record will be used to split the 80x87 status word up into
;  bit-fields.  Use the MASK directive to specify individual bits.

Gap             dq  0.0     ;Store the 'room' between pixels.
Column          dw  0       ;Current screen column.
Row             dw  0       ;Current screen row.
ColumnR         dq  0       ;Column defined as a real number.
RowR            dq  0       ;Row stored as a real number.

;BCD_Storage    dt          ;Storage for the packed Binary Coded
;Not used yet               ;  Decimal coordinate entered by the user.

;CHANGE THE NEXT THREE VARIABLES TO SEE A NEW 'VIEW' OF THE SET.
A_Corner        dq  -1.49   ;Initial Real coordinate.
B_Corner        dq  -0.012  ;Initial Imag coordinate.
Side            dq  0.07    ;Initial length of the side.

Num_Rows        dq  199.0   ;Zero-based.
Num_Columns     dq  319.0   ;Zero-based.
SpecialSize     dq  2.0     ;Compare this to the size of Z.

Real_C          dq  0.0     ;Real part of C.
Imag_C          dq  0.0     ;Imaginary part of C.
Real_Z          dq  0.0     ;Real part of Z.
Imag_Z          dq  0.0     ;Imaginary part of Z.

RealZSquared    dq  0.0     ;Temporary storage for the binomial expansion.
TwoxRealZxImagZ dq  0.0     ;Temporary storage for a part of it.
ImagZSquared    dq  0.0     ;Temporary storage.
Store_Imag      dq  0.0     ;Temporary storage for the imag. part of Z.
ZSize           dq  0.0     ;Storage for the size of Z.

V_Mode          db  13h     ;Video mode 13h.

    Data_Seg        ends

    Code_Seg        segment
            assume  cs:Code_Seg,ds:Data_Seg,ss:Stack_Seg

;----------------------------------------------------------------------
Find_Gap    proc
    ;----------------------------------------
    ;   Calculate the value of Gap          -
    ;----------------------------------------
    ;this should go in-line...
    ;Gap is the 'area' between pixels: it's defined as Side/Num_Rows.
            fld     Side                ;Put Side onto 80x87's ST.
            fdiv    Num_Rows            ;Result ends up in Stack Top.
            fstp    Gap                 ;Store the result and pop.

            ret
Find_Gap    endp
;----------------------------------------------------------------------

;----------------------------------------------------------------------
Find_Z      proc

            fldz                        ;Load +0.0 onto ST.
            fstp    Real_Z              ;These zero out Real_Z.

            fldz                        ;This is probably redundant.
            fstp    Imag_Z

            fld     Gap                 ;Put Gap onto 80x87's ST.
;Get Real_C
            fld     ColumnR             ;Put Column on ST.
            fmul                        ;Column x Gap --> ST  (pop).
            fadd    A_Corner            ;ST now contains Real_C.
            fstp    Real_C              ;Store value and pop stack.
;Get Imag_C                              The stack is now clear.
            fld     Gap
            fld     RowR                ;Put Row onto ST.
            fmul                        ;Row x Gap --> ST.
            fadd    B_Corner            ;ST now contains Imag_C.
            fstp    Imag_C              ;Value in memory, pop stack.
;Now, the complex number C is stored in memory as Real_C and Imag_C.
;This can be considered a complex variable (or a pain in the neck).
;----------------------------------------
;The complex number Z is stored in memory as Real_Z and Imag_Z.  This
;  complex variable starts out as zero, and gets successively iterated
;  according to this relationship:   Z <-- Z^2 + C  until either:
;  1    Z > 2            or
;  2    Counter = 256   (the counter is the CL register).
;The value of counter (when the loop terminates) will determine the
;  color of the particular pixel referenced by C.

            xor     cx,cx               ;Loop counter.
SquareZplusC:
;Get RZ^2
            fld     Real_Z              ;Load Real_Z into ST.
            fst     st(1)               ;Duplicate it in ST(1).
            fmul                        ;Now, ST holds (Real_Z)^2.
            fstp    RealZSquared        ;Copy it to memory (pop).
;Get RZ*IZ
            fld     Real_Z              ;Load Real_Z into ST.
            fmul    Imag_Z              ;Now, ST holds RZ*IZ.
;Get 2*(RZ*IZ)
            fmul    SpecialSize         ;Multiply ST times 2.0.
            fstp    TwoxRealZxImagZ     ;Copy it to memory (pop).
;Get IZ^2
            fld     Imag_Z              ;Load Imag_Z into ST.
            fst     st(1)               ;Duplicate it into ST(1).
            fchs                        ;Changes the sign in ST since
                                        ;  i^2 = -1.
            fmul                        ;Now, ST holds IZ^2
            fst     ImagZSquared        ;Copy it to memory (no pop).

;Now, I will add the two 'real' results.  They are stored in
;  RealZSquared and ImagZSquared.  IZ^2 is NOW 'real' because i^2=-1.

;Add Reals and get the 'new' RZ

            fadd    RealZSquared        ;Adds ST and RealZSquared.
            fst     Real_Z              ;Update Real_Z don't pop.

;Now, the Real part of Z is stored in Real_Z and the imaginary part
;  is stored in ImagZSquared.
;----------------------------------------
;Add Real_Z to Real_C and also add Imag_Z to Imag_C to get our current
;  value for Z (a complex variable consisting of Real_Z and Imag_Z).

            fadd    Real_C              ;Add Real_C to ST.
            fstp    Real_Z              ;Update Real_Z and pop stack.
            fld     TwoxRealZxImagZ     ;Put Imag_Z onto ST.
            fadd    Imag_C              ;Add Imag_C to ST.
            fst     Imag_Z              ;Update Imag_Z in memory.
;FindSizeOfZ
;I will square each of its parts (Real_Z and Imag_Z), add the two
;  squared values, and finally take the squareroot of the sum.

            fst     st(1)               ;Now, ST and ST(1) contain Imag_Z.
            fmul                        ;Now, ST contains (Imag_Z)^2
            fstp    Store_Imag

            fld     Real_Z
            fst     st(1)               ;Duplicate ST in ST(1).
            fmul                        ;Now, ST contains (Real_Z)^2

            fadd    Store_Imag          ;Now, ST contains the sum.
            fsqrt                       ;Now, ST contains the square root.
            fst     ZSize               ;Store the size in memory (no pop).
;Now I can check to see if Size > 2 (if so, end the loop).

            fcomp   SpecialSize         ;Compare the ST and pop stack.
            fstsw   ST8087              ;Store the status word in memory.
            ;If C3=0 and C0=0 --> ST > SpecialSize (terminate loop).
            test    ST8087,Mask C0      ;If C0 = 0, ST>=SpecialSize
            jz      WritePic

DoNotTerminate:                         ;Size is still < SpecialSize.
                                        ;Can't use a loop instruction.
            cmp     cx,255              ;  due to size of procedure.
            jge     WritePic
            inc     cx
            jmp     SquareZplusC        ;Loop again.

WritePic:
            mov     ah,0ch              ;Request function 0Ch (BIOS).
            mov     al,cl               ;Color value.
;Already 0  xor     bh,bh               ;Page number.
            mov     cx,Column           ;Pixel column number.
            mov     dx,Row              ;Pixel row number.
            int     10h                 ;Bios's Write Graphics Pixel.

            ret
Find_Z      endp
;----------------------------------------------------------------------

;----------------------------------------------------------------------
SetVideo    proc

            mov     ah,00h              ;Request function 00h (BIOS).
            mov     al,V_Mode           ;Get requested Video Mode.
            int     10h                 ;Bios's Set Video Mode.

            ret
SetVideo    endp
;----------------------------------------------------------------------

;----------------------------------------------------------------------
MandZoom    proc                        ;This is the entry point!

            mov     ax,Data_Seg
            mov     ds,ax
            finit                       ;Initialize the 80x87.
            call    Find_Gap            ;Calculate Gap constant.
            call    SetVideo            ;Set the video mode.

Main_Loop:
            ;this keyboard stuff can be remmed out to speed up a bit.
            mov     ah,01h              ;Request function 01h (BIOS).
            int     16h                 ;Bios's Read Keyboard Status.
            jz      DoItNow
            jmp     short   Exit
DoItNow:
            call    Find_Z              ;Procedure Find_Z.

            inc     Column
            fld1                        ;Load +1.0 onto ST.
            fadd    ColumnR             ;These increment Column1.
            fstp    ColumnR
            cmp     Column,NumOfCols
            jb      NewCol
            mov     Column,0
            fldz                        ;Load a +0.0 onto ST.
            fstp    ColumnR
            inc     Row
            fld1
            fadd    RowR
            fstp    RowR

NewCol:
            cmp     Row,NumOfRows
            jb      NewRow
            jmp     short   _Wait
Newrow:
            jmp     short   Main_Loop

    ;This stuff waits after picture has been drawn for a keypress.
_Wait:      mov     ah,01h              ;Request function 01h (BIOS).
            int     16h                 ;Read K'Board status (BIOS).
            jz      _Wait               ;If no key, loop again.

Exit:
            xor     ah,ah
            int     16h                 ;Clear Keyboard Buffer.
            mov     V_Mode,03h
            call    SetVideo            ;Restore Text Mode (03h).
            mov     ax,4C00h            ;Request function 40h (DOS).
            int     21h                 ;Dos's Exit with Return Code.

MandZoom    endp
;----------------------------------------------------------------------

    Code_Seg        ends

Stack_Seg   segment     stack
    db      50 dup("STACK")             ;We could get by with less stack.
Stack_Seg   ends
    End     MandZoom
