{
    IntelMMX – модуль для работы с расширением MMX процессоров Intel.

    Copyright © 2016, 2019, 2022 Малик Разработчик

    Это свободная программа: вы можете перераспространять её и/или
    изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она может быть полезна,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <http://www.gnu.org/licenses/>.
}

unit IntelMMX;

{$MODE DELPHI}
{$ASMMODE INTEL}

interface

uses
    Lang;

{%region public }
type
    byteMMX = packed array [0..7] of byte;
    shortMMX = packed array [0..3] of short;
    intMMX = packed array [0..1] of int;

    byteMMX_Array1d = packed array of byteMMX;
    shortMMX_Array1d = packed array of shortMMX;
    intMMX_Array1d = packed array of intMMX;
{%endregion}

{%region routine }
    function mmxSupported(): boolean;
    function toByteMMX(byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7: int): byteMMX; inline;
    function toShortMMX(short0, short1, short2, short3: int): shortMMX; inline;
    function toIntMMX(int0, int1: int): intMMX; inline;
    function byteMMX_Array1d_create(length: int): byteMMX_Array1d;
    function shortMMX_Array1d_create(length: int): shortMMX_Array1d;
    function intMMX_Array1d_create(length: int): intMMX_Array1d;
    function toByteMMXArray1d(const arr: array of byteMMX): byteMMX_Array1d; overload;
    function toByteMMXArray1d(const arr: byte_Array1d): byteMMX_Array1d; overload;
    function toByteMMXArray1d(const arr: byte_Array1d; offset, length: int): byteMMX_Array1d; overload;
    function toShortMMXArray1d(const arr: array of shortMMX): shortMMX_Array1d; overload;
    function toShortMMXArray1d(const arr: short_Array1d): shortMMX_Array1d; overload;
    function toShortMMXArray1d(const arr: short_Array1d; offset, length: int): shortMMX_Array1d; overload;
    function toIntMMXArray1d(const arr: array of intMMX): intMMX_Array1d; overload;
    function toIntMMXArray1d(const arr: int_Array1d): intMMX_Array1d; overload;
    function toIntMMXArray1d(const arr: int_Array1d; offset, length: int): intMMX_Array1d; overload;
    function mulhMMX(const data1, data2: shortMMX): shortMMX;
    function mullMMX(const data1, data2: shortMMX): shortMMX;
    function umulhMMX(const data1, data2: shortMMX): shortMMX;
    function salMMX(const data: shortMMX; bits: int): shortMMX; overload;
    function salMMX(const data: intMMX; bits: int): intMMX; overload;
    function sarMMX(const data: shortMMX; bits: int): shortMMX; overload;
    function sarMMX(const data: intMMX; bits: int): intMMX; overload;
    function shrMMX(const data: shortMMX; bits: int): shortMMX; overload;
    function shrMMX(const data: intMMX; bits: int): intMMX; overload;
{%endregion}

procedure returnToFPU();

implementation

