Rozszerzenia MMX firm AMD i Cyrix

Autor: Wojciech Muła
Dodany:31.01.2002
Aktualizacja:5.07.2007

Treść

Wprowadzenie

Główni konkurenci Intela, czyli AMD i Cyrix, podczas projektowania swoich implementacji MMX ruszyli głową i wprowadzili kilka rozszerzeń w stosunku do oryginalnego zestawu instrukcji. Okazały się na tyle interesujące i przydatne, że Intel wprowadził je do zestawu instrukcji wraz z SSE.

Artykuł powstał, by pokazać co stracili posiadacze starych procesorów Pentium MMX.

Rozszerzenia AMD

PMAXUB/PMINUB

Składnia:

PMAXUB mmreg1, mmreg2/mem64
PMINUB mmreg1, mmreg2/mem64

Działanie (pokazane dla PMAXUB):

mmreg1[ 0.. 7] = (mmreg1[ 0.. 7] > mmreg2/mem64[ 0.. 7]) ? mmreg1[ 0.. 7] : mmreg2/mem64[ 0.. 7]
mmreg1[ 8..15] = (mmreg1[ 8..15] > mmreg2/mem64[ 8..15]) ? mmreg1[ 8..15] : mmreg2/mem64[ 8..15]

... ; itd.

mmreg1[56..63] = (mmreg1[56..63] > mmreg2/mem64[56..63]) ? mmreg1[56..63] : mmreg2/mem64[56..63]

Implementacja PMAXUB:

; mmreg1 = mm0   ; np. mm0 = |10|50|60|80|...
; mmreg2 = mm1   ;     mm1 = | 5|80|60|90|...
; niszczy: mm2, mm3

pxor    mm3, mm3 ; mm3 = packed_byte(0x00)
movq    mm2, mm1 ; ***

psubusb mm2, mm0 ; mm2 = | 0|30| 0|10|...
pcmpeq  mm2, mm3 ; mm2 = |ff|00|ff|00|...

pand    mm0, mm2 ; mm0 = |10| 0|60| 0|...
pandn   mm2, mm1 ; mm2 = | 0|80| 0|90|...

por     mm0, mm2 ; mm0 = |10|80|60|90|...

Implementacja PMINUB jest w zasadzie taka sama, wystarczy linijkę oznaczoną *** zamienić na movq mm2, mm0.

W analogiczny sposób można operować na słowach i dwusłowach bez znaku.

Czytając ostatnio Assembly Programming Journal #5 zwróciłem uwagę na kod Chrisa Dragana wyznaczania minimum i maksimum liczb bez znaku. Oto zastosowany algorytm:

int max(int a, int b)
{
 int diff_max;

 if ((diff_max=b-a) < 0) // odejmowanie z nasyceniem
        diff_max = 0;

 return a + diff_max;
}

(W podobny sposób można obliczyć minimum).

Odejmowanie z nasyceniem jest realizowane przez MMX w jednej instrukcji, wobec czego kod rozkazu PMAXUB przedstawia się następująco:

;              a = mm0 = |10|50|60|80|...
;              b = mm1 = | 5|80|60|90|...

psubusb mm1, mm0 ; mm1 = | 0|30| 0|10|
paddb   mm0, mm1 ; mm0 = |10|30|60|90|

MAXSW/PMINSW

Składnia:

PMAXSW mmreg1, mmreg2/mem64
PMINSW mmreg1, mmreg2/mem64

Działanie (operacje na packed_word):

; min
mmreg1 = (mmreg1 < mmreg2/mem64) ? mmreg1 : mmreg2/mem64

; max
mmreg1 = (mmreg1 > mmreg2/mem64) ? mmreg1 : mmreg2/mem64

Implementacja PMAXSW:

; mmreg1 = mm0
; mmreg2 = mm1

movq    mm2, mm0
pcmpgtw mm2, mm1 ; mm2 = (mm0 > mm1) ? 0xffff : 0x0000 ***

pand    mm0, mm2 ; mm0 = (mm0 > mm1) ?    mm0 : 0x0000
pandn   mm2, mm1 ; mm0 = (mm1 > mm0) ?    mm1 : 0x0000
por     mm0, mm1

Wystarczy zamienić rozkaz w linijce oznaczonej *** na pcmpgtw mm2, mm0, by powyższy kod symulował działanie rozkazu PMINSW.

PAVGUSB new

Składnia:

pavgusb mmreg1, mmereg2/mem

Opis (operacje na packed_byte):

