; Generic assembler routines that have very little at all ; to do with fractals. ; ; (NOTE: The video routines have been moved over to VIDEO.ASM) ; ; ---- Overall Support ; ; initasmvars() ; ; ---- Quick-copy to/from Extraseg support ; ; toextra() ; fromextra() ; cmpextra() ; ; ---- far memory allocation support ; ; farmemalloc() ; farmemfree() ; erasesegment() ; ; ---- General Turbo-C (artificial) support ; ; disable() ; enable() ; ; ---- 32-bit Multiply/Divide Routines (includes 16-bit emulation) ; ; multiply() ; divide() ; ; ---- Keyboard, audio (and, hidden inside them, Mouse) support ; ; keypressed() ; getakey() ; buzzer() ; delay() ; tone() ; snd() ; nosnd() ; ; ---- Expanded Memory Support ; ; emmquery() ; emmgetfree() ; emmallocate() ; emmdeallocate() ; emmgetpage() ; emmclearpage() ; ; ---- Extended Memory Support ; ; xmmquery() ; xmmallocate() ; xmmdeallocate() ; xmmmoveextended() ; ; ---- CPU, FPU Detectors ; ; cputype() ; fputype() ; required for compatibility if Turbo ASM IFDEF ??version MASM51 QUIRKS ENDIF .MODEL medium,c .8086 ; these must NOT be in any segment!! ; 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 adapter_detect:far ; video adapter detector (in video.asm) .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 xdots:word ; horizontal pixels extrn timedsave:word ; triggers autosave ; ************************ 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 _dataseg_xx ; used by TARGA, other Turbo Code public extraseg ; extra 64K segment, if any public overflow ; Mul, Div overflow flag: 0 means none ; arrays declared here, used elsewhere ; arrays not used simultaneously are deliberately overlapped public prefix, suffix, dstack, decoderline ; for the Decoder public strlocn, teststring, 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 ************************** ; 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 ; vidreset temp during video reset prompts.c, fractint.c, rotate.c, cmdfiles.c ; 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 ; contpot image gen, cont.potential, to .tga calcfrac.c ; 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(266) suffix dw 2048 dup(0) ; decoder(4k), vidswitch(256) teststring label byte ; encoder(100) dstack dw 2048 dup(0) ; decoder(4k), solidguess(4k), btm(2k) ; zoom(2k) strlocn label word ; encoder(10k) prefix label word ; decoder(8k), solidguess(6k) olddacbox label byte ; vidreset(768) boxx dw 2048 dup(0) ; zoom(4k), contpot(4k), tgaview(4k) boxy dw 2048 dup(0) ; zoom(4k) boxvalues label byte ; zoom(2k) decoderline db 2050 dup(0) ; decoder(2049), btm(2k) rlebuf label byte ; f16.c(258) .tga save/restore/contpot? 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, or 386 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 .CODE ; *************** Function toextra(tooffset,fromaddr, fromcount) ********* toextra proc uses es di si, tooffset:word, fromaddr:word, fromcount:word cmp extraseg,0 ; IS there extra memory? je tobad ; nope. too bad. 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. tobad: 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 cmp extraseg,0 ; IS there extra memory? je frombad ; nope. too bad. 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. frombad: 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 cmp extraseg,0 ; IS there extra memory? je cmpbad ; nope. too bad. 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. mov ax,0 ; 0 == true jmp 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. .MODEL medium,c .8086 .DATA temp dw 5 dup(0) ; temporary 64-bit result goes here sign db 0 ; sign flag goes here .CODE multiply proc x:dword, y:dword, n:word cmp cpu,386 ; go-fast time? jne 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! pop es pop si pop di ret multiply endp ; ======================================================= ; ; 32-bit integer divide routine with an 'n'-bit shift. ; Overflow condition returns 0x7fffh with overflow = 1; ; ; long x, y, z, divide(); ; int n; ; ; z = divide(x,y,n); /* z = x / y; */ ; ; requires the presence of an external variable, 'cpu'. ; 'cpu' == 386 if a 386 is present. .8086 divide proc uses di si es, x:dword, y:dword, n:word cmp cpu,386 ; go-fast time? jne slowdivide ; no. yawn... .386 ; 386-specific code starts here mov edx,x ; load X into EDX (shifts to EDX:EAX) mov ebx,y ; load Y into EBX mov sign,0 ; clear out the sign flag cmp edx,0 ; is X negative? jge short divides1 ; nope not sign ; yup. flip signs neg edx ; ... divides1: cmp ebx,0 ; is Y negative? jge short divides2 ; nope 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? jge divides5 ; nope not sign ; yup. flip signs not di ; ... not si ; ... stc ; ... adc di,0 ; ... adc si,0 ; ... divides5: mov byte ptr temp+4,33 ; main loop counter mov temp,0 ; results in temp mov word ptr temp+2,0 ; ... dividel1: shl temp,1 ; shift the result up 1 rcl word ptr temp+2,1 ; ... cmp dx,si ; is DX:AX >= Y? jb dividel3 ; nope ja dividel2 ; yup cmp ax,di ; maybe jb dividel3 ; nope dividel2: cmp byte ptr temp+4,32 ; overflow city? jge overd1 ; yup. sub ax,di ; subtract Y sbb dx,si ; ... inc temp ; add 1 to the result adc word ptr temp+2,0 ; ... dividel3: shl bx,1 ; shift all 64 bits rcl cx,1 ; ... rcl ax,1 ; ... rcl dx,1 ; ... dec byte ptr temp+4 ; time to quit? jnz dividel1 ; nope. try again. mov ax,temp ; copy the result to DX:AX mov dx,word ptr temp+2 ; ... cmp sign,0 ; should we negate the result? je divides6 ; nope. not ax ; yup. flip signs. not dx ; ... mov bx,0 ; ... stc ; ... adc ax,0 ; ... adc dx,0 ; ... divides6: jmp short dividereturn overd1: mov ax,0ffffh ; overflow value mov dx,07fffh ; overflow value mov overflow,1 ; flag overflow 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 uses di si es keypressed1: cmp keybuffer,0 ; is a keypress stacked up? jne keypressed3 ; yup. use it. mov ah,kbd_type ; get the keyboard type or ah,1 ; check if a key is ready int 16h ; has a key been hit? jnz keypressed2 ; yes. handle it call mouseread ; mouse activity or save time? jnc keypressed99 ; nope. return: no action. mov keybuffer,ax ; yes. handle it. jmp short keypressed3 keypressed2: call far ptr getakey ; get the keypress code mov keybuffer,ax ; and save the result. keypressed3: mov ax,keybuffer ; return the keypress code. cmp helpmode,1 ; is this HELPAUTHORS mode? je keypressed99 ; yup. forget help. cmp ax,1059 ; help called? je keypressed4 ; ... ; cmp ax,'h' ; help called? ; je keypressed4 ; ... ; cmp ax,'H' ; help called? ; je keypressed4 ; ... cmp ax,'?' ; help called? je keypressed4 ; ... cmp ax,'/' ; help called? je keypressed4 ; ... jmp keypressed5 ; no help asked for. keypressed4: mov keybuffer,0 ; say no key hit mov ax,2 ; ask for general help push ax ; ... call far ptr help ; help! mov keybuffer,ax ; save the result pop ax ; returned value jmp keypressed99 keypressed5: cmp ax,9 ; TAB key hit? jne keypressed99 ; nope. no TAB display. mov keybuffer,0 ; say no key hit call far ptr tab_display ; show the TAB status keypressed99: mov ax,keybuffer ; return keypress, if any ret keypressed endp getakey proc uses di si es ; TARGA 31 May 89 j mclain ; when using '.model size,c' with TASM, ; TASM 'gifts' us with automatic insertions of: ; proc xxxx ; -> push bp ; -> mov bp,sp ; ... ; ->pop bp ; ret ; ; we corrected a situation here where we had a constant 'jz getakey' ; can we say 'stack overflow' ; getakey0: mov ax,keybuffer ; keypress may be here mov keybuffer,0 ; if it was, clear it cmp ax,0 ; is a keypress outstanding? jne getakey4 ; if so, we're done! call mouseread ; mouse activity or savetime? jc getakey4 ; yup, ax holds the phoney key mov ah,kbd_type ; get the keyboard type or ah,1 ; check if a key is ready int 16h ; now check a key jz getakey0 ; so check the mouse again mov ah,kbd_type ; get the keyboard type int 16h ; now get a key cmp al,0e0h ; check: Enhanced Keyboard key? jne short getakey1 ; nope. proceed cmp ah,0 ; part 2 of Enhanced Key check je short getakey1 ; failed. normal key. mov al,0 ; Turn enhanced key "normal" jmp short getakey2 ; jump to common code getakey1: cmp ah,0e0h ; check again: Enhanced Key? jne short getakey2 ; nope. proceed. mov ah,al ; Turn Enhanced key "normal" mov al,0 ; ... getakey2: cmp al,0 ; Function Key? jne short getakey3 ; nope. proceed. mov al,ah ; klooge into ASCII Key mov ah,0 ; clobber the scan code add ax,1000 ; + 1000 jmp short getakey4 ; go to common return getakey3: mov ah,0 ; clobber the scan code getakey4: ret getakey endp ; ****************** Function buzzer(int buzzertype) ******************* ; ; Sound a tone based on the value of the parameter ; ; 0 = normal completion of task ; 1 = interrupted task ; 2 = error contition ; "buzzer()" codes: strings of two-word pairs ; (frequency in cycles/sec, delay in milliseconds) ; frequency == 0 means no sound ; delay == 0 means end-of-tune buzzer0 dw 1047,100 ; "normal" completion dw 1109,100 dw 1175,100 dw 0,0 buzzer1 dw 2093,100 ; "interrupted" completion dw 1976,100 dw 1857,100 dw 0,0 buzzer2 dw 40,500 ; "error" condition (razzberry) dw 0,0 ; *********************************************************************** buzzer proc uses si, buzzertype:word cmp soundflag,0 ; is the sound supressed? je buzzerreturn ; yup. bail out. mov si, offset buzzer0 ; normal completion frequency cmp buzzertype,0 ; normal completion? je buzzerdoit ; do it mov si,offset buzzer1 ; interrupted task frequency cmp buzzertype,1 ; interrupted task? je buzzerdoit ; do it mov si,offset buzzer2 ; error condition frequency buzzerdoit: mov ax,cs:0[si] ; get the (next) frequency mov bx,cs:2[si] ; get the (next) delay add si,4 ; get ready for the next tone cmp bx,0 ; are we done? je buzzerreturn ; yup. push bx ; put delay time on the stack push ax ; put tone value on the stack call far ptr tone ; do it pop ax ; restore stack pop bx ; restore stack jmp short buzzerdoit ; get the next tone buzzerreturn: ret ; we done buzzer endp ; ***************** Function delay(int delaytime) ************************ ; ; performs a delay loop for 'delaytime' milliseconds ; ; ************************************************************************ delayamillisecond proc near ; internal delay-a-millisecond code mov bx,delaycount ; set up to burn another millisecond delayamill1: mov cx,delayloop ; start up the counter delayamill2: ; loop delayamill2 ; burn up some time dec bx ; have we burned up a millisecond? jnz delayamill1 ; nope. try again. ret ; we done delayamillisecond endp delay proc uses es, delaytime:word ; delay loop (arg in milliseconds) mov ax,delaytime ; get the number of milliseconds cmp ax,0 ; any delay time at all? je delayreturn ; nope. delayloop1: call delayamillisecond ; burn up a millisecond of time dec ax ; have we burned up enough m-seconds? jnz delayloop1 ; nope. try again. delayreturn: ret ; we done. delay endp ; ************** Function tone(int frequency,int delaytime) ************** ; ; buzzes the speaker with this frequency for this amount of time ; ; ************************************************************************ tone proc uses es, tonefrequency:word, tonedelay:word mov al,0b6h ; latch to channel 2 out 43h,al ; ... cmp tonefrequency,12h ; was there a frequency? jbe tonebypass ; nope. delay only mov bx,tonefrequency ; get the frequency value mov ax,0 ; ugly klooge: convert this to the mov dx,12h ; divisor the 8253 wants to see div bx ; ... out 42h,al ; send the low value mov al,ah ; then the high value out 42h,al ; ... in al,61h ; get the current 8255 bits or al,3 ; turn bits 0 and 1 on out 61h,al ; ... tonebypass: mov ax,tonedelay ; get the delay value push ax ; set the parameter call far ptr delay ; and force a delay pop ax ; restore the parameter in al,61h ; get the current 8255 bits and al,11111100b ; turn bits 0 and 1 off out 61h,al ret ; we done tone endp ; ************** Function snd(int hertz) and nosnd() ************** ; ; turn the speaker on with this frequency (snd) or off (nosnd) ; ; ***************************************************************** snd proc hertz:word ;Sound the speaker cmp hertz, 20 jle hertzbad cmp hertz, 5000 jge hertzbad mov ax,0 ;Convert hertz mov dx, 12h ;for use by routine div hertz mov bx, ax mov al,10110110b ;Put magic number out 43h, al ;into timer2 mov ax, bx ;Pitch into AX out 42h, al ;LSB into timer2 mov al, ah ;MSB to AL then out 42h, al ;to timer2 in al, 61h ;read I/O port B into AL or al,3 ;turn on bits 0 and 1 out 61h,al ;to turn on speaker hertzbad: ret snd endp nosnd proc ;Turn off speaker in al, 61h ;Read I/O port B into AL and al, 11111100b ;mask lower two bits out 61h, al ;to turn off speaker ret nosnd endp ; ****************** Function initasmvars() ***************************** initasmvars proc uses es si di cmp cpu,0 ; have we been called yet: je initasmvarsgo ; nope. proceed. jmp initreturn ; yup. no need to be here. initasmvarsgo: mov ax,ds ; save the data segment mov _dataseg_xx,ax ; for the C code mov overflow,0 ; indicate no overflows so far mov dx,1 ; ask for 96K of far space mov ax,8000h ; ... push dx ; ... push ax ; ... call far ptr farmemalloc ; use the assembler routine to do it pop ax ; restore the stack pop ax ; ... mov extraseg,dx ; save the results here. call adapter_detect ; call the video adapter detector 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 delaystartuploop: cmp dx,es:46ch ; has the timer value changed? je delaystartuploop ; nope. check again. mov dx,es:46ch ; obtain the current timer value again mov ax,0 ; clear the delay counter mov delaycount,55 ; 55 millisecs = 1/18.2 secs delaytestloop: call delayamillisecond ; burn up a (fake) millisecond inc ax ; indicate another loop has passed cmp dx,es:46ch ; has the timer value changed? je delaytestloop ; nope. burn up some more time. mov delaycount,ax ; save the results here pop es ; restore ES again ; first see if a mouse is installed push es ; (no, first check to ensure that 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. xor ax,ax ; function for mouse check int 33h ; call mouse driver noint33: mov mouse,al ; al holds info about mouse ; now get the information about the kbd push es ; save ES for a tad mov ax,40h ; reload ES with BIOS data seg mov es,ax ; ... mov ah,es:96h ; get the keyboard byte pop es ; restore ES and ah,10h ; isolate the Enhanced KBD bit mov kbd_type,ah ; and save it call far ptr cputype ; what kind of CPU do we have here? cmp ax,0 ; protected mode of some sort? jge positive ; nope. proceed. neg ax ; yup. flip the sign. positive: mov cpu,ax ; save the cpu type. itsa386: 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 initreturn: ret ; return to caller initasmvars endp ; 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. ; translate table for mouse movement -> fake keys mousefkey dw 1077,1075,1080,1072 ; right,left,down,up just movement dw 0, 0,1081,1073 ; ,pgdn,pgup + left button dw 1144,1142,1147,1146 ; kpad+,kpad-,cdel,cins + rt button dw 1117,1119,1118,1132 ; ctl-end,home,pgdn,pgup + mid/multi DclickTime equ 9 ; ticks within which 2nd click must occur JitterTime equ 6 ; idle ticks before turfing unreported mickeys TextHSens equ 160 ; horizontal sensitivity in text mode TextVSens equ 50 ; vertical sensitivity in text mode GraphSens equ 5 ; sensitivity in graphics mode; gets lower @ higher res ZoomSens equ 20 ; sensitivity for zoom box sizing/rotation TextVHLimit equ 6 ; treat angles < 1:6 as straight GraphVHLimit equ 14 ; treat angles < 1:14 as straight ZoomVHLimit equ 1 ; treat angles < 1:1 as straight JitterMickeys equ 3 ; mickeys to ignore before noticing motion mouseread proc near local moveaxis:word ; check if it is time to do an autosave cmp saveticks,0 ; autosave timer running? je mouse0 ; nope 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 cmp ax,savechktime ; a new clock tick since last check? je mouse0 ; nope, save a dozen opcodes or so 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 mov savechktime,ax sub ax,savebase ; calculate ticks since timer started sbb dx,savebase+2 jns tickcompare add ax,0b0h ; wrapped past midnight, add a day adc dx,018h tickcompare: cmp dx,saveticks+2 ; check if past autosave time jb mouse0 ja tickdosave cmp ax,saveticks jb mouse0 tickdosave: 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 ; have the required int, the addr is constant in bios to date, and ; fractint startup already counts on it, so: mov ax,0 ; reset ES to BIOS data area mov es,ax ; ... mov dx,es:46ch ; obtain the current timer value cmp dx,mousetime ; if timer same as last call, skip int 33s: reduces expense and gives ; caller a chance to read all pending stuff and paint something jne mnewtick cmp lookatmouse,0 ; interested in anything other than left button? jl mouseidle ; nope, done jmp mouse5 mouseidle: clc ; tell caller no mouse activity this time ret mnewtick: ; new tick, read buttons and motion mov mousetime,dx ; note current timer cmp lookatmouse,3 je mouse2 ; skip button press if mode 3 ; check press of left button mov ax,05h ; get button press info mov bx,0 ; for left button int 33h or bx,bx jnz mleftb cmp lookatmouse,0 jl mouseidle ; exit if nothing but left button matters jmp mouse3 ; not mode 3 so skip past button release stuff mleftb: mov ax,13 cmp lookatmouse,0 jg mouser ; return fake key enter mov ax,lookatmouse ; return fake key 0-lookatmouse neg ax mouser: jmp mouseret mouse2: ; mode 3, check for double clicks mov ax,06h ; get button release info mov bx,0 ; left button int 33h mov dx,mousetime cmp bx,1 ; left button released? jl msnolb ; go check timer if not jg mslbgo ; double click test mbclicks,1 ; had a 1st click already? jnz mslbgo ; yup, double click mov mlbtimer,dx ; note time of 1st click or mbclicks,1 jmp short mousrb mslbgo: and mbclicks,0ffh-1 mov ax,13 ; fake key enter jmp mouseret msnolb: sub dx,mlbtimer ; clear 1st click if its been too long cmp dx,DclickTime jb mousrb and mbclicks,0ffh-1 ; forget 1st click if any ; next all the same code for right button mousrb: mov ax,06h ; get button release info mov bx,1 ; right button int 33h ; now much the same as for left mov dx,mousetime cmp bx,1 jl msnorb jg msrbgo test mbclicks,2 jnz msrbgo mov mrbtimer,dx or mbclicks,2 jmp short mouse3 msrbgo: and mbclicks,0ffh-2 mov ax,1010 ; fake key ctl-enter jmp mouseret msnorb: sub dx,mrbtimer cmp dx,DclickTime jb mouse3 and mbclicks,0ffh-2 ; get buttons state, if any changed reset mickey counters mouse3: mov ax,03h ; get button status int 33h and bl,7 ; just the button bits cmp bl,mbstatus ; any changed? je mouse4 mov mbstatus,bl ; yup, reset stuff mov mhmickeys,0 mov mvmickeys,0 mov ax,0Bh 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 cmp bx,cx ; horiz>=vert? jge mangle xchg bx,cx ; nope, use vert 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,xdots ; 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 ; -286 80286 in protected mode ; -386 80386 or 80386sx 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 ; ; No provisions are made for the 80486 CPU/FPU or Weitek FPA chips. ; ; Neither function takes any arguments or affects any external storage, ; so there should be no memory-model dependencies. .model medium, c .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 mov ax, 86 ; Zero: shifted out all bits jmp short 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 is386 is286: mov ax, 286 jmp short testprot ; Check for protected mode is32bit: db 66h ; 16-bit override in 32-bit mode is386: mov ax, 386 testprot: smsw cx ; Protected? Machine status -> CX ror cx,1 ; Protection bit -> carry flag jnc exit ; Real mode if no carry neg ax ; Protected: return neg value exit: pop bp ret cputype endp .data control dw 0 ; Temp storage for 8087 control ; and status registers .code fputype proc push bp fninit ; Defaults to 64-bit mantissa mov byte ptr control+1, 0 fnstcw control ; Store control word over 0 ; dw 3ed9h ; (klooge to avoid the MASM \e switch) ; dw offset control ; ((equates to the above 'fnstcw' cmd)) mov ah, byte ptr control+1 ; Test contents of byte written cmp ah, 03h ; Test for 64-bit precision flags je gotone ; Got one! Now let's find which xor ax, ax jmp short 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 fexit 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 fexit got387: ; Only one left (until 487/Weitek mov ax, 387 ; test is added) 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 jne farmemallocfailed ; bail out if we do mov ah,48h ; invoke DOS to allocate memory int 21h ; ... jc farmemallocfailed ; bail out on failure mov dx,ax ; set up DX:AX as far address mov ax,0 ; ... jmp short farmemallocreturn ; and return farmemallocfailed: mov ax,0 ; (load up with a failed response) mov dx,0 ; ... farmemallocreturn: ret ; we done. farmemalloc endp farmemfree proc uses es, farptr:dword les ax,farptr ; get the segment into ES mov ah,49h ; invoke DOS to free the segment int 21h ; ... ret farmemfree endp erasesegment proc uses es di si, segaddress:word, segvalue:word mov ax,segaddress ; load up the segment address mov es,ax ; ... mov di,0 ; start at the beginning mov ax,segvalue ; use this value mov cx,8000h ; over the entire segment repnz stosw ; do it ret ; we done erasesegment endp ; *************** Far string/memory functions ********* ; far_strcpy( char far *, char far *); ; far_strcmp( char far *, char far *); ; far_stricmp(char far *, char far *); ; far_strcat( char far *, char far *); ; far_memset( char far *, char far, int); ; far_memcpy( char far *, char far *, int); ; far_memcmp( char far *, char far *, int); ; far_memicmp(char far *, char far *, int); ; xxxfar_routines are called internally with: ; ds:si pointing to the source ; es:di pointing to the destination ; cx containing a byte count ; al contining a character (set) value ; (and they destroy registers willy-nilly) xxxfar_memlen proc near ; return string length - INCLUDING the 0 mov ax,0 mov cx,1024 repne scasb sub cx,1024 neg cx ret xxxfar_memlen endp xxxfar_memcmp proc near ; compare two strings - length in CX mov ax,0 rep cmpsb jz wedone mov ax,1 wedone: ret xxxfar_memcmp endp xxxfar_memicmp proc near ; compare two caseless strings - length in CX mov ax,0 cmp cx,0 je wedone dec si dec di loop1: inc si inc di mov al,es:[di] mov ah,ds:[si] cmp al,ah je loop2 cmp al,'A' jb lower1 cmp al,'Z' ja lower1 add al,20h lower1: cmp ah,'A' jb lower2 cmp ah,'Z' ja lower2 add ah,20h lower2: cmp al,ah jne uneql loop2: loop loop1 mov ax,0 jmp short wedone uneql: mov ax,1 wedone: ret xxxfar_memicmp 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:byte, slength:word mov al,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' .MODEL medium,c .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 3H = close int 21h ; BX still cintains handle pop ax ; restore AX for the status query jc emmqueryfailed ; huh? close FAILED? or al,al ; was the status 0? jz emmqueryfailed ; well then, it wasn't EMM! mov ah,40h ; query EMM: hardware ok? int 67h ; EMM call cmp ah,0 ; is it ok? jne emmqueryfailed ; if not, fail mov ah,41h ; query EMM: Get Page Frame Segment int 67h ; EMM call cmp ah,0 ; is it ok? jne emmqueryfailed ; if not, fail mov emm_segment,bx ; save page frame segment mov dx,bx ; return page frame address mov ax,0 ; ... jmp short emmqueryreturn ; we done. emmqueryfailed: mov ax,0 ; return 0 (no EMM found) mov dx,0 ; ... emmqueryreturn: ret ; we done. emmquery endp emmgetfree proc ; get # of free EMM pages mov ah,42h ; EMM call: get total and free pages int 67h ; EMM call cmp ah,0 ; did we suceed? jne emmgetfreefailed ; nope. return 0 free pages mov ax,bx ; else return # of free pages jmp emmgetfreereturn ; we done. emmgetfreefailed: mov ax,0 ; failure mode emmgetfreereturn: ret ; we done emmgetfree endp emmallocate proc pages:word ; allocate EMM pages mov bx,pages ; BX = # of 16K pages mov ah,43h ; ask for the memory int 67h ; EMM call mov emm_zeroflag,0 ; clear the klooge flag cmp ah,0 ; did the call work? jne emmallocatebad ; nope. mov ax,dx ; yup. save the handle here cmp ax,0 ; was the handle a zero? jne emmallocatereturn ; yup. no kloogy fixes mov emm_zeroflag,1 ; oops. set an internal flag mov ax,1234 ; and make up a dummy handle. jmp short emmallocatereturn ; and return emmallocatebad: mov ax,0 ; indicate no handle emmallocatereturn: ret ; we done. emmallocate endp emmdeallocate proc emm_handle:word ; De-allocate EMM memory emmdeallocatestart: mov dx,emm_handle ; get the EMM handle cmp dx,1234 ; was it our special klooge value? jne emmdeallocatecontinue ; nope. proceed. cmp emm_zeroflag,1 ; was it really a zero handle? jne emmdeallocatecontinue ; nope. proceed. mov dx,0 ; yup. use zero instead. emmdeallocatecontinue: mov ah,45h ; EMM function: deallocate int 67h ; EMM call cmp ah,0 ; did it work? jne emmdeallocatestart ; well then, try it again! emmdeallocatereturn: ret ; we done emmdeallocate endp emmgetpage proc pagenum:word, emm_handle:word ; get EMM page mov bx,pagenum ; BX = page numper mov dx,emm_handle ; DX = EMM handle cmp dx,1234 ; was it our special klooge value? jne emmgetpagecontinue ; nope. proceed. cmp emm_zeroflag,1 ; was it really a zero handle? jne emmgetpagecontinue ; nope. proceed. mov dx,0 ; yup. use zero instead. emmgetpagecontinue: mov ah,44h ; EMM call: get page mov al,0 ; get it into page 0 int 67h ; EMM call ret ; we done emmgetpage endp emmclearpage proc pagenum:word, emm_handle:word ; clear EMM page mov bx,pagenum ; BX = page numper mov dx,emm_handle ; DX = EMM handle cmp dx,1234 ; was it our special klooge value? jne emmclearpagecontinue ; nope. proceed. cmp emm_zeroflag,1 ; was it really a zero handle? jne emmclearpagecontinue ; nope. proceed. mov dx,0 ; yup. use zero instead. emmclearpagecontinue: mov ah,44h ; EMM call: get page mov al,0 ; get it into page 0 int 67h ; EMM call mov ax,emm_segment ; get EMM segment into ES push es ; ... mov es,ax ; ... mov di,0 ; start at offset 0 mov cx,8192 ; for 16K (in words) mov ax,0 ; clear out EMM segment to zeroes rep stosw ; clear the page pop es ; restore ES ret ; we done emmclearpage endp ; *************** Extended Memory Manager Support Routines ****************** ; for use XMS 2.0 and later Extended Memory ; ; xmmquery() ; Query presence of XMM and initialize XMM code ; ; returns 0 if no XMM ; handle = xmmallocate(Kbytes) ; allocate XMM block in Kbytes ; ; returns handle # if OK, or else 0 ; 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. .model medium,c .data xmscontrol dd dword ptr (?) ; Address of driver's control function .code xmmquery proc mov ax,4300h ; Is an XMS driver installed? int 2fh cmp al, 80h ; Did it succeed? jne xmmqueryfailed ; No mov ax,4310h ; Get control function address int 2fh mov word ptr [xmscontrol], bx ; Put address in xmscontrol mov word ptr [xmscontrol+2], es ; ... mov ah,00h ; Get version number call [xmscontrol] cmp ax,0200h ; Is 2.00 or higher? jge xmmquerydone ; Yes xmmqueryfailed: mov ax,0 ; return failure mov dx,0 xmmquerydone: ret xmmquery endp xmmallocate proc ksize:word mov ah,09h ; Allocate extended memory block mov dx,ksize ; size of block in Kbytes call [xmscontrol] cmp ax,0001h ; did it succeed? jne xmmallocatefail ; nope mov ax,dx ; Put handle here jmp short xmmallocatedone xmmallocatefail: mov ax, 0 ; Indicate failure; xmmallocatedone: ret xmmallocate endp xmmdeallocate proc xmm_handle:word mov ah,0ah ; Deallocate extended memory block mov dx, xmm_handle ; Give it handle call [xmscontrol] ret xmmdeallocate endp xmmmoveextended proc uses si, MoveStruct:word ; Call the XMS MoveExtended function. mov ah,0Bh mov si,MoveStruct ; the move structure. call [xmscontrol] ; ; The call to xmscontrol returns a 1 in AX if successful, 0 otherwise. ret xmmmoveextended endp end