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