mmreg1 = (mmreg1 + mmreg2 + 1) >> 1

Implementacja 1:

Ponieważ średnia jest liczona z trzech, a nie dwóch argumentów, najrozsądniej jest rozszerzyć zakresy liczb. Unika się wtedy wielu problemów z wykrywaniem przekroczenia zakresu.

; mmereg1 = mm0
; mmereg2 = mm1

pxor mm4, mm4      ; mm5 = packed_word(0x0000)

movq mm2, mm0
movq mm3, mm1

punpcklbw mm0, mm4 ;
punpckhbw mm2, mm4 ; mm2:mm0 = extend_bw(mm0)

punpcklbw mm1, mm4 ;
punpckhbw mm3, mm4 ; mm3:mm1 = extend_bw(mm1)

pcmpeqw   mm5, mm5 ; mm5 = packed_word(0xffff)
psubw     mm4, mm5 ; mm3 = packed_word(0x0001)

paddw     mm0, mm1 ; mm2:mm0 += mm3:mm1
paddw     mm2, mm3 ;

paddw     mm0, mm4 ; mm2:mm0 += 1
paddw     mm2, mm4 ;

psrlw     mm0, 1   ; /2
psrlw     mm2, 1   ; /2

packuswb  mm0, mm2 ; mm0 = packed_uw2ub(mm2:mm0)

Implementacja 2 [5.07.2007]:

Lepszy sposób realizacji instrukcji pavgb (czy pavgusb), który równolegle wykonuje działanie na bajtach bez znaku:

mm1[i] = (mm1[i] + mm2[i] + 1) >> 1, i=0..7

Metodę pokażę na przykładzie dla jednego bajtu:

-- a, b - argumenty (bajty)

a1 = (a & 0xfe) >> 1     -- a1, b1 - starsze 7 bitów
b1 = (b & 0xfe) >> 1

cc = (a | b) & 0x01      -- bit przeniesienia z pozycji 0
                         -- dla działania a + b + 1

avg = a1 + a2 + cc       -- średnia (a + b + 1) >> 1

Jedna bardzo miła cecha tej metody, to także możliwość zastosowania jej w zwykłym, nie-MMX-owym kodzie. Np.:

; eax, ebx - wektory 4 bajtów

movl    %eax, %ecx
orl     %ebx, %ecx
andl    $0x01010101, %ecx       ; ecx = cc

shrl    $1, %eax
andl    $0x7f7f7f7f, %eax       ; eax = a1

shrl    $1, %ebx
andl    $0x7f7f7f7f, %ebx       ; ebx = b1

addl    %ebx, %eax
addl    %ecx, %eax              ; eax = avg

Z MMX-em będzie tylko taka różnica, że trzeba maski bitowe albo mieć załadowane do jakiś rejestrów, albo odczytywać z pamięci.

PEXTRW

Składnia:

PEXTRW reg32, mmreg, imm8

Opis:

index = imm[1:0]*16

reg32[31:16] = 0
reg32[15: 0] = mmreg[index+15:index]

Implementacja:

; reg32 = eax
; mmreg = mm0
; imm8  = ?

index equ (imm8 & 0x3)*16

psrlq mm0, index      ; umieść wymagane słowo na pozycji 0

movd  eax, mm0        ; przepisz do rejestru
and   eax, 0x0000ffff ; i wyzeruj starsze słowo

PINSRW

Składnia:

PINSRW mmreg, reg32, imm8

Opis:

index = imm8[1:0]*16

mmreg[index+15:index] = reg32[15:0]

Implementacja:

; mmreg = mm0
; reg32 = eax
; imm8  = ?

index equ (imm8 & 0x3)*16 ; np. index=2*16=32

movq    mm1, mm0

pcmpeqw mm0, mm0   ; mm1 = |ffff|ffff|ffff|ffff|
psrlq   mm0, 3*16  ; mm1 = |0000|0000|0000|ffff|

movd    mm2, eax   ; mm2 = |0000|0000|xxxx|aaaa|
pand    mm2, mm0   ; mm2 = |0000|0000|0000|aaaa|

psllq   mm2, index ; mm2 = |0000|aaaa|0000|0000|
psll1   mm0, index ; mm0 = |0000|ffff|0000|0000|

pandn   mm0, mm1   ; mm0 = (not mm0) and mm2
por     mm0, mm2

Jeśli maska |0000|0000|0000|ffff| jest w pamięci i dodatkowo założymy, że starsze słowo reg32 jest wyzerowane, to kod będzie krótszy.

