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