{%region routine }
    function mmxSupported(): boolean; assembler;
    asm
                pushfd
                or      dword[esp+$00], $00200000
                popfd
                pushfd
                pop     eax
                test    eax, $00200000
                jz      @0
                and     eax, $ffdfffff
                push    eax
                popfd
                mov     eax, $00000001
                cpuid
                test    edx, $00800000
                jz      @0
                mov     eax, $00000001
                ret
        @0:     xor     eax, eax
    end;

    function toByteMMX(byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7: int): byteMMX; inline;
    begin
        result[0] := byte(byte0);
        result[1] := byte(byte1);
        result[2] := byte(byte2);
        result[3] := byte(byte3);
        result[4] := byte(byte4);
        result[5] := byte(byte5);
        result[6] := byte(byte6);
        result[7] := byte(byte7);
    end;

    function toShortMMX(short0, short1, short2, short3: int): shortMMX; inline;
    begin
        result[0] := short(short0);
        result[1] := short(short1);
        result[2] := short(short2);
        result[3] := short(short3);
    end;

    function toIntMMX(int0, int1: int): intMMX; inline;
    begin
        result[0] := int0;
        result[1] := int1;
    end;

    function byteMMX_Array1d_create(length: int): byteMMX_Array1d;
    begin
        setLength(result, length);
    end;

    function shortMMX_Array1d_create(length: int): shortMMX_Array1d;
    begin
        setLength(result, length);
    end;

    function intMMX_Array1d_create(length: int): intMMX_Array1d;
    begin
        setLength(result, length);
    end;

    function toByteMMXArray1d(const arr: array of byteMMX): byteMMX_Array1d;
    begin
        result := byteMMX_Array1d_create(length(arr));
        move(arr[0], result[0], length(result) * sizeof(byteMMX));
    end;

    function toByteMMXArray1d(const arr: byte_Array1d): byteMMX_Array1d;
    begin
        result := toByteMMXArray1d(arr, 0, length(arr));
    end;

    function toByteMMXArray1d(const arr: byte_Array1d; offset, length: int): byteMMX_Array1d;
    var
        lim: int;
        len: int;
        alen: int;
    begin
        lim := offset + length;
        len := System.length(arr);
        if (lim > len) or (lim < offset) or (offset < 0) or (offset > len) then begin
            raise ArrayIndexOutOfBoundsException.create('toByteMMXArray1d: индекс элемента массива выходит из диапазона.');
        end;
        alen := length shr 3;
        if (length and 7) <> 0 then begin
            inc(alen);
        end;
        result := byteMMX_Array1d_create(alen);
        move(arr[offset], result[0], length * sizeof(byte));
    end;

    function toShortMMXArray1d(const arr: array of shortMMX): shortMMX_Array1d;
    begin
        result := shortMMX_Array1d_create(length(arr));
        move(arr[0], result[0], length(result) * sizeof(shortMMX));
    end;

    function toShortMMXArray1d(const arr: short_Array1d): shortMMX_Array1d;
    begin
        result := toShortMMXArray1d(arr, 0, length(arr));
    end;

    function toShortMMXArray1d(const arr: short_Array1d; offset, length: int): shortMMX_Array1d;
    var
        lim: int;
        len: int;
        alen: int;
    begin
        lim := offset + length;
        len := System.length(arr);
        if (lim > len) or (lim < offset) or (offset < 0) or (offset > len) then begin
            raise ArrayIndexOutOfBoundsException.create('toShortMMXArray1d: индекс элемента массива выходит из диапазона.');
        end;
        alen := length shr 2;
        if (length and 3) <> 0 then begin
            inc(alen);
        end;
        result := shortMMX_Array1d_create(alen);
        move(arr[offset], result[0], length * sizeof(short));
    end;

    function toIntMMXArray1d(const arr: array of intMMX): intMMX_Array1d;
    begin
        result := intMMX_Array1d_create(length(arr));
        move(arr[0], result[0], length(result) * sizeof(intMMX));
    end;

    function toIntMMXArray1d(const arr: int_Array1d): intMMX_Array1d;
    begin
        result := toIntMMXArray1d(arr, 0, length(arr));
    end;

    function toIntMMXArray1d(const arr: int_Array1d; offset, length: int): intMMX_Array1d;
    var
        lim: int;
        len: int;
        alen: int;
    begin
        lim := offset + length;
        len := System.length(arr);
        if (lim > len) or (lim < offset) or (offset < 0) or (offset > len) then begin
            raise ArrayIndexOutOfBoundsException.create('toIntMMXArray1d: индекс элемента массива выходит из диапазона.');
        end;
        alen := length shr 1;
        if (length and 1) <> 0 then begin
            inc(alen);
        end;
        result := intMMX_Array1d_create(alen);
        move(arr[offset], result[0], length * sizeof(int));
    end;

    function mulhMMX(const data1, data2: shortMMX): shortMMX; assembler; nostackframe;
    asm
                movq    mm0, [eax+$00]
                pmulhw  mm0, [edx+$00]
                movq    qword[ecx+$00], mm0
    end;

    function mullMMX(const data1, data2: shortMMX): shortMMX; assembler; nostackframe;
    asm
                movq    mm0, [eax+$00]
                pmullw  mm0, [edx+$00]
                movq    qword[ecx+$00], mm0
    end;

    function umulhMMX(const data1, data2: shortMMX): shortMMX; assembler; nostackframe;
    asm
                movq    mm0, [eax+$00]
                pmulhuw mm0, [edx+$00]
                movq    qword[ecx+$00], mm0
    end;

    function salMMX(const data: shortMMX; bits: int): shortMMX; assembler; nostackframe;
    asm
                and     edx, $0f
                push    dword $00
                push    edx
                movq    mm0, [eax+$00]
                psllw   mm0, [esp+$00]
                movq    qword[ecx+$00], mm0
                lea     esp, [esp+$08]
    end;

    function salMMX(const data: intMMX; bits: int): intMMX; assembler; nostackframe;
    asm
                and     edx, $1f
                push    dword $00
                push    edx
                movq    mm0, [eax+$00]
                pslld   mm0, [esp+$00]
                movq    qword[ecx+$00], mm0
                lea     esp, [esp+$08]
    end;

    function sarMMX(const data: shortMMX; bits: int): shortMMX; assembler; nostackframe;
    asm
                and     edx, $0f
                push    dword $00
                push    edx
                movq    mm0, [eax+$00]
                psraw   mm0, [esp+$00]
                movq    qword[ecx+$00], mm0
                lea     esp, [esp+$08]
    end;

    function sarMMX(const data: intMMX; bits: int): intMMX; assembler; nostackframe;
    asm
                and     edx, $1f
                push    dword $00
                push    edx
                movq    mm0, [eax+$00]
                psrad   mm0, [esp+$00]
                movq    qword[ecx+$00], mm0
                lea     esp, [esp+$08]
    end;

    function shrMMX(const data: shortMMX; bits: int): shortMMX; assembler; nostackframe;
    asm
                and     edx, $0f
                push    dword $00
                push    edx
                movq    mm0, [eax+$00]
                psrlw   mm0, [esp+$00]
                movq    qword[ecx+$00], mm0
                lea     esp, [esp+$08]
    end;

    function shrMMX(const data: intMMX; bits: int): intMMX; assembler; nostackframe;
    asm
                and     edx, $1f
                push    dword $00
                push    edx
                movq    mm0, [eax+$00]
                psrld   mm0, [esp+$00]
                movq    qword[ecx+$00], mm0
                lea     esp, [esp+$08]
    end;

    procedure returnToFPU();
    asm
                emms
    end;
{%endregion}

end.

