File: dos\general.asm
1 ; Generic assembler routines that have very little at all
2 ; to do with fractals.
3 ;
4 ; (NOTE: The video routines have been moved over to VIDEO.ASM)
5 ;
6 ; ---- Overall Support
7 ;
8 ; initasmvars()
9 ;
10 ; ---- Quick-copy to/from Extraseg support
11 ;
12 ; toextra()
13 ; fromextra()
14 ; cmpextra()
15 ;
16 ; ---- far memory allocation support
17 ;
18 ; farmemalloc()
19 ; farmemfree()
20 ; erasesegment()
21 ;
22 ; ---- General Turbo-C (artificial) support
23 ;
24 ; disable()
25 ; enable()
26 ;
27 ; ---- 32-bit Multiply/Divide Routines (includes 16-bit emulation)
28 ;
29 ; multiply()
30 ; divide()
31 ;
32 ; ---- Keyboard, audio (and, hidden inside them, Mouse) support
33 ;
34 ; keypressed()
35 ; getakey()
36 ; buzzer() ; renamed to buzzerpcspkr()
37 ; delay()
38 ; tone()
39 ; snd()
40 ; nosnd()
41 ;
42 ; ---- Expanded Memory Support
43 ;
44 ; emmquery()
45 ; emmgetfree()
46 ; emmallocate()
47 ; emmdeallocate()
48 ; emmgetpage()
49 ; emmclearpage()
50 ;
51 ; ---- Extended Memory Support
52 ;
53 ; xmmquery()
54 ; xmmlongest()
55 ; xmmallocate()
56 ; xmmreallocate()
57 ; xmmdeallocate()
58 ; xmmmoveextended()
59 ;
60 ; ---- CPU, FPU Detectors
61 ;
62 ; cputype()
63 ; fputype()
64 ;
65
66
67 ; required for compatibility if Turbo ASM
68 IFDEF ??version
69 MASM51
70 QUIRKS
71 ENDIF
72
73 .MODEL medium,c
74
75 .8086
76
77 ; these must NOT be in any segment!!
78 ; this get's rid of TURBO-C fixup errors
extrn help:far ; help code (in help.c)
extrn tab_display:far ; TAB display (in fractint.c)
; extrn restore_active_ovly:far
extrn edit_text_colors:far
extrn adapter_init:far ; video adapter init (in video.asm)
extrn slideshw:far
extrn recordshw:far
extrn stopslideshow:far
extrn mute:far
extrn scrub_orbit:far
.DATA
; ************************ External variables *****************************
extrn soundflag:word ; if 0, supress sounds
extrn debugflag:word ; for debugging purposes only
extrn helpmode:word ; help mode (AUTHORS is special)
extrn tabmode:word ; tab key enabled?
extrn sxdots:word ; horizontal pixels
extrn timedsave:word ; triggers autosave
extrn calc_status:word ; in calcfrac.c
extrn got_status:word ; in calcfrac.c
extrn currow:word ; in calcfrac.c
extrn slides:word ; in cmdfiles.c
extrn show_orbit:word ; in calcfrac.c
extrn taborhelp:word ; in fracsubr.c
; ************************ Public variables *****************************
public cpu ; used by 'calcmand'
public fpu ; will be used by somebody someday
public lookatmouse ; used by 'calcfrac'
public saveticks ; set by fractint
public savebase ; set by fractint
public finishrow ; set by fractint
public _dataseg_xx ; used by TARGA, other Turbo Code
public extraseg ; extra 64K segment
public overflow ; Mul, Div overflow flag: 0 means none
; arrays declared here, used elsewhere
; arrays not used simultaneously are deliberately overlapped
public keybuffer ; needed for ungetakey
public prefix, suffix, dstack, decoderline ; for the Decoder
public tstack ; for the prompting routines
public strlocn, block ; used by the Encoder
public boxx, boxy, boxvalues ; zoom-box arrays
public olddacbox ; temporary DAC saves
public rlebuf ; Used ty the TARGA En/Decoder
public paldata, stbuff ; 8514A arrays, (FR8514A.ASM)
; ************************* "Shared" array areas **************************
; Shared near arrays are discussed below. First some comments about "extraseg".
; It is a 96k far area permanently allocated during fractint runup.
; The first part is used for:
; 64k coordinate arrays during image calculation (initialized in fracsubr.c)
; 64k create gif save file, encoder.c
; 64k 3d transforms, line3d.c
; 64k credits screen, intro.c
; 22k video mode selection (for fractint.cfg, loadfdos, miscovl)
; 2k .frm .ifs .l .par entry selection and file selection (prompts.c)
; ??k printing, printer.c
; ??k fractint.doc creation, help.c, not important cause it saves/restores
; 64K arbitrary precision - critical variables at top, safe from encoder
; 10K cmdfiles.c input buffer
; 4K miscovl.c PAR file buffer
;
; The high 32k is used for graphics image save during text mode; video.asm
; and realdos.c.
; Short forms used in subsequent comments:
; name........duration of use......................modules....................
; encoder "s"aving an image encoder.c
; decoder "r"estoring an image decoder.c, gifview.c
; zoom zoom box is visible zoom.c, video.asm
; vidswitch temp during video mode setting video.asm
; 8514a 8514a is in use (graphics only?) fr8514a.asm
; tgaview restore of tga image tgaview.c
; solidguess image gen with "g", not to disk calcfrac.c
; btm image gen with "b", not to disk calcfrac.c
; editpal palette editor "heap" editpal.c
; cellular cellular fractal type generation misfrac.c
; browse browsing loadfile.c
; lsystem lsystem fractal type generation lsys.c, lsysf.c, lsysa.asm, lsysaf.asm
; Several arrays used in cmdfiles.c and miscovl.c, but are saved and
; restored to extraseg so not critical.
;
; Note that decoder using an 8514a is worst case, uses all arrays at once.
; Keep db lengths even so that word alignment is preserved for speed.
block label byte ; encoder(4k), loadfile(4k),
suffix dw 2048 dup(0) ; decoder(4k), vidswitch(256),
; savegraphics/restoregraphics(4k)
tstack label byte ; prompts(4k), ifsload(4k),
; make_batch(4k)
olddacbox label byte ; fractint(768), prompts(768)
dstack dw 2048 dup(0) ; decoder(4k), solidguess(4k), btm(2k)
; zoom(2k), printer(2400)
; loadfdos(2000), cellular(2025)
strlocn label word ; encoder(10k), editpal(10k)
prefix label word ; decoder(8k), solidguess(6k)
boxx dw 2048 dup(0) ; zoom(4k), tgaview(4k), prompts(9k)
; make_batch(8k), parser(8k)
; browse(?)
boxy dw 2048 dup(0) ; zoom(4k), cellular(2025), browse(?)
; lsystem(1000)
boxvalues label byte ; zoom(2k), browse(?)
decoderline db 2050 dup(0) ; decoder(2049), btm(2k)
rlebuf label byte ; f16.c(258) .tga save/restore?
paldata db 1024 dup(0) ; 8514a(1k)
stbuff db 415 dup(0) ; 8514a(415)
; ************************ Internal variables *****************************
align 2
cpu dw 0 ; cpu type: 86, 186, 286, 386, 486, 586...
fpu dw 0 ; fpu type: 0, 87, 287, 387
_dataseg_xx dw 0 ; our "near" data segment
overflow dw 0 ; overflow flag
kbd_type db 0 ; type of keyboard
align 2
keybuffer dw 0 ; real small keyboard buffer
delayloop dw 32 ; delay loop value
delaycount dw 0 ; number of delay "loops" per ms.
extraseg dw 0 ; extra 64K segment (allocated by init)
; ********************** Mouse Support Variables **************************
lookatmouse dw 0 ; see notes at mouseread routine
prevlamouse dw 0 ; previous lookatmouse value
mousetime dw 0 ; time of last mouseread call
mlbtimer dw 0 ; time of left button 1st click
mrbtimer dw 0 ; time of right button 1st click
mhtimer dw 0 ; time of last horiz move
mvtimer dw 0 ; time of last vert move
mhmickeys dw 0 ; pending horiz movement
mvmickeys dw 0 ; pending vert movement
mbstatus db 0 ; status of mouse buttons
mouse db 0 ; == -1 if/when a mouse is found.
mbclicks db 0 ; had 1 click so far? &1 mlb, &2 mrb
align 2
; timed save variables, handled by readmouse:
savechktime dw 0 ; time of last autosave check
savebase dw 2 dup(0) ; base clock ticks
saveticks dw 2 dup(0) ; save after this many ticks
finishrow dw 0 ; save when this row is finished
.CODE
; *************** Function toextra(tooffset,fromaddr, fromcount) *********
toextra proc uses es di si, tooffset:word, fromaddr:word, fromcount:word
cld ; move forward
mov ax,extraseg ; load ES == extra segment
mov es,ax ; ..
mov di,tooffset ; load to here
mov si,fromaddr ; load from here
mov cx,fromcount ; this many bytes
rep movsb ; do it.
ret ; we done.
toextra endp
; *************** Function fromextra(fromoffset, toaddr, tocount) *********
fromextra proc uses es di si, fromoffset:word, toaddr:word, tocount:word
push ds ; save DS for a tad
pop es ; restore it to ES
cld ; move forward
mov si,fromoffset ; load from here
mov di,toaddr ; load to here
mov cx,tocount ; this many bytes
mov ax,extraseg ; load DS == extra segment
mov ds,ax ; ..
rep movsb ; do it.
push es ; save ES again.
pop ds ; restore DS
ret ; we done.
fromextra endp
; *************** Function cmpextra(cmpoffset,cmpaddr, cmpcount) *********
cmpextra proc uses es di si, cmpoffset:word, cmpaddr:word, cmpcount:word
cld ; move forward
mov ax,extraseg ; load ES == extra segment
mov es,ax ; ..
mov di,cmpoffset ; load to here
mov si,cmpaddr ; load from here
mov cx,cmpcount ; this many bytes
rep cmpsb ; do it.
jnz cmpbad ; failed.
sub ax,ax ; 0 == true
jmp short cmpend
cmpbad:
mov ax,1 ; 1 == false
cmpend:
ret ; we done.
cmpextra endp
; =======================================================
;
; 32-bit integer multiply routine with an 'n'-bit shift.
; Overflow condition returns 0x7fffh with overflow = 1;
;
; long x, y, z, multiply();
; int n;
;
; z = multiply(x,y,n)
;
; requires the presence of an external variable, 'cpu'.
; 'cpu' >= 386 if a >=386 is present.
.8086
.DATA
temp dw 5 dup(0) ; temporary 64-bit result goes here
sign db 0 ; sign flag goes here
.CODE
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
multiply proc x:dword, y:dword, n:word
cmp cpu,386 ; go-fast time?
jb slowmultiply ; no. yawn...
.386 ; 386-specific code starts here
mov eax,x ; load X into EAX
imul y ; do the multiply
mov cx,n ; set up the shift
cmp cx,32 ; ugly klooge: check for 32-bit shift
jb short fastm1 ; < 32 bits: no problem
mov eax,edx ; >= 32 bits: manual shift
mov edx,0 ; ...
sub cx,32 ; ...
fastm1: shrd eax,edx,cl ; shift down 'n' bits
js fastm3
sar edx,cl
jne overmf
shld edx,eax,16
ret
fastm3: sar edx,cl
inc edx
jne overmf
shld edx,eax,16
ret
overmf:
mov ax,0ffffh ; overflow value
mov dx,07fffh ; overflow value
mov overflow,1 ; flag overflow
ret
.8086 ; 386-specific code ends here
slowmultiply: ; (sigh) time to do it the hard way...
push di
push si
push es
mov ax,0
mov temp+4,ax ; first, zero out the (temporary)
mov temp+6,ax ; result
mov temp+8,ax
les bx,x ; move X to SI:BX
mov si,es ; ...
les cx,y ; move Y to DI:CX
mov di,es ; ...
mov sign,0 ; clear out the sign flag
cmp si,0 ; is X negative?
jge mults1 ; nope
not sign ; yup. flip signs
not bx ; ...
not si ; ...
stc ; ...
adc bx,ax ; ...
adc si,ax ; ...
mults1: cmp di,0 ; is DI:CX negative?
jge mults2 ; nope
not sign ; yup. flip signs
not cx ; ...
not di ; ...
stc ; ...
adc cx,ax ; ...
adc di,ax ; ...
mults2:
mov ax,bx ; perform BX x CX
mul cx ; ...
mov temp,ax ; results in lowest 32 bits
mov temp+2,dx ; ...
mov ax,bx ; perform BX x DI
mul di ; ...
add temp+2,ax ; results in middle 32 bits
adc temp+4,dx ; ...
jnc mults3 ; carry bit set?
inc word ptr temp+6 ; yup. overflow
mults3:
mov ax,si ; perform SI * CX
mul cx ; ...
add temp+2,ax ; results in middle 32 bits
adc temp+4,dx ; ...
jnc mults4 ; carry bit set?
inc word ptr temp+6 ; yup. overflow
mults4:
mov ax,si ; perform SI * DI
mul di ; ...
add temp+4,ax ; results in highest 32 bits
adc temp+6,dx ; ...
mov cx,n ; set up for the shift loop
cmp cx,24 ; shifting by three bytes or more?
jl multc1 ; nope. check for something else
sub cx,24 ; quick-shift 24 bits
mov ax,temp+3 ; load up the registers
mov dx,temp+5 ; ...
mov si,temp+7 ; ...
mov bx,0 ; ...
jmp short multc4 ; branch to common code
multc1: cmp cx,16 ; shifting by two bytes or more?
jl multc2 ; nope. check for something else
sub cx,16 ; quick-shift 16 bits
mov ax,temp+2 ; load up the registers
mov dx,temp+4 ; ...
mov si,temp+6 ; ...
mov bx,0 ; ...
jmp short multc4 ; branch to common code
multc2: cmp cx,8 ; shifting by one byte or more?
jl multc3 ; nope. check for something else
sub cx,8 ; quick-shift 8 bits
mov ax,temp+1 ; load up the registers
mov dx,temp+3 ; ...
mov si,temp+5 ; ...
mov bx,temp+7 ; ...
jmp short multc4 ; branch to common code
multc3: mov ax,temp ; load up the regs
mov dx,temp+2 ; ...
mov si,temp+4 ; ...
mov bx,temp+6 ; ...
multc4: cmp cx,0 ; done shifting?
je multc5 ; yup. bail out
multloop:
shr bx,1 ; shift down 1 bit, cascading
rcr si,1 ; ...
rcr dx,1 ; ...
rcr ax,1 ; ...
loop multloop ; try the next bit, if any
multc5:
cmp si,0 ; overflow time?
jne overm1 ; yup. Bail out.
cmp bx,0 ; overflow time?
jne overm1 ; yup. Bail out.
cmp dx,0 ; overflow time?
jl overm1 ; yup. Bail out.
cmp sign,0 ; should we negate the result?
je mults5 ; nope.
not ax ; yup. flip signs.
not dx ; ...
mov bx,0 ; ...
stc ; ...
adc ax,bx ; ...
adc dx,bx ; ...
mults5:
jmp multiplyreturn
overm1:
mov ax,0ffffh ; overflow value
mov dx,07fffh ; overflow value
mov overflow,1 ; flag overflow
multiplyreturn: ; that's all, folks!
79 pop es
80 pop si
81 pop di
82 ret
83 multiply endp
84
85
86 ; =======================================================
87 ;
88 ; 32-bit integer divide routine with an 'n'-bit shift.
89 ; Overflow condition returns 0x7fffh with overflow = 1;
90 ;
91 ; long x, y, z, divide();
92 ; int n;
93 ;
94 ; z = divide(x,y,n); /* z = x / y; */
95 ;
96 ; requires the presence of an external variable, 'cpu'.
97 ; 'cpu' >= 386 if a >=386 is present.
98
99
100 .8086
101
102 divide proc uses di si es, x:dword, y:dword, n:word
103
104 cmp cpu,386 ; go-fast time?
105 jb slowdivide ; no. yawn...
106
107 .386 ; 386-specific code starts here
108
109 mov edx,x ; load X into EDX (shifts to EDX:EAX)
110 mov ebx,y ; load Y into EBX
111
112 mov sign,0 ; clear out the sign flag
113 cmp edx,0 ; is X negative?
114 jge short divides1 ; nope
115 not sign ; yup. flip signs
116 neg edx ; ...
117 divides1:
118 cmp ebx,0 ; is Y negative?
119 jg short divides2 ; nope
120 jl short setsign1
121 jmp overd1 ; don't divide by zero, set overflow
setsign1:
not sign ; yup. flip signs
neg ebx ; ...
divides2:
mov eax,0 ; clear out the low-order bits
mov cx,32 ; set up the shift
sub cx,n ; (for large shift counts - faster)
fastd1: cmp cx,0 ; done shifting?
je fastd2 ; yup.
shr edx,1 ; shift one bit
rcr eax,1 ; ...
loop fastd1 ; and try again
fastd2:
cmp edx,ebx ; umm, will the divide blow out?
jae overd1 ; yup. better skip it.
div ebx ; do the divide
cmp eax,0 ; did the sign flip?
jl overd1 ; then we overflowed
cmp sign,0 ; is the sign reversed?
je short divides3 ; nope
neg eax ; flip the sign
divides3:
push eax ; save the 64-bit result
pop ax ; low-order 16 bits
pop dx ; high-order 16 bits
jmp dividereturn ; back to common code
.8086 ; 386-specific code ends here
slowdivide: ; (sigh) time to do it the hard way...
les ax,x ; move X to DX:AX
mov dx,es ; ...
mov sign,0 ; clear out the sign flag
cmp dx,0 ; is X negative?
jge divides4 ; nope
not sign ; yup. flip signs
not ax ; ...
not dx ; ...
stc ; ...
adc ax,0 ; ...
adc dx,0 ; ...
divides4:
mov cx,32 ; get ready to shift the bits
sub cx,n ; (shift down rather than up)
mov byte ptr temp+4,cl ; ...
mov cx,0 ; clear out low bits of DX:AX:CX:BX
mov bx,0 ; ...
cmp byte ptr temp+4,16 ; >= 16 bits to shift?
jl dividex0 ; nope
mov bx,cx ; yup. Take a short-cut
mov cx,ax ; ...
mov ax,dx ; ...
mov dx,0 ; ...
sub byte ptr temp+4,16 ; ...
dividex0:
cmp byte ptr temp+4,8 ; >= 8 bits to shift?
jl dividex1 ; nope
mov bl,bh ; yup. Take a short-cut
mov bh,cl ; ...
mov cl,ch ; ...
mov ch,al ; ...
mov al,ah ; ...
mov ah,dl ; ...
mov dl,dh ; ...
mov dh,0 ; ...
sub byte ptr temp+4,8 ; ...
dividex1:
cmp byte ptr temp+4,0 ; are we done yet?
je dividex2 ; yup
shr dx,1 ; shift all 64 bits
rcr ax,1 ; ...
rcr cx,1 ; ...
rcr bx,1 ; ...
dec byte ptr temp+4 ; decrement the shift counter
jmp short dividex1 ; and try again
dividex2:
les di,y ; move Y to SI:DI
mov si,es ; ...
cmp si,0 ; is Y negative?
jg divides5 ; nope
jl short setsign2
cmp di,0 ; high part is zero, check low part
je overd1 ; don't divide by zero, set overflow
122 jmp short divides5
123 setsign2:
124 not sign ; yup. flip signs
125 not di ; ...
126 not si ; ...
127 stc ; ...
128 adc di,0 ; ...
129 adc si,0 ; ...
130 divides5:
131
132 mov byte ptr temp+4,33 ; main loop counter
133 mov temp,0 ; results in temp
134 mov word ptr temp+2,0 ; ...
135
136 dividel1:
137 shl temp,1 ; shift the result up 1
138 rcl word ptr temp+2,1 ; ...
139 cmp dx,si ; is DX:AX >= Y?
140 jb dividel3 ; nope
141 ja dividel2 ; yup
142 cmp ax,di ; maybe
143 jb dividel3 ; nope
144 dividel2:
145 cmp byte ptr temp+4,32 ; overflow city?
146 jge overd1 ; yup.
147 sub ax,di ; subtract Y
148 sbb dx,si ; ...
149 inc temp ; add 1 to the result
150 adc word ptr temp+2,0 ; ...
151 dividel3:
152 shl bx,1 ; shift all 64 bits
153 rcl cx,1 ; ...
154 rcl ax,1 ; ...
155 rcl dx,1 ; ...
156 dec byte ptr temp+4 ; time to quit?
157 jnz dividel1 ; nope. try again.
158
159 mov ax,temp ; copy the result to DX:AX
160 mov dx,word ptr temp+2 ; ...
161 cmp sign,0 ; should we negate the result?
162 je divides6 ; nope.
163 not ax ; yup. flip signs.
164 not dx ; ...
165 mov bx,0 ; ...
166 stc ; ...
167 adc ax,0 ; ...
168 adc dx,0 ; ...
169 divides6:
170 jmp short dividereturn
171
172 overd1:
173 mov ax,0ffffh ; overflow value
174 mov dx,07fffh ; overflow value
175 mov overflow,1 ; flag overflow
176
177 dividereturn: ; that's all, folks!
ret
divide endp
; ****************** Function getakey() *****************************
; **************** Function keypressed() ****************************
; 'getakey()' gets a key from either a "normal" or an enhanced
; keyboard. Returns either the vanilla ASCII code for regular
; keys, or 1000+(the scan code) for special keys (like F1, etc)
; Use of this routine permits the Control-Up/Down arrow keys on
; enhanced keyboards.
;
; The concept for this routine was "borrowed" from the MSKermit
; SCANCHEK utility
;
; 'keypressed()' returns a zero if no keypress is outstanding,
; and the value that 'getakey()' will return if one is. Note
; that you must still call 'getakey()' to flush the character.
; As a sidebar function, calls 'help()' if appropriate, or
; 'tab_display()' if appropriate.
; Think of 'keypressed()' as a super-'kbhit()'.
keypressed proc
call far ptr getkeynowait ; check for key
jc keypressed1 ; got a key
sub ax,ax ; fast no-key return
ret
keypressed1:
FRAME ; std frame, for TC++ overlays
mov keybuffer,ax ; remember it for next time
cmp ax,1059 ; help called?
jne keypressed2 ; no help asked for.
cmp helpmode,0 ; help enabled?
jl keypressedx ; nope.
mov keybuffer,0 ; say no key hit
xor ax,ax
push ax
cmp show_orbit,0
je noscrub1
call far ptr scrub_orbit ; clean up the orbits
mov taborhelp,1
noscrub1:
call far ptr help ; help!
pop ax
; call far ptr restore_active_ovly ; help might've clobbered ovly
178 sub ax,ax
179 jmp short keypressedx
180 keypressed2:
181 cmp ax,9 ; TAB key hit?
182 je keypressed3 ; yup. TAB display.
183 cmp ax,1148 ; CTL_TAB key hit?
184 je keypressed3 ; yup. TAB display.
185 jmp keypressedx ; nope. no TAB display.
186 keypressed3:
187 cmp tabmode,0 ; tab enabled?
188 je keypressedx ; nope
189 mov keybuffer,0 ; say no key hit
190 cmp show_orbit,0
191 je noscrub2
192 call far ptr scrub_orbit ; clean up the orbits
193 mov taborhelp,1
194 noscrub2:
195 call far ptr tab_display ; show the TAB status
196 ; call far ptr restore_active_ovly ; tab might've clobbered ovly
sub ax,ax
keypressedx:
UNFRAME ; pop frame
ret
keypressed endp
getakeynohelp proc
gknhloop:
call far ptr getakey ; get keystroke
cmp ax,1059 ; help key?
je gknhloop ; ignore help, none available
ret
getakeynohelp endp
getakey proc
mov bx,soundflag
and bx,7 ; is the sound on?
jz getakeyloop ; ok, sound is off
cmp bx,1 ; if = 1, just using buzzer
je getakeyloop
call far ptr mute ; turn off sound
getakeyloop:
call far ptr getkeynowait ; check for keystroke
jnc getakeyloop ; no key, loop till we get one
ret
getakey endp
getkeynowait proc
FRAME ; std frame, for TC++ overlays
; The following fixes the "midnight bug". If two midnights are crossed
; before a call to a dos date call (such as clock()), the system date
; will be wrong and therefore the calculation time will be wrong.
mov ah, 2Ah ; Get Date function
int 21h ; don't do anything with the results, just call it
197
198 getkeyn0:
199 cmp keybuffer,0 ; got a key buffered?
200 je getkeynobuf ; nope
201 mov ax,keybuffer ; key was buffered here
202 mov keybuffer,0 ; clear buffer
203 jmp getkeyyup ; exit with the key
204 getkeynobuf:
205 call mouseread ; mouse activity or savetime?
206 jc getkeyn4 ; yup, ax holds the phoney key
207 mov ah,kbd_type ; get the keyboard type
208 or ah,1 ; check if a key is ready
209 int 16h ; now check a key
210 jnz gotkeyn ; got one
211 cmp slides,1 ; slideshow playback active?
212 jne getkeynope ; nope, return no key
213 call far ptr slideshw ; check next playback keystroke
214 cmp ax,0 ; got one?
215 jne getkeyn5 ; yup, use it
216 getkeynope:
217 clc ; return no key
218 UNFRAME ; pop frame
219 ret
220 gotkeyn: ; got a real keyboard keystroke
221 mov ah,kbd_type ; get the keyboard type
222 int 16h ; now get a key
223 cmp al,0e0h ; check: Enhanced Keyboard key?
224 jne short getkeyn1 ; nope. proceed
225 cmp ah,0 ; part 2 of Enhanced Key check
226 je short getkeyn1 ; failed. normal key.
227 mov al,0 ; Turn enhanced key "normal"
228 jmp short getkeyn2 ; jump to common code
229 getkeyn1:
230 cmp ah,0e0h ; check again: Enhanced Key?
231 jne short getkeyn2 ; nope. proceed.
232 mov ah,al ; Turn Enhanced key "normal"
233 mov al,0 ; ...
234 getkeyn2:
235 cmp al,0 ; Function Key?
236 jne short getkeyn3 ; nope. proceed.
237 mov al,ah ; klooge into ASCII Key
238 mov ah,0 ; clobber the scan code
239 add ax,1000 ; + 1000
240 jmp short getkeyn4 ; go to common return
241 getkeyn3:
242 mov ah,0 ; clobber the scan code
243 getkeyn4: ; got real key (not playback)
244 cmp ax,9999 ; savetime from mousread?
245 je getkeyn6 ; yup, do it and don't record
cmp slides,1 ; slideshow playback active?
jne getkeyn5 ; nope
cmp ax,1bh ; escape?
jne getkeyn0 ; nope, ignore the key
call far ptr stopslideshow ; terminate playback
jmp short getkeyn0 ; go check for another key
getkeyn5:
cmp slides,2 ; slideshow record mode?
jne getkeyn6 ; nope
push ax
call far ptr recordshw ; record the key
pop ax
getkeyn6:
cmp debugflag,3000 ; color play enabled?
jne getkeyyup ; nope
cmp ax,'~' ; color play requested?
jne getkeyyup ; nope
call far ptr edit_text_colors ; play
; call far ptr restore_active_ovly ; might've clobbered ovly
246 jmp getkeyn0 ; done playing, back around
247 getkeyyup:
248 stc ; indicate we have a key
249 UNFRAME ; pop frame
250 ret
251 getkeynowait endp
252
253 ; ****************** Function buzzerpcspkr(int buzzertype) *******************
254 ;
255 ; Sound a tone based on the value of the parameter
256 ;
257 ; 0 = normal completion of task
258 ; 1 = interrupted task
259 ; 2 = error contition
260
261 ; "buzzer()" codes: strings of two-word pairs
262 ; (frequency in cycles/sec, delay in milliseconds)
263 ; frequency == 0 means no sound
264 ; delay == 0 means end-of-tune
265 buzzer0 dw 1047,100 ; "normal" completion
266 dw 1109,100
267 dw 1175,100
268 dw 0,0
269 buzzer1 dw 2093,100 ; "interrupted" completion
270 dw 1976,100
271 dw 1857,100
272 dw 0,0
273 buzzer2 dw 40,500 ; "error" condition (razzberry)
274 dw 0,0
275
276 ; ***********************************************************************
277
278 buzzerpcspkr proc uses si, buzzertype:word
279 test soundflag,08h ; is the speaker sound supressed?
280 jz buzzerreturn ; yup. bail out.
281 mov si, offset buzzer0 ; normal completion frequency
282 cmp buzzertype,0 ; normal completion?
283 je buzzerdoit ; do it
284 mov si,offset buzzer1 ; interrupted task frequency
285 cmp buzzertype,1 ; interrupted task?
286 je buzzerdoit ; do it
287 mov si,offset buzzer2 ; error condition frequency
288 buzzerdoit:
289 mov ax,cs:0[si] ; get the (next) frequency
290 mov bx,cs:2[si] ; get the (next) delay
291 add si,4 ; get ready for the next tone
292 cmp bx,0 ; are we done?
293 je buzzerreturn ; yup.
294 push bx ; put delay time on the stack
295 push ax ; put tone value on the stack
296 call far ptr tone ; do it
297 pop ax ; restore stack
298 pop bx ; restore stack
299 jmp short buzzerdoit ; get the next tone
300 buzzerreturn:
301 ret ; we done
302 buzzerpcspkr endp
303
304 ; ***************** Function delay(int delaytime) ************************
305 ;
306 ; performs a delay loop for 'delaytime' milliseconds
307 ;
308 ; ************************************************************************
309
310 delayamillisecond proc near ; internal delay-a-millisecond code
311 mov bx,delaycount ; set up to burn another millisecond
312 delayamill1:
313 mov cx,delayloop ; start up the counter
314 delayamill2: ;
315 loop delayamill2 ; burn up some time
316 dec bx ; have we burned up a millisecond?
317 jnz delayamill1 ; nope. try again.
318 ret ; we done
319 delayamillisecond endp
320
321 delay proc uses es, delaytime:word ; delay loop (arg in milliseconds)
322 mov ax,delaytime ; get the number of milliseconds
323 cmp ax,0 ; any delay time at all?
324 je delayreturn ; nope.
325 delayloop1:
326 call delayamillisecond ; burn up a millisecond of time
327 dec ax ; have we burned up enough m-seconds?
328 jnz delayloop1 ; nope. try again.
329 delayreturn:
330 ret ; we done.
331 delay endp
332
333 ; ************** Function tone(int frequency,int delaytime) **************
334 ;
335 ; buzzes the speaker with this frequency for this amount of time
336 ;
337 ; ************************************************************************
338
339 tone proc uses es, tonefrequency:word, tonedelay:word
340 mov al,0b6h ; latch to channel 2
341 out 43h,al ; ...
342 cmp tonefrequency,12h ; was there a frequency?
343 jbe tonebypass ; nope. delay only
344 mov bx,tonefrequency ; get the frequency value
345 mov ax,0 ; ugly klooge: convert this to the
346 mov dx,12h ; divisor the 8253 wants to see
347 div bx ; ...
348 out 42h,al ; send the low value
349 mov al,ah ; then the high value
350 out 42h,al ; ...
351 in al,61h ; get the current 8255 bits
352 or al,3 ; turn bits 0 and 1 on
353 out 61h,al ; ...
354 tonebypass:
355 mov ax,tonedelay ; get the delay value
356 push ax ; set the parameter
357 call far ptr delay ; and force a delay
358 pop ax ; restore the parameter
359
360 in al,61h ; get the current 8255 bits
361 and al,11111100b ; turn bits 0 and 1 off
362 out 61h,al
363
364 ret ; we done
365 tone endp
366
367 ; ************** Function snd(int hertz) and nosnd() **************
368 ;
369 ; turn the speaker on with this frequency (snd) or off (nosnd)
370 ;
371 ; *****************************************************************
372
373 snd proc hertz:word ;Sound the speaker
374 cmp hertz, 20
375 jl hertzbad
376 cmp hertz, 5000
377 jg hertzbad
378 mov ax,0 ;Convert hertz
379 mov dx, 12h ;for use by routine
380 div hertz
381 mov bx, ax
382 mov al,10110110b ;Put magic number
383 out 43h, al ;into timer2
384 mov ax, bx ;Pitch into AX
385 out 42h, al ;LSB into timer2
386 mov al, ah ;MSB to AL then
387 out 42h, al ;to timer2
388 in al, 61h ;read I/O port B into AL
389 or al,3 ;turn on bits 0 and 1
390 out 61h,al ;to turn on speaker
391 hertzbad:
392 ret
393 snd endp
394
395 nosnd proc ;Turn off speaker
396 in al, 61h ;Read I/O port B into AL
397 and al, 11111100b ;mask lower two bits
398 out 61h, al ;to turn off speaker
399 ret
400 nosnd endp
401
402
403 ; ****************** Function initasmvars() *****************************
404
405 initasmvars proc uses es si di
406
407 cmp cpu,0 ; have we been called yet:
408 je initasmvarsgo ; nope. proceed.
409 jmp initreturn ; yup. no need to be here.
410
411 initasmvarsgo:
412 mov ax,ds ; save the data segment
413 mov _dataseg_xx,ax ; for the C code
414
415 mov overflow,0 ; indicate no overflows so far
416
417 mov dx,1 ; ask for 96K of far space
418 mov ax,8000h ; ...
419 push dx ; ...
420 push ax ; ...
421 call far ptr farmemalloc ; use the assembler routine to do it
422 pop ax ; restore the stack
423 pop ax ; ...
424 mov extraseg,dx ; save the results here.
425
426 call adapter_init ; call the video adapter init
427
428 ; first see if a mouse is installed
429
430 push es ; (no, first check to ensure that
431 mov ax,0 ; int 33h doesn't point to 0:0)
mov es,ax ; ...
mov ax,es:0cch ; ...
pop es ; ...
cmp ax,0 ; does int 33h have a non-zero value?
je noint33 ; nope. then there's no mouse.
432
433 xor ax,ax ; function for mouse check
434 int 33h ; call mouse driver
435 noint33:
436 mov mouse,al ; al holds info about mouse
437
438 ; now get the information about the kbd
439 push es ; save ES for a tad
440 mov ax,40h ; reload ES with BIOS data seg
441 mov es,ax ; ...
442 mov ah,es:96h ; get the keyboard byte
443 pop es ; restore ES
444 and ah,10h ; isolate the Enhanced KBD bit
445 mov kbd_type,ah ; and save it
446
447 call far ptr cputype ; what kind of CPU do we have here?
448 cmp ax,0 ; protected mode of some sort?
449 jge positive ; nope. proceed.
450 neg ax ; yup. flip the sign.
451 positive:
452 mov cpu,ax ; save the cpu type.
453 itsa386:
454 cmp debugflag,8088 ; say, should we pretend it's an 8088?
jne nodebug ; nope.
mov cpu,86 ; yup. use 16-bit emulation.
nodebug:
call far ptr fputype ; what kind of an FPU do we have?
mov fpu,ax ; save the results
push es ; save ES for a tad
mov ax,0 ; reset ES to BIOS data area
mov es,ax ; ...
mov dx,es:46ch ; obtain the current timer value
cmp cpu,386 ; are we on a 386 or above?
jb delaystartuploop ; nope. don't adjust anything
455 mov delayloop, 256 ; yup. slow down the timer loop
456 delaystartuploop:
457 cmp dx,es:46ch ; has the timer value changed?
458 je delaystartuploop ; nope. check again.
459 mov dx,es:46ch ; obtain the current timer value again
460 mov ax,0 ; clear the delay counter
461 mov delaycount,55 ; 55 millisecs = 1/18.2 secs
462 delaytestloop:
463 call delayamillisecond ; burn up a (fake) millisecond
464 inc ax ; indicate another loop has passed
465 cmp dx,es:46ch ; has the timer value changed?
466 je delaytestloop ; nope. burn up some more time.
467 mov delaycount,ax ; save the results here
468 pop es ; restore ES again
469
470 initreturn:
471 ret ; return to caller
472 initasmvars endp
473
474
475 ; New (Apr '90) mouse code by Pieter Branderhorst follows.
; The variable lookatmouse controls it all. Callers of keypressed and
; getakey should set lookatmouse to:
; 0 ignore the mouse entirely
; <0 only test for left button click; if it occurs return fake key
; number 0-lookatmouse
; 1 return enter key for left button, arrow keys for mouse movement,
; mouse sensitivity is suitable for graphics cursor
; 2 same as 1 but sensitivity is suitable for text cursor
; 3 specials for zoombox, left/right double-clicks generate fake
; keys, mouse movement generates a variety of fake keys
; depending on state of buttons
; Mouse movement is accumulated & saved across calls. Eg if mouse has been
; moved up-right quickly, the next few calls to getakey might return:
; right,right,up,right,up
; Minor jiggling of the mouse generates no keystroke, and is forgotten (not
; accumulated with additional movement) if no additional movement in the
; same direction occurs within a short interval.
; Movements on angles near horiz/vert are treated as horiz/vert; the nearness
; tolerated varies depending on mode.
; Any movement not picked up by calling routine within a short time of mouse
; stopping is forgotten. (This does not apply to button pushes in modes<3.)
; Mouseread would be more accurate if interrupt-driven, but with the usage
; in fractint (tight getakey loops while mouse active) there's no need.
476
477 ; translate table for mouse movement -> fake keys
478 mousefkey dw 1077,1075,1080,1072 ; right,left,down,up just movement
479 dw 0, 0,1081,1073 ; ,pgdn,pgup + left button
480 dw 1144,1142,1147,1146 ; kpad+,kpad-,cdel,cins + rt button
481 dw 1117,1119,1118,1132 ; ctl-end,home,pgdn,pgup + mid/multi
482
483 DclickTime equ 9 ; ticks within which 2nd click must occur
484 JitterTime equ 6 ; idle ticks before turfing unreported mickeys
485 TextHSens equ 22 ; horizontal sensitivity in text mode
486 TextVSens equ 44 ; vertical sensitivity in text mode
487 GraphSens equ 5 ; sensitivity in graphics mode; gets lower @ higher res
488 ZoomSens equ 20 ; sensitivity for zoom box sizing/rotation
489 TextVHLimit equ 6 ; treat angles < 1:6 as straight
490 GraphVHLimit equ 14 ; treat angles < 1:14 as straight
491 ZoomVHLimit equ 1 ; treat angles < 1:1 as straight
492 JitterMickeys equ 3 ; mickeys to ignore before noticing motion
493
494 mouseread proc near USES bx cx dx
495 local moveaxis:word
496
497 ; check if it is time to do an autosave
498 cmp saveticks,0 ; autosave timer running?
499 je mouse0 ; nope
500 sub ax,ax ; reset ES to BIOS data area
501 mov es,ax ; see notes at mouse1 in similar code
502 tickread:
503 mov ax,es:046ch ; obtain the current timer value
504 cmp ax,savechktime ; a new clock tick since last check?
505 je mouse0 ; nope, save a dozen opcodes or so
506 mov dx,es:046eh ; high word of ticker
507 cmp ax,es:046ch ; did a tick get counted just as we looked?
508 jne tickread ; yep, reread both words to be safe
509 mov savechktime,ax
510 sub ax,savebase ; calculate ticks since timer started
511 sbb dx,savebase+2
512 jns tickcompare
513 add ax,0b0h ; wrapped past midnight, add a day
514 adc dx,018h
515 tickcompare:
516 cmp dx,saveticks+2 ; check if past autosave time
517 jb mouse0
518 ja ticksavetime
519 cmp ax,saveticks
520 jb mouse0
521 ticksavetime: ; it is time to do a save
522 mov ax,finishrow
523 cmp ax,-1 ; waiting for the end of a row before save?
524 jne tickcheckrow ; yup, go check row
525 cmp calc_status,1 ; safety check, calc active?
526 jne tickdosave ; nope, don't check type of calc
cmp got_status,0 ; 1pass or 2pass?
je ticknoterow ; yup
cmp got_status,1 ; solid guessing?
jne tickdosave ; not 1pass, 2pass, ssg, so save immediately
ticknoterow:
mov ax,currow ; note the current row
mov finishrow,ax ; ...
jmp short mouse0 ; and keep working for now
tickcheckrow:
cmp ax,currow ; started a new row since timer went off?
je mouse0 ; nope, don't do the save yet
527 tickdosave:
528 mov timedsave,1 ; tell mainline what's up
mov ax,9999 ; a dummy key value, never gets used
jmp mouseret
mouse0: ; now the mouse stuff
cmp mouse,-1
jne mouseidle ; no mouse, that was easy
mov ax,lookatmouse
cmp ax,prevlamouse
je mouse1
; lookatmouse changed, reset everything
mov prevlamouse,ax
mov mbclicks,0
mov mbstatus,0
mov mhmickeys,0
mov mvmickeys,0
; note: don't use int 33 func 0 nor 21 to reset, they're SLOW
mov ax,06h ; reset button counts by reading them
mov bx,0
int 33h
mov ax,06h
mov bx,1
int 33h
mov ax,05h
mov bx,0
int 33h
mov ax,0Bh ; reset motion counters by reading
int 33h
mov ax,lookatmouse
mouse1: or ax,ax
jz mouseidle ; check nothing when lookatmouse=0
; following code directly accesses bios tick counter; it would be
; better not to rely on addr (use int 1A instead) but old PCs don't
529 ; have the required int, the addr is constant in bios to date, and
530 ; fractint startup already counts on it, so:
531 mov ax,0 ; reset ES to BIOS data area
532 mov es,ax ; ...
533 mov dx,es:46ch ; obtain the current timer value
534 cmp dx,mousetime
535 ; if timer same as last call, skip int 33s: reduces expense and gives
536 ; caller a chance to read all pending stuff and paint something
537 jne mnewtick
538 cmp lookatmouse,0 ; interested in anything other than left button?
539 jl mouseidle ; nope, done
540 jmp mouse5
541
542 mouseidle:
543 clc ; tell caller no mouse activity this time
544 ret
545
546 mnewtick: ; new tick, read buttons and motion
547 mov mousetime,dx ; note current timer
548 cmp lookatmouse,3
549 je mouse2 ; skip button press if mode 3
550
551 ; check press of left button
552 mov ax,05h ; get button press info
553 mov bx,0 ; for left button
554 int 33h
555 or bx,bx
556 jnz mleftb
557 cmp lookatmouse,0
558 jl mouseidle ; exit if nothing but left button matters
559 jmp mouse3 ; not mode 3 so skip past button release stuff
560 mleftb: mov ax,13
561 cmp lookatmouse,0
562 jg mouser ; return fake key enter
563 mov ax,lookatmouse ; return fake key 0-lookatmouse
564 neg ax
565 mouser: jmp mouseret
566
567 mouse2: ; mode 3, check for double clicks
568 mov ax,06h ; get button release info
569 mov bx,0 ; left button
570 int 33h
571 mov dx,mousetime
572 cmp bx,1 ; left button released?
573 jl msnolb ; go check timer if not
574 jg mslbgo ; double click
575 test mbclicks,1 ; had a 1st click already?
576 jnz mslbgo ; yup, double click
577 mov mlbtimer,dx ; note time of 1st click
578 or mbclicks,1
579 jmp short mousrb
580 mslbgo: and mbclicks,0ffh-1
581 mov ax,13 ; fake key enter
582 jmp mouseret
583 msnolb: sub dx,mlbtimer ; clear 1st click if its been too long
584 cmp dx,DclickTime
585 jb mousrb
586 and mbclicks,0ffh-1 ; forget 1st click if any
587 ; next all the same code for right button
588 mousrb: mov ax,06h ; get button release info
589 mov bx,1 ; right button
590 int 33h
591 ; now much the same as for left
592 mov dx,mousetime
593 cmp bx,1
594 jl msnorb
595 jg msrbgo
596 test mbclicks,2
597 jnz msrbgo
598 mov mrbtimer,dx
599 or mbclicks,2
600 jmp short mouse3
601 msrbgo: and mbclicks,0ffh-2
602 mov ax,1010 ; fake key ctl-enter
603 jmp mouseret
604 msnorb: sub dx,mrbtimer
605 cmp dx,DclickTime
606 jb mouse3
607 and mbclicks,0ffh-2
608
609 ; get buttons state, if any changed reset mickey counters
610 mouse3: mov ax,03h ; get button status
611 int 33h
612 and bl,7 ; just the button bits
613 cmp bl,mbstatus ; any changed?
614 je mouse4
615 mov mbstatus,bl ; yup, reset stuff
616 mov mhmickeys,0
617 mov mvmickeys,0
618 mov ax,0Bh
619 int 33h ; reset driver's mickeys by reading them
; get motion counters, forget any jiggle
mouse4: mov ax,0Bh ; get motion counters
int 33h
mov bx,mousetime ; just to have it in a register
cmp cx,0 ; horiz motion?
jne moushm ; yup, go accum it
mov ax,bx
sub ax,mhtimer
cmp ax,JitterTime ; timeout since last horiz motion?
jb mousev
mov mhmickeys,0
jmp short mousev
moushm: mov mhtimer,bx ; note time of latest motion
add mhmickeys,cx
; same as above for vertical movement:
mousev: cmp dx,0 ; vert motion?
jne mousvm
mov ax,bx
sub ax,mvtimer
cmp ax,JitterTime
jb mouse5
mov mvmickeys,0
jmp short mouse5
mousvm: mov mvtimer,bx
add mvmickeys,dx
; pick the axis with largest pending movement
mouse5: mov bx,mhmickeys
or bx,bx
jns mchkv
neg bx ; make it +ve
mchkv: mov cx,mvmickeys
or cx,cx
jns mchkmx
neg cx
mchkmx: mov moveaxis,0 ; flag that we're going horiz
620 cmp bx,cx ; horiz>=vert?
621 jge mangle
622 xchg bx,cx ; nope, use vert
623 mov moveaxis,1 ; flag that we're going vert
; if moving nearly horiz/vert, make it exactly horiz/vert
mangle: mov ax,TextVHLimit
cmp lookatmouse,2 ; slow (text) mode?
je mangl2
mov ax,GraphVHLimit
cmp lookatmouse,3 ; special mode?
jne mangl2
cmp mbstatus,0 ; yup, any buttons down?
je mangl2
mov ax,ZoomVHLimit ; yup, special zoom functions
mangl2: mul cx ; smaller axis * limit
cmp ax,bx
ja mchkmv ; min*ratio <= max?
cmp moveaxis,0 ; yup, clear the smaller movement axis
jne mzeroh
mov mvmickeys,0
jmp short mchkmv
mzeroh: mov mhmickeys,0
; pick sensitivity to use
mchkmv: cmp lookatmouse,2 ; slow (text) mode?
je mchkmt
mov dx,ZoomSens+JitterMickeys
cmp lookatmouse,3 ; special mode?
jne mchkmg
cmp mbstatus,0 ; yup, any buttons down?
jne mchkm2 ; yup, use zoomsens
mchkmg: mov dx,GraphSens
mov cx,sxdots ; reduce sensitivity for higher res
mchkg2: cmp cx,400 ; horiz dots >= 400?
jl mchkg3
shr cx,1 ; horiz/2
shr dx,1
inc dx ; sensitivity/2+1
jmp short mchkg2
mchkg3: add dx,JitterMickeys
jmp short mchkm2
mchkmt: mov dx,TextVSens+JitterMickeys
cmp moveaxis,0
jne mchkm2
mov dx,TextHSens+JitterMickeys ; slower on X axis than Y
; is largest movement past threshold?
mchkm2: cmp bx,dx
jge mmove
jmp mouseidle ; no movement past threshold, return nothing
; set bx for right/left/down/up, and reduce the pending mickeys
mmove: sub dx,JitterMickeys
cmp moveaxis,0
jne mmovev
cmp mhmickeys,0
jl mmovh2
sub mhmickeys,dx ; horiz, right
mov bx,0
jmp short mmoveb
mmovh2: add mhmickeys,dx ; horiz, left
mov bx,2
jmp short mmoveb
mmovev: cmp mvmickeys,0
jl mmovv2
sub mvmickeys,dx ; vert, down
mov bx,4
jmp short mmoveb
mmovv2: add mvmickeys,dx ; vert, up
mov bx,6
; modify bx if a button is being held down
mmoveb: cmp lookatmouse,3
jne mmovek ; only modify in mode 3
cmp mbstatus,1
jne mmovb2
add bx,8 ; modify by left button
jmp short mmovek
mmovb2: cmp mbstatus,2
jne mmovb3
add bx,16 ; modify by rb
jmp short mmovek
mmovb3: cmp mbstatus,0
je mmovek
add bx,24 ; modify by middle or multiple
; finally, get the fake key number
mmovek: mov ax,mousefkey[bx]
mouseret:
stc
ret
mouseread endp
; long readticker() returns current bios ticker value
readticker proc uses es
sub ax,ax ; reset ES to BIOS data area
mov es,ax ; see notes at mouse1 in similar code
tickread:
mov ax,es:046ch ; obtain the current timer value
mov dx,es:046eh ; high word of ticker
cmp ax,es:046ch ; did a tick get counted just as we looked?
jne tickread ; yep, reread both words to be safe
ret
readticker endp
;===============================================================
;
; CPUTYPE.ASM : C-callable functions cputype() and ndptype() adapted
; by Lee Daniel Crocker from code appearing in the late PC Tech Journal,
; August 1987 and November 1987. PC Tech Journal was a Ziff-Davis
; Publication. Code herein is copyrighted and used with permission.
;
; The function cputype() returns an integer value based on what kind
; of CPU it found, as follows:
;
; Value CPU Type
; ===== ========
; 86 8086, 8088, V20, or V30
; 186 80186 or 80188
; 286 80286
; 386 80386 or 80386sx
; 486 80486 or 80486sx
; 586 pentium
; -286 80286 in protected mode
; -386 80386 or 80386sx in protected or 32-bit address mode
; -486 80486 or 80486sx in protected or 32-bit address mode
; -586 pentium in protected or 32-bit address mode
;
; The function ndptype() returns an integer based on the type of NDP
; it found, as follows:
;
; Value NDP Type
; ===== ========
; 0 No NDP found
; 87 8087
; 287 80287
; 387 80387
; 487 80487
; 587 80587
;
; No provisions are made for the Weitek FPA chips. For the 80486 and above,
; if an FPU is present it is CPU + 1.
;
; Neither function takes any arguments or affects any external storage,
; so there should be no memory-model dependencies.
.286P
.code
cputype proc
push bp
push sp ; 86/186 will push SP-2;
pop ax ; 286/386 will push SP.
cmp ax, sp
jz not86 ; If equal, SP was pushed
mov ax, 186
mov cl, 32 ; 186 uses count mod 32 = 0;
shl ax, cl ; 86 shifts 32 so ax = 0
; jnz exit ; Non-zero: no shift, so 186
jz notexit
jmp exit ; Non-zero: no shift, so 186
notexit:
mov ax, 86 ; Zero: shifted out all bits
jmp exit
not86:
pushf ; Test 16 or 32 operand size:
mov ax, sp ; Pushed 2 or 4 bytes of flags?
popf
inc ax
inc ax
cmp ax, sp ; Did pushf change SP by 2?
jnz is32bit ; If not, then 4 bytes of flags
is16bit:
sub sp, 6 ; Is it 286 or 386 in 16-bit mode?
mov bp, sp ; Allocate stack space for GDT pointer
sgdt fword ptr [bp]
add sp, 4 ; Discard 2 words of GDT pointer
pop ax ; Get third word
inc ah ; 286 stores -1, 386 stores 0 or 1
jnz is32bit
is286:
mov ax, 286
jmp testprot ; Check for protected mode
is32bit:
.386
pushfd ; save extended flags
mov eax,040000h
push eax ; push 40000h onto stack
popfd ; pop extended flags
pushfd ; push extended flags
pop eax ; put in eax
popfd ; clean the stack
and eax,040000h ; is bit 18 set?
jne tst486 ; yes, it's a 486
624 is386:
625 mov ax, 386
626 jmp short testprot ; Check for protected mode
627 tst486:
628 pushfd ; push flags to save them
629 pushfd ; push flags to look at
630 pop eax ; get eflags
631 mov ebx, eax ; save for later
632 xor eax, 200000h ; toggle bit 21
633 push eax
634 popfd ; load modified eflags to CPU
635 pushfd ; push eflags to look at
636 pop eax ; get current eflags
637 popfd ; restore original flags
638 xor eax, ebx ; check if bit changed
639 jnz is586 ; changed, it's a Pentium
is486:
mov ax, 486
jmp short testprot ; Check for protected mode
is586:
push ecx ; CPUID changes eax to edx
push edx
mov eax, 1 ; get family info function
; ; get the CPUID information
db 0Fh, 0A2h ; into eax, ebx, ecx, edx
and eax, 0F00h ; find family info
shr eax, 8 ; move to al
pop edx
pop ecx
cmp ax,4 ; 4 = 80486, some 486's have CPUID
640 je is486
641 cmp ax,5 ; 5 = pentium
642 ja is686
643 mov ax,586
644 jmp testprot
645 is686:
646 cmp ax,6 ; 6 = pentium pro
647 ja is786
648 mov ax,686
649 jmp testprot
650 is786:
651 cmp ax,7
652 ja is886
653 mov ax,786 ; 7 = ??????
654 jmp testprot
655 is886:
656 mov ax,886 ; 8 = ??????
657
658 .286P
659 testprot:
660 smsw cx ; Protected? Machine status -> CX
661 ror cx,1 ; Protection bit -> carry flag
662 jnc exit ; Real mode if no carry
663 neg ax ; Protected: return neg value
664 exit:
665 pop bp
666 ret
667 cputype endp
668
669 .data
670
671 control dw 0 ; Temp storage for 8087 control
672 ; and status registers
673 .code
674
675 fputype proc
676 push bp
677
678 fninit ; Defaults to 64-bit mantissa
679 mov byte ptr control+1, 0
680 fnstcw control ; Store control word over 0
681 ; dw 3ed9h ; (klooge to avoid the MASM \e switch)
682 ; dw offset control ; ((equates to the above 'fnstcw' cmd))
683 mov ah, byte ptr control+1 ; Test contents of byte written
684 cmp ah, 03h ; Test for 64-bit precision flags
685 je gotone ; Got one! Now let's find which
xor ax, ax
jmp fexit ; No NDP found
gotone:
and control, not 0080h ; IEM = 0 (interrupts on)
fldcw control
fdisi ; Disable ints; 287/387 will ignore
fstcw control
test control, 0080h
jz not87 ; Got 287/387; keep testing
mov ax, 87
jmp short freset
not87:
finit
fld1
fldz
fdiv ; Divide 1/0 to create infinity
fld st
fchs ; Push -infinity on stack
fcompp ; Compare +-infinity
fstsw control
mov ax, control
sahf
jnz got387 ; 387 will compare correctly
mov ax, 287
jmp short freset
got387: ; add 1 to cpu # if fpu and >= 386
cmp cpu, 386
jg got487
mov ax, 387
jmp short freset
got487:
cmp cpu, 486
jg got587
mov ax, 487
jmp short freset
got587:
cmp cpu, 586
jg got687
mov ax, 587
jmp short freset
got687:
cmp cpu, 686
jg got787
mov ax, 687
jmp short freset
got787:
cmp cpu, 786
jg got887
mov ax, 787
jmp short freset
got887:
mov ax, 887
freset:
fninit ; in case tests have had strange
finit ; side-effects, reset
fexit:
pop bp
ret
fputype endp
; ************************* Far Segment RAM Support **************************
;
;
; farptr = (char far *)farmemalloc(long bytestoalloc);
; (void)farmemfree(farptr);
;
; alternatives to Microsoft/TurboC routines
;
;
.8086
farmemalloc proc uses es, bytestoallocate:dword
les bx,bytestoallocate ; get the # of bytes into DX:BX
mov dx,es ; ...
add bx,15 ; round up to next paragraph boundary
adc dx,0 ; ...
shr dx,1 ; convert to paragraphs
rcr bx,1 ; ...
shr dx,1 ; ...
rcr bx,1 ; ...
shr dx,1 ; ...
rcr bx,1 ; ...
shr dx,1 ; ...
rcr bx,1 ; ...
cmp dx,0 ; ensure that we don't want > 1MB
686 jne farmemallocfailed ; bail out if we do
687 mov ah,48h ; invoke DOS to allocate memory
688 int 21h ; ...
689 jc farmemallocfailed ; bail out on failure
690 mov dx,ax ; set up DX:AX as far address
691 mov ax,0 ; ...
692 jmp short farmemallocreturn ; and return
693 farmemallocfailed:
694 mov ax,0 ; (load up with a failed response)
695 mov dx,0 ; ...
696 farmemallocreturn:
697 ret ; we done.
698 farmemalloc endp
699
700 farmemfree proc uses es, farptr:dword
701 les ax,farptr ; get the segment into ES
702 mov ah,49h ; invoke DOS to free the segment
703 int 21h ; ...
704 ret
705 farmemfree endp
706
707 erasesegment proc uses es di si, segaddress:word, segvalue:word
708 mov ax,segaddress ; load up the segment address
709 mov es,ax ; ...
710 mov di,0 ; start at the beginning
711 mov ax,segvalue ; use this value
712 mov cx,8000h ; over the entire segment
713 rep stosw ; do it
714 ret ; we done
715 erasesegment endp
716
717
718 farread proc uses ds, handle:word, buf:dword, len:word
719 mov ah, 03Fh
720 mov bx, [handle]
721 mov cx, [len]
722 lds dx, [buf]
723 int 21h
724 jnc farreaddone
725 mov ax, -1
726 farreaddone:
727 ret
728 farread endp
729
730
731 farwrite proc uses ds, handle:word, buf:dword, len:word
732 mov ah, 040h
733 mov bx, [handle]
734 mov cx, [len]
735 lds dx, [buf]
736 int 21h
737 jnc farwritedone
738 mov ax, -1
739 farwritedone:
740 ret
741 farwrite endp
742
743
744 ; Convert segment:offset to equiv pointer with minimum possible offset
745 normalize proc p: dword
746 ; mov ax, [word ptr p]
747 ; mov dx, [word ptr p+2]
748 les ax, p
749 mov dx, es
750 mov bx, ax
751 shr bx, 1
752 shr bx, 1
753 shr bx, 1
754 shr bx, 1
755 and ax, 0Fh
756 add dx, bx
757 ret
758 normalize endp
759
760
761 ; *************** Far string/memory functions *********
762 ; far_strlen ( char far *);
763 ; far_strcpy ( char far *, char far *);
764 ; far_strcmp ( char far *, char far *);
765 ; far_stricmp( char far *, char far *);
766 ; far_strnicmp(char far *, char far *, int);
767 ; far_strcat ( char far *, char far *);
768 ; far_memset ( char far *, int, long);
769 ; far_memcpy ( char far *, char far *, int);
770 ; far_memcmp ( char far *, char far *, int);
771 ; far_memicmp( char far *, char far *, int);
772
773 ; xxxfar_routines are called internally with:
774 ; ds:si pointing to the source
775 ; es:di pointing to the destination
776 ; cx containing a byte count
777 ; al contining a character (set) value
778 ; (and they destroy registers willy-nilly)
779
780 xxxfar_memlen proc near ; return string length - INCLUDING the 0
781 mov ax,0
782 mov cx,65535
783 repne scasb
784 sub cx,65535
785 neg cx
786 ret
787 xxxfar_memlen endp
788
789 xxxfar_memcmp proc near ; compare two strings - length in CX
790 mov ax,0
791 rep cmpsb
792 jz wedone
793 mov ax,1
794 wedone: ret
795 xxxfar_memcmp endp
796
797 xxxfar_memicmp proc near ; compare two caseless strings - length in CX
798 mov ax,0
799 cmp cx,0
800 je wedone
801 dec si
802 dec di
803 loop1: inc si
804 inc di
805 mov al,es:[di]
806 mov ah,ds:[si]
807 cmp al,ah
808 je loop2
809 cmp al,'A'
810 jb lower1
811 cmp al,'Z'
812 ja lower1
813 add al,20h
814 lower1: cmp ah,'A'
815 jb lower2
816 cmp ah,'Z'
817 ja lower2
818 add ah,20h
819 lower2: cmp al,ah
820 jne uneql
821 loop2: loop loop1
822 mov ax,0
823 jmp short wedone
824 uneql: mov ax,1
825 wedone: ret
826 xxxfar_memicmp endp
827
828
829 far_strlen proc uses ds es di si, fromaddr:dword
830 les di,fromaddr ; point to start-of-string
831 call xxxfar_memlen ; find the string length
832 mov ax,cx ; return len
833 dec ax ; don't count null
ret ; we done.
far_strlen endp
far_strnicmp proc uses ds es di si, toaddr:dword, fromaddr:dword, len:word
les di,fromaddr ; point to start-of-string
call xxxfar_memlen ; find the string length
cmp cx,len ; source less than or equal to len?
jle cxbigger ; yup - use cx
mov cx,len ; nope - use len
cxbigger:
les di,toaddr ; get the dest string
lds si,fromaddr ; get the source string
call xxxfar_memicmp ; compare them
ret ; we done.
far_strnicmp endp
far_strcpy proc uses ds es di si, toaddr:dword, fromaddr:dword
les di,fromaddr ; point to start-of-string
call xxxfar_memlen ; find the string length
les di,toaddr ; now move to here
lds si,fromaddr ; from here
rep movsb ; move them
ret ; we done.
far_strcpy endp
far_strcmp proc uses ds es di si, toaddr:dword, fromaddr:dword
les di,fromaddr ; point to start-of-string
call xxxfar_memlen ; find the string length
les di,toaddr ; now compare to here
lds si,fromaddr ; compare here
call xxxfar_memcmp ; compare them
ret ; we done.
far_strcmp endp
far_stricmp proc uses ds es di si, toaddr:dword, fromaddr:dword
les di,fromaddr ; point to start-of-string
call xxxfar_memlen ; find the string length
les di,toaddr ; get the dest string
lds si,fromaddr ; get the source string
call xxxfar_memicmp ; compare them
ret ; we done.
far_stricmp endp
far_strcat proc uses ds es di si, toaddr:dword, fromaddr:dword
les di,fromaddr ; point to start-of-string
call xxxfar_memlen ; find the string length
push cx ; save it
les di,toaddr ; point to start-of-string
call xxxfar_memlen ; find the string length
les di,toaddr ; now move to here
add di,cx ; but start at the end of string
dec di ; (less the EOS zero)
lds si,fromaddr ; from here
pop cx ; get the string length
rep movsb ; move them
ret ; we done.
far_strcat endp
far_memset proc uses es di, toaddr:dword, fromvalue:word, slength:word
mov ax,fromvalue ; get the value to store
mov cx,slength ; get the store length
les di,toaddr ; now move to here
rep stosb ; store them
ret ; we done.
far_memset endp
far_memcpy proc uses ds es di si, toaddr:dword, fromaddr:dword, slength:word
mov cx,slength ; get the move length
les di,toaddr ; now move to here
lds si,fromaddr ; from here
rep movsb ; move them
ret ; we done.
far_memcpy endp
far_memcmp proc uses ds es di si, toaddr:dword, fromaddr:dword, slength:word
mov cx,slength ; get the compare length
les di,toaddr ; now compare to here
lds si,fromaddr ; compare here
call xxxfar_memcmp ; compare them
ret ; we done.
far_memcmp endp
far_memicmp proc uses ds es di si, toaddr:dword, fromaddr:dword, slength:word
mov cx,slength ; get the compare length
les di,toaddr ; get the dest string
lds si,fromaddr ; get the source string
call xxxfar_memicmp ; compare them
ret ; we done.
far_memicmp endp
disable proc ; disable interrupts
cli
ret
disable endp
enable proc ; re-enable interrupts
sti
ret
enable endp
; *************** Expanded Memory Manager Support Routines ******************
; for use with LIM 3.2 or 4.0 Expanded Memory
;
; farptr = emmquery() ; Query presence of EMM and initialize EMM code
; ; returns EMM FAR Address, or 0 if no EMM
; freepages = emmgetfree(); Returns the number of pages (1 page = 16K)
; ; not already allocated for something else
; handle = emmallocate(pages) ; allocate EMM pages (1 page = 16K)
; ; returns handle # if OK, or else 0
; emmdeallocate(handle) ; return EMM pages to system - MUST BE CALLED
; ; or allocated EMM memory fills up
; emmgetpage(page, handle); get an EMM page (actually, links the EMM
; ; page to the EMM Segment ADDR, saving any
; ; prior page in the process)
; emmclearpage(page, handle) ; performs an 'emmgetpage()' and then clears
; ; it out (quickly) to zeroes with a 'REP STOSW'
.8086
.DATA
emm_name db 'EMMXXXX0',0 ; device driver for EMM
emm_segment dw 0 ; EMM page frame segment
emm_zeroflag db 0 ; klooge flag for handle==0
.CODE
emmquery proc
mov ah,3dh ; function 3dh = open file
mov al,0 ; read only
mov dx,offset emm_name ; DS:DX = address of name of EMM
int 21h ; open it
jc emmqueryfailed ; oops. no EMM.
mov bx,ax ; BX = handle for EMM
mov ah,44h ; function 44h = IOCTL
mov al,7 ; get outo. status
mov cx,0 ; CX = # of bytes to read
int 21h ; do it.
push ax ; save the IOCTL handle.
mov ah,3eh ; function 3eH = close
int 21h ; BX still contains handle
jc emmqueryfailed ; huh? close FAILED?
pop ax ; restore AX for the status query
or al,al ; was the status 0?
jz emmqueryfailed ; well then, it wasn't EMM!
834
835 mov ah,40h ; query EMM: hardware ok?
836 int 67h ; EMM call
837 cmp ah,0 ; is it ok?
838 jne emmqueryfailed ; if not, fail
839
840 mov ah,41h ; query EMM: Get Page Frame Segment
841 int 67h ; EMM call
842 cmp ah,0 ; is it ok?
843 jne emmqueryfailed ; if not, fail
844 mov emm_segment,bx ; save page frame segment
845 mov dx,bx ; return page frame address
846 mov ax,0 ; ...
847 jmp short emmqueryreturn ; we done.
848
849 emmqueryfailed:
850 mov ax,0 ; return 0 (no EMM found)
851 mov dx,0 ; ...
852 emmqueryreturn:
853 ret ; we done.
854 emmquery endp
855
856 emmgetfree proc ; get # of free EMM pages
857 mov ah,42h ; EMM call: get total and free pages
858 int 67h ; EMM call
859 cmp ah,0 ; did we suceed?
860 jne emmgetfreefailed ; nope. return 0 free pages
861 mov ax,bx ; else return # of free pages
862 jmp emmgetfreereturn ; we done.
863 emmgetfreefailed:
864 mov ax,0 ; failure mode
865 emmgetfreereturn:
866 ret ; we done
867 emmgetfree endp
868
869 emmallocate proc pages:word ; allocate EMM pages
870 mov bx,pages ; BX = # of 16K pages
871 mov ah,43h ; ask for the memory
872 int 67h ; EMM call
873 ; mov emm_zeroflag,0 ; clear the klooge flag
874 cmp ah,0 ; did the call work?
875 jne emmallocatebad ; nope.
876 mov ax,dx ; yup. save the handle here
877 cmp ax,0 ; was the handle a zero?
878 jne emmallocatereturn ; yup. no kloogy fixes
879 mov emm_zeroflag,1 ; oops. set an internal flag
880 mov ax,1234 ; and make up a dummy handle.
881 jmp short emmallocatereturn ; and return
882 emmallocatebad:
883 mov ax,0 ; indicate no handle
884 emmallocatereturn:
885 ret ; we done.
886 emmallocate endp
887
888 emmdeallocate proc emm_handle:word ; De-allocate EMM memory
889 emmdeallocatestart:
890 mov dx,emm_handle ; get the EMM handle
891 cmp dx,1234 ; was it our special klooge value?
892 jne emmdeallocatecontinue ; nope. proceed.
893 cmp emm_zeroflag,1 ; was it really a zero handle?
894 jne emmdeallocatecontinue ; nope. proceed.
895 mov dx,0 ; yup. use zero instead.
896 mov emm_zeroflag,0 ; clear the klooge flag
897 emmdeallocatecontinue:
898 mov ah,45h ; EMM function: deallocate
899 int 67h ; EMM call
900 cmp ah,0 ; did it work?
901 jne emmdeallocatestart ; well then, try it again!
902 emmdeallocatereturn:
903 ret ; we done
904 emmdeallocate endp
905
906 emmgetpage proc pagenum:word, emm_handle:word ; get EMM page
907 mov bx,pagenum ; BX = page numper
908 mov dx,emm_handle ; DX = EMM handle
909 cmp dx,1234 ; was it our special klooge value?
910 jne emmgetpagecontinue ; nope. proceed.
911 cmp emm_zeroflag,1 ; was it really a zero handle?
912 jne emmgetpagecontinue ; nope. proceed.
913 mov dx,0 ; yup. use zero instead.
914 emmgetpagecontinue:
915 mov ah,44h ; EMM call: get page
916 mov al,0 ; get it into page 0
917 int 67h ; EMM call
918 ret ; we done
919 emmgetpage endp
920
921 emmclearpage proc pagenum:word, emm_handle:word ; clear EMM page
922 mov bx,pagenum ; BX = page numper
923 mov dx,emm_handle ; DX = EMM handle
924 cmp dx,1234 ; was it our special klooge value?
925 jne emmclearpagecontinue ; nope. proceed.
926 cmp emm_zeroflag,1 ; was it really a zero handle?
927 jne emmclearpagecontinue ; nope. proceed.
928 mov dx,0 ; yup. use zero instead.
929 emmclearpagecontinue:
930 mov ah,44h ; EMM call: get page
931 mov al,0 ; get it into page 0
932 int 67h ; EMM call
933 mov ax,emm_segment ; get EMM segment into ES
934 push es ; ...
935 mov es,ax ; ...
936 mov di,0 ; start at offset 0
937 mov cx,8192 ; for 16K (in words)
938 mov ax,0 ; clear out EMM segment to zeroes
939 rep stosw ; clear the page
940 pop es ; restore ES
941 ret ; we done
942 emmclearpage endp
943
944 ; *************** Extended Memory Manager Support Routines ******************
945 ; for use XMS 2.0 and later Extended Memory
946 ;
947 ; xmmquery() ; Query presence of XMM and initialize XMM code
948 ; ; returns 0 if no XMM
949 ; xmmlongest() ; return size of largest available
950 ; ; XMM block in Kbytes (or zero if none)
951 ; xmmfree() ; return amount of available
952 ; ; XMM blocks in Kbytes (or zero if none)
953 ; handle = xmmallocate(Kbytes) ; allocate XMM block in Kbytes
954 ; ; returns handle # if OK, or else 0
955 ; xmmreallocate(handle, Kbytes) ; change size of handle's block
; ; to size of Kbytes. Returns 0 if failed
; xmmdeallocate(handle) ; return XMM block to system - MUST BE CALLED
; ; or allocated XMM memory is not released.
; xmmmoveextended(&MoveStruct) ; Moves a block of memory to or
; ; from extended memory. Returns 1 if OK
; ; else returns 0.
; The structure format for use with xmmoveextended is:
;
; ASM | C
;--------------------------------+----------------------------------------
; XMM_Move struc | struct XMM_Move
; | {
; Length dd ? | unsigned long Length;
; SourceHandle dw ? | unsigned int SourceHandle;
; SourceOffset dd ? | unsigned long SourceOffset;
; DestHandle dw ? | unsigned int DestHandle;
; DestOffset dd ? | unsigned long DestOffset;
; XMM_Move ends | };
; |
;
; Please refer to XMS spec version 2.0 for further information.
.data
xmscontrol dd dword ptr (0) ; Address of driver's control function
956
957 .code
958 xmmquery proc
959 mov ax,4300h ; Is an XMS driver installed?
960 int 2fh
961 cmp al, 80h ; Did it succeed?
962 jne xmmqueryfailed ; No
963 mov ax,4310h ; Get control function address
964 int 2fh
965 mov word ptr [xmscontrol], bx ; Put address in xmscontrol
966 mov word ptr [xmscontrol+2], es ; ...
967 mov ah,00h ; Get version number
968 call [xmscontrol]
969 cmp ax,0200h ; Is 2.00 or higher?
970 jge xmmquerydone ; Yes
971 xmmqueryfailed:
972 mov ax,0 ; return failure
973 mov dx,0
974 xmmquerydone:
975 ret
976 xmmquery endp
977
978
979 xmmlongest proc ; query length of largest avail block
980 mov ah, 08h ;
981 call [xmscontrol]
982 mov dx, 0
983 ret
984 xmmlongest endp
985
986 xmmfree proc ; query total avail extended memory
987 mov ah, 08h ;
988 call [xmscontrol]
989 mov ax, dx
990 mov dx, 0
991 ret
992 xmmfree endp
993
994 xmmallocate proc ksize:word
995 mov ah,09h ; Allocate extended memory block
996 mov dx,ksize ; size of block in Kbytes
997 call [xmscontrol]
998 cmp ax,0001h ; did it succeed?
999 jne xmmallocatefail ; nope
1000 mov ax,dx ; Put handle here
1001 jmp short xmmallocatedone
1002 xmmallocatefail:
1003 mov ax, 0 ; Indicate failure;
1004 xmmallocatedone:
1005 ret
1006 xmmallocate endp
1007
1008
1009 xmmreallocate proc handle:word, newsize:word
1010 mov ah, 0Fh ; Change size of extended mem block
1011 mov dx, handle ; handle of block to reallocate
1012 mov bx, newsize ; new size for block
1013 call [xmscontrol]
1014 cmp ax, 0001h ; one indicates success
1015 je xmmreallocdone
1016 mov ax, 0 ; we return zero for failure
1017 xmmreallocdone:
1018 ret
1019 xmmreallocate endp
1020
1021
1022 xmmdeallocate proc xmm_handle:word
1023 mov ah,0ah ; Deallocate extended memory block
1024 mov dx, xmm_handle ; Give it handle
1025 call [xmscontrol]
1026 ret
1027 xmmdeallocate endp
1028
1029 xmmmoveextended proc uses si, MoveStruct:word
1030
1031 ; Call the XMS MoveExtended function.
1032 mov ah,0Bh
1033 mov si,MoveStruct ; the move structure.
1034 call [xmscontrol] ;
1035
1036 ; The call to xmscontrol returns a 1 in AX if successful, 0 otherwise.
1037
1038 ret
1039
1040 xmmmoveextended endp
1041
1042 END
1043