File: dos\calmanfp.asm

    1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    2 ; calmanfp.asm - floating point version of the calcmand.asm file
    3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    4 ; The following code was adapted from a little program called "Mandelbrot
    5 ; Sets by Wesley Loewer" which had a very limited distribution (my
    6 ; Algebra II class).  It didn't have any of the fancy integer math, but it
; did run floating point stuff pretty fast.
;
; The code was originally optimized for a 287 ('cuz that's what I've got)
    7 ; and for a large maxit (ie: use of generous overhead outside the loop to get
    8 ; slightly faster code inside the loop), which is generally the case when
    9 ; Fractint chooses to use floating point math.  This code also has the
   10 ; advantage that once the initial parameters are loaded into the fpu
   11 ; register, no transfers of fp values to/from memory are needed except to
   12 ; check periodicity and to show orbits and the like.  Thus, values keep all
   13 ; the significant digits of the full 10 byte real number format internal to
   14 ; the fpu.  Intermediate results are not rounded to the normal IEEE 8 byte
   15 ; format (double) at any time.
   16 ;
   17 ; The non fpu specific stuff, such as periodicity checking and orbits,
   18 ; was adapted from CALCFRAC.C and CALCMAND.ASM.
   19 ;
   20 ; This file must be assembled with floating point emulation turned on.  I
   21 ; suppose there could be some compiler differences in the emulation
   22 ; libraries, but this code has been successfully tested with the MSQC 2.51
   23 ; and MSC 5.1 emulation libraries.
   24 ;
   25 ;                                               Wes Loewer
   26 ;
   27 ; and now for some REAL fractal calculations...
   28 ; (get it, real, floating point..., never mind)
   29 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   30 
   31 ;   1. Made maxit a dword variable. 1/18/94
   32 ;   2. Added p5 support, fixed bug with real, imag, mult, & summ and
   33 ;       negative numbers. 7/13/97
   34 ;   3. Fixed FPUatan bugs, made individual _p5, _287, and _87 per pixel
   35 ;       routines for speed.  Put check for FPU in setup code (frasetup.c).
   36 ;       7/20/97
   37 
   38 ;                        required for compatibility if Turbo ASM
   39 IFDEF ??version
   40 MASM51
   41 QUIRKS
   42 ENDIF
   43 
   44 .8086
   45 .8087
   46 
   47 .MODEL medium,c
   48 
   49 ; external functions
   50 EXTRN   keypressed:FAR          ; this routine is in 'general.asm'
   51 EXTRN   getakey:FAR             ; this routine is in 'general.asm'
   52 EXTRN   plot_orbit:FAR          ; this routine is in 'fracsubr.c'
   53 EXTRN   scrub_orbit:FAR         ; this routine is in 'fracsubr.c'
   54 
   55 ; external data
   56 EXTRN init:WORD                 ; declared as type complex
   57 EXTRN parm:WORD                 ; declared as type complex
   58 EXTRN new:WORD                  ; declared as type complex
   59 EXTRN maxit:DWORD
   60 EXTRN inside:WORD
   61 EXTRN outside:WORD
   62 EXTRN fpu:WORD                  ; fpu type: 87, 287, or 387
   63 EXTRN cpu:WORD                  ; cpu type
   64 EXTRN rqlim:QWORD               ; bailout (I never did figure out
   65                                 ;   what "rqlim" stands for. -Wes)
   66 EXTRN coloriter:DWORD
   67 EXTRN oldcoloriter:DWORD
   68 EXTRN realcoloriter:DWORD
   69 EXTRN periodicitycheck:WORD
   70 EXTRN reset_periodicity:WORD
   71 EXTRN closenuff:QWORD
   72 EXTRN fractype:WORD             ; Mandelbrot or Julia
   73 EXTRN kbdcount:WORD            ; keyboard counter
   74 EXTRN dotmode:WORD
   75 EXTRN show_orbit:WORD           ; "show-orbit" flag
   76 EXTRN orbit_ptr:WORD            ; "orbit pointer" flag
   77 EXTRN potflag:WORD              ; potential flag
   78 EXTRN magnitude:QWORD           ; when using potential
   79 extrn   nextsavedincr:word              ; for incrementing AND value
   80 extrn   firstsavedand:dword             ; AND value
   81 extrn   bad_outside:word        ; old FPU code with bad: real,imag,mult,summ
   82 extrn   save_release:word
   83 extrn   showdot:word
   84 extrn   orbit_delay:word
   85 extrn   atan_colors:word
   86 
   87 JULIAFP  EQU 6                  ; from FRACTYPE.H
   88 MANDELFP EQU 4
   89 GREEN    EQU 2                  ; near y-axis
   90 YELLOW   EQU 6                  ; near x-axis
   91 KEYPRESSDELAY equ 16383         ; 3FFFh
   92 
   93 initx    EQU    ; just to make life easier
   94 inity    EQU 8>
   95 parmx    EQU 
   96 parmy    EQU 8>
   97 newx     EQU 
   98 newy     EQU 8>
   99 
  100 ; Apparently, these might be needed for TC++ overlays. I don't know if
; these are really needed here since I am not familiar with TC++. -Wes
FRAME   MACRO regs
        push    bp
        mov     bp, sp
        IRP     reg, 
          push  reg
          ENDM
        ENDM

UNFRAME MACRO regs
        IRP     reg, 
          pop reg
          ENDM
        pop bp
        ENDM


.DATA
        align   2
savedx                  DQ  ?
savedy                  DQ  ?
orbit_real              DQ  ?
orbit_imag              DQ  ?
_2_                     DQ  2.0
_4_                     DQ  4.0
close                   DD  0.01
round_down_half         DD  0.5
tmp_word                DW  ?
tmp_dword               DD  ?
inside_color            DD  ?
periodicity_color       DD  7
savedand                DD  ?   ; need 4 bytes now, not 2
;savedincr              DW  ?
;savedand                EQU     SI      ; this doesn't save much time or
  101 savedincr               EQU     DI      ; space, but it doesn't hurt either

.CODE
.8086
.8087
EVEN
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This routine is called once per image.
; Put things here that won't change from one pixel to the next.
  102 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  103 PUBLIC calcmandfpasmstart
  104 calcmandfpasmstart   PROC
  105                                         ; not sure if needed here
  106         FRAME                    ; std frame, for TC++ overlays
  107 
  108         sub     dx,dx                   ; clear dx
  109         mov     ax,inside
  110         cmp     ax,0                    ; if (inside color == maxiter)
  111         jnl     non_neg_inside
  112         mov     ax,word ptr maxit       ;   use maxit as inside_color
  113         mov     dx,word ptr maxit+2     ;   use maxit as inside_color
  114 
  115 non_neg_inside:                         ; else
  116         mov     word ptr inside_color,ax  ;   use inside as inside_color
  117         mov     word ptr inside_color+2,dx ;   use inside as inside_color
  118 
  119         cmp     periodicitycheck,0      ; if periodicitycheck < 0
  120         jnl     non_neg_periodicitycheck
  121         mov     ax,7                    ;   use color 7 (default white)
  122         sub     dx,dx                   ; clear dx
  123 non_neg_periodicitycheck:               ; else
  124         mov     word ptr periodicity_color,ax ; use inside_color still in ax
  125         mov     word ptr periodicity_color+2,dx ; use inside_color still in dx
  126         mov     word ptr oldcoloriter,0 ; no periodicity checking on 1st pixel
  127         mov     word ptr oldcoloriter+2,0 ; no periodicity checking on 1st pixel
  128 ;        sub     ax,ax                   ; ax=0
  129 ;        sub     dx,dx
  130         UNFRAME                  ; pop stack frame
  131         ret
  132 calcmandfpasmstart       ENDP
  133 
  134 .286
  135 .287
  136 EVEN
  137 PUBLIC calcmandfpasm_287
  138 calcmandfpasm_287  PROC
  139         FRAME                    ; std frame, for TC++ overlays
  140 ; initialization stuff
  141         sub     ax,ax                   ; clear ax
  142         mov     dx,ax                   ; dx=0
  143         cmp     periodicitycheck,ax     ; periodicity checking?
  144         je      initoldcolor            ;  no, set oldcolor 0 to disable it
  145 ;        cmp     inside,-59              ; zmag?
  146 ;        je      initoldcolor            ;  set oldcolor to 0
  147         cmp     reset_periodicity,ax    ; periodicity reset?
  148         je      short initparms         ;  no, inherit oldcolor from prior invocation
  149         mov     ax,word ptr maxit       ; yup.  reset oldcolor to maxit-250
  150         mov     dx,word ptr maxit+2
  151         sub     ax,250                  ; (avoids slowness at high maxits)
  152         sbb     dx,0                            ; (faster than conditional jump)
  153 initoldcolor:
  154         mov     word ptr oldcoloriter,ax   ; reset oldcolor
  155         mov     word ptr oldcoloriter+2,dx ; reset oldcolor
  156 
  157 initparms:
  158         sub     ax,ax                   ; clear ax
  159         mov     dx,ax                   ; clear dx
  160         mov     word ptr savedx,ax      ; savedx = 0.0
  161         mov     word ptr savedx+2,ax    ; needed since savedx is a QWORD
  162         mov     word ptr savedx+4,ax
  163         mov     word ptr savedx+6,ax
  164         mov     word ptr savedy,ax      ; savedy = 0.0
  165         mov     word ptr savedy+2,ax    ; needed since savedy is a QWORD
  166         mov     word ptr savedy+4,ax
  167         mov     word ptr savedy+6,ax
  168         mov     ax,word ptr firstsavedand+2 ; high part of savedand=0
  169         mov     word ptr savedand+2,ax ; high part of savedand=0
  170         mov     ax,word ptr firstsavedand    ; low part of savedand
  171         mov     word ptr savedand,ax    ; low part of savedand
  172         mov     savedincr,1             ; savedincr = 1
  173         mov     orbit_ptr,0             ; clear orbits
  174         dec     kbdcount                ; decrement the keyboard counter
  175         jns     short nokey        ;  skip keyboard test if still positive
  176         mov     kbdcount,10             ; stuff in a low kbd count
  177         cmp     show_orbit,0            ; are we showing orbits?
  178         jne     quickkbd                ;  yup.  leave it that way.
  179         cmp     orbit_delay,0           ; are we delaying orbits?
  180         je      slowkbd                 ;  nope.  change it.
  181         cmp     showdot,0               ; are we showing the current pixel?
  182         jge     quickkbd                ;  yup.  leave it that way.
  183 ;this may need to be adjusted, I'm guessing at the "appropriate" values -Wes
slowkbd:
        mov     kbdcount,3000           ; else, stuff an appropriate count val
                                        ; ("appropriate" to the FPU)
kbddiskadj:
        cmp     dotmode,11              ; disk video?
        jne     quickkbd                ;  no, leave as is
        shr     kbdcount,1              ; yes, reduce count
        shr     kbdcount,1              ; yes, reduce count

quickkbd:
        call    far ptr keypressed      ; has a key been pressed?
        cmp     ax,0                    ;  ...
        je      nokey                   ; nope.  proceed
        mov     kbdcount,0              ; make sure it goes negative again
        cmp     ax,'o'                  ; orbit toggle hit?
        je      orbitkey                ;  yup.  show orbits
        cmp     ax,'O'                  ; orbit toggle hit?
        jne     keyhit                  ;  nope.  normal key.
orbitkey:
        call    far ptr getakey         ; read the key for real
        mov     ax,1                    ; reset orbittoggle = 1 - orbittoggle
        sub     ax,show_orbit           ;  ...
        mov     show_orbit,ax           ;  ...
        jmp     short nokey             ; pretend no key was hit
keyhit:
        mov     ax,-1                   ; return with -1
        mov     dx,ax
        mov     word ptr coloriter,ax   ; set color to -1
        mov     word ptr coloriter+2,dx ; set color to -1
        UNFRAME                  ; pop stack frame
        ret                             ; bail out!
nokey:

; OK, here's the heart of the floating point code.
  184 ; In my original program, the bailout value was loaded once per image and
  185 ; was left on the floating point stack after each pixel, and finally popped
  186 ; off the stack when the fractal was finished.  A lot of overhead for very
  187 ; little gain in my opinion, so I changed it so that it loads and unloads
  188 ; per pixel. -Wes
  189 
  190         fld     rqlim                   ; everything needs bailout first
  191         mov     cx,word ptr maxit+2     ; using cx and bx as loop counter
  192         mov     bx,word ptr maxit       ; using cx and bx as loop counter
  193 
  194 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  195 ; _287 version (closely resembles original code)
  196 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  197 .286
  198 .287
  199 start_287:      ; 287
  200         cmp     fractype,JULIAFP        ; julia or mandelbrot set?
  201         je      short dojulia_287       ; julia set - go there
  202 
  203 ; Mandelbrot _287 initialization of stack
  204         sub     bx,1                      ; always requires at least 1 iteration
  205         sbb     cx,0
  206                                         ; the fpu stack is shown below
  207                                         ; st(0) ... st(7)
  208                                         ; b (already on stack)
  209         fld     inity                   ; Cy b
  210         fld     initx                   ; Cx Cy b
  211         fld     st(1)                   ; Cy Cx Cy b
  212         fadd    parmy                   ; Py+Cy Cx Cy b
  213         fld1                            ; 1 Py+Cy Cx Cy b
  214         fld     st(1)                   ; Py+Cy 1 Py+Cy Cx Cy b
  215         fmul    st,st                   ; (Py+Cy)^2 1 Py+Cy Cx Cy b
  216         fld     st(3)                   ; Cx (Py+Cy)^2 1 Py+Cy Cx Cy b
  217         fadd    parmx                   ; Px+Cx (Py+Cy)^2 1 Py+Cy Cx Cy b
  218         fmul    st(3),st                ; Px+Cx (Py+Cy)^2 1 (Py+Cy)(Px+Cx) Cx Cy b
  219         fmul    st,st                   ; (Px+Cx)^2 (Py+Cy)^2 1 (Py+Cy)(Px+Cx) Cx Cy b
  220         ; which is the next                x^2 y^2 1 xy Cx Cy b
  221         jmp     short top_of_cx_loop_287 ; branch around the julia switch
  222 
  223 dojulia_287:
  224                                         ; Julia 287 initialization of stack
  225                                         ; note that init and parm are "reversed"
  226                                         ; b (already on stack)
  227         fld     parmy                   ; Cy b
  228         fld     parmx                   ; Cx Cy b
  229         fld     inity                   ; y Cx Cy b
  230         fld1                            ; 1 y Cx Cy b
  231         fld     st(1)                   ; y 1 y Cx Cy b
  232         fmul    st,st                   ; y^2 1 y Cx Cy b
  233         fld     initx                   ; x y^2 1 y Cx Cy b
  234         fmul    st(3),st                ; x y^2 1 xy Cx Cy b
  235         fmul    st,st                   ; x^2 y^2 1 xy Cx Cy b
  236 
  237 top_of_cx_loop_287:                     ; x^2 y^2 1 xy Cx Cy b
  238         fsubr                           ; x^2-y^2 1 xy Cx Cy b
  239         fadd    st,st(3)                ; x^2-y^2+Cx 1 xy Cx Cy b
  240         fxch    st(2)                   ; xy 1 x^2-y^2+Cx Cx Cy b
  241 ; FSCALE is faster than FADD for 287
  242         fscale                          ; 2xy 1 x^2-y^2+Cx Cx Cy b
  243         fadd    st,st(4)                ; 2xy+Cy 1 x^2-y^2+Cx Cx Cy b
  244                                         ; now same as the new
  245                                         ; y 1 x Cx Cy b
  246 
  247         cmp     outside,-2              ; real, imag, mult, or sum ?
  248         jg      no_save_new_xy_287      ; if not, then skip this
  249         fld     st(2)                   ; x y 1 x Cx Cy b
  250         fstp    newx                    ; y 1 x Cx Cy b
  251         fst     newy                    ; y 1 x Cx Cy b
  252 no_save_new_xy_287:
  253 
  254 ;        cmp     inside,-100                     ; epsilon cross ?
  255 ;        jne     end_epsilon_cross_287
  256 ;        call    near ptr epsilon_cross          ; y 1 x Cx Cy b
  257 ;        cmp     bx,0
  258 ;        jnz     end_epsilon_cross_287
  259 ;        cmp     cx,0
  260 ;        jnz   end_epsilon_cross_287             ; if cx=0, pop stack
  261 ;        jmp     pop_stack_287
  262 ;end_epsilon_cross_287:
  263 
  264         test    bx,KEYPRESSDELAY        ; bx holds the low word of the loop count
  265         jne     notakey3                ; don't test yet
        push    cx
        push    bx
        call    far ptr keypressed      ; has a key been pressed?
        pop     bx
        pop     cx
        cmp     ax,0                    ;  ...
        je      notakey3                        ; nope.  proceed
        jmp     keyhit
notakey3:

        cmp     cx,word ptr oldcoloriter+2      ; if cx > oldcolor
        ja      end_periodicity_check_287       ; don't check periodicity
  266         cmp     bx,word ptr oldcoloriter        ; if bx >= oldcolor
  267         jae     end_periodicity_check_287       ; don't check periodicity
        call    near ptr periodicity_check_287_387 ; y 1 x Cx Cy b
        cmp     bx,0
        jnz     end_periodicity_check_287
        cmp     cx,0
        jnz   end_periodicity_check_287                   ; if cx=0, pop stack
        jmp     pop_stack_287
end_periodicity_check_287:

        cmp     show_orbit,0            ; is show_orbit clear
        je      no_show_orbit_287       ; if so then skip
        call    near ptr show_orbit_xy  ; y 1 x Cx Cy b
no_show_orbit_287:
                                        ; y 1 x Cx Cy b
        fld     st(2)                   ; x y 1 x Cx Cy b
        fld     st(1)                   ; y x y 1 x Cx Cy b
        fmul    st(4),st                ; y x y 1 xy Cx Cy b
        fmulp   st(2),st                ; x y^2 1 xy Cx Cy b
        fmul    st,st                   ; x^2 y^2 1 xy Cx Cy b
        fld     st                      ; x^2 x^2 y^2 1 xy Cx Cy b
        fadd    st,st(2)                ; x^2+y^2 x^2 y^2 1 xy Cx Cy b

        cmp     potflag,0               ; check for potential
        je      no_potflag_287
        fst     magnitude               ; if so, save magnitude
no_potflag_287:

        fcomp   st(7)                   ; x^2 y^2 1 xy Cx Cy b
        fstsw   ax
        sahf
        ja      over_bailout_287

;less than or equal to bailout
;       loop    top_of_cx_loop_287      ; x^2 y^2 1 xy Cx Cy b
        sub     bx,1
        sbb     cx,0
;        jnz     top_of_cx_loop_287
        jz      chk_bx
        jmp     top_of_cx_loop_287
chk_bx:
        cmp     bx,0
;        jnz     top_of_cx_loop_287
        jz      not_top
        jmp     top_of_cx_loop_287
not_top:

; reached maxit, inside
        mov     word ptr oldcoloriter,-1   ; check periodicity immediately next time
        mov     word ptr oldcoloriter+2,-1 ; check periodicity immediately next time
        mov     ax,word ptr maxit
        mov     dx,word ptr maxit+2
        sub     kbdcount,ax                 ; adjust the keyboard count
        mov     word ptr realcoloriter,ax   ; save unadjusted realcolor
        mov     word ptr realcoloriter+2,dx ; save unadjusted realcolor
        mov     ax,word ptr inside_color
        mov     dx,word ptr inside_color+2

;        cmp     inside,-59              ; zmag ?
;        jne     no_zmag_287
;        fadd    st,st(1)                ; x^2+y^2 y^2 1 xy Cx Cy b
;        fimul   maxit                   ; maxit*|z^2| x^2 y^2 1 xy Cx Cy b

; When type casting floating point variables to integers in C, the decimal
; is truncated.  When using FIST in asm, the value is rounded.  The following
; line cause the positive value to be truncated.
;        fsub    round_down_half

;        fist    tmp_dword                ; tmp_word = |z^2|*maxit
;        fwait
;        mov     ax,word ptr tmp_dword
;        shr     dx,1                    ; |z^2|*maxit/2
;        rcr     ax,1
;        add     ax,1                    ; |z^2|*maxit/2+1
;        adc     dx,0

;no_zmag_287:

pop_stack_287:
        fninit

        mov     word ptr coloriter,ax
        mov     word ptr coloriter+2,dx

        cmp     orbit_ptr,0             ; any orbits to clear?
        je      calcmandfpasm_ret_287   ; nope.
        call    far ptr scrub_orbit     ; clear out any old orbits
        mov     ax,word ptr coloriter   ; restore color
        mov     dx,word ptr coloriter+2 ; restore color
                                        ; speed not critical here in orbit land

calcmandfpasm_ret_287:
        UNFRAME                  ; pop stack frame
        fwait                           ; just to make sure
        ret

over_bailout_287:                       ; x^2 y^2 1 xy Cx Cy b
; outside
        mov     dx,cx
        mov     ax,bx
        sub     ax,10                   ; 10 more next time before checking
        sbb     dx,0
        jns     no_fix_underflow_287
; if the number of iterations was within 10 of maxit, then subtracting
; 10 would underflow and cause periodicity checking to start right
; away.  Catching a period doesn't occur as often in the pixels at
  268 ; the edge of the set anyway.
  269         sub     ax,ax                   ; don't check next time
        mov     dx,ax
no_fix_underflow_287:
        mov     word ptr oldcoloriter,ax ; check when past this - 10 next time
        mov     word ptr oldcoloriter+2,dx ; check when past this - 10 next time
        mov     ax,word ptr maxit
        mov     dx,word ptr maxit+2
        sub     ax,bx                   ; leave 'times through loop' in ax
        sbb     dx,cx                   ; and dx

; zero color fix
        jnz     zero_color_fix_287
        cmp     ax,0
        jnz     zero_color_fix_287
        inc     ax                      ; if (ax == 0 ) ax = 1
zero_color_fix_287:
        mov     word ptr realcoloriter,ax   ; save unadjusted realcolor
        mov     word ptr realcoloriter+2,dx ; save unadjusted realcolor
        sub     kbdcount,ax                 ; adjust the keyboard count

        cmp     outside,-1              ; iter ? (most common case)
        je      pop_stack_287
        cmp     outside,-2              ; outside <= -2 ?
        jle     special_outside_287     ; yes, go do special outside options
        mov     ax,outside              ; use outside color
        sub     dx,dx
        jmp     short pop_stack_287
special_outside_287:
        call    near ptr special_outside
        jmp     short pop_stack_287

calcmandfpasm_287  ENDP


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Since periodicity checking is used most of the time, I decided to
; separate the periodicity_check routines into a _287_387 version
; and an _87 version to achieve a slight increase in speed.  The
; epsilon_cross, show_orbit_xy, and special_outside routines are less
; frequently used and therefore have been implemented as single routines
; usable by the 8087 and up. -Wes
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.286
.287
EVEN
periodicity_check_287_387   PROC    NEAR
; REMEMBER, the cx counter is counting BACKWARDS from maxit to 0
                                        ; fpu stack is either
                                        ; y x Cx Cy b (387)
                                        ; y 1 x Cx Cy b (287/emul)
        cmp     fpu,387
        jb      pc_load_x
        fld     st(1)                   ; if 387
        jmp     short pc_end_load_x
pc_load_x:
        fld     st(2)                   ; if 287/emul
pc_end_load_x:
                                        ; x y ...
        test    cx,word ptr savedand+2  ; save on 0, check on anything else
        jnz     do_check_287_387        ;  time to save a new "old" value
        test    bx,word ptr savedand    ; save on 0, check on anything else
        jnz     do_check_287_387        ;  time to save a new "old" value

; save last value                       ; fpu stack is
        fstp    savedx                  ; x y ...
        fst     savedy                  ; y ...
        dec     savedincr               ; time to lengthen the periodicity?
        jnz     per_check_287_387_ret   ; if not 0, then skip
        shl     word ptr savedand,1     ; savedand = (savedand << 1) + 1
        rcl     word ptr savedand+2,1   ; savedand = (savedand << 1) + 1
        add     word ptr savedand,1     ; for longer periodicity
        adc     word ptr savedand+2,0   ; for longer periodicity
        mov     savedincr,nextsavedincr      ; and restart counter
;        mov     ax,nextsavedincr      ; and restart counter
;        mov     savedincr,ax      ; and restart counter
        ret                             ; y ...

do_check_287_387:                       ; fpu stack is
                                        ; x y ...
        fsub    savedx                  ; x-savedx y ...
        fabs                            ; |x-savedx| y ...
        fcomp   closenuff               ; y ...
        fstsw   ax
        sahf
        ja      per_check_287_387_ret
        fld     st                      ; y y ...
        fsub    savedy                  ; y-savedy y ...
        fabs                            ; |y-savedy| y ...
        fcomp   closenuff               ; y ...
        fstsw   ax
        sahf
        ja      per_check_287_387_ret
                                        ; caught a cycle!!!
        mov     word ptr oldcoloriter,-1    ; check periodicity immediately next time
        mov     word ptr oldcoloriter+2,-1    ; check periodicity immediately next time

        mov     ax,word ptr maxit
        mov     dx,word ptr maxit+2
        mov     word ptr realcoloriter,ax ; save unadjusted realcolor as maxit
        mov     word ptr realcoloriter+2,dx ; save unadjusted realcolor as maxit
        sub     dx,cx                   ; subtract half c
        sbb     ax,bx                   ; subtract half c
        sub     kbdcount,ax             ; adjust the keyboard count
        mov     ax,word ptr periodicity_color    ; set color
        mov     dx,word ptr periodicity_color+2    ; set color
        sub     cx,cx                   ; flag to exit cx loop immediately
        mov     bx,cx

per_check_287_387_ret:
        ret
periodicity_check_287_387   ENDP

.8086
.8087

EVEN
PUBLIC calcmandfpasm_87
calcmandfpasm_87  PROC
        FRAME                    ; std frame, for TC++ overlays
; initialization stuff
        sub     ax,ax                   ; clear ax
        mov     dx,ax                   ; dx=0
        cmp     periodicitycheck,ax     ; periodicity checking?
        je      initoldcolor            ;  no, set oldcolor 0 to disable it
        cmp     inside,-59              ; zmag?
        je      initoldcolor            ;  set oldcolor to 0
        cmp     reset_periodicity,ax    ; periodicity reset?
        je      short initparms         ;  no, inherit oldcolor from prior invocation
        mov     ax,word ptr maxit       ; yup.  reset oldcolor to maxit-250
        mov     dx,word ptr maxit+2
        sub     ax,250                  ; (avoids slowness at high maxits)
        sbb     dx,0                            ; (faster than conditional jump)
initoldcolor:
        mov     word ptr oldcoloriter,ax   ; reset oldcolor
        mov     word ptr oldcoloriter+2,dx ; reset oldcolor

initparms:
        sub     ax,ax                   ; clear ax
        mov     dx,ax                   ; clear dx
        mov     word ptr savedx,ax      ; savedx = 0.0
        mov     word ptr savedx+2,ax    ; needed since savedx is a QWORD
        mov     word ptr savedx+4,ax
        mov     word ptr savedx+6,ax
        mov     word ptr savedy,ax      ; savedy = 0.0
        mov     word ptr savedy+2,ax    ; needed since savedy is a QWORD
        mov     word ptr savedy+4,ax
        mov     word ptr savedy+6,ax
        mov     ax,word ptr firstsavedand+2 ; high part of savedand=0
        mov     word ptr savedand+2,ax ; high part of savedand=0
        mov     ax,word ptr firstsavedand    ; low part of savedand
        mov     word ptr savedand,ax    ; low part of savedand
        mov     savedincr,1             ; savedincr = 1
        mov     orbit_ptr,0             ; clear orbits
        dec     kbdcount                ; decrement the keyboard counter
        jns     short nokey        ;  skip keyboard test if still positive
        mov     kbdcount,10             ; stuff in a low kbd count
        cmp     show_orbit,0            ; are we showing orbits?
        jne     quickkbd                ;  yup.  leave it that way.
;this may need to be adjusted, I'm guessing at the "appropriate" values -Wes
  270         mov     kbdcount,1000           ; else, stuff an appropriate count val
  271                                         ; ("appropriate" to the FPU)
  272 kbddiskadj:
  273         cmp     dotmode,11              ; disk video?
  274         jne     quickkbd                ;  no, leave as is
  275         shr     kbdcount,1              ; yes, reduce count
  276         shr     kbdcount,1              ; yes, reduce count
  277 
  278 quickkbd:
  279         call    far ptr keypressed      ; has a key been pressed?
  280         cmp     ax,0                    ;  ...
  281         je      nokey                   ; nope.  proceed
  282         mov     kbdcount,0              ; make sure it goes negative again
  283         cmp     ax,'o'                  ; orbit toggle hit?
  284         je      orbitkey                ;  yup.  show orbits
  285         cmp     ax,'O'                  ; orbit toggle hit?
  286         jne     keyhit                  ;  nope.  normal key.
  287 orbitkey:
  288         call    far ptr getakey         ; read the key for real
  289         mov     ax,1                    ; reset orbittoggle = 1 - orbittoggle
  290         sub     ax,show_orbit           ;  ...
  291         mov     show_orbit,ax           ;  ...
  292         jmp     short nokey             ; pretend no key was hit
  293 keyhit:
  294         mov     ax,-1                   ; return with -1
  295         mov     dx,ax
  296         mov     word ptr coloriter,ax   ; set color to -1
  297         mov     word ptr coloriter+2,dx ; set color to -1
  298         UNFRAME                  ; pop stack frame
  299         ret                             ; bail out!
  300 nokey:
  301 
  302 ; OK, here's the heart of the floating point code.
; In my original program, the bailout value was loaded once per image and
; was left on the floating point stack after each pixel, and finally popped
; off the stack when the fractal was finished.  A lot of overhead for very
; little gain in my opinion, so I changed it so that it loads and unloads
; per pixel. -Wes

        fld     rqlim                   ; everything needs bailout first
        mov     cx,word ptr maxit+2     ; using cx and bx as loop counter
        mov     bx,word ptr maxit       ; using cx and bx as loop counter

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; _87 code is just like 287 code except that it must use
;       fstsw   tmp_word
;       fwait
;       mov     ax,tmp_word
; instead of
;       fstsw   ax
;
.8086
.8087
;start_87:
; let emulation fall through to the 87 code here
; as it seems not emulate correctly on an 8088/86 otherwise
        cmp     fractype,JULIAFP        ; julia or mandelbrot set?
        je      short dojulia_87        ; julia set - go there

; Mandelbrot _87 initialization of stack
        sub     bx,1                      ; always requires at least 1 iteration
        sbb     cx,0
                                        ; the fpu stack is shown below
                                        ; st(0) ... st(7)
                                        ; b (already on stack)
        fld     inity                   ; Cy b
        fld     initx                   ; Cx Cy b
        fld     st(1)                   ; Cy Cx Cy b
        fadd    parmy                   ; Py+Cy Cx Cy b
        fld1                            ; 1 Py+Cy Cx Cy b
        fld     st(1)                   ; Py+Cy 1 Py+Cy Cx Cy b
        fmul    st,st                   ; (Py+Cy)^2 1 Py+Cy Cx Cy b
        fld     st(3)                   ; Cx (Py+Cy)^2 1 Py+Cy Cx Cy b
        fadd    parmx                   ; Px+Cx (Py+Cy)^2 1 Py+Cy Cx Cy b
        fmul    st(3),st                ; Px+Cx (Py+Cy)^2 1 (Py+Cy)(Px+Cx) Cx Cy b
        fmul    st,st                   ; (Px+Cx)^2 (Py+Cy)^2 1 (Py+Cy)(Px+Cx) Cx Cy b
        ; which is the next               x^2 y^2 1 xy Cx Cy b
        jmp     short top_of_cx_loop_87 ; branch around the julia switch

dojulia_87:
                                        ; Julia 87 initialization of stack
                                        ; note that init and parm are "reversed"
                                        ; b (already on stack)
        fld     parmy                   ; Cy b
        fld     parmx                   ; Cx Cy b
        fld     inity                   ; y Cx Cy b
        fld1                            ; 1 y Cx Cy b
        fld     st(1)                   ; y 1 y Cx Cy b
        fmul    st,st                   ; y^2 1 y Cx Cy b
        fld     initx                   ; x y^2 1 y Cx Cy b
        fmul    st(3),st                ; x y^2 1 xy Cx Cy b
        fmul    st,st                   ; x^2 y^2 1 xy Cx Cy b

top_of_cx_loop_87:                      ; x^2 y^2 1 xy Cx Cy b
        fsubr                           ; x^2-y^2 1 xy Cx Cy b
        fadd    st,st(3)                ; x^2-y^2+Cx 1 xy Cx Cy b
        fxch    st(2)                   ; xy 1 x^2-y^2+Cx Cx Cy b
; FSCALE is faster than FADD for 87
        fscale                          ; 2xy 1 x^2-y^2+Cx Cx Cy b
        fadd    st,st(4)                ; 2xy+Cy 1 x^2-y^2+Cx Cx Cy b
                                        ; now same as the new
                                        ; y 1 x Cx Cy b

        cmp     outside,-2              ; real, imag, mult, or sum ?
        jg      no_save_new_xy_87       ; if not, then skip this
        fld     st(2)                   ; x y 1 x Cx Cy b
        fstp    newx                    ; y 1 x Cx Cy b
        fst     newy                    ; y 1 x Cx Cy b
no_save_new_xy_87:

;        cmp     inside,-100                     ; epsilon cross ?
;        jne     end_epsilon_cross_87
;        call    near ptr epsilon_cross          ; y 1 x Cx Cy b
;        cmp     bx,0
;        jnz     end_epsilon_cross_87
;        cmp     cx,0
;        jnz    end_epsilon_cross_87                   ; if cx=0, pop stack
;        jmp     pop_stack_6_87                  ; with a long jump
;end_epsilon_cross_87:

        test    bx,KEYPRESSDELAY        ; bx holds the low word of the loop count
        jne     notakey4                ; don't test yet
  303         push    cx
  304         push    bx
  305         call    far ptr keypressed      ; has a key been pressed?
  306         pop     bx
  307         pop     cx
  308         cmp     ax,0                    ;  ...
  309         je      notakey4                        ; nope.  proceed
  310         jmp     keyhit
  311 notakey4:
  312 
  313         cmp     cx,word ptr oldcoloriter+2      ; if cx > oldcolor
  314         ja      no_periodicity_check_87         ; don't check periodicity
        cmp     bx,word ptr oldcoloriter        ; if bx >= oldcolor
        jae     no_periodicity_check_87         ; don't check periodicity
  315         call    near ptr periodicity_check_87   ; y 1 x Cx Cy b
  316         cmp     bx,0
  317         jnz     no_periodicity_check_87
  318         cmp     cx,0
  319         jnz   no_periodicity_check_87           ; if cx=0, pop stack
  320         jmp     pop_stack_6_87
  321 no_periodicity_check_87:
  322 
  323         cmp     show_orbit,0            ; is show_orbit clear
  324         je      no_show_orbit_87        ; if so then skip
  325         call    near ptr show_orbit_xy  ; y 1 x Cx Cy b
  326 no_show_orbit_87:
  327 
  328                                         ; y 1 x Cx Cy b
  329         fld     st(2)                   ; x y 1 x Cx Cy b
  330         fld     st(1)                   ; y x y 1 x Cx Cy b
  331         fmul    st(4),st                ; y x y 1 xy Cx Cy b
  332         fmulp   st(2),st                ; x y^2 1 xy Cx Cy b
  333         fmul    st,st                   ; x^2 y^2 1 xy Cx Cy b
  334         fld     st                      ; x^2 x^2 y^2 1 xy Cx Cy b
  335         fadd    st,st(2)                ; x^2+y^2 x^2 y^2 1 xy Cx Cy b
  336 
  337         cmp     potflag,0               ; check for potential
  338         je      no_potflag_87
  339         fst     magnitude               ; if so, save magnitude
  340 no_potflag_87:
  341 
  342         fcomp   st(7)                   ; x^2 y^2 1 xy Cx Cy b
  343         fstsw   tmp_word
  344         fwait
  345         mov     ax,tmp_word
  346         sahf
  347         ja      over_bailout_87
  348 
  349 ;less than or equal to bailout
  350 ;       loop    top_of_cx_loop_87       ; x^2 y^2 1 xy Cx Cy b
  351         sub     bx,1
  352         sbb     cx,0
  353 ;        jnz     top_of_cx_loop_87
  354         jz      not_top1
  355         jmp     top_of_cx_loop_87
  356 not_top1:
  357         cmp     bx,0
  358 ;        jnz     top_of_cx_loop_87
  359         jz      not_top2
  360         jmp     top_of_cx_loop_87
  361 not_top2:
  362 
  363 ; reached maxit
  364         mov     word ptr oldcoloriter,-1   ; check periodicity immediately next time
  365         mov     word ptr oldcoloriter+2,-1 ; check periodicity immediately next time
  366         mov     ax,word ptr maxit
  367         mov     dx,word ptr maxit+2
  368         sub     kbdcount,ax                 ; adjust the keyboard count
  369         mov     word ptr realcoloriter,ax   ; save unadjusted realcolor
  370         mov     word ptr realcoloriter+2,dx ; save unadjusted realcolor
  371         mov     ax,word ptr inside_color
  372         mov     dx,word ptr inside_color+2
  373 
  374 ;        cmp     inside,-59              ; zmag ?
  375 ;        jne     no_zmag_87
  376 ;        fadd    st,st(1)                ; x^2+y^2 y^2 1 xy Cx Cy b
  377 ;        fimul   maxit                   ; maxit*|z^2| x^2 y^2 1 xy Cx Cy b
  378 
  379 ; When type casting floating point variables to integers in C, the decimal
  380 ; is truncated.  When using FIST in asm, the value is rounded.  The following
  381 ; line cause the positive value to be truncated.
  382 ;        fsub    round_down_half
  383 
  384 ;        fist    tmp_dword                ; tmp_word = |z^2|*maxit
  385 ;        fwait
  386 ;        mov     ax,word ptr tmp_dword
  387 ;        mov     dx,word ptr tmp_dword+2
  388 ;        shr     dx,1                    ; |z^2|*maxit/2
  389 ;        rcr     ax,1
  390 ;        add     ax,1                    ; |z^2|*maxit/2+1
  391 ;        adc     dx,0
  392 
  393 ;no_zmag_87:
  394 
  395 pop_stack_7_87:
  396 ; The idea here is just to clear the floating point stack.  There was a
  397 ; problem using FNINIT with the emulation library.  It didn't seem to
; properly clear the emulated stack, resulting in "stack overflow"
; messages.  Therefore, if emulation is being used, then FSTP's are used
  398 ; instead.
  399 
  400         cmp     fpu,0                   ; are we using emulation?
  401         jne     no_emulation            ; if not, then jump
  402         fstp    st
  403 ; you could just jump over this next check, but its faster to just check again
  404 pop_stack_6_87:
  405         cmp     fpu,0                   ; are we using emulation?
  406         jne     no_emulation            ; if not, then jump
  407         fstp    st
  408         fstp    st
  409         fstp    st
  410         fstp    st
  411         fstp    st
  412         fstp    st
  413         jmp     short end_pop_stack_87
  414 no_emulation:                           ; if no emulation, then
  415         fninit                          ;   use the faster FNINIT
  416 end_pop_stack_87:
  417 
  418         mov     word ptr coloriter,ax
  419         mov     word ptr coloriter+2,dx
  420 
  421         cmp     orbit_ptr,0             ; any orbits to clear?
  422         je      calcmandfpasm_ret_87    ; nope.
  423         call    far ptr scrub_orbit     ; clear out any old orbits
  424         mov     ax,word ptr coloriter   ; restore color
  425         mov     dx,word ptr coloriter+2 ; restore color
  426                                         ; speed not critical here in orbit land
  427 
  428 calcmandfpasm_ret_87:
  429         UNFRAME                  ; pop stack frame
  430         fwait                           ; just to make sure
  431         ret
  432 
  433 over_bailout_87:                        ; x^2 y^2 1 xy Cx Cy b
  434 ; outside
  435         mov     dx,cx
  436         mov     ax,bx
  437         sub     ax,10                   ; 10 more next time before checking
  438         sbb     dx,0
  439         jns     no_fix_underflow_87
  440 ; if the number of iterations was within 10 of maxit, then subtracting
  441 ; 10 would underflow and cause periodicity checking to start right
  442 ; away.  Catching a period doesn't occur as often in the pixels at
; the edge of the set anyway.
        sub     ax,ax                   ; don't check next time
  443         mov     dx,ax
  444 no_fix_underflow_87:
  445         mov     word ptr oldcoloriter,ax ; check when past this - 10 next time
  446         mov     word ptr oldcoloriter+2,dx ; check when past this - 10 next time
  447         mov     ax,word ptr maxit
  448         mov     dx,word ptr maxit+2
  449         sub     ax,bx                   ; leave 'times through loop' in ax
  450         sbb     dx,cx                   ; and dx
  451 
  452 ; zero color fix
  453         jnz     zero_color_fix_87
  454         cmp     ax,0
  455         jnz     zero_color_fix_87
  456         inc     ax                      ; if (ax == 0 ) ax = 1
  457 zero_color_fix_87:
  458         mov     word ptr realcoloriter,ax   ; save unadjusted realcolor
  459         mov     word ptr realcoloriter+2,dx ; save unadjusted realcolor
  460         sub     kbdcount,ax                 ; adjust the keyboard count
  461 
  462         cmp     outside,-1              ; iter ? (most common case)
  463         jne     dont_pop
  464 ;        je      pop_stack_7_87
  465         jmp     pop_stack_7_87
  466 dont_pop:
  467         cmp     outside,-2              ; outside <= -2 ?
  468         jle     special_outside_87      ; yes, go do special outside options
  469         mov     ax,outside              ; use outside color
  470         sub     dx,dx
  471         jmp     pop_stack_7_87
  472 special_outside_87:
  473         call    near ptr special_outside
  474         jmp     pop_stack_7_87
  475 
  476 calcmandfpasm_87  ENDP
  477 
  478 
  479 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  480 .8086
  481 .8087
  482 EVEN
  483 periodicity_check_87    PROC    NEAR
  484 ; just like periodicity_check_287_387 except for the use of
  485 ;       fstsw tmp_word
  486 ; instead of
  487 ;       fstsw ax
  488 
  489 ; REMEMBER, the cx counter is counting BACKWARDS from maxit to 0
  490         fld     st(2)                   ; x y ...
  491         test    cx,word ptr savedand+2  ; save on 0, check on anything else
  492         jnz     do_check_87             ;  time to save a new "old" value
  493         test    bx,word ptr savedand    ; save on 0, check on anything else
  494         jnz     do_check_87             ;  time to save a new "old" value
  495 
  496 ; save last value                       ; fpu stack is
  497                                         ; x y ...
  498         fstp    savedx                  ; y ...
  499         fst     savedy                  ; y ...
  500         dec     savedincr               ; time to lengthen the periodicity?
  501         jnz     per_check_87_ret        ; if not 0, then skip
  502         shl     word ptr savedand,1     ; savedand = (savedand << 1) + 1
  503         rcl     word ptr savedand+2,1   ; savedand = (savedand << 1) + 1
  504         add     word ptr savedand,1     ; for longer periodicity
  505         adc     word ptr savedand+2,0   ; for longer periodicity
  506         mov     savedincr,nextsavedincr        ; and restart counter
  507 ;        mov     ax,nextsavedincr        ; and restart counter
  508 ;        mov     savedincr,ax             ; and restart counter
  509         ret                             ; y ...
  510 
  511 do_check_87:                            ; fpu stack is
  512                                         ; x y ...
  513         fsub    savedx                  ; x-savedx y ...
  514         fabs                            ; |x-savedx| y ...
  515         fcomp   closenuff               ; y ...
  516         fstsw   tmp_word
  517         fwait
  518         mov     ax,tmp_word
  519         sahf
  520         ja      per_check_87_ret
  521         fld     st                      ; y y ...
  522         fsub    savedy                  ; y-savedy y ...
  523         fabs                            ; |y-savedy| y ...
  524         fcomp   closenuff               ; y ...
  525         fstsw   tmp_word
  526         fwait
  527         mov     ax,tmp_word
  528         sahf
  529         ja      per_check_87_ret
  530                                         ; caught a cycle!!!
  531         mov     word ptr oldcoloriter,-1    ; check periodicity immediately next time
  532         mov     word ptr oldcoloriter+2,-1  ; check periodicity immediately next time
  533 
  534         mov     ax,word ptr maxit
  535         mov     dx,word ptr maxit+2
  536         mov     word ptr realcoloriter,ax ; save unadjusted realcolor as maxit
  537         mov     word ptr realcoloriter+2,dx ; save unadjusted realcolor as maxit
  538         sub     dx,cx                   ; subtract half c
  539         sbb     ax,bx                   ; subtract half c
  540         sub     kbdcount,ax             ; adjust the keyboard count
  541         mov     ax,word ptr periodicity_color ; set color
  542         mov     dx,word ptr periodicity_color+2 ; set color
  543         sub     cx,cx                   ; flag to exit cx loop immediately
  544         mov     bx,cx
  545 
  546 per_check_87_ret:
  547         ret
  548 periodicity_check_87       ENDP
  549 
  550 .8086
  551 .8087
  552 EVEN
  553 show_orbit_xy   PROC NEAR USES bx cx dx si di
  554 IFDEF @Version        ; MASM
  555 IF @Version lt 600
  556         local   tmp_ten_byte_0:tbyte    ; stupid klooge for MASM 5.1 LOCAL bug
  557 ENDIF
  558 ENDIF
  559         local   tmp_ten_byte_1:tbyte
  560         local   tmp_ten_byte_2:tbyte
  561         local   tmp_ten_byte_3:tbyte
  562         local   tmp_ten_byte_4:tbyte
  563         local   tmp_ten_byte_5:tbyte
  564         local   tmp_ten_byte_6:tbyte
  565 ; USES is needed because in all likelyhood, plot_orbit surely
  566 ; uses these registers.  It's ok to have to push/pop's here in the
  567 ; orbits as speed is not crucial when showing orbits.
  568 
  569                                         ; fpu stack is either
  570                                         ; y x Cx Cy b (387)
  571                                         ; y 1 x Cx Cy b (287/87/emul)
  572         cmp     fpu,387
  573         jb      so_load_x
  574         fld     st(1)                   ; if 387
  575         jmp     short so_end_load_x
  576 so_load_x:
  577         fld     st(2)                   ; if 287/87/emul
  578 so_end_load_x:
  579                                         ; x y ...
  580                                         ; and needs to returned as
  581                                         ; y ...
  582 
  583         fstp    orbit_real              ; y ...
  584         fst     orbit_imag              ; y ...
  585         mov     ax,-1                   ; color for plot orbit
  586         push    ax                      ;       ...
  587 ; since the number fpu registers that plot_orbit() preserves is compiler
  588 ; dependant, it's best to fstp the entire stack into 10 byte memories
; and fld them back after plot_orbit() returns.
        fstp    tmp_ten_byte_1          ; store the stack in 80 bit form
        fstp    tmp_ten_byte_2
        fstp    tmp_ten_byte_3
        fstp    tmp_ten_byte_4
        fstp    tmp_ten_byte_5
        cmp     fpu,287                 ; with 287/87/emul the stack is 6 high
        jg      no_store_6              ; with 387 it is only 5 high
        fstp    tmp_ten_byte_6
no_store_6:
        fwait                           ; just to be safe
        push    word ptr orbit_imag+6   ; co-ordinates for plot orbit
        push    word ptr orbit_imag+4   ;       ...
        push    word ptr orbit_imag+2   ;       ...
        push    word ptr orbit_imag     ;       ...
        push    word ptr orbit_real+6   ; co-ordinates for plot orbit
        push    word ptr orbit_real+4   ;       ...
        push    word ptr orbit_real+2   ;       ...
        push    word ptr orbit_real     ;       ...
        call    far ptr plot_orbit      ; display the orbit
        add     sp,9*2                  ; clear out the parameters

        cmp     fpu,287
        jg      no_load_6
        fld     tmp_ten_byte_6          ; load them back in reverse order
no_load_6:
        fld     tmp_ten_byte_5
        fld     tmp_ten_byte_4
        fld     tmp_ten_byte_3
        fld     tmp_ten_byte_2
        fld     tmp_ten_byte_1
        fwait                           ; just to be safe
        ret
show_orbit_xy   ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.8086
.8087
EVEN
special_outside PROC NEAR
; When type casting floating point variables to integers in C, the decimal
; is truncated.  When using FIST in asm, the value is rounded.  Using
; "FSUB round_down_half" causes the values to be rounded down.
LOCAL Control:word
IFDEF @Version        ; MASM
IF @Version lt 600
        local   tmp_ten_byte_0:tbyte    ; stupid klooge for MASM 5.1 LOCAL bug
ENDIF
ENDIF
        local   tmp_ten_byte_1:tbyte
        fstcw Control
        push  Control                       ; Save control word on the stack
        or    Control, 0000110000000000b
        fldcw Control                       ; Set control to round towards zero

        cmp     outside,-2
        jne     not_real
        fld     newx
        test    bad_outside,1h
        jz      over_bad_real
        fsub    round_down_half
        jmp     over_good_real
over_bad_real:
        frndint
over_good_real:
        fistp   tmp_dword
        add     ax,7
        adc     dx,0
        fwait
        add     ax,word ptr tmp_dword
        adc     dx,word ptr tmp_dword+2
        jmp     check_color
not_real:
        cmp     outside,-3
        jne     not_imag
        fld     newy
        test    bad_outside,1h
        jz      short over_bad_imag
        fsub    round_down_half
        jmp     short over_good_imag
over_bad_imag:
        frndint
over_good_imag:
        fistp   tmp_dword
        add     ax,7
        adc     dx,0
        fwait
        add     ax,word ptr tmp_dword
        adc     dx,word ptr tmp_dword+2
        jmp     check_color
not_imag:
        cmp     outside,-4
        jne     not_mult
        fld     newy
        ftst                    ; check to see if newy == 0
        fstsw   tmp_word
        push    ax              ; save current ax value
        fwait
        mov     ax,tmp_word
        sahf
        pop     ax              ; retrieve ax (does not affect flags)
        jne     non_zero_y
        jmp     special_outside_ret
;        ret                     ; if y==0, return with normal ax
non_zero_y:
        fdivr   newx            ; newx/newy
        mov     word ptr tmp_dword,ax
        mov     word ptr tmp_dword+2,dx
        fimul   tmp_dword       ; (ax,dx)*newx/newy  (Use FIMUL instead of MUL
        test    bad_outside,1h
        jz      short over_bad_mult
        fsub    round_down_half ; to make it match the C code.)
        jmp     short over_good_mult
over_bad_mult:
        frndint
over_good_mult:
        fistp   tmp_dword
        fwait
        mov     ax,word ptr tmp_dword
        mov     dx,word ptr tmp_dword+2
        jmp     short check_color
not_mult:
        cmp     outside,-5
        jne     not_sum
        fld     newx
        fadd    newy            ; newx+newy
        test    bad_outside,1h
        jz     short over_bad_summ
        fsub    round_down_half
        jmp     short over_good_summ
over_bad_summ:
        frndint
over_good_summ:
        fistp   tmp_dword
        fwait
        add     ax,word ptr tmp_dword
        adc     dx,word ptr tmp_dword+2
        jmp     short check_color
not_sum:
        cmp     outside,-6      ; currently always equal, but put here
        jne     not_atan        ; for future outside types
; FPUatan needs 2 spots on the FPU stack, only 1 is available.
        fstp    tmp_ten_byte_1   ; store the top of stack in 80 bit form

        call    near ptr FPUatan ; return with atan on FPU stack
        fimul   atan_colors     ; atan_colors*Angle
        fldpi                   ; pi atan_colors*Angle
        fdiv                    ; atan_colors*Angle/pi
        fabs
        frndint
        fistp   tmp_dword

        fld     tmp_ten_byte_1  ; restore the top of stack

        fwait
        mov     ax,word ptr tmp_dword
        mov     dx,word ptr tmp_dword+2

not_atan:
check_color:
        cmp     dx,word ptr maxit+2     ; use UNSIGNED comparison
        jb      check_release           ; color < 0 || color > maxit
        cmp     ax,word ptr maxit       ; use UNSIGNED comparison
        jbe     check_release           ; color < 0 || color > maxit
        sub     ax,ax                   ; ax = 0
        mov     dx,ax                   ; dx = 0
check_release:
        cmp     save_release,1961
        jb      special_outside_ret
        cmp     dx,0
        jne     special_outside_ret
        cmp     ax,0
        jne     special_outside_ret
        mov     ax,1                    ; ax = 1
        mov     dx,0                    ; dx = 0
special_outside_ret:
        pop   Control
        fldcw Control              ; Restore control word
        ret
special_outside ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

.8086   ;just to be sure
.8087
EVEN
FPUatan PROC NEAR USES ax dx
; This is derived from FPUcplxlog in fpu087.asm
; The arctan is returned on the FPU stack
LOCAL Status:word

        mov     ax, word ptr newy+6
        or      ax, word ptr newx+6
        jnz     NotBothZero

        fldz
        jmp     atandone

NotBothZero:
        fld     newy            ; newy
        fld     newx            ; newx newy

        mov     dh, BYTE PTR newx+7
        or      dh, dh
        jns     ChkYSign

        fchs                    ; |newx| newy

ChkYSign:
        mov     dl, BYTE PTR newy+7
        or      dl, dl
        jns     ChkMagnitudes

        fxch                    ; newy |newx|
        fchs                    ; |newy| |newx|
        fxch                    ; |newx| |newy|

ChkMagnitudes:
        fcom    st(1)           ; |newx| |newy|
        fstsw   Status
        fwait
        test    Status, 4500h
        jz      XisGTY

        test    Status, 4000h
        jz      short XneY

; newx = newy and atan = pi/4
        fstp    st              ; newy
        fstp    st              ; empty
        fldpi                   ; pi
        fdiv    _4_             ; pi/4

        or      dh, dh
        js      NegX_pi4

        or      dl, dl
        jns     short atandone  ; x+ y+

        fchs
        jmp     short atandone  ; x+ y-

NegX_pi4:
        fld     st(0)           ; pi/4 pi/4
        fadd    st,st           ; pi/2 pi/4
        fadd                    ; 3pi/4

        or      dl, dl
        jns     short atandone  ; x- y+

        fchs
        jmp     short atandone  ; x- y-

XneY: ; y > x
        fxch                    ; newy newx
        fpatan                  ; pi/2 - Angle
        fldpi                   ; pi, pi/2 - Angle
        fdiv    _2_             ; pi/2, pi/2 - Angle
        fsubr                   ; Angle

        or      dh, dh
        js      NegX

        or      dl, dl
        jns     short atandone  ; x+ y+

        fchs
        jmp     short atandone  ; x+ y-

XisGTY: ; x > y
        fpatan                  ; pi-Angle or Angle+pi

        or      dh, dh
        js      NegX

        or      dl, dl
        jns     short atandone  ; x+ y+

        fchs
        jmp     short atandone  ; x+ y-

NegX:
        fldpi                   ; pi, Angle
        fsubr                   ; pi - Angle

        or      dl, dl
        jns     short atandone  ; x- y+

        fchs
;        jmp     short atandone  ; x- y-

atandone:
        ret                     ; Leave result on FPU stack and return

FPUatan ENDP

END