home *** CD-ROM | disk | FTP | other *** search
/ Photo CD Demo 1 / Demo.bin / fractint / fras1611.zip / GENERAL.ASM < prev    next >
Assembly Source File  |  1991-07-03  |  63KB  |  2,251 lines

  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()
  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. ;    xmmallocate()
  55. ;    xmmdeallocate()
  56. ;    xmmmoveextended()
  57. ;
  58. ; ---- CPU, FPU Detectors
  59. ;
  60. ;    cputype()
  61. ;    fputype()
  62. ;
  63. ; ---- IIT FPU Support
  64. ;
  65. ;    load_mat()
  66. ;    mult_vec_iit()
  67. ;    IITCoPro()
  68. ;
  69.  
  70.  
  71. ;             required for compatibility if Turbo ASM
  72. IFDEF ??version
  73.     MASM51
  74.     QUIRKS
  75. ENDIF
  76.  
  77.     .MODEL    medium,c
  78.  
  79.     .8086
  80.  
  81.     ; these must NOT be in any segment!!
  82.     ; this get's rid of TURBO-C fixup errors
  83.  
  84.     extrn    help:far        ; help code (in help.c)
  85.     extrn    tab_display:far     ; TAB display (in fractint.c)
  86.     extrn    restore_active_ovly:far
  87.     extrn    edit_text_colors:far
  88.     extrn    adapter_init:far    ; video adapter init (in video.asm)
  89.     extrn    slideshw:far
  90.     extrn    recordshw:far
  91.     extrn    stopslideshow:far
  92.  
  93. .DATA
  94.  
  95. ; ************************ External variables *****************************
  96.  
  97.     extrn    soundflag:word        ; if 0, supress sounds
  98.     extrn    debugflag:word        ; for debugging purposes only
  99.     extrn    helpmode:word        ; help mode (AUTHORS is special)
  100.     extrn    tabmode:word        ; tab key enabled?
  101.     extrn    sxdots:word        ; horizontal pixels
  102.     extrn    timedsave:word        ; triggers autosave
  103.     extrn    calc_status:word    ; in calcfrac.c
  104.     extrn    got_status:word     ; in calcfrac.c
  105.     extrn    currow:word        ; in calcfrac.c
  106.     extrn    slides:word        ; in cmdfiles.c
  107.  
  108.  
  109. ; ************************ Public variables *****************************
  110.  
  111. public        cpu            ; used by 'calcmand'
  112. public        fpu            ; will be used by somebody someday
  113. public        iit            ; IIT fpu?
  114. public        lookatmouse        ; used by 'calcfrac'
  115. public        saveticks        ; set by fractint
  116. public        savebase        ; set by fractint
  117. public        finishrow        ; set by fractint
  118. public        _dataseg_xx        ; used by TARGA, other Turbo Code
  119.  
  120. public        extraseg        ; extra 64K segment
  121.  
  122. public        overflow        ; Mul, Div overflow flag: 0 means none
  123.  
  124. ;        arrays declared here, used elsewhere
  125. ;        arrays not used simultaneously are deliberately overlapped
  126.  
  127. public        prefix, suffix, dstack, decoderline ; for the Decoder
  128. public        strlocn, teststring, block    ; used by the Encoder
  129. public        boxx, boxy, boxvalues        ; zoom-box arrays
  130. public        olddacbox            ; temporary DAC saves
  131. public        rlebuf                ; Used ty the TARGA En/Decoder
  132. public        paldata, stbuff         ; 8514A arrays, (FR8514A.ASM)
  133.  
  134. ; ************************* "Shared" array areas **************************
  135.  
  136. ; Shared near arrays are discussed below. First some comments about "extraseg".
  137. ; It is a 96k far area permanently allocated during fractint runup.
  138. ; The first part is used for:
  139. ;   64k coordinate arrays during image calculation (initialized in fracsubr.c)
  140. ;   64k create gif save file, encoder.c
  141. ;   64k 3d transforms, line3d.c
  142. ;   64k credits screen, intro.c
  143. ;   22k video mode selection (for fractint.cfg, loadfdos, miscovl)
  144. ;    2k .frm .ifs .l .par entry selection (prompts.c)
  145. ;   ??k printing, printer.c
  146. ;   ??k fractint.doc creation, help.c, not important cause it saves/restores
  147. ; The high 32k is used for graphics image save during text mode; video.asm
  148. ; and realdos.c.
  149.  
  150. ; Short forms used in subsequent comments:
  151. ;   name........duration of use......................modules....................
  152. ;   encoder    "s"aving an image                    encoder.c
  153. ;   decoder    "r"estoring an image                 decoder.c, gifview.c
  154. ;   zoom    zoom box is visible             zoom.c, video.asm
  155. ;   vidswitch    temp during video mode setting         video.asm
  156. ;   8514a    8514a is in use (graphics only?)     fr8514a.asm
  157. ;   tgaview    restore of tga image             tgaview.c
  158. ;   solidguess    image gen with "g", not to disk      calcfrac.c
  159. ;   btm     image gen with "b", not to disk      calcfrac.c
  160. ;   editpal    palette editor "heap"                editpal.c
  161. ; Note that decoder using an 8514a is worst case, uses all arrays at once.
  162. ; Keep db lengths even so that word alignment is preserved for speed.
  163.  
  164. block        label    byte        ; encoder(266)
  165. suffix        dw    2048 dup(0)    ; decoder(4k), vidswitch(256),
  166.                     ; savegraphics/restoregraphics(4k)
  167.  
  168. teststring    label    byte        ; encoder(100)
  169. olddacbox    label    byte        ; fractint(768), prompts(768)
  170. dstack        dw    2048 dup(0)    ; decoder(4k), solidguess(4k), btm(2k)
  171.                     ;   zoom(2k), ifsload(2k), prompts(4k)
  172.                     ;   make_batch(4k), printer(2400)
  173.                     ;   loadfdos(2000)
  174.  
  175. strlocn     label    word        ; encoder(10k), editpal(10k)
  176. prefix        label    word        ; decoder(8k), solidguess(6k)
  177. boxx        dw    2048 dup(0)    ; zoom(4k), tgaview(4k), prompts(9k)
  178.                     ;   make_batch(8k), parser(8k)
  179. boxy        dw    2048 dup(0)    ; zoom(4k)
  180. boxvalues    label    byte        ; zoom(2k)
  181. decoderline    db    2050 dup(0)    ; decoder(2049), btm(2k)
  182.  
  183. rlebuf        label    byte        ; f16.c(258) .tga save/restore?
  184. paldata     db    1024 dup(0)    ; 8514a(1k)
  185. stbuff        db    415 dup(0)    ; 8514a(415)
  186.  
  187. ; ************************ Internal variables *****************************
  188.  
  189.         align    2
  190. cpu        dw    0        ; cpu type: 86, 186, 286, or 386
  191. fpu        dw    0        ; fpu type: 0, 87, 287, 387
  192. iit        dw    0        ; iit fpu:  0=no, 1=yes
  193. _dataseg_xx    dw    0        ; our "near" data segment
  194.  
  195. overflow    dw    0        ; overflow flag
  196.  
  197. kbd_type    db    0        ; type of keyboard
  198.         align    2
  199. keybuffer    dw    0        ; real small keyboard buffer
  200.  
  201. delayloop    dw    32        ; delay loop value
  202. delaycount    dw    0        ; number of delay "loops" per ms.
  203.  
  204. extraseg    dw    0        ; extra 64K segment (allocated by init)
  205.  
  206. ; ********************** Mouse Support Variables **************************
  207.  
  208. lookatmouse    dw    0        ; see notes at mouseread routine
  209. prevlamouse    dw    0        ; previous lookatmouse value
  210. mousetime    dw    0        ; time of last mouseread call
  211. mlbtimer    dw    0        ; time of left button 1st click
  212. mrbtimer    dw    0        ; time of right button 1st click
  213. mhtimer     dw    0        ; time of last horiz move
  214. mvtimer     dw    0        ; time of last vert  move
  215. mhmickeys    dw    0        ; pending horiz movement
  216. mvmickeys    dw    0        ; pending vert    movement
  217. mbstatus    db    0        ; status of mouse buttons
  218. mouse        db    0        ; == -1 if/when a mouse is found.
  219. mbclicks    db    0        ; had 1 click so far? &1 mlb, &2 mrb
  220.  
  221.         align    2
  222. ; timed save variables, handled by readmouse:
  223. savechktime    dw    0        ; time of last autosave check
  224. savebase    dw    2 dup(0)    ; base clock ticks
  225. saveticks    dw    2 dup(0)    ; save after this many ticks
  226. finishrow    dw    0        ; save when this row is finished
  227.  
  228.  
  229. .CODE
  230.  
  231. ; *************** Function toextra(tooffset,fromaddr, fromcount) *********
  232.  
  233. toextra proc    uses es di si, tooffset:word, fromaddr:word, fromcount:word
  234.     cld                ; move forward
  235.     mov    ax,extraseg        ; load ES == extra segment
  236.     mov    es,ax            ;  ..
  237.     mov    di,tooffset        ; load to here
  238.     mov    si,fromaddr        ; load from here
  239.     mov    cx,fromcount        ; this many bytes
  240.     rep    movsb            ; do it.
  241.     ret                ; we done.
  242. toextra endp
  243.  
  244.  
  245. ; *************** Function fromextra(fromoffset, toaddr, tocount) *********
  246.  
  247. fromextra proc    uses es di si, fromoffset:word, toaddr:word, tocount:word
  248.     push    ds            ; save DS for a tad
  249.     pop    es            ; restore it to ES
  250.     cld                ; move forward
  251.     mov    si,fromoffset        ; load from here
  252.     mov    di,toaddr        ; load to here
  253.     mov    cx,tocount        ; this many bytes
  254.     mov    ax,extraseg        ; load DS == extra segment
  255.     mov    ds,ax            ;  ..
  256.     rep    movsb            ; do it.
  257.     push    es            ; save ES again.
  258.     pop    ds            ; restore DS
  259.     ret                ; we done.
  260. fromextra endp
  261.  
  262.  
  263. ; *************** Function cmpextra(cmpoffset,cmpaddr, cmpcount) *********
  264.  
  265. cmpextra proc    uses es di si, cmpoffset:word, cmpaddr:word, cmpcount:word
  266.     cld                ; move forward
  267.     mov    ax,extraseg        ; load ES == extra segment
  268.     mov    es,ax            ;  ..
  269.     mov    di,cmpoffset        ; load to here
  270.     mov    si,cmpaddr        ; load from here
  271.     mov    cx,cmpcount        ; this many bytes
  272.     rep    cmpsb            ; do it.
  273.     jnz    cmpbad            ; failed.
  274.     sub    ax,ax            ; 0 == true
  275.     jmp    short cmpend
  276. cmpbad:
  277.     mov    ax,1            ; 1 == false
  278. cmpend:
  279.     ret                ; we done.
  280. cmpextra    endp
  281.  
  282.  
  283. ; =======================================================
  284. ;
  285. ;    32-bit integer multiply routine with an 'n'-bit shift.
  286. ;    Overflow condition returns 0x7fffh with overflow = 1;
  287. ;
  288. ;    long x, y, z, multiply();
  289. ;    int n;
  290. ;
  291. ;    z = multiply(x,y,n)
  292. ;
  293. ;    requires the presence of an external variable, 'cpu'.
  294. ;        'cpu' == 386 if a 386 is present.
  295.  
  296. .MODEL    medium,c
  297.  
  298. .8086
  299.  
  300. .DATA
  301.  
  302. temp    dw    5 dup(0)        ; temporary 64-bit result goes here
  303. sign    db    0            ; sign flag goes here
  304.  
  305. .CODE
  306.  
  307. FRAME    MACRO regs
  308.     push    bp
  309.     mov    bp, sp
  310.     IRP    reg, <regs>
  311.       push    reg
  312.       ENDM
  313.     ENDM
  314.  
  315. UNFRAME MACRO regs
  316.     IRP    reg, <regs>
  317.       pop reg
  318.       ENDM
  319.     pop bp
  320.     ENDM
  321.  
  322.  
  323. multiply    proc    x:dword, y:dword, n:word
  324.  
  325.     cmp    cpu,386         ; go-fast time?
  326.     jne    slowmultiply        ; no.  yawn...
  327.  
  328. .386                    ; 386-specific code starts here
  329.  
  330.     mov    eax,x            ; load X into EAX
  331.     imul    y            ; do the multiply
  332.     mov    cx,n            ; set up the shift
  333.     cmp    cx,32            ; ugly klooge:    check for 32-bit shift
  334.     jb    short fastm1        ;  < 32 bits:  no problem
  335.     mov    eax,edx         ;  >= 32 bits:    manual shift
  336.     mov    edx,0            ;  ...
  337.     sub    cx,32            ;  ...
  338. fastm1: shrd    eax,edx,cl        ; shift down 'n' bits
  339.     js    fastm3
  340.     sar    edx,cl
  341.     jne    overmf
  342.     shld    edx,eax,16
  343.     ret
  344. fastm3: sar    edx,cl
  345.     inc    edx
  346.     jne    overmf
  347.     shld    edx,eax,16
  348.     ret
  349. overmf:
  350.     mov    ax,0ffffh        ; overflow value
  351.     mov    dx,07fffh        ; overflow value
  352.     mov    overflow,1        ; flag overflow
  353.     ret
  354.  
  355. .8086                    ; 386-specific code ends here
  356.  
  357. slowmultiply:                ; (sigh)  time to do it the hard way...
  358.     push    di
  359.     push    si
  360.     push    es
  361.  
  362.     mov    ax,0
  363.     mov    temp+4,ax        ; first, zero out the (temporary)
  364.     mov    temp+6,ax        ;  result
  365.     mov    temp+8,ax
  366.  
  367.     les    bx,x            ; move X to SI:BX
  368.     mov    si,es            ;  ...
  369.     les    cx,y            ; move Y to DI:CX
  370.     mov    di,es            ;  ...
  371.  
  372.     mov    sign,0            ; clear out the sign flag
  373.     cmp    si,0            ; is X negative?
  374.     jge    mults1            ;  nope
  375.     not    sign            ;  yup.  flip signs
  376.     not    bx            ;   ...
  377.     not    si            ;   ...
  378.     stc                ;   ...
  379.     adc    bx,ax            ;   ...
  380.     adc    si,ax            ;   ...
  381. mults1: cmp    di,0            ; is DI:CX negative?
  382.     jge    mults2            ;  nope
  383.     not    sign            ;  yup.  flip signs
  384.     not    cx            ;   ...
  385.     not    di            ;   ...
  386.     stc                ;   ...
  387.     adc    cx,ax            ;   ...
  388.     adc    di,ax            ;   ...
  389. mults2:
  390.  
  391.     mov    ax,bx            ; perform BX x CX
  392.     mul    cx            ;  ...
  393.     mov    temp,ax         ;  results in lowest 32 bits
  394.     mov    temp+2,dx        ;  ...
  395.  
  396.     mov    ax,bx            ; perform BX x DI
  397.     mul    di            ;  ...
  398.     add    temp+2,ax        ;  results in middle 32 bits
  399.     adc    temp+4,dx        ;  ...
  400.     jnc    mults3            ;  carry bit set?
  401.     inc    word ptr temp+6     ;  yup.  overflow
  402. mults3:
  403.  
  404.     mov    ax,si            ; perform SI * CX
  405.     mul    cx            ;  ...
  406.     add    temp+2,ax        ;  results in middle 32 bits
  407.     adc    temp+4,dx        ;  ...
  408.     jnc    mults4            ;  carry bit set?
  409.     inc    word ptr temp+6     ;  yup.  overflow
  410. mults4:
  411.  
  412.     mov    ax,si            ; perform SI * DI
  413.     mul    di            ;  ...
  414.     add    temp+4,ax        ; results in highest 32 bits
  415.     adc    temp+6,dx        ;  ...
  416.  
  417.     mov    cx,n            ; set up for the shift loop
  418.     cmp    cx,24            ; shifting by three bytes or more?
  419.     jl    multc1            ;  nope.  check for something else
  420.     sub    cx,24            ; quick-shift 24 bits
  421.     mov    ax,temp+3        ; load up the registers
  422.     mov    dx,temp+5        ;  ...
  423.     mov    si,temp+7        ;  ...
  424.     mov    bx,0            ;  ...
  425.     jmp    short multc4        ; branch to common code
  426. multc1: cmp    cx,16            ; shifting by two bytes or more?
  427.     jl    multc2            ;  nope.  check for something else
  428.     sub    cx,16            ; quick-shift 16 bits
  429.     mov    ax,temp+2        ; load up the registers
  430.     mov    dx,temp+4        ;  ...
  431.     mov    si,temp+6        ;  ...
  432.     mov    bx,0            ;  ...
  433.     jmp    short multc4        ; branch to common code
  434. multc2: cmp    cx,8            ; shifting by one byte or more?
  435.     jl    multc3            ;  nope.  check for something else
  436.     sub    cx,8            ; quick-shift 8 bits
  437.     mov    ax,temp+1        ; load up the registers
  438.     mov    dx,temp+3        ;  ...
  439.     mov    si,temp+5        ;  ...
  440.     mov    bx,temp+7        ;  ...
  441.     jmp    short multc4        ; branch to common code
  442. multc3: mov    ax,temp         ; load up the regs
  443.     mov    dx,temp+2        ;  ...
  444.     mov    si,temp+4        ;  ...
  445.     mov    bx,temp+6        ;  ...
  446. multc4: cmp    cx,0            ; done shifting?
  447.     je    multc5            ;  yup.  bail out
  448.  
  449. multloop:
  450.     shr    bx,1            ; shift down 1 bit, cascading
  451.     rcr    si,1            ;  ...
  452.     rcr    dx,1            ;  ...
  453.     rcr    ax,1            ;  ...
  454.     loop    multloop        ; try the next bit, if any
  455. multc5:
  456.     cmp    si,0            ; overflow time?
  457.     jne    overm1            ; yup.    Bail out.
  458.     cmp    bx,0            ; overflow time?
  459.     jne    overm1            ; yup.    Bail out.
  460.     cmp    dx,0            ; overflow time?
  461.     jl    overm1            ; yup.    Bail out.
  462.  
  463.     cmp    sign,0            ; should we negate the result?
  464.     je    mults5            ;  nope.
  465.     not    ax            ;  yup.  flip signs.
  466.     not    dx            ;   ...
  467.     mov    bx,0            ;   ...
  468.     stc                ;   ...
  469.     adc    ax,bx            ;   ...
  470.     adc    dx,bx            ;   ...
  471. mults5:
  472.     jmp    multiplyreturn
  473.  
  474. overm1:
  475.     mov    ax,0ffffh        ; overflow value
  476.     mov    dx,07fffh        ; overflow value
  477.     mov    overflow,1        ; flag overflow
  478.  
  479. multiplyreturn:             ; that's all, folks!
  480.     pop    es
  481.     pop    si
  482.     pop    di
  483.     ret
  484. multiply    endp
  485.  
  486.  
  487. ; =======================================================
  488. ;
  489. ;    32-bit integer divide routine with an 'n'-bit shift.
  490. ;    Overflow condition returns 0x7fffh with overflow = 1;
  491. ;
  492. ;    long x, y, z, divide();
  493. ;    int n;
  494. ;
  495. ;    z = divide(x,y,n);    /* z = x / y; */
  496. ;
  497. ;    requires the presence of an external variable, 'cpu'.
  498. ;        'cpu' == 386 if a 386 is present.
  499.  
  500.  
  501. .8086
  502.  
  503. divide        proc    uses di si es, x:dword, y:dword, n:word
  504.  
  505.     cmp    cpu,386         ; go-fast time?
  506.     jne    slowdivide        ; no.  yawn...
  507.  
  508. .386                    ; 386-specific code starts here
  509.  
  510.     mov    edx,x            ; load X into EDX (shifts to EDX:EAX)
  511.     mov    ebx,y            ; load Y into EBX
  512.  
  513.     mov    sign,0            ; clear out the sign flag
  514.     cmp    edx,0            ; is X negative?
  515.     jge    short divides1        ;  nope
  516.     not    sign            ;  yup.  flip signs
  517.     neg    edx            ;   ...
  518. divides1:
  519.     cmp    ebx,0            ; is Y negative?
  520.     jge    short divides2        ;  nope
  521.     not    sign            ;  yup.  flip signs
  522.     neg    ebx            ;   ...
  523. divides2:
  524.  
  525.     mov    eax,0            ; clear out the low-order bits
  526.     mov    cx,32            ; set up the shift
  527.     sub    cx,n            ; (for large shift counts - faster)
  528. fastd1: cmp    cx,0            ; done shifting?
  529.     je    fastd2            ; yup.
  530.     shr    edx,1            ; shift one bit
  531.     rcr    eax,1            ;  ...
  532.     loop    fastd1            ; and try again
  533. fastd2:
  534.     cmp    edx,ebx         ; umm, will the divide blow out?
  535.     jae    overd1            ;  yup.  better skip it.
  536.     div    ebx            ; do the divide
  537.     cmp    eax,0            ; did the sign flip?
  538.     jl    overd1            ;  then we overflowed
  539.     cmp    sign,0            ; is the sign reversed?
  540.     je    short divides3        ;  nope
  541.     neg    eax            ; flip the sign
  542. divides3:
  543.     push    eax            ; save the 64-bit result
  544.     pop    ax            ; low-order  16 bits
  545.     pop    dx            ; high-order 16 bits
  546.     jmp    dividereturn        ; back to common code
  547.  
  548. .8086                    ; 386-specific code ends here
  549.  
  550. slowdivide:                ; (sigh)  time to do it the hard way...
  551.  
  552.     les    ax,x            ; move X to DX:AX
  553.     mov    dx,es            ;  ...
  554.  
  555.     mov    sign,0            ; clear out the sign flag
  556.     cmp    dx,0            ; is X negative?
  557.     jge    divides4        ;  nope
  558.     not    sign            ;  yup.  flip signs
  559.     not    ax            ;   ...
  560.     not    dx            ;   ...
  561.     stc                ;   ...
  562.     adc    ax,0            ;   ...
  563.     adc    dx,0            ;   ...
  564. divides4:
  565.  
  566.     mov    cx,32            ; get ready to shift the bits
  567.     sub    cx,n            ; (shift down rather than up)
  568.     mov    byte ptr temp+4,cl    ;  ...
  569.  
  570.     mov    cx,0            ;  clear out low bits of DX:AX:CX:BX
  571.     mov    bx,0            ;  ...
  572.  
  573.     cmp    byte ptr temp+4,16    ; >= 16 bits to shift?
  574.     jl    dividex0        ;  nope
  575.     mov    bx,cx            ;  yup.  Take a short-cut
  576.     mov    cx,ax            ;   ...
  577.     mov    ax,dx            ;   ...
  578.     mov    dx,0            ;   ...
  579.     sub    byte ptr temp+4,16    ;   ...
  580. dividex0:
  581.     cmp    byte ptr temp+4,8    ; >= 8 bits to shift?
  582.     jl    dividex1        ;  nope
  583.     mov    bl,bh            ;  yup.  Take a short-cut
  584.     mov    bh,cl            ;   ...
  585.     mov    cl,ch            ;   ...
  586.     mov    ch,al            ;   ...
  587.     mov    al,ah            ;   ...
  588.     mov    ah,dl            ;   ...
  589.     mov    dl,dh            ;   ...
  590.     mov    dh,0            ;   ...
  591.     sub    byte ptr temp+4,8    ;   ...
  592. dividex1:
  593.     cmp    byte ptr temp+4,0    ; are we done yet?
  594.     je    dividex2        ;  yup
  595.     shr    dx,1            ; shift all 64 bits
  596.     rcr    ax,1            ;  ...
  597.     rcr    cx,1            ;  ...
  598.     rcr    bx,1            ;  ...
  599.     dec    byte ptr temp+4     ; decrement the shift counter
  600.     jmp    short dividex1        ;  and try again
  601. dividex2:
  602.  
  603.     les    di,y            ; move Y to SI:DI
  604.     mov    si,es            ;  ...
  605.  
  606.     cmp    si,0            ; is Y negative?
  607.     jge    divides5        ;  nope
  608.     not    sign            ;  yup.  flip signs
  609.     not    di            ;   ...
  610.     not    si            ;   ...
  611.     stc                ;   ...
  612.     adc    di,0            ;   ...
  613.     adc    si,0            ;   ...
  614. divides5:
  615.  
  616.     mov    byte ptr temp+4,33    ; main loop counter
  617.     mov    temp,0            ; results in temp
  618.     mov    word ptr temp+2,0    ;  ...
  619.  
  620. dividel1:
  621.     shl    temp,1            ; shift the result up 1
  622.     rcl    word ptr temp+2,1    ;  ...
  623.     cmp    dx,si            ; is DX:AX >= Y?
  624.     jb    dividel3        ;  nope
  625.     ja    dividel2        ;  yup
  626.     cmp    ax,di            ;  maybe
  627.     jb    dividel3        ;  nope
  628. dividel2:
  629.     cmp    byte ptr temp+4,32    ; overflow city?
  630.     jge    overd1            ;  yup.
  631.     sub    ax,di            ; subtract Y
  632.     sbb    dx,si            ;  ...
  633.     inc    temp            ; add 1 to the result
  634.     adc    word ptr temp+2,0    ;  ...
  635. dividel3:
  636.     shl    bx,1            ; shift all 64 bits
  637.     rcl    cx,1            ;  ...
  638.     rcl    ax,1            ;  ...
  639.     rcl    dx,1            ;  ...
  640.     dec    byte ptr temp+4     ; time to quit?
  641.     jnz    dividel1        ;  nope.  try again.
  642.  
  643.     mov    ax,temp         ; copy the result to DX:AX
  644.     mov    dx,word ptr temp+2    ;  ...
  645.     cmp    sign,0            ; should we negate the result?
  646.     je    divides6        ;  nope.
  647.     not    ax            ;  yup.  flip signs.
  648.     not    dx            ;   ...
  649.     mov    bx,0            ;   ...
  650.     stc                ;   ...
  651.     adc    ax,0            ;   ...
  652.     adc    dx,0            ;   ...
  653. divides6:
  654.     jmp    short dividereturn
  655.  
  656. overd1:
  657.     mov    ax,0ffffh        ; overflow value
  658.     mov    dx,07fffh        ; overflow value
  659.     mov    overflow,1        ; flag overflow
  660.  
  661. dividereturn:                ; that's all, folks!
  662.     ret
  663. divide        endp
  664.  
  665.  
  666. ; ****************** Function getakey() *****************************
  667. ; **************** Function keypressed() ****************************
  668.  
  669. ;    'getakey()' gets a key from either a "normal" or an enhanced
  670. ;    keyboard.   Returns either the vanilla ASCII code for regular
  671. ;    keys, or 1000+(the scan code) for special keys (like F1, etc)
  672. ;    Use of this routine permits the Control-Up/Down arrow keys on
  673. ;    enhanced keyboards.
  674. ;
  675. ;    The concept for this routine was "borrowed" from the MSKermit
  676. ;    SCANCHEK utility
  677. ;
  678. ;    'keypressed()' returns a zero if no keypress is outstanding,
  679. ;    and the value that 'getakey()' will return if one is.  Note
  680. ;    that you must still call 'getakey()' to flush the character.
  681. ;    As a sidebar function, calls 'help()' if appropriate, or
  682. ;    'tab_display()' if appropriate.
  683. ;    Think of 'keypressed()' as a super-'kbhit()'.
  684.  
  685. keypressed  proc
  686.     call    far ptr getkeynowait        ; check for key
  687.     jc    keypressed1            ;  got a key
  688.     sub    ax,ax                ; fast no-key return
  689.     ret
  690. keypressed1:
  691.     FRAME    <di,si,es>            ; std frame, for TC++ overlays
  692.     mov    keybuffer,ax            ; remember it for next time
  693.     cmp    ax,1059             ; help called?
  694.     jne    keypressed2            ; no help asked for.
  695.     mov    keybuffer,0            ; say no key hit
  696.     cmp    helpmode,0            ; help disabled?
  697.     jl    keypressed1a            ;  yup.  forget help.
  698.     xor    ax,ax
  699.     push    ax
  700.     call    far ptr help            ; help!
  701.     pop    ax
  702.     call    far ptr restore_active_ovly    ; help might've clobbered ovly
  703. keypressed1a:
  704.     sub    ax,ax
  705.     jmp    short keypressedx
  706. keypressed2:
  707.     cmp    ax,9                ; TAB key hit?
  708.     jne    keypressedx            ;  nope.  no TAB display.
  709.     cmp    tabmode,0            ; tab enabled?
  710.     je    keypressedx            ;  nope
  711.     mov    keybuffer,0            ; say no key hit
  712.     call    far ptr tab_display        ; show the TAB status
  713.     call    far ptr restore_active_ovly    ; tab might've clobbered ovly
  714.     sub    ax,ax
  715. keypressedx:
  716.     UNFRAME <es,si,di>            ; pop frame
  717.     ret
  718. keypressed    endp
  719.  
  720. getakeynohelp proc
  721. gknhloop:
  722.     call    far ptr getakey         ; get keystroke
  723.     cmp    ax,1059             ; help key?
  724.     je    gknhloop            ;  ignore help, none available
  725.     ret
  726. getakeynohelp endp
  727.  
  728. getakey proc
  729. getakeyloop:
  730.     call    far ptr getkeynowait        ; check for keystroke
  731.     jnc    getakeyloop            ;  no key, loop till we get one
  732.     ret
  733. getakey endp
  734.  
  735. getkeynowait proc
  736.     FRAME    <di,si,es>            ; std frame, for TC++ overlays
  737. getkeyn0:
  738.     cmp    keybuffer,0            ; got a key buffered?
  739.     je    getkeynobuf            ;  nope
  740.     mov    ax,keybuffer            ; key was buffered here
  741.     mov    keybuffer,0            ; clear buffer
  742.     jmp    getkeyyup            ; exit with the key
  743. getkeynobuf:
  744.     call    mouseread            ; mouse activity or savetime?
  745.     jc    getkeyn4            ;  yup, ax holds the phoney key
  746.     mov    ah,kbd_type            ; get the keyboard type
  747.     or    ah,1                ; check if a key is ready
  748.     int    16h                ; now check a key
  749.     jnz    gotkeyn             ;  got one
  750.     cmp    slides,1            ; slideshow playback active?
  751.     jne    getkeynope            ;  nope, return no key
  752.     call    far ptr slideshw        ; check next playback keystroke
  753.     cmp    ax,0                ; got one?
  754.     jne    getkeyn5            ;  yup, use it
  755. getkeynope:
  756.     clc                    ; return no key
  757.     UNFRAME <es,si,di>            ; pop frame
  758.     ret
  759. gotkeyn:                    ; got a real keyboard keystroke
  760.     mov    ah,kbd_type            ; get the keyboard type
  761.     int    16h                ; now get a key
  762.     cmp    al,0e0h             ; check: Enhanced Keyboard key?
  763.     jne    short getkeyn1            ; nope.  proceed
  764.     cmp    ah,0                ; part 2 of Enhanced Key check
  765.     je    short getkeyn1            ; failed.  normal key.
  766.     mov    al,0                ; Turn enhanced key "normal"
  767.     jmp    short getkeyn2            ; jump to common code
  768. getkeyn1:
  769.     cmp    ah,0e0h             ; check again:    Enhanced Key?
  770.     jne    short getkeyn2            ;  nope.  proceed.
  771.     mov    ah,al                ; Turn Enhanced key "normal"
  772.     mov    al,0                ;  ...
  773. getkeyn2:
  774.     cmp    al,0                ; Function Key?
  775.     jne    short getkeyn3            ;  nope.  proceed.
  776.     mov    al,ah                ; klooge into ASCII Key
  777.     mov    ah,0                ; clobber the scan code
  778.     add    ax,1000             ;  + 1000
  779.     jmp    short getkeyn4            ; go to common return
  780. getkeyn3:
  781.     mov    ah,0                ; clobber the scan code
  782. getkeyn4:                    ; got real key (not playback)
  783.     cmp    ax,9999             ; savetime from mousread?
  784.     je    getkeyn6            ;  yup, do it and don't record
  785.     cmp    slides,1            ; slideshow playback active?
  786.     jne    getkeyn5            ;  nope
  787.     cmp    ax,1bh                ; escape?
  788.     jne    getkeyn0            ;  nope, ignore the key
  789.     call    far ptr stopslideshow        ; terminate playback
  790.     jmp    short getkeyn0            ; go check for another key
  791. getkeyn5:
  792.     cmp    slides,2            ; slideshow record mode?
  793.     jne    getkeyn6            ;  nope
  794.     push    ax
  795.     call    far ptr recordshw        ; record the key
  796.     pop    ax
  797. getkeyn6:
  798.     cmp    debugflag,3000            ; color play enabled?
  799.     jne    getkeyyup            ;  nope
  800.     cmp    ax,'~'                          ; color play requested?
  801.     jne    getkeyyup            ;  nope
  802.     call    far ptr edit_text_colors    ; play
  803.     call    far ptr restore_active_ovly    ; might've clobbered ovly
  804.     jmp    getkeyn0            ; done playing, back around
  805. getkeyyup:
  806.     stc                    ; indicate we have a key
  807.     UNFRAME <es,si,di>            ; pop frame
  808.     ret
  809. getkeynowait endp
  810.  
  811. ; ****************** Function buzzer(int buzzertype) *******************
  812. ;
  813. ;    Sound a tone based on the value of the parameter
  814. ;
  815. ;    0 = normal completion of task
  816. ;    1 = interrupted task
  817. ;    2 = error contition
  818.  
  819. ;    "buzzer()" codes:  strings of two-word pairs
  820. ;        (frequency in cycles/sec, delay in milliseconds)
  821. ;        frequency == 0 means no sound
  822. ;        delay      == 0 means end-of-tune
  823. buzzer0     dw    1047,100    ; "normal" completion
  824.         dw    1109,100
  825.         dw    1175,100
  826.         dw    0,0
  827. buzzer1     dw    2093,100    ; "interrupted" completion
  828.         dw    1976,100
  829.         dw    1857,100
  830.         dw    0,0
  831. buzzer2     dw    40,500        ; "error" condition (razzberry)
  832.         dw    0,0
  833.  
  834. ; ***********************************************************************
  835.  
  836. buzzer    proc    uses si, buzzertype:word
  837.     cmp    soundflag,0        ; is the sound supressed?
  838.     je    buzzerreturn        ;  yup.  bail out.
  839.     mov    si, offset buzzer0    ; normal completion frequency
  840.     cmp    buzzertype,0        ; normal completion?
  841.     je    buzzerdoit        ; do it
  842.     mov    si,offset buzzer1    ; interrupted task frequency
  843.     cmp    buzzertype,1        ; interrupted task?
  844.     je    buzzerdoit        ; do it
  845.     mov    si,offset buzzer2    ; error condition frequency
  846. buzzerdoit:
  847.     mov    ax,cs:0[si]        ; get the (next) frequency
  848.     mov    bx,cs:2[si]        ; get the (next) delay
  849.     add    si,4            ; get ready for the next tone
  850.     cmp    bx,0            ; are we done?
  851.     je    buzzerreturn        ;  yup.
  852.     push    bx            ; put delay time on the stack
  853.     push    ax            ; put tone value on the stack
  854.     call    far ptr tone        ; do it
  855.     pop    ax            ; restore stack
  856.     pop    bx            ; restore stack
  857.     jmp    short buzzerdoit    ; get the next tone
  858. buzzerreturn:
  859.     ret                ; we done
  860. buzzer    endp
  861.  
  862. ; ***************** Function delay(int delaytime) ************************
  863. ;
  864. ;    performs a delay loop for 'delaytime' milliseconds
  865. ;
  866. ; ************************************************************************
  867.  
  868. delayamillisecond    proc    near    ; internal delay-a-millisecond code
  869.     mov    bx,delaycount        ; set up to burn another millisecond
  870. delayamill1:
  871.     mov    cx,delayloop        ; start up the counter
  872. delayamill2:                ;
  873.     loop    delayamill2        ; burn up some time
  874.     dec    bx            ; have we burned up a millisecond?
  875.     jnz    delayamill1        ;  nope.  try again.
  876.     ret                ; we done
  877. delayamillisecond    endp
  878.  
  879. delay    proc    uses es, delaytime:word ; delay loop (arg in milliseconds)
  880.     mov    ax,delaytime        ; get the number of milliseconds
  881.     cmp    ax,0            ; any delay time at all?
  882.     je    delayreturn        ;  nope.
  883. delayloop1:
  884.     call    delayamillisecond    ; burn up a millisecond of time
  885.     dec    ax            ; have we burned up enough m-seconds?
  886.     jnz    delayloop1        ;  nope.  try again.
  887. delayreturn:
  888.     ret                ; we done.
  889. delay    endp
  890.  
  891. ; ************** Function tone(int frequency,int delaytime) **************
  892. ;
  893. ;    buzzes the speaker with this frequency for this amount of time
  894. ;
  895. ; ************************************************************************
  896.  
  897. tone    proc    uses es, tonefrequency:word, tonedelay:word
  898.     mov    al,0b6h         ; latch to channel 2
  899.     out    43h,al            ;  ...
  900.     cmp    tonefrequency,12h    ; was there a frequency?
  901.     jbe    tonebypass        ;  nope.  delay only
  902.     mov    bx,tonefrequency    ; get the frequency value
  903.     mov    ax,0            ; ugly klooge: convert this to the
  904.     mov    dx,12h            ; divisor the 8253 wants to see
  905.     div    bx            ;  ...
  906.     out    42h,al            ; send the low value
  907.     mov    al,ah            ; then the high value
  908.     out    42h,al            ;  ...
  909.     in    al,61h            ; get the current 8255 bits
  910.     or    al,3            ; turn bits 0 and 1 on
  911.     out    61h,al            ;  ...
  912. tonebypass:
  913.     mov    ax,tonedelay        ; get the delay value
  914.     push    ax            ; set the parameter
  915.     call    far ptr delay        ; and force a delay
  916.     pop    ax            ; restore the parameter
  917.  
  918.     in    al,61h            ; get the current 8255 bits
  919.     and    al,11111100b        ; turn bits 0 and 1 off
  920.     out    61h,al
  921.  
  922.     ret                ; we done
  923. tone    endp
  924.  
  925. ; ************** Function snd(int hertz) and nosnd() **************
  926. ;
  927. ;    turn the speaker on with this frequency (snd) or off (nosnd)
  928. ;
  929. ; *****************************************************************
  930.  
  931. snd    proc    hertz:word        ;Sound the speaker
  932.     cmp    hertz, 20
  933.     jle    hertzbad
  934.     cmp    hertz, 5000
  935.     jge    hertzbad
  936.     mov    ax,0            ;Convert hertz
  937.     mov    dx, 12h         ;for use by routine
  938.     div    hertz
  939.     mov    bx, ax
  940.     mov    al,10110110b        ;Put magic number
  941.     out    43h, al         ;into timer2
  942.     mov    ax, bx            ;Pitch into AX
  943.     out    42h, al         ;LSB into timer2
  944.     mov    al, ah            ;MSB to AL then
  945.     out    42h, al         ;to timer2
  946.     in    al, 61h         ;read I/O port B into AL
  947.     or    al,3            ;turn on bits 0 and 1
  948.     out    61h,al            ;to turn on speaker
  949. hertzbad:
  950.     ret
  951. snd        endp
  952.  
  953. nosnd        proc                ;Turn off speaker
  954.     in    al, 61h         ;Read I/O port B into AL
  955.     and    al, 11111100b        ;mask lower two bits
  956.     out    61h, al         ;to turn off speaker
  957.     ret
  958. nosnd    endp
  959.  
  960.  
  961. ; ****************** Function initasmvars() *****************************
  962.  
  963. initasmvars    proc    uses es si di
  964.  
  965.      cmp    cpu,0            ; have we been called yet:
  966.      je    initasmvarsgo        ;  nope.  proceed.
  967.      jmp    initreturn        ;  yup.  no need to be here.
  968.  
  969. initasmvarsgo:
  970.     mov    ax,ds            ; save the data segment
  971.     mov    _dataseg_xx,ax        ;  for the C code
  972.  
  973.     mov    overflow,0        ; indicate no overflows so far
  974.  
  975.     mov    dx,1            ; ask for 96K of far space
  976.     mov    ax,8000h        ;  ...
  977.     push    dx            ;  ...
  978.     push    ax            ;  ...
  979.     call    far ptr farmemalloc    ; use the assembler routine to do it
  980.     pop    ax            ; restore the stack
  981.     pop    ax            ;  ...
  982.     mov    extraseg,dx        ; save the results here.
  983.  
  984.     call    adapter_init        ; call the video adapter init
  985.  
  986.                        ; first see if a mouse is installed
  987.  
  988.     push    es            ; (no, first check to ensure that
  989.     mov    ax,0            ; int 33h doesn't point to 0:0)
  990.     mov    es,ax            ; ...
  991.     mov    ax,es:0cch        ; ...
  992.     pop    es            ; ...
  993.     cmp    ax,0            ; does int 33h have a non-zero value?
  994.     je    noint33         ;  nope.  then there's no mouse.
  995.  
  996.      xor    ax,ax               ; function for mouse check
  997.      int    33h               ; call mouse driver
  998. noint33:
  999.      mov    mouse,al           ; al holds info about mouse
  1000.  
  1001.                        ; now get the information about the kbd
  1002.      push    es               ; save ES for a tad
  1003.      mov    ax,40h               ; reload ES with BIOS data seg
  1004.      mov    es,ax               ;  ...
  1005.      mov    ah,es:96h           ; get the keyboard byte
  1006.      pop    es               ; restore ES
  1007.      and    ah,10h               ; isolate the Enhanced KBD bit
  1008.      mov    kbd_type,ah           ; and save it
  1009.  
  1010.     call    far ptr cputype     ; what kind of CPU do we have here?
  1011.     cmp    ax,0            ; protected mode of some sort?
  1012.     jge    positive        ;  nope.  proceed.
  1013.     neg    ax            ;  yup.  flip the sign.
  1014. positive:
  1015.     mov    cpu,ax            ; save the cpu type.
  1016. itsa386:
  1017.     cmp    debugflag,8088        ; say, should we pretend it's an 8088?
  1018.     jne    nodebug         ;  nope.
  1019.     mov    cpu,86            ; yup.    use 16-bit emulation.
  1020. nodebug:
  1021.     call far ptr fputype        ; what kind of an FPU do we have?
  1022.     mov    fpu,ax            ;  save the results
  1023.  
  1024.     push    es            ; save ES for a tad
  1025.     mov    ax,0            ; reset ES to BIOS data area
  1026.     mov    es,ax            ;  ...
  1027.     mov    dx,es:46ch        ; obtain the current timer value
  1028.     cmp    cpu,386            ; are we on a 386 or above?
  1029.     jb    delaystartuploop    ;  nope.  don't adjust anything
  1030.     mov    delayloop, 256        ;  yup.  slow down the timer loop
  1031. delaystartuploop:
  1032.     cmp    dx,es:46ch        ; has the timer value changed?
  1033.     je    delaystartuploop    ;  nope.  check again.
  1034.     mov    dx,es:46ch        ; obtain the current timer value again
  1035.     mov    ax,0            ; clear the delay counter
  1036.     mov    delaycount,55        ; 55 millisecs = 1/18.2 secs
  1037. delaytestloop:
  1038.     call    delayamillisecond    ; burn up a (fake) millisecond
  1039.     inc    ax            ; indicate another loop has passed
  1040.     cmp    dx,es:46ch        ; has the timer value changed?
  1041.     je    delaytestloop        ; nope.  burn up some more time.
  1042.     mov    delaycount,ax        ; save the results here
  1043.     pop    es            ; restore ES again
  1044.  
  1045. initreturn:
  1046.      ret                   ; return to caller
  1047. initasmvars endp
  1048.  
  1049.  
  1050. ; New (Apr '90) mouse code by Pieter Branderhorst follows.
  1051. ; The variable lookatmouse controls it all.  Callers of keypressed and
  1052. ; getakey should set lookatmouse to:
  1053. ;      0  ignore the mouse entirely
  1054. ;     <0  only test for left button click; if it occurs return fake key
  1055. ;        number 0-lookatmouse
  1056. ;      1  return enter key for left button, arrow keys for mouse movement,
  1057. ;        mouse sensitivity is suitable for graphics cursor
  1058. ;      2  same as 1 but sensitivity is suitable for text cursor
  1059. ;      3  specials for zoombox, left/right double-clicks generate fake
  1060. ;        keys, mouse movement generates a variety of fake keys
  1061. ;        depending on state of buttons
  1062. ; Mouse movement is accumulated & saved across calls.  Eg if mouse has been
  1063. ; moved up-right quickly, the next few calls to getakey might return:
  1064. ;      right,right,up,right,up
  1065. ; Minor jiggling of the mouse generates no keystroke, and is forgotten (not
  1066. ; accumulated with additional movement) if no additional movement in the
  1067. ; same direction occurs within a short interval.
  1068. ; Movements on angles near horiz/vert are treated as horiz/vert; the nearness
  1069. ; tolerated varies depending on mode.
  1070. ; Any movement not picked up by calling routine within a short time of mouse
  1071. ; stopping is forgotten.  (This does not apply to button pushes in modes<3.)
  1072. ; Mouseread would be more accurate if interrupt-driven, but with the usage
  1073. ; in fractint (tight getakey loops while mouse active) there's no need.
  1074.  
  1075. ; translate table for mouse movement -> fake keys
  1076. mousefkey dw   1077,1075,1080,1072  ; right,left,down,up     just movement
  1077.     dw      0,   0,1081,1073  ;         ,pgdn,pgup  + left button
  1078.     dw    1144,1142,1147,1146  ; kpad+,kpad-,cdel,cins  + rt   button
  1079.     dw    1117,1119,1118,1132  ; ctl-end,home,pgdn,pgup + mid/multi
  1080.  
  1081. DclickTime    equ 9   ; ticks within which 2nd click must occur
  1082. JitterTime    equ 6   ; idle ticks before turfing unreported mickeys
  1083. TextHSens     equ 22  ; horizontal sensitivity in text mode
  1084. TextVSens     equ 44  ; vertical sensitivity in text mode
  1085. GraphSens     equ 5   ; sensitivity in graphics mode; gets lower @ higher res
  1086. ZoomSens      equ 20  ; sensitivity for zoom box sizing/rotation
  1087. TextVHLimit   equ 6   ; treat angles < 1:6  as straight
  1088. GraphVHLimit  equ 14  ; treat angles < 1:14 as straight
  1089. ZoomVHLimit   equ 1   ; treat angles < 1:1  as straight
  1090. JitterMickeys equ 3   ; mickeys to ignore before noticing motion
  1091.  
  1092. mouseread proc near
  1093.     local    moveaxis:word
  1094.  
  1095.     ; check if it is time to do an autosave
  1096.     cmp    saveticks,0    ; autosave timer running?
  1097.     je    mouse0        ;  nope
  1098.     sub    ax,ax        ; reset ES to BIOS data area
  1099.     mov    es,ax        ;  see notes at mouse1 in similar code
  1100. tickread:
  1101.     mov    ax,es:046ch    ; obtain the current timer value
  1102.     cmp    ax,savechktime    ; a new clock tick since last check?
  1103.     je    mouse0        ;  nope, save a dozen opcodes or so
  1104.     mov    dx,es:046eh    ; high word of ticker
  1105.     cmp    ax,es:046ch    ; did a tick get counted just as we looked?
  1106.     jne    tickread    ; yep, reread both words to be safe
  1107.     mov    savechktime,ax
  1108.     sub    ax,savebase    ; calculate ticks since timer started
  1109.     sbb    dx,savebase+2
  1110.     jns    tickcompare
  1111.     add    ax,0b0h     ; wrapped past midnight, add a day
  1112.     adc    dx,018h
  1113. tickcompare:
  1114.     cmp    dx,saveticks+2    ; check if past autosave time
  1115.     jb    mouse0
  1116.     ja    ticksavetime
  1117.     cmp    ax,saveticks
  1118.     jb    mouse0
  1119. ticksavetime:            ; it is time to do a save
  1120.     mov    ax,finishrow
  1121.     cmp    ax,-1        ; waiting for the end of a row before save?
  1122.     jne    tickcheckrow    ;  yup, go check row
  1123.     cmp    calc_status,1    ; safety check, calc active?
  1124.     jne    tickdosave    ;  nope, don't check type of calc
  1125.     cmp    got_status,0    ; 1pass or 2pass?
  1126.     je    ticknoterow    ;  yup
  1127.     cmp    got_status,1    ; solid guessing?
  1128.     jne    tickdosave    ;  not 1pass, 2pass, ssg, so save immediately
  1129. ticknoterow:
  1130.     mov    ax,currow    ; note the current row
  1131.     mov    finishrow,ax    ;  ...
  1132.     jmp    short mouse0    ; and keep working for now
  1133. tickcheckrow:
  1134.     cmp    ax,currow    ; started a new row since timer went off?
  1135.     je    mouse0        ;  nope, don't do the save yet
  1136. tickdosave:
  1137.     mov    timedsave,1    ; tell mainline what's up
  1138.     mov    ax,9999     ; a dummy key value, never gets used
  1139.     jmp    mouseret
  1140.  
  1141. mouse0: ; now the mouse stuff
  1142.     cmp    mouse,-1
  1143.     jne    mouseidle    ; no mouse, that was easy
  1144.     mov    ax,lookatmouse
  1145.     cmp    ax,prevlamouse
  1146.     je    mouse1
  1147.  
  1148.     ; lookatmouse changed, reset everything
  1149.     mov    prevlamouse,ax
  1150.     mov    mbclicks,0
  1151.     mov    mbstatus,0
  1152.     mov    mhmickeys,0
  1153.     mov    mvmickeys,0
  1154.     ; note: don't use int 33 func 0 nor 21 to reset, they're SLOW
  1155.     mov    ax,06h        ; reset button counts by reading them
  1156.     mov    bx,0
  1157.     int    33h
  1158.     mov    ax,06h
  1159.     mov    bx,1
  1160.     int    33h
  1161.     mov    ax,05h
  1162.     mov    bx,0
  1163.     int    33h
  1164.     mov    ax,0Bh        ; reset motion counters by reading
  1165.     int    33h
  1166.     mov    ax,lookatmouse
  1167.  
  1168. mouse1: or    ax,ax
  1169.     jz    mouseidle    ; check nothing when lookatmouse=0
  1170.     ; following code directly accesses bios tick counter; it would be
  1171.     ; better not to rely on addr (use int 1A instead) but old PCs don't
  1172.     ; have the required int, the addr is constant in bios to date, and
  1173.     ; fractint startup already counts on it, so:
  1174.     mov    ax,0        ; reset ES to BIOS data area
  1175.     mov    es,ax        ;  ...
  1176.     mov    dx,es:46ch    ; obtain the current timer value
  1177.     cmp    dx,mousetime
  1178.     ; if timer same as last call, skip int 33s:  reduces expense and gives
  1179.     ; caller a chance to read all pending stuff and paint something
  1180.     jne    mnewtick
  1181.     cmp    lookatmouse,0    ; interested in anything other than left button?
  1182.     jl    mouseidle    ; nope, done
  1183.     jmp    mouse5
  1184.  
  1185. mouseidle:
  1186.     clc            ; tell caller no mouse activity this time
  1187.     ret
  1188.  
  1189. mnewtick: ; new tick, read buttons and motion
  1190.     mov    mousetime,dx    ; note current timer
  1191.     cmp    lookatmouse,3
  1192.     je    mouse2        ; skip button press if mode 3
  1193.  
  1194.     ; check press of left button
  1195.     mov    ax,05h        ; get button press info
  1196.     mov    bx,0        ; for left button
  1197.     int    33h
  1198.     or    bx,bx
  1199.     jnz    mleftb
  1200.     cmp    lookatmouse,0
  1201.     jl    mouseidle    ; exit if nothing but left button matters
  1202.     jmp    mouse3        ; not mode 3 so skip past button release stuff
  1203. mleftb: mov    ax,13
  1204.     cmp    lookatmouse,0
  1205.     jg    mouser        ; return fake key enter
  1206.     mov    ax,lookatmouse    ; return fake key 0-lookatmouse
  1207.     neg    ax
  1208. mouser: jmp    mouseret
  1209.  
  1210. mouse2: ; mode 3, check for double clicks
  1211.     mov    ax,06h        ; get button release info
  1212.     mov    bx,0        ; left button
  1213.     int    33h
  1214.     mov    dx,mousetime
  1215.     cmp    bx,1        ; left button released?
  1216.     jl    msnolb        ; go check timer if not
  1217.     jg    mslbgo        ; double click
  1218.     test    mbclicks,1    ; had a 1st click already?
  1219.     jnz    mslbgo        ; yup, double click
  1220.     mov    mlbtimer,dx    ; note time of 1st click
  1221.     or    mbclicks,1
  1222.     jmp    short mousrb
  1223. mslbgo: and    mbclicks,0ffh-1
  1224.     mov    ax,13        ; fake key enter
  1225.     jmp    mouseret
  1226. msnolb: sub    dx,mlbtimer    ; clear 1st click if its been too long
  1227.     cmp    dx,DclickTime
  1228.     jb    mousrb
  1229.     and    mbclicks,0ffh-1 ; forget 1st click if any
  1230.     ; next all the same code for right button
  1231. mousrb: mov    ax,06h        ; get button release info
  1232.     mov    bx,1        ; right button
  1233.     int    33h
  1234.     ; now much the same as for left
  1235.     mov    dx,mousetime
  1236.     cmp    bx,1
  1237.     jl    msnorb
  1238.     jg    msrbgo
  1239.     test    mbclicks,2
  1240.     jnz    msrbgo
  1241.     mov    mrbtimer,dx
  1242.     or    mbclicks,2
  1243.     jmp    short mouse3
  1244. msrbgo: and    mbclicks,0ffh-2
  1245.     mov    ax,1010     ; fake key ctl-enter
  1246.     jmp    mouseret
  1247. msnorb: sub    dx,mrbtimer
  1248.     cmp    dx,DclickTime
  1249.     jb    mouse3
  1250.     and    mbclicks,0ffh-2
  1251.  
  1252.     ; get buttons state, if any changed reset mickey counters
  1253. mouse3: mov    ax,03h        ; get button status
  1254.     int    33h
  1255.     and    bl,7        ; just the button bits
  1256.     cmp    bl,mbstatus    ; any changed?
  1257.     je    mouse4
  1258.     mov    mbstatus,bl    ; yup, reset stuff
  1259.     mov    mhmickeys,0
  1260.     mov    mvmickeys,0
  1261.     mov    ax,0Bh
  1262.     int    33h        ; reset driver's mickeys by reading them
  1263.  
  1264.     ; get motion counters, forget any jiggle
  1265. mouse4: mov    ax,0Bh        ; get motion counters
  1266.     int    33h
  1267.     mov    bx,mousetime    ; just to have it in a register
  1268.     cmp    cx,0        ; horiz motion?
  1269.     jne    moushm        ; yup, go accum it
  1270.     mov    ax,bx
  1271.     sub    ax,mhtimer
  1272.     cmp    ax,JitterTime    ; timeout since last horiz motion?
  1273.     jb    mousev
  1274.     mov    mhmickeys,0
  1275.     jmp    short mousev
  1276. moushm: mov    mhtimer,bx    ; note time of latest motion
  1277.     add    mhmickeys,cx
  1278.     ; same as above for vertical movement:
  1279. mousev: cmp    dx,0        ; vert motion?
  1280.     jne    mousvm
  1281.     mov    ax,bx
  1282.     sub    ax,mvtimer
  1283.     cmp    ax,JitterTime
  1284.     jb    mouse5
  1285.     mov    mvmickeys,0
  1286.     jmp    short mouse5
  1287. mousvm: mov    mvtimer,bx
  1288.     add    mvmickeys,dx
  1289.  
  1290.     ; pick the axis with largest pending movement
  1291. mouse5: mov    bx,mhmickeys
  1292.     or    bx,bx
  1293.     jns    mchkv
  1294.     neg    bx        ; make it +ve
  1295. mchkv:    mov    cx,mvmickeys
  1296.     or    cx,cx
  1297.     jns    mchkmx
  1298.     neg    cx
  1299. mchkmx: mov    moveaxis,0    ; flag that we're going horiz
  1300.     cmp    bx,cx        ; horiz>=vert?
  1301.     jge    mangle
  1302.     xchg    bx,cx        ; nope, use vert
  1303.     mov    moveaxis,1    ; flag that we're going vert
  1304.  
  1305.     ; if moving nearly horiz/vert, make it exactly horiz/vert
  1306. mangle: mov    ax,TextVHLimit
  1307.     cmp    lookatmouse,2    ; slow (text) mode?
  1308.     je    mangl2
  1309.     mov    ax,GraphVHLimit
  1310.     cmp    lookatmouse,3    ; special mode?
  1311.     jne    mangl2
  1312.     cmp    mbstatus,0    ; yup, any buttons down?
  1313.     je    mangl2
  1314.     mov    ax,ZoomVHLimit    ; yup, special zoom functions
  1315. mangl2: mul    cx        ; smaller axis * limit
  1316.     cmp    ax,bx
  1317.     ja    mchkmv        ; min*ratio <= max?
  1318.     cmp    moveaxis,0    ; yup, clear the smaller movement axis
  1319.     jne    mzeroh
  1320.     mov    mvmickeys,0
  1321.     jmp    short mchkmv
  1322. mzeroh: mov    mhmickeys,0
  1323.  
  1324.     ; pick sensitivity to use
  1325. mchkmv: cmp    lookatmouse,2    ; slow (text) mode?
  1326.     je    mchkmt
  1327.     mov    dx,ZoomSens+JitterMickeys
  1328.     cmp    lookatmouse,3    ; special mode?
  1329.     jne    mchkmg
  1330.     cmp    mbstatus,0    ; yup, any buttons down?
  1331.     jne    mchkm2        ; yup, use zoomsens
  1332. mchkmg: mov    dx,GraphSens
  1333.     mov    cx,sxdots    ; reduce sensitivity for higher res
  1334. mchkg2: cmp    cx,400        ; horiz dots >= 400?
  1335.     jl    mchkg3
  1336.     shr    cx,1        ; horiz/2
  1337.     shr    dx,1
  1338.     inc    dx        ; sensitivity/2+1
  1339.     jmp    short mchkg2
  1340. mchkg3: add    dx,JitterMickeys
  1341.     jmp    short mchkm2
  1342. mchkmt: mov    dx,TextVSens+JitterMickeys
  1343.     cmp    moveaxis,0
  1344.     jne    mchkm2
  1345.     mov    dx,TextHSens+JitterMickeys ; slower on X axis than Y
  1346.  
  1347.     ; is largest movement past threshold?
  1348. mchkm2: cmp    bx,dx
  1349.     jge    mmove
  1350.     jmp    mouseidle    ; no movement past threshold, return nothing
  1351.  
  1352.     ; set bx for right/left/down/up, and reduce the pending mickeys
  1353. mmove:    sub    dx,JitterMickeys
  1354.     cmp    moveaxis,0
  1355.     jne    mmovev
  1356.     cmp    mhmickeys,0
  1357.     jl    mmovh2
  1358.     sub    mhmickeys,dx    ; horiz, right
  1359.     mov    bx,0
  1360.     jmp    short mmoveb
  1361. mmovh2: add    mhmickeys,dx    ; horiz, left
  1362.     mov    bx,2
  1363.     jmp    short mmoveb
  1364. mmovev: cmp    mvmickeys,0
  1365.     jl    mmovv2
  1366.     sub    mvmickeys,dx    ; vert, down
  1367.     mov    bx,4
  1368.     jmp    short mmoveb
  1369. mmovv2: add    mvmickeys,dx    ; vert, up
  1370.     mov    bx,6
  1371.  
  1372.     ; modify bx if a button is being held down
  1373. mmoveb: cmp    lookatmouse,3
  1374.     jne    mmovek        ; only modify in mode 3
  1375.     cmp    mbstatus,1
  1376.     jne    mmovb2
  1377.     add    bx,8        ; modify by left button
  1378.     jmp    short mmovek
  1379. mmovb2: cmp    mbstatus,2
  1380.     jne    mmovb3
  1381.     add    bx,16        ; modify by rb
  1382.     jmp    short mmovek
  1383. mmovb3: cmp    mbstatus,0
  1384.     je    mmovek
  1385.     add    bx,24        ; modify by middle or multiple
  1386.  
  1387.     ; finally, get the fake key number
  1388. mmovek: mov    ax,mousefkey[bx]
  1389.  
  1390. mouseret:
  1391.     stc
  1392.     ret
  1393. mouseread endp
  1394.  
  1395.  
  1396. ; long readticker() returns current bios ticker value
  1397.  
  1398. readticker proc uses es
  1399.     sub    ax,ax        ; reset ES to BIOS data area
  1400.     mov    es,ax        ;  see notes at mouse1 in similar code
  1401. tickread:
  1402.     mov    ax,es:046ch    ; obtain the current timer value
  1403.     mov    dx,es:046eh    ; high word of ticker
  1404.     cmp    ax,es:046ch    ; did a tick get counted just as we looked?
  1405.     jne    tickread    ; yep, reread both words to be safe
  1406.     ret
  1407. readticker endp
  1408.  
  1409.  
  1410. ;===============================================================
  1411. ;
  1412. ; CPUTYPE.ASM : C-callable functions cputype() and ndptype() adapted
  1413. ; by Lee Daniel Crocker from code appearing in the late PC Tech Journal,
  1414. ; August 1987 and November 1987.  PC Tech Journal was a Ziff-Davis
  1415. ; Publication.    Code herein is copyrighted and used with permission.
  1416. ;
  1417. ; The function cputype() returns an integer value based on what kind
  1418. ; of CPU it found, as follows:
  1419. ;
  1420. ;    Value    CPU Type
  1421. ;    =====    ========
  1422. ;    86    8086, 8088, V20, or V30
  1423. ;    186    80186 or 80188
  1424. ;    286    80286
  1425. ;    386    80386 or 80386sx
  1426. ;    -286    80286 in protected mode
  1427. ;    -386    80386 or 80386sx in protected or 32-bit address mode
  1428. ;
  1429. ; The function ndptype() returns an integer based on the type of NDP
  1430. ; it found, as follows:
  1431. ;
  1432. ;    Value    NDP Type
  1433. ;    =====    ========
  1434. ;    0    No NDP found
  1435. ;    87    8087
  1436. ;    287    80287
  1437. ;    387    80387
  1438. ;
  1439. ; No provisions are made for the 80486 CPU/FPU or Weitek FPA chips.
  1440. ;
  1441. ; Neither function takes any arguments or affects any external storage,
  1442. ; so there should be no memory-model dependencies.
  1443.  
  1444. .model medium, c
  1445. .286P
  1446. .code
  1447.  
  1448. cputype proc
  1449.     push    bp
  1450.  
  1451.     push    sp            ; 86/186 will push SP-2;
  1452.     pop    ax            ; 286/386 will push SP.
  1453.     cmp    ax, sp
  1454.     jz    not86            ; If equal, SP was pushed
  1455.     mov    ax, 186
  1456.     mov    cl, 32            ;   186 uses count mod 32 = 0;
  1457.     shl    ax, cl            ;   86 shifts 32 so ax = 0
  1458.     jnz    exit            ; Non-zero: no shift, so 186
  1459.     mov    ax, 86            ; Zero: shifted out all bits
  1460.     jmp    short exit
  1461. not86:
  1462.     pushf                ; Test 16 or 32 operand size:
  1463.     mov    ax, sp            ;   Pushed 2 or 4 bytes of flags?
  1464.     popf
  1465.     inc    ax
  1466.     inc    ax
  1467.     cmp    ax, sp            ;   Did pushf change SP by 2?
  1468.     jnz    is32bit         ;   If not, then 4 bytes of flags
  1469. is16bit:
  1470.     sub    sp, 6            ; Is it 286 or 386 in 16-bit mode?
  1471.     mov    bp, sp            ; Allocate stack space for GDT pointer
  1472.     sgdt    fword ptr [bp]
  1473.     add    sp, 4            ; Discard 2 words of GDT pointer
  1474.     pop    ax            ; Get third word
  1475.     inc    ah            ; 286 stores -1, 386 stores 0 or 1
  1476.     jnz    is386
  1477. is286:
  1478.     mov    ax, 286
  1479.     jmp    short testprot        ; Check for protected mode
  1480. is32bit:
  1481.     db    66h            ; 16-bit override in 32-bit mode
  1482. is386:
  1483.     mov    ax, 386
  1484. testprot:
  1485.     smsw    cx            ; Protected?  Machine status -> CX
  1486.     ror    cx,1            ; Protection bit -> carry flag
  1487.     jnc    exit            ; Real mode if no carry
  1488.     neg    ax            ; Protected:  return neg value
  1489. exit:
  1490.     pop    bp
  1491.     ret
  1492. cputype endp
  1493.  
  1494. .data
  1495.  
  1496. control dw    0            ; Temp storage for 8087 control
  1497.                     ;   and status registers
  1498. .code
  1499.  
  1500. fputype proc
  1501.     push    bp
  1502.  
  1503.     fninit                ; Defaults to 64-bit mantissa
  1504.     mov    byte ptr control+1, 0
  1505.     fnstcw    control         ; Store control word over 0
  1506. ;    dw    3ed9h            ; (klooge to avoid the MASM \e switch)
  1507. ;    dw    offset control        ; ((equates to the above 'fnstcw' cmd))
  1508.     mov    ah, byte ptr control+1    ; Test contents of byte written
  1509.     cmp    ah, 03h         ; Test for 64-bit precision flags
  1510.     je    gotone            ; Got one!  Now let's find which
  1511.     xor    ax, ax
  1512.     jmp    short fexit        ; No NDP found
  1513. gotone:
  1514.     and    control, not 0080h    ; IEM = 0 (interrupts on)
  1515.     fldcw    control
  1516.     fdisi                ; Disable ints; 287/387 will ignore
  1517.     fstcw    control
  1518.     test    control, 0080h
  1519.     jz    not87            ; Got 287/387; keep testing
  1520.     mov    ax, 87
  1521.     jmp    short freset
  1522. not87:
  1523.     finit
  1524.     fld1
  1525.     fldz
  1526.     fdiv                ; Divide 1/0 to create infinity
  1527.     fld    st
  1528.     fchs                ; Push -infinity on stack
  1529.     fcompp                ; Compare +-infinity
  1530.     fstsw    control
  1531.     mov    ax, control
  1532.     sahf
  1533.     jnz    got387            ; 387 will compare correctly
  1534.     mov    ax, 287
  1535.     jmp    short freset
  1536. got387:                 ; Only one left (until 487/Weitek
  1537.     mov    ax, 387         ;   test is added)
  1538. freset:
  1539.     fninit                ; in case tests have had strange
  1540.     finit                ; side-effects, reset
  1541. fexit:
  1542.     pop    bp
  1543.     ret
  1544. fputype endp
  1545.  
  1546. ; ************************* Far Segment RAM Support **************************
  1547. ;
  1548. ;
  1549. ;    farptr = (char far *)farmemalloc(long bytestoalloc);
  1550. ;    (void)farmemfree(farptr);
  1551. ;
  1552. ;    alternatives to Microsoft/TurboC routines
  1553. ;
  1554. ;
  1555. .8086
  1556.  
  1557. farmemalloc    proc    uses es, bytestoallocate:dword
  1558.     les    bx,bytestoallocate    ; get the # of bytes into DX:BX
  1559.     mov    dx,es            ;  ...
  1560.     add    bx,15            ; round up to next paragraph boundary
  1561.     adc    dx,0            ;  ...
  1562.     shr    dx,1            ; convert to paragraphs
  1563.     rcr    bx,1            ;  ...
  1564.     shr    dx,1            ;  ...
  1565.     rcr    bx,1            ;  ...
  1566.     shr    dx,1            ;  ...
  1567.     rcr    bx,1            ;  ...
  1568.     shr    dx,1            ;  ...
  1569.     rcr    bx,1            ;  ...
  1570.     cmp    dx,0            ; ensure that we don't want > 1MB
  1571.     jne    farmemallocfailed    ;  bail out if we do
  1572.     mov    ah,48h            ; invoke DOS to allocate memory
  1573.     int    21h            ;  ...
  1574.     jc    farmemallocfailed    ; bail out on failure
  1575.     mov    dx,ax            ; set up DX:AX as far address
  1576.     mov    ax,0            ;  ...
  1577.     jmp    short farmemallocreturn ; and return
  1578. farmemallocfailed:
  1579.     mov    ax,0            ; (load up with a failed response)
  1580.     mov    dx,0            ;  ...
  1581. farmemallocreturn:
  1582.     ret                ; we done.
  1583. farmemalloc    endp
  1584.  
  1585. farmemfree    proc    uses es, farptr:dword
  1586.     les    ax,farptr        ; get the segment into ES
  1587.     mov    ah,49h            ; invoke DOS to free the segment
  1588.     int    21h            ;  ...
  1589.     ret
  1590. farmemfree    endp
  1591.  
  1592. erasesegment    proc    uses es di si, segaddress:word, segvalue:word
  1593.     mov    ax,segaddress        ; load up the segment address
  1594.     mov    es,ax            ;  ...
  1595.     mov    di,0            ; start at the beginning
  1596.     mov    ax,segvalue        ; use this value
  1597.     mov    cx,8000h        ; over the entire segment
  1598.     repnz    stosw            ; do it
  1599.     ret                ; we done
  1600. erasesegment    endp
  1601.  
  1602.  
  1603. farread proc uses ds, handle:word, buf:dword, len:word
  1604.     mov    ah, 03Fh
  1605.     mov    bx, [handle]
  1606.     mov    cx, [len]
  1607.     lds    dx, [buf]
  1608.     int    21h
  1609.     jnc    farreaddone
  1610.     mov    ax, -1
  1611. farreaddone:
  1612.     ret
  1613. farread endp
  1614.  
  1615.  
  1616. farwrite proc uses ds, handle:word, buf:dword, len:word
  1617.     mov    ah, 040h
  1618.     mov    bx, [handle]
  1619.     mov    cx, [len]
  1620.     lds    dx, [buf]
  1621.     int    21h
  1622.     jnc    farwritedone
  1623.     mov    ax, -1
  1624. farwritedone:
  1625.     ret
  1626. farwrite endp
  1627.  
  1628.  
  1629. ; Convert segment:offset to equiv pointer with minimum possible offset
  1630. normalize proc p: dword
  1631. ;    mov    ax, [word ptr p]
  1632. ;    mov    dx, [word ptr p+2]
  1633.     les    ax, p
  1634.     mov    dx, es
  1635.     mov    bx, ax
  1636.     shr    bx, 1
  1637.     shr    bx, 1
  1638.     shr    bx, 1
  1639.     shr    bx, 1
  1640.     and    ax, 0Fh
  1641.     add    dx, bx
  1642.     ret
  1643. normalize endp
  1644.  
  1645.  
  1646. ; *************** Far string/memory functions *********
  1647. ;    far_strlen ( char far *);
  1648. ;    far_strcpy ( char far *, char far *);
  1649. ;    far_strcmp ( char far *, char far *);
  1650. ;    far_stricmp( char far *, char far *);
  1651. ;    far_strnicmp(char far *, char far *, int);
  1652. ;    far_strcat ( char far *, char far *);
  1653. ;    far_memset ( char far *, char far,   int);
  1654. ;    far_memcpy ( char far *, char far *, int);
  1655. ;    far_memcmp ( char far *, char far *, int);
  1656. ;    far_memicmp( char far *, char far *, int);
  1657.  
  1658. ;    xxxfar_routines are called internally with:
  1659. ;        ds:si pointing to the source
  1660. ;        es:di pointing to the destination
  1661. ;        cx    containing a byte count
  1662. ;        al    contining  a character (set) value
  1663. ;        (and they destroy registers willy-nilly)
  1664.  
  1665. xxxfar_memlen    proc    near    ; return string length - INCLUDING the 0
  1666.     mov    ax,0
  1667.     mov    cx,1024
  1668.     repne    scasb
  1669.     sub    cx,1024
  1670.     neg    cx
  1671.     ret
  1672. xxxfar_memlen    endp
  1673.  
  1674. xxxfar_memcmp    proc    near    ; compare two strings - length in CX
  1675.     mov    ax,0
  1676.     rep    cmpsb
  1677.     jz    wedone
  1678.     mov    ax,1
  1679. wedone: ret
  1680. xxxfar_memcmp    endp
  1681.  
  1682. xxxfar_memicmp    proc    near    ; compare two caseless strings - length in CX
  1683.     mov    ax,0
  1684.     cmp    cx,0
  1685.     je    wedone
  1686.     dec    si
  1687.     dec    di
  1688. loop1:    inc    si
  1689.     inc    di
  1690.     mov    al,es:[di]
  1691.     mov    ah,ds:[si]
  1692.     cmp    al,ah
  1693.     je    loop2
  1694.     cmp    al,'A'
  1695.     jb    lower1
  1696.     cmp    al,'Z'
  1697.     ja    lower1
  1698.     add    al,20h
  1699. lower1: cmp    ah,'A'
  1700.     jb    lower2
  1701.     cmp    ah,'Z'
  1702.     ja    lower2
  1703.     add    ah,20h
  1704. lower2: cmp    al,ah
  1705.     jne    uneql
  1706. loop2:    loop    loop1
  1707.     mov    ax,0
  1708.     jmp    short wedone
  1709. uneql:    mov    ax,1
  1710. wedone: ret
  1711. xxxfar_memicmp    endp
  1712.  
  1713.  
  1714. far_strlen proc uses ds es di si, fromaddr:dword
  1715.     les    di,fromaddr        ; point to start-of-string
  1716.     call    xxxfar_memlen        ; find the string length
  1717.     mov    ax,cx            ; return len
  1718.     dec    ax            ; don't count null
  1719.     ret                ; we done.
  1720. far_strlen endp
  1721.  
  1722. far_strnicmp proc    uses ds es di si, toaddr:dword, fromaddr:dword, len:word
  1723.     les    di,fromaddr        ; point to start-of-string
  1724.     call    xxxfar_memlen        ; find the string length
  1725.     cmp    cx,len            ; source less than or equal to len?
  1726.     jle    cxbigger        ; yup - use cx
  1727.     mov    cx,len            ; nope - use len
  1728. cxbigger:
  1729.     les    di,toaddr        ; get the dest string
  1730.     lds    si,fromaddr        ; get the source string
  1731.     call    xxxfar_memicmp        ; compare them
  1732.     ret                ; we done.
  1733. far_strnicmp endp
  1734.  
  1735. far_strcpy proc uses ds es di si, toaddr:dword, fromaddr:dword
  1736.     les    di,fromaddr        ; point to start-of-string
  1737.     call    xxxfar_memlen        ; find the string length
  1738.     les    di,toaddr        ; now move to here
  1739.     lds    si,fromaddr        ; from here
  1740.     rep    movsb            ; move them
  1741.     ret                ; we done.
  1742. far_strcpy endp
  1743.  
  1744. far_strcmp proc uses ds es di si, toaddr:dword, fromaddr:dword
  1745.     les    di,fromaddr        ; point to start-of-string
  1746.     call    xxxfar_memlen        ; find the string length
  1747.     les    di,toaddr        ; now compare to here
  1748.     lds    si,fromaddr        ; compare here
  1749.     call    xxxfar_memcmp        ; compare them
  1750.     ret                ; we done.
  1751. far_strcmp endp
  1752.  
  1753. far_stricmp proc    uses ds es di si, toaddr:dword, fromaddr:dword
  1754.     les    di,fromaddr        ; point to start-of-string
  1755.     call    xxxfar_memlen        ; find the string length
  1756.     les    di,toaddr        ; get the dest string
  1757.     lds    si,fromaddr        ; get the source string
  1758.     call    xxxfar_memicmp        ; compare them
  1759.     ret                ; we done.
  1760. far_stricmp endp
  1761.  
  1762. far_strcat proc uses ds es di si, toaddr:dword, fromaddr:dword
  1763.     les    di,fromaddr        ; point to start-of-string
  1764.     call    xxxfar_memlen        ; find the string length
  1765.     push    cx            ; save it
  1766.     les    di,toaddr        ; point to start-of-string
  1767.     call    xxxfar_memlen        ; find the string length
  1768.     les    di,toaddr        ; now move to here
  1769.     add    di,cx            ; but start at the end of string
  1770.     dec    di            ; (less the EOS zero)
  1771.     lds    si,fromaddr        ; from here
  1772.     pop    cx            ; get the string length
  1773.     rep    movsb            ; move them
  1774.     ret                ; we done.
  1775. far_strcat endp
  1776.  
  1777. far_memset proc uses es di, toaddr:dword, fromvalue:byte, slength:word
  1778.     mov    al,fromvalue        ; get the value to store
  1779.     mov    cx,slength        ; get the store length
  1780.     les    di,toaddr        ; now move to here
  1781.     rep    stosb            ; store them
  1782.     ret                ; we done.
  1783. far_memset endp
  1784.  
  1785. far_memcpy proc uses ds es di si, toaddr:dword, fromaddr:dword, slength:word
  1786.     mov    cx,slength        ; get the move length
  1787.     les    di,toaddr        ; now move to here
  1788.     lds    si,fromaddr        ; from here
  1789.     rep    movsb            ; move them
  1790.     ret                ; we done.
  1791. far_memcpy endp
  1792.  
  1793. far_memcmp proc uses ds es di si, toaddr:dword, fromaddr:dword, slength:word
  1794.     mov    cx,slength        ; get the compare length
  1795.     les    di,toaddr        ; now compare to here
  1796.     lds    si,fromaddr        ; compare here
  1797.     call    xxxfar_memcmp        ; compare them
  1798.     ret                ; we done.
  1799. far_memcmp endp
  1800.  
  1801. far_memicmp proc uses ds es di si, toaddr:dword, fromaddr:dword, slength:word
  1802.     mov    cx,slength        ; get the compare length
  1803.     les    di,toaddr        ; get the dest string
  1804.     lds    si,fromaddr        ; get the source string
  1805.     call    xxxfar_memicmp        ; compare them
  1806.     ret                ; we done.
  1807. far_memicmp endp
  1808.  
  1809. disable proc                ; disable interrupts
  1810.     cli
  1811.     ret
  1812. disable endp
  1813.  
  1814. enable    proc                ; re-enable interrupts
  1815.     sti
  1816.     ret
  1817. enable    endp
  1818.  
  1819. ; *************** Expanded Memory Manager Support Routines ******************
  1820. ;        for use with LIM 3.2 or 4.0 Expanded Memory
  1821. ;
  1822. ;    farptr = emmquery()    ; Query presence of EMM and initialize EMM code
  1823. ;                ; returns EMM FAR Address, or 0 if no EMM
  1824. ;    freepages = emmgetfree(); Returns the number of pages (1 page = 16K)
  1825. ;                ; not already allocated for something else
  1826. ;    handle = emmallocate(pages)    ; allocate EMM pages (1 page = 16K)
  1827. ;                ; returns handle # if OK, or else 0
  1828. ;    emmdeallocate(handle)    ; return EMM pages to system - MUST BE CALLED
  1829. ;                ; or allocated EMM memory fills up
  1830. ;    emmgetpage(page, handle); get an EMM page (actually, links the EMM
  1831. ;                ; page to the EMM Segment ADDR, saving any
  1832. ;                ; prior page in the process)
  1833. ;    emmclearpage(page, handle) ; performs an 'emmgetpage()' and then clears
  1834. ;                ; it out (quickly) to zeroes with a 'REP STOSW'
  1835.  
  1836. .MODEL    medium,c
  1837.  
  1838. .8086
  1839.  
  1840. .DATA
  1841.  
  1842. emm_name    db    'EMMXXXX0',0    ; device driver for EMM
  1843. emm_segment    dw    0        ; EMM page frame segment
  1844. emm_zeroflag    db    0        ; klooge flag for handle==0
  1845.  
  1846. .CODE
  1847.  
  1848. emmquery    proc
  1849.     mov    ah,3dh            ; function 3dh = open file
  1850.     mov    al,0            ;  read only
  1851.     mov    dx,offset emm_name    ; DS:DX = address of name of EMM
  1852.     int    21h            ; open it
  1853.     jc    emmqueryfailed        ;  oops.  no EMM.
  1854.  
  1855.     mov    bx,ax            ; BX = handle for EMM
  1856.     mov    ah,44h            ; function 44h = IOCTL
  1857.     mov    al,7            ; get outo. status
  1858.     mov    cx,0            ; CX = # of bytes to read
  1859.     int    21h            ; do it.
  1860.     push    ax            ; save the IOCTL handle.
  1861.  
  1862.     mov    ah,3eh            ; function 3H = close
  1863.     int    21h            ; BX still cintains handle
  1864.     pop    ax            ; restore AX for the status query
  1865.     jc    emmqueryfailed        ; huh?    close FAILED?
  1866.  
  1867.     or    al,al            ; was the status 0?
  1868.     jz    emmqueryfailed        ; well then, it wasn't EMM!
  1869.  
  1870.     mov    ah,40h            ; query EMM: hardware ok?
  1871.     int    67h            ; EMM call
  1872.     cmp    ah,0            ; is it ok?
  1873.     jne    emmqueryfailed        ; if not, fail
  1874.  
  1875.     mov    ah,41h            ; query EMM: Get Page Frame Segment
  1876.     int    67h            ; EMM call
  1877.     cmp    ah,0            ; is it ok?
  1878.     jne    emmqueryfailed        ; if not, fail
  1879.     mov    emm_segment,bx        ; save page frame segment
  1880.     mov    dx,bx            ; return page frame address
  1881.     mov    ax,0            ;  ...
  1882.     jmp    short    emmqueryreturn    ; we done.
  1883.  
  1884. emmqueryfailed:
  1885.     mov    ax,0            ; return 0 (no EMM found)
  1886.     mov    dx,0            ;  ...
  1887. emmqueryreturn:
  1888.     ret                ; we done.
  1889. emmquery    endp
  1890.  
  1891. emmgetfree    proc            ; get # of free EMM pages
  1892.     mov    ah,42h            ; EMM call: get total and free pages
  1893.     int    67h            ; EMM call
  1894.     cmp    ah,0            ; did we suceed?
  1895.     jne    emmgetfreefailed    ;  nope.  return 0 free pages
  1896.     mov    ax,bx            ; else return # of free pages
  1897.     jmp    emmgetfreereturn    ; we done.
  1898. emmgetfreefailed:
  1899.     mov    ax,0            ; failure mode
  1900. emmgetfreereturn:
  1901.     ret                ; we done
  1902. emmgetfree    endp
  1903.  
  1904. emmallocate    proc    pages:word    ; allocate EMM pages
  1905.     mov    bx,pages        ; BX = # of 16K pages
  1906.     mov    ah,43h            ; ask for the memory
  1907.     int    67h            ; EMM call
  1908.     mov    emm_zeroflag,0        ; clear the klooge flag
  1909.     cmp    ah,0            ; did the call work?
  1910.     jne    emmallocatebad        ;  nope.
  1911.     mov    ax,dx            ; yup.    save the handle here
  1912.     cmp    ax,0            ; was the handle a zero?
  1913.     jne    emmallocatereturn    ;  yup.  no kloogy fixes
  1914.     mov    emm_zeroflag,1        ; oops.  set an internal flag
  1915.     mov    ax,1234         ; and make up a dummy handle.
  1916.     jmp    short    emmallocatereturn ; and return
  1917. emmallocatebad:
  1918.     mov    ax,0            ; indicate no handle
  1919. emmallocatereturn:
  1920.     ret                ; we done.
  1921. emmallocate    endp
  1922.  
  1923. emmdeallocate    proc    emm_handle:word ; De-allocate EMM memory
  1924. emmdeallocatestart:
  1925.     mov    dx,emm_handle        ; get the EMM handle
  1926.     cmp    dx,1234         ; was it our special klooge value?
  1927.     jne    emmdeallocatecontinue    ;  nope.  proceed.
  1928.     cmp    emm_zeroflag,1        ; was it really a zero handle?
  1929.     jne    emmdeallocatecontinue    ;  nope.  proceed.
  1930.     mov    dx,0            ; yup.    use zero instead.
  1931. emmdeallocatecontinue:
  1932.     mov    ah,45h            ; EMM function: deallocate
  1933.     int    67h            ; EMM call
  1934.     cmp    ah,0            ; did it work?
  1935.     jne    emmdeallocatestart    ; well then, try it again!
  1936. emmdeallocatereturn:
  1937.     ret                ; we done
  1938. emmdeallocate    endp
  1939.  
  1940. emmgetpage    proc    pagenum:word, emm_handle:word    ; get EMM page
  1941.     mov    bx,pagenum        ; BX = page numper
  1942.     mov    dx,emm_handle        ; DX = EMM handle
  1943.     cmp    dx,1234         ; was it our special klooge value?
  1944.     jne    emmgetpagecontinue    ;  nope.  proceed.
  1945.     cmp    emm_zeroflag,1        ; was it really a zero handle?
  1946.     jne    emmgetpagecontinue    ;  nope.  proceed.
  1947.     mov    dx,0            ; yup.    use zero instead.
  1948. emmgetpagecontinue:
  1949.     mov    ah,44h            ; EMM call: get page
  1950.     mov    al,0            ; get it into page 0
  1951.     int    67h            ; EMM call
  1952.     ret                ; we done
  1953. emmgetpage    endp
  1954.  
  1955. emmclearpage    proc    pagenum:word, emm_handle:word    ; clear EMM page
  1956.     mov    bx,pagenum        ; BX = page numper
  1957.     mov    dx,emm_handle        ; DX = EMM handle
  1958.     cmp    dx,1234         ; was it our special klooge value?
  1959.     jne    emmclearpagecontinue    ;  nope.  proceed.
  1960.     cmp    emm_zeroflag,1        ; was it really a zero handle?
  1961.     jne    emmclearpagecontinue    ;  nope.  proceed.
  1962.     mov    dx,0            ; yup.    use zero instead.
  1963. emmclearpagecontinue:
  1964.     mov    ah,44h            ; EMM call: get page
  1965.     mov    al,0            ; get it into page 0
  1966.     int    67h            ; EMM call
  1967.     mov    ax,emm_segment        ; get EMM segment into ES
  1968.     push    es            ;  ...
  1969.     mov    es,ax            ;  ...
  1970.     mov    di,0            ; start at offset 0
  1971.     mov    cx,8192         ; for 16K (in words)
  1972.     mov    ax,0            ; clear out EMM segment to zeroes
  1973.     rep    stosw            ; clear the page
  1974.     pop    es            ; restore ES
  1975.     ret                ; we done
  1976. emmclearpage    endp
  1977.  
  1978. ; *************** Extended Memory Manager Support Routines ******************
  1979. ;          for use XMS 2.0 and later Extended Memory
  1980. ;
  1981. ;    xmmquery()        ; Query presence of XMM and initialize XMM code
  1982. ;                ; returns 0 if no XMM
  1983. ;    handle = xmmallocate(Kbytes)    ; allocate XMM block in Kbytes
  1984. ;                ; returns handle # if OK, or else 0
  1985. ;    xmmdeallocate(handle)    ; return XMM block to system - MUST BE CALLED
  1986. ;                ; or allocated XMM memory is not released.
  1987. ;    xmmmoveextended(&MoveStruct)    ; Moves a block of memory to or
  1988. ;                ; from extended memory.  Returns 1 if OK
  1989. ;                ; else returns 0.
  1990. ; The structure format for use with xmmoveextended is:
  1991. ;
  1992. ;    ASM             |        C
  1993. ;--------------------------------+----------------------------------------
  1994. ; XMM_Move    struc         |    struct XMM_Move
  1995. ;                 |    {
  1996. ;   Length      dd ?         |        unsigned long   Length;
  1997. ;   SourceHandle  dw ?         |        unsigned int    SourceHandle;
  1998. ;   SourceOffset  dd ?         |        unsigned long   SourceOffset;
  1999. ;   DestHandle      dw ?         |        unsigned int    DestHandle;
  2000. ;   DestOffset      dd ?         |        unsigned long   DestOffset;
  2001. ; XMM_Move    ends         |    };
  2002. ;                 |
  2003. ;
  2004. ; Please refer to XMS spec version 2.0 for further information.
  2005.  
  2006. .model medium,c
  2007.  
  2008. .data
  2009. xmscontrol    dd dword ptr (?) ; Address of driver's control function
  2010.  
  2011. .code
  2012. xmmquery    proc
  2013.     mov    ax,4300h        ; Is an XMS driver installed?
  2014.     int    2fh
  2015.     cmp    al, 80h         ; Did it succeed?
  2016.     jne    xmmqueryfailed        ; No
  2017.     mov    ax,4310h        ; Get control function address
  2018.     int    2fh
  2019.     mov    word ptr [xmscontrol], bx   ; Put address in xmscontrol
  2020.     mov    word ptr [xmscontrol+2], es ; ...
  2021.     mov    ah,00h            ; Get version number
  2022.     call [xmscontrol]
  2023.     cmp    ax,0200h        ; Is 2.00 or higher?
  2024.     jge  xmmquerydone        ; Yes
  2025. xmmqueryfailed:
  2026.     mov    ax,0            ; return failure
  2027.     mov    dx,0
  2028. xmmquerydone:
  2029.     ret
  2030. xmmquery    endp
  2031.  
  2032. xmmallocate    proc    ksize:word
  2033.     mov    ah,09h            ; Allocate extended memory block
  2034.     mov    dx,ksize        ; size of block in Kbytes
  2035.     call    [xmscontrol]
  2036.     cmp    ax,0001h        ; did it succeed?
  2037.     jne    xmmallocatefail     ; nope
  2038.     mov    ax,dx            ; Put handle here
  2039.     jmp    short xmmallocatedone
  2040. xmmallocatefail:
  2041.     mov    ax, 0            ; Indicate failure;
  2042. xmmallocatedone:
  2043.     ret
  2044. xmmallocate    endp
  2045.  
  2046. xmmdeallocate    proc    xmm_handle:word
  2047.     mov    ah,0ah            ; Deallocate extended memory block
  2048.     mov    dx, xmm_handle        ; Give it handle
  2049.     call    [xmscontrol]
  2050.     ret
  2051. xmmdeallocate    endp
  2052.  
  2053. xmmmoveextended proc uses si, MoveStruct:word
  2054.  
  2055.     ; Call the XMS MoveExtended function.
  2056.     mov    ah,0Bh
  2057.     mov    si,MoveStruct            ; the move structure.
  2058.     call    [xmscontrol]            ;
  2059.  
  2060. ; The call to xmscontrol returns a 1 in AX if successful, 0 otherwise.
  2061.  
  2062.     ret
  2063.  
  2064. xmmmoveextended endp
  2065.  
  2066.  
  2067. ; ********************* IIT FPU Chip Support Routines ******************
  2068. ;             for use with 2C87 and 3C87 FPU chips
  2069. ;
  2070. ; load_mat(double matrix[16])     ; Load a 4x4 matrix of doubles into IIT
  2071. ;                 ; IIT registers
  2072. ;
  2073. ; mult_vec_iit(double vector[3]) ; Multiply matrix times vector. Routine
  2074. ;                 ; is not completely general - makes use of
  2075. ;                 ; the fact that Fractint 3D vectors always
  2076. ;                 ; have a fourth component of 1. Only three
  2077. ;                 ; array elements are actually accessed.
  2078. ;                 ; Source and target vectors are the same.
  2079. ;
  2080. ; IITCoPro()             ; Detect IIT chip - return 1. Do not call
  2081. ;                 ; unless at least a 287 already detected.
  2082. ;
  2083. ; Code adapted by Tim Wegner from IIT documentation and detect routine
  2084. ; sent by Jonathan Osuch and modified by Charles Marslett -- 01/29/91
  2085. ;
  2086. ; The following routines were provided by IIT to implement a semaphore
  2087. ; system to protect the IIT extra registers from multi-tasking:
  2088. ;
  2089. ; F4x4Check()              ; returns 1 if semaphore TSR loaded           
  2090. ; F4x4Lock()             ; returns 1 if semaphore free and locks
  2091. ; F4x4Free()             ; frees locked semaphore
  2092.  
  2093. .286
  2094. .287
  2095. .data
  2096. one dq 1.0
  2097. .code
  2098. ;
  2099. ;load_mat(double *array)
  2100. ;
  2101. load_mat proc    array:WORD
  2102.     finit
  2103.     db    0DBh,0EBh    ; select register set 0
  2104.     fwait
  2105.     mov    bx, array
  2106.     fld    QWORD PTR [bx+64 ] ; load row 3
  2107.     fld    QWORD PTR [bx+72 ]
  2108.     fld    QWORD PTR [bx+80 ]
  2109.     fld    QWORD PTR [bx+88 ]
  2110.     fld    QWORD PTR [bx+96 ] ; load row 4
  2111.     fld    QWORD PTR [bx+104]
  2112.     fld    QWORD PTR [bx+112]
  2113.     fld    QWORD PTR [bx+120]
  2114.  
  2115.     finit
  2116.     db    0DBh,0EAh    ; select register set 1
  2117.     fld    QWORD PTR [bx+0 ] ; load row 1
  2118.     fld    QWORD PTR [bx+8 ]
  2119.     fld    QWORD PTR [bx+16]
  2120.     fld    QWORD PTR [bx+24]
  2121.     fld    QWORD PTR [bx+32] ; load row 2
  2122.     fld    QWORD PTR [bx+40]
  2123.     fld    QWORD PTR [bx+48]
  2124.     fld    QWORD PTR [bx+56]
  2125.  
  2126.     finit
  2127.     db    0DBh,0E8h    ; select register set 0
  2128.     fwait
  2129.     ret
  2130. load_mat endp
  2131.  
  2132. .code
  2133. ;
  2134. ;mult_vec_iit(vector)
  2135. ;
  2136. mult_vec_iit proc uses bx, vector:WORD
  2137.     mov    bx,vector
  2138.     fld    one ; last component always 1 in fractint
  2139. ;    fld    QWORD PTR [bx+24 ] ; 4
  2140.     fld    QWORD PTR [bx+16 ] ; 3
  2141.     fld    QWORD PTR [bx+8  ] ; 2
  2142.     fld    QWORD PTR [bx+0  ] ; 1
  2143.  
  2144.     db    0DBh,0F1h    ; multiply the column vector
  2145.     fwait
  2146.     fstp    QWORD PTR [bx+0  ] ; 1
  2147.     fstp    QWORD PTR [bx+8  ] ; 2
  2148.     fstp    QWORD PTR [bx+16 ] ; 3
  2149. ;    fstp    QWORD PTR [bx+24 ] ; 4 vectors length 3 in Fractint
  2150.  
  2151.     fwait
  2152.     ret
  2153. mult_vec_iit endp
  2154.  
  2155. ;
  2156. ; IITCoPro()
  2157. ;
  2158.  
  2159. .data
  2160. testdata db 0FFh,0FFh,00h,00h,00h,00h,00h,00h,00h,00h
  2161. .code
  2162.  
  2163. IITCoPro proc
  2164.      finit
  2165.      fld   tbyte ptr testdata
  2166.      fstp  tbyte ptr dstack
  2167.      fwait
  2168.      mov   ax,word ptr dstack
  2169.      or    ax,ax             ; test for 1st word of result zero
  2170.      mov   ax,1             ; return 1 if next branch taken
  2171.      jz    must_be_IIT         ;  result was zero, is IIT
  2172.      xor   ax,ax             ; return 0
  2173. must_be_IIT:
  2174.      ret
  2175. IITCoPro endp
  2176.  
  2177. .8086
  2178. .8087
  2179. .model medium,C
  2180.  
  2181. eIIT2fService        equ    0C0h        ; user services: 0C0h - 0FFh
  2182.  
  2183. ; services provided by int 2F
  2184. eInstallationCheck    equ    0        ; <== must be zero
  2185. eSetSemaphore        equ    1
  2186. eClearSemaphore        equ    2
  2187.  
  2188. .code
  2189. F4x4Check    PROC    FAR
  2190. ; IIT F4x4 semaphore installation check
  2191.     
  2192.     inc    bp
  2193.     push    bp
  2194.     mov    bp, sp
  2195.     mov    ax, (eIIT2fService SHL 8) + eInstallationCheck
  2196.     mov    bx, 'II'
  2197.     mov    cx, 'Ts'
  2198.     mov    dx, 'em'
  2199.     int    2Fh
  2200.     cmp    ax, (eIIT2fService SHL 8) + 0FFh
  2201.     jne    not_installed
  2202.     cmp    bx, 'OK'
  2203.     jne    not_installed
  2204.     cmp    cx, ' I'
  2205.     jne    not_installed
  2206.     cmp    dx, 'IT'
  2207.     jne    not_installed
  2208. installed:
  2209.     mov    ax, 1
  2210.     pop    bp
  2211.     dec    bp
  2212.     ret
  2213. not_installed:
  2214.     xor    ax, ax
  2215.     pop    bp
  2216.     dec    bp
  2217.     ret
  2218. F4x4Check    ENDP
  2219.  
  2220.  
  2221. F4x4Lock    PROC    FAR
  2222.     inc    bp
  2223.     push    bp
  2224.     mov    bp, sp
  2225.     mov    ax, (eIIT2fService SHL 8) + eSetSemaphore 
  2226.     mov    bx, 'II'
  2227.     mov    cx, 'Ts'
  2228.     mov    dx, 'em'
  2229.     int    2Fh
  2230.     pop    bp
  2231.     dec    bp
  2232.     ret
  2233. F4x4Lock    ENDP
  2234.  
  2235.  
  2236. F4x4Free    PROC    FAR
  2237.     inc    bp
  2238.     push    bp
  2239.     mov    bp, sp
  2240.     mov    ax, (eIIT2fService SHL 8) + eClearSemaphore
  2241.     mov    bx, 'II'
  2242.     mov    cx, 'Ts'
  2243.     mov    dx, 'em'
  2244.     int    2Fh
  2245.     pop    bp
  2246.     dec    bp
  2247.     ret
  2248. F4x4Free    ENDP
  2249.     END
  2250.  
  2251.