movq    mm1, mm0
movq    mm0, [mmx_mask]

movd    mm2, eax   ; mm2 = |0000|0000|xxxx|aaaa|
psllq   mm2, index ; mm2 = |0000|aaaa|0000|0000|
psll1   mm0, index ; mm0 = |0000|ffff|0000|0000|
pandn   mm0, mm1   ; mm0 = (not mm0) and mm2
por     mm0, mm2

PMOVMSKB

Składnia:

PMOVMSKB reg32, mmreg

Opis: z najstarszych bitów każdego bajtu tworzy jeden bajt

reg32[31:8] = 0
reg32[7] = mmreg[63]
reg32[6] = mmreg[55]
reg32[5] = mmreg[47]
reg32[4] = mmreg[39]
reg32[3] = mmreg[31]
reg32[2] = mmreg[23]
reg32[1] = mmreg[15]
reg32[0] = mmreg[7]

Implementacja #1:

; mmreg = mm0
; reg32 = eax
; mask  = packed_byte(0x01)

                   ; mm0 = axxxxxxx bxxxxxxx cxxxxxxx dxxxxxxx exxxxxxx fxxxxxxx gxxxxxxx hxxxxxxx
psrlq mm0, 7       ;
pand  mm0, [maxsk] ; mm0 = _______a _______b _______c _______d _______e _______f _______g _______h

; A = (A >>  7) | A
movq  mm1, mm0     ; mm1 = _______a _______b _______c _______d _______e _______f _______g _______h
psrlq mm0, 7       ; mm0 = ________ ______a_ ______b_ ______c_ ______d_ ______e_ ______f_ ______g_
por   mm0, mm1     ; mm0 = _______a ______ab ______bc ______cd ______de ______ef ______fg ______gh

; A = (A >> 14) | A
movq  mm1, mm0     ; mm1 = _______a ______ab ______bc ______cd ______de ______ef ______fg ______gh
psrlq mm0, 14      ; mm0 = ________ ________ _____a__ ____ab__ ____bc__ ____cd__ ____de__ ____ef__
por   mm0, mm1     ; mm0 = _______a ______ab _____abc ____abcd ____bcde ____cdef ____defg ____efgh

; A = (A >> 28) | A
movq  mm1, mm0     ; mm1 = _______a ______ab _____abc ____abcd ____bcde ____cdef ____defg ____efgh
psrlq mm0, 28      ; mm0 = ________ ________ ________ ________ ___a____ __ab____ _abc____ abcd____
por   mm0, mm1     ; mm0 = _______a ______ab _____abc ____abcd ___abcde __abcdef _abcdefg **abcdefgh**

movd  eax, mm0
and   eax, 0x000000ff

Implementacja #2:

segment .text

psrlq   mm0, 7       ; przesuń MSB na pozycję 0
pand    mm0, [mask]  ; pozostaw tylko bity 0

                     ; mm0 =  | _______a _______b | _______c _______d | _______e _______f | _______g _______h |
pmaddwd mm0, [const]

; starsze dwusłowo:
; A   = | ________ ________ | _______b _______a | * ((1 << 1) + (1 <<  8)) = | ________ _______b | ______ba ______a_ |
; B   = | ________ ________ | _______d _______c | * ((1 << 3) + (1 << 10)) = | ________ _____d__ | ____dc__ ____c___ |
;
; A+B = | ________ _____d_b | ____**dcba** ____c_a_ |
;
; młodsze słowo analogicznie

movq     mm1, mm0    ; mm1 = | ________ _____d_b | ____dcba ____c_a_ | ________ _____h_f | ____hgfe ____g_e_ |

psrlq    mm0, 4      ; mm0 = | ________ ________ | _____d_b ____dcba | ____c_a_ ________ | _h_f____ hgfe____ |
psrlq    mm1, 40     ; mm1 = | ________ ________ | ________ ________ | ________ ________ | _____d_b ____dcba |
por      mm0, mm1    ; mm0 = | ________ ________ | ________ ________ | ________ ________ | _h_f_d_b hgfedcba |

movd     eax, mm0          ; eax = | _h_f_d_b hgfedcba |
and      eax, 0x000000ff   ; eax = | ________ hgfedcba |

segment .data

const dw ((1 << 1) + (1 << 8))
      dw ((1 << 3) + (1 << 10))

      dw ((1 << 1) + (1 << 8))
      dw ((1 << 3) + (1 << 10))

