home *** CD-ROM | disk | FTP | other *** search
/ Fractal Creations (Second Edition) / FRACTALS_2E.iso / frasrc.exe / LYAPUNOV.ASM < prev    next >
Assembly Source File  |  1993-04-07  |  20KB  |  495 lines

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ; Wesley Loewer's attempt at writing his own
  3. ; 80x87 assembler implementation of Lyapunov fractals
  4. ; based on lyapunov_cycles_in_c() in miscfrac.c
  5. ;
  6. ; Nicholas Wilt, April 1992, originally wrote an 80x87 assembler
  7. ; implementation of Lyapunov fractals, but I couldn't get his
  8. ; lyapunov_cycles() to work right with fractint.
  9. ; So I'm starting mostly from scratch with credits to Nicholas Wilt's
  10. ; code marked with NW.
  11.  
  12. .8086
  13. .8087
  14. .MODEL medium,c
  15.  
  16. ; Increase the overflowcheck value at your own risk.
  17. ; Bigger value -> check less often -> faster run time, increased risk of overflow.
  18. ; I've had failures with 6 when using "set no87" as emulation can be less forgiving.
  19. overflowcheck EQU 5
  20.  
  21. EXTRN   Population:QWORD,Rate:QWORD
  22. EXTRN   colors:WORD, maxit:WORD, lyaLength:WORD
  23. EXTRN   lyaRxy:WORD, LogFlag:WORD
  24. EXTRN   fpu:WORD
  25. EXTRN   filter_cycles:WORD
  26.  
  27. .DATA
  28. ALIGN 2
  29. BigNum      DD      100000.0
  30. half        DD      0.5                 ; for rounding to integers
  31. e_10        DQ      22026.4657948       ; e^10
  32. e_neg10     DQ      4.53999297625e-5    ; e^(-10)
  33.  
  34. .CODE
  35.  
  36. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  37. ; BifurcLambda - not used in lyapunov.asm, but used in miscfrac.c
  38. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  39. PUBLIC  BifurcLambda
  40. BifurcLambda    PROC
  41. ;   Population = Rate * Population * (1 - Population);
  42. ;   return (fabs(Population) > BIG);
  43.         push    bp              ; Establish stack frame
  44.         mov     bp,sp           ;
  45.         sub     sp,2            ; Local for 80x87 flags
  46.         fld     Population      ; Get population
  47.         fld     st              ; Population Population
  48.         fld1                    ; 1 Population Population
  49.         fsubr                   ; 1 - Population Population
  50.         fmul                    ; Population * (1 - Population)
  51.         fmul    Rate            ; Rate * Population * (1 - Population)
  52.         fst     Population      ; Store in Population
  53.         fabs                    ; Take absolute value
  54.         fcomp   BigNum          ; Compare to 100000.0
  55.         fstsw   [bp-2]          ; Return 1 if greater than 100000.0
  56.         mov     ax,[bp-2]       ;
  57.         sahf                    ;
  58.         ja      Return1         ;
  59.         xor     ax,ax           ;
  60.         jmp     short Done      ;
  61. Return1:
  62.         mov     ax,1            ;
  63. Done:   inc     sp              ; Deallocate locals
  64.         inc     sp              ;
  65.         pop     bp              ; Restore stack frame
  66.         ret                     ; Return
  67. BifurcLambda    ENDP
  68.  
  69. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  70. ; OneMinusExpMacro - calculates e^x
  71. ; parameter : x in st(0)
  72. ; returns   : e^x in st(0)
  73. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  74. OneMinusExpMacro   MACRO
  75. LOCAL not_387, positive_x, was_positive_x
  76. LOCAL long_way_386, long_way_286
  77. LOCAL positive_x_long, was_positive_x_long, done
  78.  
  79. ; To take e^x, one can use 2^(x*log_2(e)), however on on 8087/287
  80. ; the valid domain for x is 0 <= x <= 0.5  while the 387/487 allows
  81. ; -1.0 <= x <= +1.0.
  82.  
  83. ; I wrote the following 387+ specific code to take advantage of the
  84. ; improved domain in the f2xm1 command, but the 287- code seems to work
  85. ; about as fast.
  86.  
  87.         cmp     fpu,387                 ; is fpu >= 387 ?
  88.         jl      not_387
  89.  
  90. ;.386   ; a 387 or better is present so we might as well use .386/7 here
  91. ;.387   ; so "fstsw ax" can be used.  Note that the same can be accomplished 
  92. .286    ; with .286/7 which is recognized by more more assemblers.
  93. .287    ; (ie: MS QuickAssembler doesn't recognize .386/7)
  94.  
  95. ; |x*log_2(e)| must be <= 1.0 in order to work with 386 or greater.
  96. ; It usually is, so it's worth taking these extra steps to check.
  97.         fldl2e                          ; log_2(e) x
  98.         fmul                            ; x*log_2(e)
  99. ;begining of short_way code
  100.         fld1                            ; 1 x*log_2(e)
  101.         fld     st(1)                   ; x*log_2(e) 1 x*log_2(e)
  102.         fabs                            ; |x*log_2(e)| 1 x*log_2(e)
  103.         fcompp                          ; x*log_2(e)
  104.         fstsw   ax
  105.         sahf
  106.         ja      long_way_386            ; do it the long way
  107.         f2xm1                           ; e^x-1=2^(x*log_2(e))-1
  108.         fchs                            ; 1-e^x which is what we wanted anyway
  109.     jmp     done                    ; done here.
  110.  
  111. long_way_386:
  112. ; mostly taken from NW's code
  113.         fld     st                      ; x x
  114.         frndint                         ; i x
  115.         fsub    st(1),st                ; i x-i
  116.         fxch                            ; x-i i
  117.         f2xm1                           ; e^(x-i)-1 i
  118.         fld1                            ; 1 e^(x-i)-1 i
  119.         fadd                            ; e^(x-i) i
  120.         fscale                          ; e^x i
  121.         fstp    st(1)                   ; e^x
  122.         fld1                            ; 1 e^x
  123.         fsubr                           ; 1-e^x
  124.         jmp     done
  125.  
  126. not_387:
  127. .8086
  128. .8087
  129. ; |x*log_2(e)| must be <= 0.5 in order to work with 286 or less.
  130. ; It usually is, so it's worth taking these extra steps to check.
  131.         fldl2e                          ; log_2(e) x
  132.         fmul                            ; x*log_2(e)
  133.  
  134. ;begining of short_way code
  135.         fld     st                      ; x*log_2(e) x*log_2(e)
  136.         fabs                            ; |x*log_2(e)| x*log_2(e)
  137.         fcomp   half                    ; x*log_2(e)
  138.         fstsw   status_word
  139.         fwait
  140.         mov     ax,status_word
  141.         sahf
  142.         ja      long_way_286            ; do it the long way
  143.  
  144. ; 286 or less requires x>=0, if x is negative, use e^(-x) = 1/e^x
  145.         ftst                            ; x
  146.         fstsw   status_word
  147.         fwait
  148.         mov     ax,status_word
  149.         sahf
  150.         jae     positive_x
  151.         fabs                            ; -x
  152.         f2xm1                           ; e^(-x)-1=2^(-x*log_2(e))-1
  153.         fld1                            ; 1 e^(-x)-1
  154.         fadd    st,st(1)                ; e^(-x) e^(-x)-1
  155.         fdiv                            ; 1-e^x=(e^(-x)-1)/e^(-x)
  156.         jmp     SHORT done              ; done here.
  157.  
  158. positive_x:
  159.         f2xm1                           ; e^x-1=2^(x*log_2(e))-1
  160.         fchs                            ; 1-e^x which is what we wanted anyway
  161.         jmp     SHORT done              ; done here.
  162.  
  163. long_way_286:
  164. ; mostly taken from NW's code
  165.         fld     st                      ; x x
  166.         frndint                         ; i x
  167.         fsub    st(1),st                ; i x-i
  168.         fxch                            ; x-i i
  169. ; x-i could be pos or neg
  170. ; 286 or less requires x>=0, if negative, use e^(-x) = 1/e^x
  171.         ftst
  172.         fstsw   status_word
  173.         fwait
  174.         mov     ax,status_word
  175.         sahf
  176.         jae     positive_x_long_way
  177.         fabs                            ; i-x
  178.         f2xm1                           ; e^(i-x)-1 i
  179.         fld1                            ; 1 e^(i-x)-1 i
  180.         fadd    st(1),st                ; 1 e^(i-x) i
  181.         fdivr                           ; e^(x-i) i
  182.         fscale                          ; e^x i
  183.         fstp    st(1)                   ; e^x
  184.         fld1                            ; 1 e^x
  185.         fsubr                           ; 1-e^x
  186.         jmp     SHORT done              ; done here.
  187.  
  188. positive_x_long_way:                    ; x-i
  189.         f2xm1                           ; e^(x-i)-1 i
  190.         fld1                            ; 1 e^(x-i)-1 i
  191.         fadd                            ; e^(x-i) i
  192.         fscale                          ; e^x i
  193.         fstp    st(1)                   ; e^x
  194.         fld1                            ; 1 e^x
  195.         fsubr                           ; 1-e^x
  196. done:
  197. ENDM    ; OneMinusExpMacro
  198.  
  199. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  200. ; modeled from lyapunov_cycles_in_c() in miscfrac.c
  201. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  202. ;int lyapunov_cycles_in_c(int filter_cycles, double a, double b) {
  203. ;    int color, count, i, lnadjust;
  204. ;    double lyap, total, temp;
  205.  
  206. PUBLIC lyapunov_cycles
  207. lyapunov_cycles  PROC  USES si di, a:QWORD, b:QWORD
  208.     LOCAL color:WORD, i:WORD, lnadjust:WORD
  209.     LOCAL halfmax:WORD, status_word:WORD
  210.     LOCAL total:QWORD
  211.  
  212.  
  213. ;    for (i=0; i<filter_cycles; i++) {
  214. ;        for (count=0; count<lyaLength; count++) {
  215. ;            Rate = lyaRxy[count] ? a : b;
  216. ;            if (curfractalspecific->orbitcalc()) {
  217. ;                overflow = TRUE;
  218. ;                goto jumpout;
  219. ;                }
  220. ;            }
  221. ;        }
  222.  
  223. ; Popalation is left on the stack during most of this procedure
  224.         fld     Population              ; Population
  225.         mov     dx,1                    ; using dx for overflowcheck counter
  226.         xor     si, si                  ; using si for i
  227. filter_cycles_top:
  228.         cmp     si, filter_cycles       ; si < filter_cycles ?
  229.         jnl     filter_cycles_bottom    ; if not, end loop
  230.  
  231.         mov     cx, lyaLength           ; using cx for count
  232.         mov     di,OFFSET lyaRxy        ; use di as lyaRxy[] index
  233.         ; no need to compare cx with lyaLength at top of loop
  234.         ; since lyaLength is guaranteed to be > 0
  235.  
  236. filter_cycles_count_top:
  237.         cmp     WORD PTR [di],0         ; lyaRxy[count] ?
  238.         jz      filter_cycles_use_b
  239.         fld     a                       ; if lyaRxy[count]==true,  use a
  240.         jmp     SHORT filter_cycles_used_a
  241. filter_cycles_use_b:
  242.         fld     b                       ; if lyaRxy[count]==false, use b
  243. filter_cycles_used_a:
  244.         ; leave Rate on stack for use in BifurcLambdaMacro
  245.         ; BifurcLambdaMacro               ; returns in flag register
  246.     
  247.         fld     st(1)                   ; Population Rate Population
  248.         fmul    st,st                   ; Population^2 Rate Population
  249.         fsubp   st(2),st                ; Rate Population-Population^2
  250.                                         ;      =Population*(1-Population)
  251.         fmul                            ; Rate*Population*(1-Population)
  252.         dec     dx                      ; decrement overflowcheck counter
  253.         jnz     filter_cycles_skip_overflow_check ; can we skip check?
  254.         fld     st                      ; NewPopulation NewPopulation
  255.         fabs                            ; Take absolute value
  256.         fcomp   BigNum                  ; Compare to 100000.0
  257.         fstsw   status_word             ;
  258.         fwait
  259.         mov     dx,overflowcheck        ; reset overflowcheck counter
  260.         mov     ax,status_word          ;
  261.         sahf                            ;  NewPopulation
  262.  
  263.         ja      overflowed_with_Pop
  264. filter_cycles_skip_overflow_check:
  265.         add     di,2                    ; di += sizeof(*lyaRxy)
  266.         loop    filter_cycles_count_top ; if --cx > 0 then loop
  267.  
  268.         inc     si
  269.         jmp     filter_cycles_top       ; let's do it again
  270.  
  271. filter_cycles_bottom:
  272.  
  273.  
  274. ;    total = 1.0;
  275. ;    lnadjust = 0;
  276.  
  277.         fld1
  278.         fstp    total
  279.         mov     lnadjust,0
  280.  
  281. ;    for (i=0; i < maxit/2; i++) {
  282. ;        for (count = 0; count < lyaLength; count++) {
  283. ;            Rate = lyaRxy[count] ? a : b;
  284. ;            if (curfractalspecific->orbitcalc()) {
  285. ;                overflow = TRUE;
  286. ;                goto jumpout;
  287. ;                }
  288. ;            temp = fabs(Rate-2.0*Rate*Population);
  289. ;                if ((total *= (temp))==0) {
  290. ;                overflow = TRUE;
  291. ;                goto jumpout;
  292. ;                }
  293. ;            }
  294. ;        while (total > 22026.4657948) {
  295. ;            total *= 0.0000453999297625;
  296. ;            lnadjust += 10;
  297. ;            }
  298. ;        while (total < 0.0000453999297625) {
  299. ;            total *= 22026.4657948;
  300. ;            lnadjust -= 10;
  301. ;            }
  302. ;        }
  303.  
  304.         ; don't forget Population is still on stack
  305.         mov     ax,maxit                ; calculate halfmax
  306.         shr     ax,1
  307.         mov     halfmax,ax
  308.         xor     si, si                  ; using si for i
  309. halfmax_top:
  310.         cmp     si, halfmax             ; si < halfmax ?
  311.         ; too far for a short jump, need a near jump here
  312.         jl      init_halfmax_count      ; yes, continue on with loop
  313.         jmp     halfmax_bottom          ; if not, end loop
  314. init_halfmax_count:
  315.         mov     cx, lyaLength           ; using cx for count
  316.         mov     di,OFFSET lyaRxy        ; use di as lyaRxy[] index
  317.         ; no need to compare cx with lyaLength at top of loop
  318.         ; since lyaLength is guaranteed to be > 0
  319.  
  320. halfmax_count_top:
  321.         cmp     WORD PTR [di],0         ; lyaRxy[count] ?
  322.         jz      halfmax_use_b
  323.         fld     a                       ; if lyaRxy[count]==true,  use a
  324.         jmp     SHORT halfmax_used_a
  325. halfmax_use_b:
  326.         fld     b                       ; if lyaRxy[count]==false, use b
  327. halfmax_used_a:
  328.         ; save Rate, but leave it on stack for use in BifurcLambdaMacro
  329.         fst     Rate                    ; save for not_overflowed use
  330.         ;BifurcLambdaMacro               ; returns in flag register
  331.  
  332.         fld     st(1)                   ; Population Rate Population
  333.         fmul    st,st                   ; Population^2 Rate Population
  334.         fsubp   st(2),st                ; Rate Population-Population^2
  335.                                         ;      =Population*(1-Population)
  336.         fmul                            ; Rate*Population*(1-Population)
  337.         dec     dx                      ; decrement overflowcheck counter
  338.         jnz     not_overflowed          ; can we skip overflow check?
  339.         fld     st                      ; NewPopulation NewPopulation
  340.         fabs                            ; Take absolute value
  341.         fcomp   BigNum                  ; Compare to 100000.0
  342.         fstsw   status_word             ;
  343.         fwait
  344.         mov     dx,overflowcheck        ; reset overflowcheck counter
  345.         mov     ax,status_word          ;
  346.         sahf                            ;
  347.  
  348.         jbe     not_overflowed
  349.  
  350.         ; putting overflowed_with_Pop: here saves 2 long jumps in inner loops
  351. overflowed_with_Pop:
  352.         fstp    Population              ; save Population and clear stack
  353.         jmp     color_zero              ;
  354.  
  355. not_overflowed:                         ; Population
  356.         fld     st                      ; Population Population
  357.         ; slightly faster _not_ to fld Rate here
  358.         fmul    Rate                    ; Rate*Population Population
  359.         fadd    st,st                   ; 2.0*Rate*Population Population
  360.         fsubr   Rate                    ; Rate-2.0*Rate*Population Population
  361.         fabs                            ; fabs(Rate-2.0*Rate*Population) Population
  362.         fmul    total                   ; total*fabs(Rate-2.0*Rate*Population) Population
  363.         ftst                            ; compare to 0
  364.         fstp    total                   ; save the new total
  365.         fstsw   status_word             ; Population
  366.         fwait
  367.         mov     ax,status_word
  368.         sahf
  369.         jz      overflowed_with_Pop     ; if total==0, then treat as overflow
  370.  
  371.         add     di,2                    ; di += sizeof(*lyaRxy)
  372.         loop    halfmax_count_top       ; if --cx > 0 then loop
  373.  
  374.         fld     total                   ; total Population
  375. too_big_top:
  376.         fcom    e_10                    ; total Population
  377.         fstsw   status_word
  378.         fwait
  379.         mov     ax,status_word
  380.         sahf
  381.         jna     too_big_bottom
  382.         fmul    e_neg10                 ; total*e_neg10 Population
  383.         add     lnadjust,10
  384.         jmp     SHORT too_big_top
  385. too_big_bottom:
  386.  
  387. too_small_top:                          ; total Population
  388.         fcom    e_neg10                 ; total Population
  389.         fstsw   status_word
  390.         fwait
  391.         mov     ax,status_word
  392.         sahf
  393.         jnb     too_small_bottom
  394.         fmul    e_10                    ; total*e_10 Population
  395.         sub     lnadjust,10
  396.         jmp     SHORT too_small_top
  397. too_small_bottom:
  398.  
  399.         fstp    total                   ; save as total
  400.         inc     si
  401.         jmp     halfmax_top             ; let's do it again
  402.  
  403. halfmax_bottom:
  404. ; better make another check, just to be sure
  405.                                         ; NewPopulation
  406.         cmp     dx,overflowcheck        ; was overflow just checked?
  407.         jl      last_overflowcheck      ; if not, better check one last time
  408.         fstp    Population              ; save Population and clear stack
  409.         jmp     short jumpout           ; skip overflowcheck
  410.  
  411. last_overflowcheck:
  412.         fst     Population              ; save new Population
  413.         fabs                            ; |NewPopulation|
  414.         fcomp   BigNum                  ; Compare to 100000.0
  415.         fstsw   status_word             ;
  416.         fwait
  417.         mov     ax,status_word
  418.         sahf
  419.         ja      color_zero              ; overflowed
  420.  
  421. jumpout:
  422.  
  423. ;    if (overflow || total <= 0 || (temp = log(total) + lnadjust) > 0)
  424. ;        color = 0;
  425. ;    else {
  426. ;        if (LogFlag)
  427. ;            lyap = -temp/((double) lyaLength*i);
  428. ;        else
  429. ;            lyap = 1 - exp(temp/((double) lyaLength*i));
  430. ;        color = 1 + (int)(lyap * (colors-1));
  431. ;        }
  432. ;    return color;
  433. ;}
  434.  
  435. ; no use testing for the overflow variable as you
  436. ; cannot get here and have overflow be true
  437.         fld     total                   ; total
  438.         ftst                            ; is total <= 0 ?
  439.         fstsw   status_word
  440.         fwait
  441.         mov     ax,status_word
  442.         sahf
  443.         ja      total_not_neg           ; if not, continue
  444.         fstp    st                      ; pop total from stack
  445.         jmp     SHORT color_zero        ; if so, set color to 0
  446.  
  447. total_not_neg:                          ; total is still on stack
  448.         fldln2                          ; ln(2) total
  449.         fxch                            ; total ln(2)
  450.         fyl2x                           ; ln(total)=ln(2)*log_2(total)
  451.         fiadd   lnadjust                ; ln(total)+lnadjust
  452.         ftst                            ; compare against 0
  453.         fstsw   status_word
  454.         fwait
  455.         mov     ax,status_word
  456.         sahf
  457.         jbe     not_positive
  458.         fstp    st                      ; pop temp from stack
  459.  
  460. color_zero:
  461.         xor     ax,ax                   ; return color 0
  462.         jmp     thats_all
  463.  
  464. not_positive:                           ; temp is still on stack
  465.         fild    lyaLength               ; lyaLength temp
  466.         fimul   halfmax                 ; lyaLength*halfmax temp
  467.         fdiv                            ; temp/(lyaLength*halfmax)
  468.         cmp     LogFlag,0               ; is LogFlag set?
  469.         jz      LogFlag_not_set         ; if !LogFlag goto LogFlag_not_set:
  470.         fchs                            ; -temp/(lyaLength*halfmax)
  471.         jmp     calc_color
  472.  
  473. LogFlag_not_set:                        ; temp/(lyaLength*halfmax)
  474.         OneMinusExpMacro                ; 1-exp(temp/(lyaLength*halfmax))
  475. calc_color:
  476.         ; what is now left on the stack is what the C code calls lyap
  477.                                         ; lyap
  478.         mov     ax,colors               ; colors
  479.         dec     ax                      ; colors-1
  480.         mov     i,ax                    ; temporary storage
  481.         fimul   i                       ; lyap*(colors-1)
  482.         ; the "half" makes ASM round like C does
  483.         fsub    half                    ; sub 0.5 for rounding purposes
  484.         fistp   i                       ; temporary = (int)(lyap*(colors-1))
  485.         fwait                           ; one moment please...
  486.         mov     ax,i                    ; ax = temporary
  487.         inc     ax                      ; ax = 1 + (int)(lyap * (colors-1));
  488.  
  489. thats_all:
  490.         mov     color, ax
  491.         ret
  492. lyapunov_cycles  endp
  493.  
  494. END
  495.