home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / ramfs102.zip / src / vmheap.asm < prev    next >
Assembly Source File  |  2002-10-20  |  17KB  |  663 lines

  1. ; $Id: vmheap.asm,v 1.1.2.3 2002/10/21 00:11:36 root Exp $
  2. ;
  3. ; VM block transfer routines. ALP v 4.00.007 or later required.
  4.  
  5.     .586p
  6.     .MMX
  7.     .XMM
  8.  
  9.     extern    DOS32FLATDS:abs
  10.     extern    _fix_kernel:FAR32
  11.     public    _threednow
  12.     OPTION  SEGMENT:USE16
  13.  
  14. _BSS    segment public 'BSS'
  15.     extern    _DevHlp: dword
  16.     extern    _flat_ds: word
  17.     far_gate fword    ?
  18.     _threednow dw ?
  19. _BSS    ends
  20.  
  21. DGROUP  GROUP _BSS
  22.  
  23. _TEXT32 segment para public use32 'CODE'
  24.         assume cs:_TEXT32
  25.         assume ds:FLAT, es:FLAT
  26.  
  27. ; Workhorse procedure for SIMD data transfer. Derived from:
  28. ; AMD Athlon<TM> Processor x86 Code Optimization 22007J  August 2001
  29. ; Chapter 10: "3DNow!<TM> and MMX<TM> Optimizations"
  30. ;
  31. ; AMD Athlon Processor-Specific Code
  32. ;
  33. ; The following memory copy example is written with Microsoft
  34. ; Visual C++ in-line assembler syntax, and assumes that the
  35. ; Microsoft Processor Pack is installed (available from
  36. ; Microsoft's web site). This is a general purpose memcpy()
  37. ; routine, which can efficiently copy any size block, small or
  38. ; large. Data alignment is strongly recommended for good
  39. ; performance, but this code can handle non-aligned blocks.
  40. ;
  41. ; Example 2: Optimized memcpy() for Any Data Size or Alignment
  42. ;
  43. TINY_BLOCK_COPY     EQU    64  ; upper limit for movsd type copy
  44. ; The smallest copy uses the X86 "movsd" instruction, in an optimized
  45. ; form which is an "unrolled loop".
  46. IN_CACHE_COPY         EQU    64 * 1024 ; upper limit for movq/movq copy w/SW prefetch
  47. ; Next is a copy that uses the MMX registers to copy 8 bytes at a time,
  48. ; also using the "unrolled loop" optimization. This code uses
  49. ; the software prefetch instruction to get the data into the cache.
  50. UNCACHED_COPY         EQU    197 * 1024 ; upper limit for movq/movntq w/SW prefetch
  51. ; For larger blocks, which will spill beyond the cache, it's faster to
  52. ; use the Streaming Store instruction MOVNTQ. This write instruction
  53. ; bypasses the cache and writes straight to main memory. This code also
  54. ; uses the software prefetch instruction to pre-read the data.
  55. ; USE 64 * 1024 FOR THIS VALUE IF YOU'RE ALWAYS FILLING A "CLEAN CACHE"
  56. CACHEBLOCK     EQU    80h ; # of 64-byte blocks (cache lines) for block prefetch
  57. ; For the largest size blocks, a special technique called Block Prefetch
  58. ; can be used to accelerate the read operations. Block Prefetch reads
  59. ; one address per cache line, for a series of cache lines, in a short loop.
  60. ; This is faster than using software prefetch. The technique is great for
  61. ; getting maximum read bandwidth, especially in DDR memory systems.
  62.  
  63. ramfs_3dnow_transfer proc far
  64.     push    ebp
  65.     mov    ebp, esp
  66.     sub    esp, 108
  67.      fsave    [ebp-108]
  68.     mov    ebx, ecx        ; keep a copy of count
  69.     cld
  70.  
  71.     cmp    ecx, TINY_BLOCK_COPY
  72.     jb    $memcpy_ic_3        ; tiny? skip mmx copy
  73.     cmp    ecx, 32*1024        ; don't align between 32k-64k because
  74.     jbe    $memcpy_do_align    ; it appears to be slower
  75.     cmp    ecx, 64*1024
  76.     jbe    $memcpy_align_done
  77. $memcpy_do_align:
  78.     mov    ecx, 8            ; a trick that's faster than rep movsb...
  79.     sub    ecx, edi        ; align destination to qword
  80.     and    ecx, 111b        ; get the low bits
  81.     sub    ebx, ecx        ; update copy count
  82.     neg    ecx            ; set up to jump into the array
  83.     add    ecx, offset FLAT:$memcpy_align_done
  84.     jmp    ecx            ; jump to array of movsb`s
  85.     align    4
  86.     movsb
  87.     movsb
  88.     movsb
  89.     movsb
  90.     movsb
  91.     movsb
  92.     movsb
  93.     movsb
  94. $memcpy_align_done:            ; destination is dword aligned
  95.     mov    ecx, ebx        ; number of bytes left to copy
  96.     shr    ecx, 6            ; get 64-byte block count
  97.     jz    $memcpy_ic_2        ; finish the last few bytes
  98.     cmp    ecx, IN_CACHE_COPY/64    ; too big 4 cache? use uncached copy
  99.     jae    $memcpy_uc_test
  100. ; This is small block copy that uses the MMX registers to copy 8 bytes
  101. ; at a time. It uses the "unrolled loop" optimization, and also uses
  102. ; the software prefetch instruction to get the data into the cache.
  103.     ALIGN    16
  104. $memcpy_ic_1:                ; 64-byte block copies, in-cache copy
  105.     prefetchnta ds:[esi+(200*64/34+192)] ; start reading ahead
  106.     movq    mm0, ds:[esi+0]        ; read 64 bits
  107.     movq    mm1, ds:[esi+8]
  108.     movq    es:[edi+0], mm0        ; write 64 bits
  109.     movq    es:[edi+8], mm1        ; note: the normal movq writes the
  110.     movq    mm2, ds:[esi+16]    ; data to cache; a cache line will be
  111.     movq    mm3, ds:[esi+24]    ; allocated as needed, to store the data
  112.     movq    es:[edi+16], mm2
  113.     movq    es:[edi+24], mm3
  114.     movq    mm0, ds:[esi+32]
  115.     movq    mm1, ds:[esi+40]
  116.     movq    es:[edi+32], mm0
  117.     movq    es:[edi+40], mm1
  118.     movq    mm2, ds:[esi+48]
  119.     movq    mm3, ds:[esi+56]
  120.     movq    es:[edi+48], mm2
  121.     movq    es:[edi+56], mm3
  122.     add    esi, 64            ; update source pointer
  123.     add    edi, 64            ; update destination pointer
  124.     dec    ecx            ; count down
  125.     jnz    $memcpy_ic_1        ; last 64-byte block?
  126. $memcpy_ic_2:
  127.     mov    ecx, ebx        ; has valid low 6 bits of the byte count
  128. $memcpy_ic_3:
  129.     shr    ecx, 2            ; dword count
  130.     and    ecx, 1111b        ; only look at the "remainder" bits
  131.     neg    ecx            ; set up to jump into the array
  132.     add    ecx, offset FLAT:$memcpy_last_few
  133.     jmp    ecx            ; jump to array of movsd`s
  134. $memcpy_uc_test:
  135.     cmp    ecx, UNCACHED_COPY/64    ; big enough? use block prefetch copy
  136.     jae    $memcpy_bp_1
  137. $memcpy_64_test:
  138.     or    ecx, ecx        ; tail end of block prefetch will jump here
  139.     jz    $memcpy_ic_2        ; no more 64-byte blocks left
  140. ; For larger blocks, which will spill beyond the cache, it's faster to
  141. ; use the Streaming Store instruction MOVNTQ. This write instruction
  142. ; bypasses the cache and writes straight to main memory. This code also
  143. ; uses the software prefetch instruction to pre-read the data.
  144.     align    16
  145. $memcpy_uc_1:                ; 64-byte blocks, uncached copy
  146.     prefetchnta ds:[esi+(200*64/34+192)] ; start reading ahead
  147.     movq    mm0, ds:[esi+0]        ; read 64 bits
  148.     add    edi,64            ; update destination pointer
  149.     movq    mm1, ds:[esi+8]
  150.     add    esi,64            ; update source pointer
  151.     movq    mm2, ds:[esi-48]
  152.     movntq    es:[edi-64], mm0        ; write 64 bits, bypassing the cache
  153.     movq    mm0, ds:[esi-40]        ; note: movntq also prevents the CPU
  154.     movntq    es:[edi-56], mm1        ; from READING the destination address
  155.     movq    mm1, ds:[esi-32]        ; into the cache, only to be over-written
  156.     movntq    es:[edi-48], mm2        ; so that also helps performance
  157.     movq    mm2, ds:[esi-24]
  158.     movntq    es:[edi-40], mm0
  159.     movq    mm0, ds:[esi-16]
  160.     movntq    es:[edi-32], mm1
  161.     movq    mm1, ds:[esi-8]
  162.     movntq    es:[edi-24], mm2
  163.     movntq    es:[edi-16], mm0
  164.     dec    ecx
  165.     movntq    es:[edi-8], mm1
  166.     jnz    $memcpy_uc_1        ; last 64-byte block?
  167.     jmp    $memcpy_ic_2        ; almost dont
  168. ; For the largest size blocks, a special technique called Block Prefetch
  169. ; can be used to accelerate the read operations. Block Prefetch reads
  170. ; one address per cache line, for a series of cache lines, in a short loop.
  171. ; This is faster than using software prefetch. The technique is great for
  172. ; getting maximum read bandwidth, especially in DDR memory systems.
  173. $memcpy_bp_1:                ; large blocks, block prefetch copy
  174.     cmp    ecx, CACHEBLOCK        ; big enough to run another prefetch loop?
  175.     jl    $memcpy_64_test        ; no, back to regular uncached copy
  176.     mov    eax, CACHEBLOCK / 2    ; block prefetch loop, unrolled 2X
  177.     add    esi, CACHEBLOCK * 64    ; move to the top of the block
  178.     align    16
  179. $memcpy_bp_2:
  180.     mov    edx, ds:[esi-64]    ; grab one address per cache line
  181.     mov    edx, ds:[esi-128]    ; grab one address per cache line
  182.     sub    esi, 128        ; go reverse order to suppress HW prefetcher
  183.     dec    eax            ; count down the cache lines
  184.     jnz    $memcpy_bp_2        ; keep grabbing more lines into cache
  185.     mov    eax, CACHEBLOCK        ; now that it's in cache, do the copy
  186.     align    16
  187. $memcpy_bp_3:
  188.     movq    mm0, ds:[esi]        ; read 64 bits
  189.     movq    mm1, ds:[esi+8]
  190.     movq    mm2, ds:[esi+16]
  191.     movq    mm3, ds:[esi+24]
  192.     movq    mm4, ds:[esi+32]
  193.     movq    mm5, ds:[esi+40]
  194.     movq    mm6, ds:[esi+48]
  195.     movq    mm7, ds:[esi+56]
  196.     add    esi, 64            ; update source pointer
  197.     movntq    es:[edi], mm0        ; write 64 bits, bypassing cache
  198.     movntq    es:[edi+8], mm1        ; note: movntq also prevents the CPU
  199.     movntq    es:[edi+16], mm2    ; from READING the destination address
  200.     movntq    es:[edi+24], mm3    ; into the cache, only to be over-written,
  201.     movntq    es:[edi+32], mm4    ; so that also helps performance
  202.     movntq    es:[edi+40], mm5
  203.     movntq    es:[edi+48], mm6
  204.     movntq    es:[edi+56], mm7
  205.     add    edi, 64            ; update dest pointer
  206.     dec    eax            ; count down
  207.     jnz    $memcpy_bp_3        ; keep copying
  208.     sub    ecx, CACHEBLOCK        ; update the 64-byte block count
  209.     jmp    $memcpy_bp_1        ; keep processing blocks
  210. ; The smallest copy uses the X86 "movsd" instruction, in an optimized
  211. ; form which is an "unrolled loop". Then it handles the last few bytes.
  212.     align    4
  213.     movsd
  214.     movsd    ; perform last 1-15 dword copies
  215.     movsd
  216.     movsd
  217.     movsd
  218.     movsd
  219.     movsd
  220.     movsd
  221.     movsd
  222.     movsd    ; perform last 1-7 dword copies
  223.     movsd
  224.     movsd
  225.     movsd
  226.     movsd
  227.     movsd
  228.     movsd
  229. $memcpy_last_few:            ; dword aligned from before movsd`s
  230.     mov    ecx, ebx        ; has valid low 2 bits of the byte count
  231.     and    ecx, 11b        ; the last few cows must come home
  232.     jz    $memcpy_final        ; no more, let's leave
  233.     rep    movsb            ; the last 1, 2, or 3 bytes
  234. $memcpy_final:
  235.     emms                ; clean up the MMX state
  236.     sfence                ; flush the write buffer
  237.  
  238.       frstor    [ebp-108]
  239.     mov    esp, ebp
  240.     pop    ebp
  241.     retf
  242.  
  243. ramfs_3dnow_transfer    endp
  244.  
  245. ; The old yet reliable DWORD copy procedure
  246.  
  247. ramfs_dword_transfer    proc far
  248.     mov    ebx, ecx
  249.     cld
  250.     shr    ecx, 2
  251.     and    ebx, 3
  252.     rep    movsd
  253.     mov    ecx, ebx
  254.     rep    movsb
  255.     retf
  256. ramfs_dword_transfer    endp
  257.             
  258. _TEXT32 ends
  259.  
  260. _TEXT    segment public 'CODE'
  261.  
  262.     assume es:nothing, ss:nothing, ds:DGROUP, fs:nothing, gs:nothing
  263.  
  264.     public    VMVIRTTOFLAT
  265.     public    VMALLOC
  266.     public    VMFREE
  267.     public    VMREADUCHAR
  268.     public    VMREADUSHORT
  269.     public    VMREADBLK
  270.     public    VMWRITEBLK
  271.     public    VMREAD
  272.     public    VMWRITE
  273.     public    VMCOPY
  274.  
  275.  
  276. ;-----------------------------------------------------------------------------
  277. ; FLAT _pascal VMVirtToFlat (void *p);
  278. ;-----------------------------------------------------------------------------
  279.  
  280. VMVIRTTOFLAT    proc near
  281.     push    bp
  282.     mov    bp, sp
  283.     push    si
  284.  
  285.     movzx    esi, word ptr [bp+4]    ; offset of p
  286.     mov    ax, word ptr [bp+6]    ; selector of p
  287.     mov    dl, 5Bh            ; _DevHlp_VirtToLin
  288.     call    [_DevHlp]
  289.     jnc    vvtf_end
  290.     xor    eax, eax
  291. vvtf_end:
  292.  
  293.     shld    edx, eax, 16
  294.     pop    si
  295.     pop    bp
  296.     ret    4
  297. VMVIRTTOFLAT    endp
  298.  
  299.  
  300.  
  301.  
  302. ;-----------------------------------------------------------------------------
  303. ; FLAT _pascal VMAlloc (ULONG cbSize);
  304. ;-----------------------------------------------------------------------------
  305.  
  306. VMALLOC    proc near
  307.     push    bp
  308.     mov    bp, sp
  309.  
  310.     mov    ecx, [bp+4]        ; cbSize
  311.     mov    eax, 00000004h        ; flags
  312.     mov    dl, 57h            ; _DevHlp_VMAlloc
  313.     call    [_DevHlp]
  314.     jnc    va_end
  315.     xor    eax, eax
  316. va_end:
  317.  
  318.     shld    edx, eax, 16
  319.     pop    bp
  320.     ret    4
  321. VMALLOC    endp
  322.  
  323.  
  324.  
  325.  
  326. ;-----------------------------------------------------------------------------
  327. ; void _pascal VMFree (FLAT flatBlock);
  328. ;-----------------------------------------------------------------------------
  329.  
  330. VMFREE    proc near
  331.     push    bp
  332.     mov    bp, sp
  333.  
  334.     mov    eax, [bp+4]        ; flatBlock
  335.     mov    dl, 58h            ; _DevHlp_VMFree
  336.     call    [_DevHlp]
  337.     jnc    vf_end
  338.     int    3
  339. vf_end:
  340.     pop    bp
  341.     ret     4
  342. VMFREE    endp
  343.  
  344.  
  345.  
  346.  
  347. ;-----------------------------------------------------------------------------
  348. ; UCHAR _pascal VMReadUChar (FLAT flatSrc);
  349. ;-----------------------------------------------------------------------------
  350.  
  351. VMREADUCHAR    proc near
  352.     push    bp
  353.     mov    bp, sp
  354.     mov    ax, DOS32FLATDS
  355.     mov    es, ax
  356.  
  357.     mov    eax, [bp+4]        ; flatSrc
  358.     mov    al, es:[eax]
  359.  
  360.     pop    bp
  361.     ret    4
  362. VMREADUCHAR    endp
  363.  
  364.  
  365.  
  366.  
  367. ;-----------------------------------------------------------------------------
  368. ; USHORT _pascal VMReadUShort (FLAT flatSrc);
  369. ;-----------------------------------------------------------------------------
  370.  
  371. VMREADUSHORT    proc near
  372.     push    bp
  373.     mov    bp, sp
  374.     mov    ax, DOS32FLATDS
  375.     mov    es, ax
  376.  
  377.     mov    eax, [bp+4]        ; flatSrc
  378.     mov    ax, es:[eax]
  379.  
  380.     pop    bp
  381.     ret    4
  382. VMREADUSHORT    endp
  383.  
  384.  
  385.  
  386.  
  387. ;-----------------------------------------------------------------------------
  388. ; void _pascal VMReadBlk (BLOCK _ss *pBlk, FLAT flatBlk);
  389. ;-----------------------------------------------------------------------------
  390.  
  391. VMREADBLK    proc near
  392.     push    bp
  393.     mov    bp, sp
  394.     push    ds
  395.     mov    ax, DOS32FLATDS
  396.     mov    es, ax
  397.  
  398.     mov    ebx, [bp+4]        ; flatBlk
  399.     mov    eax, es:[ebx+0]
  400.     mov    edx, es:[ebx+4]
  401.     lds    bx, dword ptr [bp+8]    ; pBlk
  402.     mov    ds:[bx+0], eax
  403.     mov    ds:[bx+4], edx
  404.  
  405.     pop    ds
  406.     pop    bp
  407.     ret    8
  408. VMREADBLK    endp
  409.  
  410.  
  411.  
  412.  
  413. ;-----------------------------------------------------------------------------
  414. ; void _pascal VMWriteBlk (FLAT flatBlk, BLOCK _ss *pBlk);
  415. ;-----------------------------------------------------------------------------
  416.  
  417. VMWRITEBLK    proc near
  418.     push    bp
  419.     mov    bp, sp
  420.     push    ds
  421.     mov    ax, DOS32FLATDS
  422.     mov    es, ax
  423.  
  424.     lds    bx, dword ptr [bp+4]    ; pBlk
  425.     mov    eax, ds:[bx+0]
  426.     mov    edx, ds:[bx+4]
  427.     mov    ebx, [bp+8]        ; flatBlk
  428.     mov    es:[ebx+0], eax
  429.     mov    es:[ebx+4], edx
  430.  
  431.     pop    ds
  432.     pop    bp
  433.     ret    8
  434. VMWRITEBLK    endp
  435.  
  436.  
  437.  
  438.  
  439. ;-----------------------------------------------------------------------------
  440. ; void _pascal VMRead (void *pDest, FLAT flatSrc, USHORT cbLen);
  441. ;-----------------------------------------------------------------------------
  442.  
  443. VMREAD    proc near
  444.     push    bp
  445.     mov    bp, sp
  446.     push    si
  447.     push    di
  448.     push    ds
  449.     push    es
  450.     mov    ax, DOS32FLATDS
  451.     mov    ds, ax
  452.  
  453.     sub    ecx, ecx
  454.     mov    cx, [bp+4]        ; number of bytes to copy
  455.     xor    edi, edi
  456.     mov    esi, [bp+6]        ; source
  457.     les    di, [bp+10]        ; destination
  458.  
  459.     push    fs
  460.     mov    ax, seg    DGROUP
  461.     mov    fs, ax
  462.     call    fword ptr fs:far_gate
  463.     pop    fs
  464.  
  465.     pop    es    
  466.     pop    ds
  467.     pop    di
  468.     pop    si
  469.     pop    bp
  470.     ret    10
  471. VMREAD    endp
  472.  
  473.  
  474.  
  475. ;-----------------------------------------------------------------------------
  476. ; void _pascal VMWrite (FLAT flatDest, void *pSrc, USHORT cbLen);
  477. ;-----------------------------------------------------------------------------
  478.  
  479. VMWRITE    proc near
  480.     push    bp
  481.     mov    bp, sp
  482.     push    si
  483.     push    di
  484.     push    ds
  485.     push    es
  486.     mov    ax, DOS32FLATDS
  487.     mov    es, ax
  488.  
  489.     mov    edi, [bp+10]        ; flatDest
  490.     xor    esi, esi
  491.     lds    si, [bp+6]        ; pSrc
  492.     xor    ecx, ecx
  493.     mov    cx, word ptr [bp+4]    ; cbLen
  494.  
  495.     push    fs
  496.     mov    ax, seg    DGROUP
  497.     mov    fs, ax
  498.     call    fword ptr fs:far_gate
  499.     pop    fs
  500.  
  501.     pop    es
  502.     pop    ds
  503.     pop    di
  504.     pop    si
  505.     pop    bp
  506.     ret     10
  507. VMWRITE    endp
  508.  
  509.  
  510.  
  511.  
  512. ;-----------------------------------------------------------------------------
  513. ; void _pascal VMCopy (FLAT flatDest, FLAT flatSrc, ULONG cbLen);
  514. ;-----------------------------------------------------------------------------
  515.  
  516. VMCOPY    proc near
  517.     push    bp
  518.     mov    bp, sp
  519.     push    es
  520.     push    ds
  521.     push    si
  522.     push    di
  523.     mov    ax, DOS32FLATDS
  524.     mov    es, ax
  525.     mov    ds, ax
  526.  
  527.     mov    edi, [bp+12]        ; flatDest
  528.     mov    esi, [bp+8]        ; flatSrc
  529.     mov    ecx, [bp+4]         ; cbLen
  530.     mov    edx, ecx
  531.     cmp    edi, esi
  532.     je    vc_end
  533.     ja    vc_backwards
  534.  
  535.     ; move forwards
  536.     push    fs
  537.     mov    ax, seg    DGROUP
  538.     mov    fs, ax
  539.     call    fword ptr fs:far_gate
  540.     pop    fs
  541.     jmp    vc_end
  542.  
  543. vc_backwards:
  544.     ; move backwards
  545.     lea    esi, [esi+ecx-1]
  546.     lea    edi, [edi+ecx-1]
  547.     and    ecx, 0003h
  548.     std
  549.     rep    movs byte ptr es:[edi], byte ptr es:[esi]
  550.     sub    esi, 3
  551.     sub    edi, 3
  552.     mov    ecx, edx
  553.     shr    ecx, 2
  554.     rep    movs dword ptr es:[edi], dword ptr es:[esi]
  555.     cld
  556.  
  557. vc_end:
  558.     pop    di
  559.     pop    si
  560.     pop    ds
  561.     pop    es
  562.     pop    bp
  563.     ret    12
  564. VMCOPY    endp
  565.  
  566. ;
  567. ; Weird initialization routine. _fix_kernel can't be issued at the init time,
  568. ; so it's deferred as we plant init_cont to sit in place of real transfer
  569. ; subroutine.
  570.  
  571. VMINIT    proc    near
  572.     push    ds
  573.     mov    ax, seg    DGROUP
  574.     mov    ds, ax
  575.     mov    dword ptr ds:far_gate, 0
  576.     mov    word ptr ds:far_gate, offset init_cont
  577.     mov    word ptr ds:far_gate+4, seg init_cont
  578.     pop    ds
  579.     ret
  580. init_cont:
  581.     pushad
  582.     push    ds
  583.     push    es
  584.     push    fs
  585.     mov    ax, seg    DGROUP
  586.     mov    ds, ax
  587.     mov    fs, ax
  588.     mov    al, 9            ; This is an undocumented DosEnv entry
  589.     mov    dl, 24h
  590.     call    [_DevHlp]
  591.         mov     es, ax
  592.     mov    cx, seg    DGROUP
  593.         movzx   ax, byte ptr es:[bx]
  594.         shl     ax, 2
  595.     mov    ds, cx
  596.         add     bx, ax
  597.         mov     ax, word ptr es:[bx+26h] ; Flat CS
  598.         mov     word ptr ds:far_gate+4, ax
  599.         mov     ax, word ptr es:[bx+2Ah] ; Flat DS
  600.         mov     word ptr ds:_flat_ds, ax
  601. ; Check if 3DNow! was forced
  602.     mov    ax, ds:_threednow
  603.     or    ax, ax
  604.     jz    no_3dnow
  605.     cmp    al, 1
  606.     je    ok_3dnow
  607. ; 3DNow!<TM> detection - AMD technote #21928G/0 March 2000
  608.     pushfd
  609.     pop    eax
  610.     mov    ebx, eax
  611.     xor    eax, 00200000h        ; Flip CPUID bit
  612.     push    eax
  613.     popfd
  614.     pushfd
  615.     pop    eax
  616.     cmp    eax, ebx
  617.     je    no_3dnow
  618.     mov    eax, 80000000h
  619.     cpuid
  620.     cmp    eax, 80000000h
  621.     jbe    no_3dnow
  622.     mov    eax, 80000001h
  623.     cpuid
  624.     test    edx, 80000000h
  625.     je    no_3dnow
  626. ; Now try to apply the patch. Compose the address manually using Flat CS
  627. ; provided by kernel.
  628. ok_3dnow:
  629.     push    es
  630.     push    ds
  631.     mov    dword ptr fs:far_gate, offset FLAT:_fix_kernel
  632.     mov    ax, fs:_flat_ds
  633.     mov    ds, ax
  634.     mov    es, ax
  635.      call    fword ptr fs:far_gate
  636.     pop    ds
  637.     pop    es
  638.     or    eax, eax
  639.     jz    kernel_fixed
  640.     mov    ax, fs:_threednow
  641.     cmp    al, 1            ; If 3DNow! was forced, let it stay
  642.     jne    no_3dnow
  643. kernel_fixed:
  644. ; Install the appropriate handler
  645.     mov    dword ptr ds:far_gate, offset FLAT:ramfs_3dnow_transfer
  646.     jmp    short far_gate_done
  647. no_3dnow:
  648.     mov    dword ptr ds:far_gate, offset FLAT:ramfs_dword_transfer
  649. far_gate_done:
  650.     pop    fs
  651.     pop    es
  652.     pop    ds
  653.     popad
  654.     jmp    fword ptr fs:far_gate
  655. VMINIT    endp
  656.  
  657.  
  658. _TEXT    ends
  659.  
  660.     end
  661.