mask  dd 0101010h, 0101010h

MASKMOVQ

Składnia:

MASKMOVQ mmreg1, mmreg2 (edi)

Opis: przypisanie warunkowe bajtów

memory[edi][63:56] = mmreg2[63] ? mmreg1[63:56] : memory[edi][63:56]
memory[edi][55:48] = mmreg2[55] ? mmreg1[55:48] : memory[edi][55:48]
memory[edi][47:40] = mmreg2[47] ? mmreg1[47:40] : memory[edi][47:40]
memory[edi][39:32] = mmreg2[39] ? mmreg1[39:32] : memory[edi][39:32]
memory[edi][31:24] = mmreg2[31] ? mmreg1[31:24] : memory[edi][31:24]
memory[edi][23:16] = mmreg2[23] ? mmreg1[23:16] : memory[edi][23:16]
memory[edi][15: 8] = mmreg2[15] ? mmreg1[15: 8] : memory[edi][15: 8]
memory[edi][ 7: 0] = mmreg2[7]  ? mmreg1[ 7: 0] : memory[edi][ 7: 0]

Implementacja:

Jedyny istotny problem to wygenerowanie maski.

; mmreg1 = mm0
; mmreg2 = mm1

; wygenerowanie w mm3 packed_byte(0x80)
pcmpeqw  mm3, mm3
psllw    mm3, 8
packsswb mm3, mm3

; generacja maski dla mmreg1
pand     mm1, mm3
pcmpeqb  mm1, mm3

; wykonanie warunku
pand     mm0, mm3
pandn    mm3, [edi]
por      mm0, mm3

; zapis do pamięci
movq    [edi], mm0

PSADBW

Składnia:

PSADBW mmreg1, mmreg2

Opis:

mmreg1[63:16] = 0
mmreg1[15: 0] = abs(mmreg1[ 7: 0] - mmreg2[ 7: 0]) +
                abs(mmreg1[15: 8] - mmreg2[15: 8]) +
                abs(mmreg1[23:16] - mmreg2[23:16]) +
                abs(mmreg1[31:24] - mmreg2[31:24]) +
                abs(mmreg1[39:32] - mmreg2[39:32]) +
                abs(mmreg1[47:40] - mmreg2[47:40]) +
                abs(mmreg1[55:48] - mmreg2[55:48]) +
                abs(mmreg1[63:56] - mmreg2[63:56])

Implementacja:

Pierwszym krokiem jest policzenie modułów różnic dla każdego bajtu, następnie należy do siebie dodać każdy bajt (co jest chyba najbardziej czasochłonne).

; mmreg1 = mm0
; mmreg1 = mm1

; obliczenie modułu różnic (za Intel MMX Developers Guide)
movq    mm2, mm0 ;
psubusb mm0, mm1 ; tam gdzie różnica jest mniejsza od zera wynik jest zerem
psubusb mm1, mm2 ;
por     mm0, mm1 ; połączenie wyników: mm0 = abs(mm0-mm1)
                 ; mm0 = |h|g|f|e|d|c|b|a|

; rozszerzania zakresu liczb bez znaku z bajtu do słowa
movq      mm1, mm0
punpcklbw mm0, mm0 ; mm0 = |d|d|c|c|b|b|a|a|
psrlw     mm0, 8   ; mm0 = |0|d|0|c|0|b|0|a| = |d|c|b|a|

punpckhbw mm1, mm1 ; mm1 = |h|h|g|g|f|f|e|e|
psrlw     mm1, 8   ; mm1 = |0|h|0|g|0|f|0|e| = |h|g|f|e|

; sumowanie        ; mm0 = |   d    |   c    |   b    |   a    |
paddw     mm0, mm1 ; mm0 = |   dh   |   cg   |   bf   |   ae   |

movq      mm1, mm0
psllq     mm1, 16  ; mm1 = |   cg   |   bf   |   ae   |    0   |
paddw     mm0, mm1 ; mm0 = |  cdgh  |  bcfg  |  abef  |   ae   |

movq      mm1, mm0
psllq     mm1, 32  ; mm1 = |  abef  |   ae   |    0   |    0   |
paddw     mm0, mm1 ; mm0 = |**abcdefgh**| ... reszta nieważna .... |

psrlq     mm0, 48  ; przesuń najstarsze słowo na pozycję zero

movd      eax, mm0

PSHUFW

Składnia:

PSHUFW mmreg1, mmreg2, imm8

Opis: rozmieszczenie słów zgodnie z podanym schematem

index3 = imm8[7:6] * 16
index2 = imm8[5:4] * 16
index1 = imm8[3:2] * 16
index0 = imm8[1:0] * 16

temp   = mmreg2

mmreg1[63:48] = temp[index3+15:index3]
mmreg1[47:32] = temp[index2+15:index2]
mmreg1[31:16] = temp[index1+15:index1]
mmreg1[15:0]  = temp[index0+15:index0]

Przykład:

; mm0 = |d|c|b|a|

pshuf mm1, mm0, 0xe3 ; 0xe3 = 11 10 00 11b
                     ; nr0  = 3
                     ; nr1  = 0
                     ; nr2  = 2
                     ; nr3  = 3

                     ; mm1 = |d|c|a|d|

Implementacja:

; mmreg1 = mm1
; mmreg2 = mm0

index0 equ ((imm8 >> 0 & 3*16 ; 16
index1 equ ((imm8 >> 2 & 3*16 ; 32
index2 equ ((imm8 >> 4 & 3*16 ; 48
index3 equ ((imm8 >> 6 & 3*16 ;  0

                       ; mm0 = d c b a
pxor    mm1, mm1       ; mm1 = 0 0 0 0
pcmpeqb mm2, mm2       ; mm2 = f f f f
psllq   mm2, 48        ; mm2 = f 0 0 0

movq    mm3, mm0       ; mm3 = d c b a
psllq   mm3, 48-index0 ; mm3 = b a 0 0
pand    mm3, mm2       ; mm3 = b 0 0 0
psrlq   mm3, 48        ; mm3 = 0 0 0 b
por     mm1, mm3       ; mm1 = 0 0 0 b

movq    mm3, mm0       ; mm3 = d c b a
psllq   mm3, 48-index1 ; mm3 = c 0 0 0
pand    mm3, mm2       ; mm3 = c 0 0 0
psrlq   mm3, 32        ; mm3 = 0 0 c 0
por     mm1, mm3       ; mm1 = 0 0 c b

movq    mm3, mm0       ; mm3 = d c b a
psllq   mm3, 48-index2 ; mm3 = d c b a
pand    mm3, mm2       ; mm3 = d 0 0 0
psrlq   mm3, 16        ; mm3 = 0 d 0 0
por     mm1, mm3       ; mm1 = 0 d c b

movq    mm3, mm0       ; mm3 = d c b a
psllq   mm3, 48-index3 ; mm3 = a 0 0 0
pand    mm3, mm2       ; mm3 = a 0 0 0
por     mm1, mm3       ; mm1 = a d a b

PMULUHW

Rozkaz ten jest podobny do PMULHW, ale operuje na liczbach bez znaku. Więc dla argumentów z przedziału 0x0000 do 0x7fff wyniki zwracane przez oba rozkazy będą takie same, dla pozostały będą się różnić. Ta cecha zostanie wykorzystana; otóż słowa A i B można zapisać jako:

a = A & 0x7fff + A & 0x8000 = lo(A) + msb(A) << 15
b = B & 0x7fff + B & 0x8000 = lo(B) + msb(B) << 15

wtedy mnożenie ma postać:

a*b = lo(A)*lo(B) + lo(A)*(msb(B) << 15) + lo(B)*(msb(A) << 15) + (msb(A) & msb(B)) << 30

Rozkład znaczących bitów (x) dla poszczególnych czynników jest następujący:

   starsze słowo   |    młodsze słowo
---- ---- ---- ----|---- ---- ---- ----
0000 xxxx xxxx xxxx|**x**xxx xxxx xxxx xxxx  lo(A)*lo(B)
0xxx xxxx xxxx xxxx|**x**000 0000 0000 0000  lo(A)*(msb(B) << 15)
0xxx xxxx xxxx xxxx|**x**000 0000 0000 0000  lo(B)*(msb(A) << 15)
00x0 0000 0000 0000|0000 0000 0000 0000  (msb(A) & msb(B)) << 30

O tym czy młodsze słowo wpływa na starsze decydują pogrubione bity: tak więc faktycznie potrzebne jest najstarszy bit wyniku lo(A)*lo(B), oraz najmłodsze lo(A) i lo(B). Wyrażenie wyznaczające starsze słowo można zapisać w sposób następujący:

result  = lo(A)*lo(B) >> 30    # wyznaczone przez pmulhw
tmp     = lo(A)*lo(B) & 0xffff # wyznaczone przez pmullw
tmp     = tmp >> 15

if msb(B):
        result += lo(A) >> 1
        tmp    += lo(A) >> 15
if msb(A):
        result += lo(B) >> 1
        tmp    += lo(B) >> 15
if msb(A) & msb(B):
        result += 1 << 14   # 0x4000

result += tmp >> 1

Składnia:

pmuluhw mmreg1, mmreg2/mem64

Opis: (rozkaz działa na packed_word)

mmreg1 = ((unsigned)mmreg1 * (unsigned)mmreg2/mem64) >> 16

Implementacja:

; mm0 - mmreg1 (A)
; mm1 - mmreg2 (B)

movq mm2, mm0
movq mm3, mm1

pand mm0, [pw_0x7ffff] ; mm0 = lo(A)
pand mm1, [pw_0x7ffff] ; mm1 = lo(B)
pand mm2, [pw_0x80000] ; mm2 = (msb(A) << 15)
pand mm3, [pw_0x80000] ; mm3 = (msb(B) << 15)

; result  = lo(A)*lo(B) >> 30    # wyznaczone przez pmulhw
; tmp     = lo(A)*lo(B) & 0xffff # wyznaczone przez pmullw

movq   mm7, mm0          ; zachowaj lo(A)
movq   mm6, mm1          ; i lo(B)
pmulhw mm0, mm1          ; mm0 = result
pmullw mm1, mm7          ; mm1 = tmp >> 15
psrlw  mm1, 15

;if msb(B):
;       result += lo(A) >> 1
;       tmp    += lo(A) >> 15

pcmpeqw mm3, [pw_0x8000] ; mm3[i] = msb(B[i]) ? 0xffff : 0x0000
pand    mm7, mm3         ;

psrlw   mm7, 1           ; result += lo(A) >> 1
paddw   mm0, mm7
psrlw   mm7, 14          ; tmp    += lo(A) >> 15
paddw   mm1, mm7

;if msb(A):
;       result += lo(B) >> 1
;       tmp    += lo(B) >> 15

pcmpeqw mm2, [pw_0x8000] ; mm2[i] = msb(A[i]) ? 0xffff : 0x0000
pand    mm6, mm2         ;

psrlw   mm6, 1           ; result += lo(B) >> 1
paddw   mm0, mm6
psrlw   mm6, 14          ; tmp    += lo(B) >> 15
paddw   mm1, mm6

;if msb(A) & msb(B):
;       result += 1 << 14   # 0x4000

pand    mm2, mm3
pand    mm2, [pw_0x4000]
paddw   mm0, mm2

;result += tmp >> 1

psrlw   mm1, 1
paddw   mm0, mm1

Rozszerzenia Cyrix

Zaprezentowane rozkazy w procesorach Cyrix używają tzw. implied registers — numer rejestru nie jest żadnym z podanych, lecz jest w jakiś sposób obliczany (nie mam dokumentacji, więc nie potrafię nic więcej powiedzieć).

PDISTUB

Składnia:

PDISTUB mmreg1, mmreg2

Opis: (rozkaz operuje na packed_byte)

mmreg1 = abs(mmreg1-mmreg2)

Implementacja:

; mmreg1 = mm0
; mmreg2 = mm1

movq    mm2, mm0
psubusb mm2, mm0 ; zostają tylko różnice większe od zera
psubusb mm0, mm1 ;

PMAGW

Składnia:

PMAGW mmreg1, mmreg2/mem64

Opis: (rozkaz operuje na packed_word ze znakiem)

mmreg1 = (abs(mmreg1) > abs(mmreg2)) ? mmreg1 : mmreg2

Implementacja:

; mmreg1 = mm0
; mmreg2 = mm1

movq  mm2, mm0   ;
movq  mm4, mm0   ;
psraw mm4, 15    ;
pxor  mm2, mm4   ;
psubw mm2, mm4   ; mm2 = abs(mm0)

movq  mm3, mm1   ;
movq  mm4, mm1   ;
psraw mm4, 15    ;
pxor  mm3, mm4   ;
psubw mm3, mm4   ; mm3 = abs(mm1)

pcmpgtw mm2, mm3 ; mm2 = (abs(mm0) > abs(mm1)) ? 0xffff : 0x0000;

pand    mm0, mm2 ; zamaskuj słowa w mm0
pandn   mm2, mm1 ; i mm1
por     mm0, mm2 ; połącz

Dokument utworzony przez rozszerzony rst2html.