diff --git a/src/video/Makefile.am b/src/video/Makefile.am index 8472e82bc..d25198987 100644 --- a/src/video/Makefile.am +++ b/src/video/Makefile.am @@ -42,6 +42,7 @@ COMMON_SRCS = \ SDL_yuv_sw.c \ SDL_yuv_sw_c.h \ SDL_yuv_mmx.c \ + mmx.h \ blank_cursor.h \ default_cursor.h diff --git a/src/video/SDL_RLEaccel.c b/src/video/SDL_RLEaccel.c index c10730eb2..e62804f1e 100644 --- a/src/video/SDL_RLEaccel.c +++ b/src/video/SDL_RLEaccel.c @@ -102,6 +102,18 @@ static char rcsid = #include "SDL_memops.h" #include "SDL_RLEaccel_c.h" +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) +#include "mmx.h" +/* Function to check the CPU flags */ +#define MMX_CPU 0x800000 +#define CPU_Flags() Hermes_X86_CPU() +#define X86_ASSEMBLER +#define HermesConverterInterface void +#define HermesClearInterface void +#define STACKCALL +#include "HeadX86.h" +#endif + #ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif @@ -125,6 +137,262 @@ do { \ #define OPAQUE_BLIT(to, from, length, bpp, alpha) \ PIXEL_COPY(to, from, length, bpp) +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) + +#define ALPHA_BLIT32_888MMX(to, from, length, bpp, alpha) \ + do { \ + Uint32 *srcp = (Uint32 *)(from); \ + Uint32 *dstp = (Uint32 *)(to); \ + int i = 0x00FF00FF; \ + movd_m2r(*(&i), mm3); \ + punpckldq_r2r(mm3, mm3); \ + i = 0xFF000000; \ + movd_m2r(*(&i), mm7); \ + punpckldq_r2r(mm7, mm7); \ + i = alpha | alpha << 16; \ + movd_m2r(*(&i), mm4); \ + punpckldq_r2r(mm4, mm4); \ + pcmpeqd_r2r(mm5,mm5); /* set mm5 to "1" */ \ + pxor_r2r(mm7, mm5); /* make clear alpha mask */ \ + i = length; \ + if(i & 1) { \ + movd_m2r((*srcp), mm1); /* src -> mm1 */ \ + punpcklbw_r2r(mm1, mm1); \ + pand_r2r(mm3, mm1); \ + movd_m2r((*dstp), mm2); /* dst -> mm2 */ \ + punpcklbw_r2r(mm2, mm2); \ + pand_r2r(mm3, mm2); \ + psubw_r2r(mm2, mm1); \ + pmullw_r2r(mm4, mm1); \ + psrlw_i2r(8, mm1); \ + paddw_r2r(mm1, mm2); \ + pand_r2r(mm3, mm2); \ + packuswb_r2r(mm2, mm2); \ + pand_r2r(mm5, mm2); /* 00000RGB -> mm2 */ \ + movd_r2m(mm2, *dstp); \ + ++srcp; \ + ++dstp; \ + i--; \ + } \ + for(; i > 0; --i) { \ + movq_m2r((*srcp), mm0); \ + movq_r2r(mm0, mm1); \ + punpcklbw_r2r(mm0, mm0); \ + movq_m2r((*dstp), mm2); \ + punpckhbw_r2r(mm1, mm1); \ + movq_r2r(mm2, mm6); \ + pand_r2r(mm3, mm0); \ + punpcklbw_r2r(mm2, mm2); \ + pand_r2r(mm3, mm1); \ + punpckhbw_r2r(mm6, mm6); \ + pand_r2r(mm3, mm2); \ + psubw_r2r(mm2, mm0); \ + pmullw_r2r(mm4, mm0); \ + pand_r2r(mm3, mm6); \ + psubw_r2r(mm6, mm1); \ + pmullw_r2r(mm4, mm1); \ + psrlw_i2r(8, mm0); \ + paddw_r2r(mm0, mm2); \ + psrlw_i2r(8, mm1); \ + paddw_r2r(mm1, mm6); \ + pand_r2r(mm3, mm2); \ + pand_r2r(mm3, mm6); \ + packuswb_r2r(mm2, mm2); \ + packuswb_r2r(mm6, mm6); \ + psrlq_i2r(32, mm2); \ + psllq_i2r(32, mm6); \ + por_r2r(mm6, mm2); \ + pand_r2r(mm5, mm2); /* 00000RGB -> mm2 */ \ + movq_r2m(mm2, *dstp); \ + srcp += 2; \ + dstp += 2; \ + i--; \ + } \ + emms(); \ + } while(0) + +#define ALPHA_BLIT16_565MMX(to, from, length, bpp, alpha) \ + do { \ + int i, n = 0; \ + Uint16 *srcp = (Uint16 *)(from); \ + Uint16 *dstp = (Uint16 *)(to); \ + Uint32 ALPHA = 0xF800; \ + movd_m2r(*(&ALPHA), mm1); \ + punpcklwd_r2r(mm1, mm1); \ + punpcklwd_r2r(mm1, mm1); \ + ALPHA = 0x07E0; \ + movd_m2r(*(&ALPHA), mm4); \ + punpcklwd_r2r(mm4, mm4); \ + punpcklwd_r2r(mm4, mm4); \ + ALPHA = 0x001F; \ + movd_m2r(*(&ALPHA), mm7); \ + punpcklwd_r2r(mm7, mm7); \ + punpcklwd_r2r(mm7, mm7); \ + alpha &= ~(1+2+4); \ + i = (Uint32)alpha | (Uint32)alpha << 16; \ + movd_m2r(*(&i), mm0); \ + punpckldq_r2r(mm0, mm0); \ + ALPHA = alpha >> 3; \ + i = ((int)(length) & 3); \ + for(; i > 0; --i) { \ + Uint32 s = *srcp++; \ + Uint32 d = *dstp; \ + s = (s | s << 16) & 0x07e0f81f; \ + d = (d | d << 16) & 0x07e0f81f; \ + d += (s - d) * ALPHA >> 5; \ + d &= 0x07e0f81f; \ + *dstp++ = d | d >> 16; \ + n++; \ + } \ + i = (int)(length) - n; \ + for(; i > 0; --i) { \ + movq_m2r((*dstp), mm3); \ + movq_m2r((*srcp), mm2); \ + movq_r2r(mm2, mm5); \ + pand_r2r(mm1 , mm5); \ + psrlq_i2r(11, mm5); \ + movq_r2r(mm3, mm6); \ + pand_r2r(mm1 , mm6); \ + psrlq_i2r(11, mm6); \ + psubw_r2r(mm6, mm5); \ + pmullw_r2r(mm0, mm5); \ + psrlw_i2r(8, mm5); \ + paddw_r2r(mm5, mm6); \ + psllq_i2r(11, mm6); \ + pand_r2r(mm1, mm6); \ + movq_r2r(mm4, mm5); \ + por_r2r(mm7, mm5); \ + pand_r2r(mm5, mm3); \ + por_r2r(mm6, mm3); \ + movq_r2r(mm2, mm5); \ + pand_r2r(mm4 , mm5); \ + psrlq_i2r(5, mm5); \ + movq_r2r(mm3, mm6); \ + pand_r2r(mm4 , mm6); \ + psrlq_i2r(5, mm6); \ + psubw_r2r(mm6, mm5); \ + pmullw_r2r(mm0, mm5); \ + psrlw_i2r(8, mm5); \ + paddw_r2r(mm5, mm6); \ + psllq_i2r(5, mm6); \ + pand_r2r(mm4, mm6); \ + movq_r2r(mm1, mm5); \ + por_r2r(mm7, mm5); \ + pand_r2r(mm5, mm3); \ + por_r2r(mm6, mm3); \ + movq_r2r(mm2, mm5); \ + pand_r2r(mm7 , mm5); \ + movq_r2r(mm3, mm6); \ + pand_r2r(mm7 , mm6); \ + psubw_r2r(mm6, mm5); \ + pmullw_r2r(mm0, mm5); \ + psrlw_i2r(8, mm5); \ + paddw_r2r(mm5, mm6); \ + pand_r2r(mm7, mm6); \ + movq_r2r(mm1, mm5); \ + por_r2r(mm4, mm5); \ + pand_r2r(mm5, mm3); \ + por_r2r(mm6, mm3); \ + movq_r2m(mm3, *dstp); \ + srcp += 4; \ + dstp += 4; \ + i -= 3; \ + } \ + emms(); \ + } while(0) + +#define ALPHA_BLIT16_555MMX(to, from, length, bpp, alpha) \ + do { \ + int i, n = 0; \ + Uint16 *srcp = (Uint16 *)(from); \ + Uint16 *dstp = (Uint16 *)(to); \ + Uint32 ALPHA = 0x7C00; \ + movd_m2r(*(&ALPHA), mm1); \ + punpcklwd_r2r(mm1, mm1); \ + punpcklwd_r2r(mm1, mm1); \ + ALPHA = 0x03E0; \ + movd_m2r(*(&ALPHA), mm4); \ + punpcklwd_r2r(mm4, mm4); \ + punpcklwd_r2r(mm4, mm4); \ + ALPHA = 0x001F; \ + movd_m2r(*(&ALPHA), mm7); \ + punpcklwd_r2r(mm7, mm7); \ + punpcklwd_r2r(mm7, mm7); \ + alpha &= ~(1+2+4); \ + i = (Uint32)alpha | (Uint32)alpha << 16; \ + movd_m2r(*(&i), mm0); \ + punpckldq_r2r(mm0, mm0); \ + i = ((int)(length) & 3); \ + ALPHA = alpha >> 3; \ + for(; i > 0; --i) { \ + Uint32 s = *srcp++; \ + Uint32 d = *dstp; \ + s = (s | s << 16) & 0x03e07c1f; \ + d = (d | d << 16) & 0x03e07c1f; \ + d += (s - d) * ALPHA >> 5; \ + d &= 0x03e07c1f; \ + *dstp++ = d | d >> 16; \ + n++; \ + } \ + i = (int)(length) - n; \ + for(; i > 0; --i) { \ + movq_m2r((*dstp), mm3); \ + movq_m2r((*srcp), mm2); \ + movq_r2r(mm2, mm5); \ + pand_r2r(mm1 , mm5); \ + psrlq_i2r(10, mm5); \ + movq_r2r(mm3, mm6); \ + pand_r2r(mm1 , mm6); \ + psrlq_i2r(10, mm6); \ + psubw_r2r(mm6, mm5); \ + pmullw_r2r(mm0, mm5); \ + psrlw_i2r(8, mm5); \ + paddw_r2r(mm5, mm6); \ + psllq_i2r(10, mm6); \ + pand_r2r(mm1, mm6); \ + movq_r2r(mm4, mm5); \ + por_r2r(mm7, mm5); \ + pand_r2r(mm5, mm3); \ + por_r2r(mm6, mm3); \ + movq_r2r(mm2, mm5); \ + pand_r2r(mm4 , mm5); \ + psrlq_i2r(5, mm5); \ + movq_r2r(mm3, mm6); \ + pand_r2r(mm4 , mm6); \ + psrlq_i2r(5, mm6); \ + psubw_r2r(mm6, mm5); \ + pmullw_r2r(mm0, mm5); \ + psrlw_i2r(8, mm5); \ + paddw_r2r(mm5, mm6); \ + psllq_i2r(5, mm6); \ + pand_r2r(mm4, mm6); \ + movq_r2r(mm1, mm5); \ + por_r2r(mm7, mm5); \ + pand_r2r(mm5, mm3); \ + por_r2r(mm6, mm3); \ + movq_r2r(mm2, mm5); \ + pand_r2r(mm7 , mm5); \ + movq_r2r(mm3, mm6); \ + pand_r2r(mm7 , mm6); \ + psubw_r2r(mm6, mm5); \ + pmullw_r2r(mm0, mm5); \ + psrlw_i2r(8, mm5); \ + paddw_r2r(mm5, mm6); \ + pand_r2r(mm7, mm6); \ + movq_r2r(mm1, mm5); \ + por_r2r(mm4, mm5); \ + pand_r2r(mm5, mm3); \ + por_r2r(mm6, mm3); \ + movq_r2m(mm3, *dstp); \ + srcp += 4; \ + dstp += 4; \ + i -= 3; \ + } \ + emms(); \ + } while(0) + +#endif + /* * For 32bpp pixels on the form 0x00rrggbb: * If we treat the middle component separately, we can process the two @@ -161,12 +429,13 @@ do { \ int i; \ Uint16 *src = (Uint16 *)(from); \ Uint16 *dst = (Uint16 *)(to); \ + Uint32 ALPHA = alpha >> 3; \ for(i = 0; i < (int)(length); i++) { \ Uint32 s = *src++; \ Uint32 d = *dst; \ s = (s | s << 16) & 0x07e0f81f; \ d = (d | d << 16) & 0x07e0f81f; \ - d += (s - d) * alpha >> 5; \ + d += (s - d) * ALPHA >> 5; \ d &= 0x07e0f81f; \ *dst++ = d | d >> 16; \ } \ @@ -177,12 +446,13 @@ do { \ int i; \ Uint16 *src = (Uint16 *)(from); \ Uint16 *dst = (Uint16 *)(to); \ + Uint32 ALPHA = alpha >> 3; \ for(i = 0; i < (int)(length); i++) { \ Uint32 s = *src++; \ Uint32 d = *dst; \ s = (s | s << 16) & 0x03e07c1f; \ d = (d | d << 16) & 0x03e07c1f; \ - d += (s - d) * alpha >> 5; \ + d += (s - d) * ALPHA >> 5; \ d &= 0x03e07c1f; \ *dst++ = d | d >> 16; \ } \ @@ -248,7 +518,48 @@ do { \ } \ } while(0) +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) +#define ALPHA_BLIT32_888_50MMX(to, from, length, bpp, alpha) \ + do { \ + Uint32 *srcp = (Uint32 *)(from); \ + Uint32 *dstp = (Uint32 *)(to); \ + int i = 0x00fefefe; \ + movd_m2r(*(&i), mm4); \ + punpckldq_r2r(mm4, mm4); \ + i = 0x00010101; \ + movd_m2r(*(&i), mm3); \ + punpckldq_r2r(mm3, mm3); \ + i = (int)(length); \ + if( i & 1 ) { \ + Uint32 s = *srcp++; \ + Uint32 d = *dstp; \ + *dstp++ = (((s & 0x00fefefe) + (d & 0x00fefefe)) >> 1) \ + + (s & d & 0x00010101); \ + i--; \ + } \ + for(; i > 0; --i) { \ + movq_m2r((*dstp), mm2); /* dst -> mm2 */ \ + movq_r2r(mm2, mm6); /* dst -> mm6 */ \ + movq_m2r((*srcp), mm1); /* src -> mm1 */ \ + movq_r2r(mm1, mm5); /* src -> mm5 */ \ + pand_r2r(mm4, mm6); /* dst & 0x00fefefe -> mm6 */ \ + pand_r2r(mm4, mm5); /* src & 0x00fefefe -> mm5 */ \ + paddd_r2r(mm6, mm5); /* (dst & 0x00fefefe) + (dst & 0x00fefefe) -> mm5 */ \ + psrld_i2r(1, mm5); \ + pand_r2r(mm1, mm2); /* s & d -> mm2 */ \ + pand_r2r(mm3, mm2); /* s & d & 0x00010101 -> mm2 */ \ + paddd_r2r(mm5, mm2); \ + movq_r2m(mm2, (*dstp)); \ + dstp += 2; \ + srcp += 2; \ + i--; \ + } \ + emms(); \ + } while(0) + +#endif + /* * Special case: 50% alpha (alpha=128) * This is treated specially because it can be optimized very well, and @@ -320,6 +631,7 @@ do { \ #define ALPHA_BLIT16_555_50(to, from, length, bpp, alpha) \ ALPHA_BLIT16_50(to, from, length, bpp, alpha, 0xfbde) +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) #define CHOOSE_BLIT(blitter, alpha, fmt) \ do { \ @@ -345,7 +657,92 @@ do { \ if(alpha == 128) \ blitter(2, Uint8, ALPHA_BLIT16_565_50); \ else { \ - alpha >>= 3; /* use 5 bit alpha */ \ + if((CPU_Flags()&MMX_CPU)!=0) \ + blitter(2, Uint8, ALPHA_BLIT16_565MMX); \ + else \ + blitter(2, Uint8, ALPHA_BLIT16_565); \ + } \ + } else \ + goto general16; \ + break; \ + \ + case 0x7fff: \ + if(fmt->Gmask == 0x03e0 \ + || fmt->Rmask == 0x03e0 \ + || fmt->Bmask == 0x03e0) { \ + if(alpha == 128) \ + blitter(2, Uint8, ALPHA_BLIT16_555_50); \ + else { \ + if((CPU_Flags()&MMX_CPU)!=0) \ + blitter(2, Uint8, ALPHA_BLIT16_555MMX); \ + else \ + blitter(2, Uint8, ALPHA_BLIT16_555); \ + } \ + break; \ + } \ + /* fallthrough */ \ + \ + default: \ + general16: \ + blitter(2, Uint8, ALPHA_BLIT_ANY); \ + } \ + break; \ + \ + case 3: \ + blitter(3, Uint8, ALPHA_BLIT_ANY); \ + break; \ + \ + case 4: \ + if((fmt->Rmask | fmt->Gmask | fmt->Bmask) == 0x00ffffff \ + && (fmt->Gmask == 0xff00 || fmt->Rmask == 0xff00 \ + || fmt->Bmask == 0xff00)) { \ + if(alpha == 128) \ + { \ + if((CPU_Flags()&MMX_CPU)!=0) \ + blitter(4, Uint16, ALPHA_BLIT32_888_50MMX);\ + else \ + blitter(4, Uint16, ALPHA_BLIT32_888_50);\ + } \ + else \ + { \ + if((CPU_Flags()&MMX_CPU)!=0) \ + blitter(4, Uint16, ALPHA_BLIT32_888MMX);\ + else \ + blitter(4, Uint16, ALPHA_BLIT32_888); \ + } \ + } else \ + blitter(4, Uint16, ALPHA_BLIT_ANY); \ + break; \ + } \ + } \ + } while(0) + +#else + +#define CHOOSE_BLIT(blitter, alpha, fmt) \ + do { \ + if(alpha == 255) { \ + switch(fmt->BytesPerPixel) { \ + case 1: blitter(1, Uint8, OPAQUE_BLIT); break; \ + case 2: blitter(2, Uint8, OPAQUE_BLIT); break; \ + case 3: blitter(3, Uint8, OPAQUE_BLIT); break; \ + case 4: blitter(4, Uint16, OPAQUE_BLIT); break; \ + } \ + } else { \ + switch(fmt->BytesPerPixel) { \ + case 1: \ + /* No 8bpp alpha blitting */ \ + break; \ + \ + case 2: \ + switch(fmt->Rmask | fmt->Gmask | fmt->Bmask) { \ + case 0xffff: \ + if(fmt->Gmask == 0x07e0 \ + || fmt->Rmask == 0x07e0 \ + || fmt->Bmask == 0x07e0) { \ + if(alpha == 128) \ + blitter(2, Uint8, ALPHA_BLIT16_565_50); \ + else { \ blitter(2, Uint8, ALPHA_BLIT16_565); \ } \ } else \ @@ -359,7 +756,6 @@ do { \ if(alpha == 128) \ blitter(2, Uint8, ALPHA_BLIT16_555_50); \ else { \ - alpha >>= 3; /* use 5 bit alpha */ \ blitter(2, Uint8, ALPHA_BLIT16_555); \ } \ break; \ @@ -391,6 +787,7 @@ do { \ } \ } while(0) +#endif /* * This takes care of the case when the surface is clipped on the left and/or diff --git a/src/video/SDL_blit.c b/src/video/SDL_blit.c index 90c8ca399..bd9bcd5fe 100644 --- a/src/video/SDL_blit.c +++ b/src/video/SDL_blit.c @@ -37,6 +37,19 @@ static char rcsid = #include "SDL_pixels_c.h" #include "SDL_memops.h" +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) +#include "mmx.h" +/* Function to check the CPU flags */ +#define MMX_CPU 0x800000 +#define SSE_CPU 0x2000000 +#define CPU_Flags() Hermes_X86_CPU() +#define X86_ASSEMBLER +#define HermesConverterInterface void +#define HermesClearInterface void +#define STACKCALL +#include "HeadX86.h" +#endif + /* The general purpose software blit routine */ static int SDL_SoftBlit(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect) @@ -106,11 +119,54 @@ static int SDL_SoftBlit(SDL_Surface *src, SDL_Rect *srcrect, return(okay ? 0 : -1); } +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) +void SDL_memcpyMMX(char* to,char* from,int len) +{ + int i; + + for(i=0; id_width*info->dst->BytesPerPixel; h = info->d_height; @@ -118,6 +174,33 @@ static void SDL_BlitCopy(SDL_BlitInfo *info) dst = info->d_pixels; srcskip = w+info->s_skip; dstskip = w+info->d_skip; +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) + f=CPU_Flags(); + if((f&(MMX_CPU|SSE_CPU))==(MMX_CPU|SSE_CPU)) + { + while ( h-- ) { + SDL_memcpySSE(dst, src, w); + src += srcskip; + dst += dstskip; + } + __asm__ __volatile__ ( + " emms\n" + ::); + } + else + if((f&(MMX_CPU))!=0) + { + while ( h-- ) { + SDL_memcpyMMX(dst, src, w); + src += srcskip; + dst += dstskip; + } + __asm__ __volatile__ ( + " emms\n" + ::); + } + else +#endif while ( h-- ) { SDL_memcpy(dst, src, w); src += srcskip; diff --git a/src/video/SDL_blit.h b/src/video/SDL_blit.h index 7fde5d36a..a082b4c7e 100644 --- a/src/video/SDL_blit.h +++ b/src/video/SDL_blit.h @@ -410,12 +410,86 @@ do { \ } \ } +/* 2 - times unrolled loop */ +#define DUFFS_LOOP_DOUBLE2(pixel_copy_increment, \ + double_pixel_copy_increment, width) \ +{ int n, w = width; \ + if( w & 1 ) { \ + pixel_copy_increment; \ + w--; \ + } \ + if ( w > 0 ) { \ + n = ( w + 2) / 4; \ + switch( w & 2 ) { \ + case 0: do { double_pixel_copy_increment; \ + case 2: double_pixel_copy_increment; \ + } while ( --n > 0 ); \ + } \ + } \ +} + +/* 2 - times unrolled loop 4 pixels */ +#define DUFFS_LOOP_QUATRO2(pixel_copy_increment, \ + double_pixel_copy_increment, \ + quatro_pixel_copy_increment, width) \ +{ int n, w = width; \ + if(w & 1) { \ + pixel_copy_increment; \ + w--; \ + } \ + if(w & 2) { \ + double_pixel_copy_increment; \ + w -= 2; \ + } \ + if ( w > 0 ) { \ + n = ( w + 7 ) / 8; \ + switch( w & 4 ) { \ + case 0: do { quatro_pixel_copy_increment; \ + case 4: quatro_pixel_copy_increment; \ + } while ( --n > 0 ); \ + } \ + } \ +} + /* Use the 8-times version of the loop by default */ #define DUFFS_LOOP(pixel_copy_increment, width) \ DUFFS_LOOP8(pixel_copy_increment, width) #else +/* Don't use Duff's device to unroll loops */ +#define DUFFS_LOOP_DOUBLE2(pixel_copy_increment, \ + double_pixel_copy_increment, width) \ +{ int n = width; \ + if( n & 1 ) { \ + pixel_copy_increment; \ + n--; \ + } \ + n=n>>1; \ + for(; n > 0; --n) { \ + double_pixel_copy_increment; \ + } \ +} + +/* Don't use Duff's device to unroll loops */ +#define DUFFS_LOOP_QUATRO2(pixel_copy_increment, \ + double_pixel_copy_increment, \ + quatro_pixel_copy_increment, width) \ +{ int n = width; \ + if(n & 1) { \ + pixel_copy_increment; \ + n--; \ + } \ + if(n & 2) { \ + double_pixel_copy_increment; \ + n -= 2; \ + } \ + n=n>>2; \ + for(; n > 0; --n) { \ + quatro_pixel_copy_increment; \ + } \ +} + /* Don't use Duff's device to unroll loops */ #define DUFFS_LOOP(pixel_copy_increment, width) \ { int n; \ diff --git a/src/video/SDL_blit_A.c b/src/video/SDL_blit_A.c index 39dcfd1f6..e58537acc 100644 --- a/src/video/SDL_blit_A.c +++ b/src/video/SDL_blit_A.c @@ -31,6 +31,19 @@ static char rcsid = #include "SDL_video.h" #include "SDL_blit.h" +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) +#include "mmx.h" +/* Function to check the CPU flags */ +#define MMX_CPU 0x800000 +#define TDNOW_CPU 0x80000000 +#define CPU_Flags() Hermes_X86_CPU() +#define X86_ASSEMBLER +#define HermesConverterInterface void +#define HermesClearInterface void +#define STACKCALL +#include "HeadX86.h" +#endif + /* Functions to perform alpha blended blitting */ /* N->1 blending with per-surface alpha */ @@ -195,6 +208,222 @@ static void BlitNto1SurfaceAlphaKey(SDL_BlitInfo *info) } } +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) +/* fast RGB888->(A)RGB888 blending with surface alpha=128 special case */ +static void BlitRGBtoRGBSurfaceAlpha128MMX(SDL_BlitInfo *info) +{ + int width = info->d_width; + int height = info->d_height; + Uint32 *srcp = (Uint32 *)info->s_pixels; + int srcskip = info->s_skip >> 2; + Uint32 *dstp = (Uint32 *)info->d_pixels; + int dstskip = info->d_skip >> 2; + Uint8 load[8]; + + *(Uint64 *)load = 0x00fefefe00fefefe;/* alpha128 mask */ + movq_m2r(*load, mm4); /* alpha128 mask -> mm4 */ + *(Uint64 *)load = 0x0001010100010101;/* !alpha128 mask */ + movq_m2r(*load, mm3); /* !alpha128 mask -> mm3 */ + *(Uint64 *)load = 0xFF000000FF000000;/* dst alpha mask */ + movq_m2r(*load, mm7); /* dst alpha mask -> mm7 */ + while(height--) { + DUFFS_LOOP_DOUBLE2( + { + Uint32 s = *srcp++; + Uint32 d = *dstp; + *dstp++ = ((((s & 0x00fefefe) + (d & 0x00fefefe)) >> 1) + + (s & d & 0x00010101)) | 0xff000000; + },{ + movq_m2r((*dstp), mm2);/* 2 x dst -> mm2(ARGBARGB) */ + movq_r2r(mm2, mm6); /* 2 x dst -> mm6(ARGBARGB) */ + + movq_m2r((*srcp), mm1);/* 2 x src -> mm1(ARGBARGB) */ + movq_r2r(mm1, mm5); /* 2 x src -> mm5(ARGBARGB) */ + + pand_r2r(mm4, mm6); /* dst & mask -> mm6 */ + pand_r2r(mm4, mm5); /* src & mask -> mm5 */ + paddd_r2r(mm6, mm5); /* mm6 + mm5 -> mm5 */ + psrld_i2r(1, mm5); /* mm5 >> 1 -> mm5 */ + + pand_r2r(mm1, mm2); /* src & dst -> mm2 */ + pand_r2r(mm3, mm2); /* mm2 & !mask -> mm2 */ + paddd_r2r(mm5, mm2); /* mm5 + mm2 -> mm2 */ + por_r2r(mm7, mm2); /* mm7(full alpha) | mm2 -> mm2 */ + movq_r2m(mm2, (*dstp));/* mm2 -> 2 x dst pixels */ + dstp += 2; + srcp += 2; + }, width); + srcp += srcskip; + dstp += dstskip; + } + emms(); +} + +/* fast RGB888->(A)RGB888 blending with surface alpha */ +static void BlitRGBtoRGBSurfaceAlphaMMX(SDL_BlitInfo *info) +{ + unsigned alpha = info->src->alpha; + if(alpha == 128) { + BlitRGBtoRGBSurfaceAlpha128MMX(info); + } else { + int width = info->d_width; + int height = info->d_height; + Uint32 *srcp = (Uint32 *)info->s_pixels; + int srcskip = info->s_skip >> 2; + Uint32 *dstp = (Uint32 *)info->d_pixels; + int dstskip = info->d_skip >> 2; + Uint8 load[8] = {alpha, alpha, alpha, alpha, + alpha, alpha, alpha, alpha}; + + movq_m2r(*load, mm4); /* alpha -> mm4 */ + *(Uint64 *)load = 0x00FF00FF00FF00FF; + movq_m2r(*load, mm3); /* mask -> mm3 */ + pand_r2r(mm3, mm4); /* mm4 & mask -> 0A0A0A0A -> mm4 */ + *(Uint64 *)load = 0xFF000000FF000000;/* dst alpha mask */ + movq_m2r(*load, mm7); /* dst alpha mask -> mm7 */ + + while(height--) { + DUFFS_LOOP_DOUBLE2({ + /* One Pixel Blend */ + movd_m2r((*srcp), mm1);/* src(ARGB) -> mm1 (0000ARGB)*/ + punpcklbw_r2r(mm1, mm1); /* AARRGGBB -> mm1 */ + pand_r2r(mm3, mm1); /* 0A0R0G0B -> mm1 */ + + movd_m2r((*dstp), mm2);/* dst(ARGB) -> mm2 (0000ARGB)*/ + movq_r2r(mm2, mm6);/* dst(ARGB) -> mm6 (0000ARGB)*/ + punpcklbw_r2r(mm2, mm2); /* AARRGGBB -> mm2 */ + pand_r2r(mm3, mm2); /* 0A0R0G0B -> mm2 */ + + psubw_r2r(mm2, mm1);/* src - dst -> mm1 */ + pmullw_r2r(mm4, mm1); /* mm1 * alpha -> mm1 */ + psrlw_i2r(8, mm1); /* mm1 >> 8 -> mm1 */ + paddw_r2r(mm1, mm2); /* mm1 + mm2(dst) -> mm2 */ + pand_r2r(mm3, mm2); /* 0A0R0G0B -> mm2 */ + packuswb_r2r(mm2, mm2); /* ARGBARGB -> mm2 */ + por_r2r(mm7, mm2); /* mm7(full alpha) | mm2 -> mm2 */ + movd_r2m(mm2, *dstp);/* mm2 -> pixel */ + ++srcp; + ++dstp; + },{ + /* Two Pixels Blend */ + movq_m2r((*srcp), mm0);/* 2 x src -> mm0(ARGBARGB)*/ + movq_r2r(mm0, mm1); /* 2 x src -> mm1(ARGBARGB) */ + punpcklbw_r2r(mm0, mm0); /* low - AARRGGBB -> mm0 */ + pand_r2r(mm3, mm0); /* 0A0R0G0B -> mm0(src1) */ + punpckhbw_r2r(mm1, mm1); /* high - AARRGGBB -> mm1 */ + pand_r2r(mm3, mm1); /* 0A0R0G0B -> mm1(src2) */ + + movq_m2r((*dstp), mm2);/* 2 x dst -> mm2(ARGBARGB) */ + movq_r2r(mm2, mm5); /* 2 x dst -> mm5(ARGBARGB) */ + movq_r2r(mm2, mm6); /* 2 x dst -> mm6(ARGBARGB) */ + punpcklbw_r2r(mm2, mm2); /* low - AARRGGBB -> mm2 */ + punpckhbw_r2r(mm6, mm6); /* high - AARRGGBB -> mm6 */ + pand_r2r(mm3, mm2); /* 0A0R0G0B -> mm2(dst1) */ + + psubw_r2r(mm2, mm0);/* src1 - dst1 -> mm0 */ + pmullw_r2r(mm4, mm0); /* mm0 * alpha -> mm0 */ + pand_r2r(mm3, mm6); /* 0A0R0G0B -> mm6(dst2) */ + psrlw_i2r(8, mm0); /* mm0 >> 8 -> mm1 */ + psubw_r2r(mm6, mm1);/* src2 - dst2 -> mm1 */ + pmullw_r2r(mm4, mm1); /* mm1 * alpha -> mm1 */ + paddw_r2r(mm0, mm2); /* mm0 + mm2(dst1) -> mm2 */ + psrlw_i2r(8, mm1); /* mm1 >> 8 -> mm0 */ + pand_r2r(mm3, mm2); /* 0A0R0G0B -> mm2 */ + paddw_r2r(mm1, mm6); /* mm1 + mm6(dst2) -> mm6 */ + pand_r2r(mm3, mm6); /* 0A0R0G0B -> mm6 */ + packuswb_r2r(mm2, mm2); /* ARGBARGB -> mm2 */ + packuswb_r2r(mm6, mm6); /* ARGBARGB -> mm6 */ + psrlq_i2r(32, mm2); /* mm2 >> 32 -> mm2 */ + psllq_i2r(32, mm6); /* mm6 << 32 -> mm6 */ + por_r2r(mm6, mm2); /* mm6 | mm2 -> mm2 */ + por_r2r(mm7, mm2); /* mm7(full alpha) | mm2 -> mm2 */ + movq_r2m(mm2, *dstp);/* mm2 -> 2 x pixel */ + srcp += 2; + dstp += 2; + }, width); + srcp += srcskip; + dstp += dstskip; + } + emms(); + } +} + +/* fast ARGB888->(A)RGB888 blending with pixel alpha */ +static void BlitRGBtoRGBPixelAlphaMMX(SDL_BlitInfo *info) +{ + int width = info->d_width; + int height = info->d_height; + Uint32 *srcp = (Uint32 *)info->s_pixels; + int srcskip = info->s_skip >> 2; + Uint32 *dstp = (Uint32 *)info->d_pixels; + int dstskip = info->d_skip >> 2; + Uint32 alpha = 0; + Uint8 load[8]; + + *(Uint64 *)load = 0x00FF00FF00FF00FF; + movq_m2r(*load, mm3); /* mask -> mm2 */ + *(Uint64 *)load = 0x00FF000000000000; + movq_m2r(*load, mm7); /* dst alpha mask -> mm2 */ + *(Uint64 *)load = 0x00FFFFFF00FFFFFF; + movq_m2r(*load, mm0); /* alpha 255 mask -> mm0 */ + *(Uint64 *)load = 0xFF000000FF000000; + movq_m2r(*load, mm6); /* alpha 255 !mask -> mm6 */ + while(height--) { + DUFFS_LOOP4({ + alpha = *srcp; + alpha >>= 24; + /* FIXME: Here we special-case opaque alpha since the + compositioning used (>>8 instead of /255) doesn't handle + it correctly. Also special-case alpha=0 for speed? + Benchmark this! */ + if(alpha) { + if(alpha == SDL_ALPHA_OPAQUE) { + movd_m2r((*srcp), mm1);/* src(ARGB) -> mm1 (0000ARGB)*/ + movd_m2r((*dstp), mm2);/* dst(ARGB) -> mm2 (0000ARGB)*/ + pand_r2r(mm0, mm1); + pand_r2r(mm6, mm2); + por_r2r(mm1, mm2); + movd_r2m(mm2, (*dstp)); + } else { + movd_m2r((*srcp), mm1);/* src(ARGB) -> mm1 (0000ARGB)*/ + punpcklbw_r2r(mm1, mm1); /* AARRGGBB -> mm1 */ + pand_r2r(mm3, mm1); /* 0A0R0G0B -> mm1 */ + + movd_m2r((*dstp), mm2);/* dst(ARGB) -> mm2 (0000ARGB)*/ + punpcklbw_r2r(mm2, mm2); /* AARRGGBB -> mm2 */ + pand_r2r(mm3, mm2); /* 0A0R0G0B -> mm2 */ + + movq_r2r(mm2, mm5);/* mm2(0A0R0G0B) -> mm5 */ + pand_r2r(mm7, mm5); /* mm5 & dst alpha mask -> mm5(0A000000) */ + psrlq_i2r(24, mm5); /* mm5 >> 24 -> mm5 (0000A000)*/ + + movq_r2r(mm1, mm4);/* mm1(0A0R0G0B) -> mm4 */ + psrlq_i2r(48, mm4); /* mm4 >> 48 -> mm4(0000000A) */ + punpcklwd_r2r(mm4, mm4); /* 00000A0A -> mm4 */ + punpcklwd_r2r(mm4, mm4); /* 0A0A0A0A -> mm4 */ + + /* blend */ + psubw_r2r(mm2, mm1);/* src - dst -> mm1 */ + pmullw_r2r(mm4, mm1); /* mm1 * alpha -> mm1 */ + psrlw_i2r(8, mm1); /* mm1 >> 8 -> mm1 */ + paddw_r2r(mm1, mm2); /* mm1 + mm2(dst) -> mm2 */ + pand_r2r(mm3, mm2); /* 0A0R0G0B -> mm2 */ + packuswb_r2r(mm2, mm2); /* ARGBARGB -> mm2 */ + pand_r2r(mm0, mm2); /* 0RGB0RGB -> mm2 */ + por_r2r(mm5, mm2); /* dst alpha | mm2 -> mm2 */ + movd_r2m(mm2, *dstp);/* mm2 -> dst */ + } + } + ++srcp; + ++dstp; + }, width); + srcp += srcskip; + dstp += dstskip; + } + emms(); +} +#endif + /* fast RGB888->(A)RGB888 blending with surface alpha=128 special case */ static void BlitRGBtoRGBSurfaceAlpha128(SDL_BlitInfo *info) { @@ -230,13 +459,14 @@ static void BlitRGBtoRGBSurfaceAlpha(SDL_BlitInfo *info) int srcskip = info->s_skip >> 2; Uint32 *dstp = (Uint32 *)info->d_pixels; int dstskip = info->d_skip >> 2; + Uint32 s; + Uint32 d; + Uint32 s1; + Uint32 d1; while(height--) { - DUFFS_LOOP4({ - Uint32 s; - Uint32 d; - Uint32 s1; - Uint32 d1; + DUFFS_LOOP_DOUBLE2({ + /* One Pixel Blend */ s = *srcp; d = *dstp; s1 = s & 0xff00ff; @@ -249,6 +479,35 @@ static void BlitRGBtoRGBSurfaceAlpha(SDL_BlitInfo *info) *dstp = d1 | d | 0xff000000; ++srcp; ++dstp; + },{ + /* Two Pixels Blend */ + s = *srcp; + d = *dstp; + s1 = s & 0xff00ff; + d1 = d & 0xff00ff; + d1 += (s1 - d1) * alpha >> 8; + d1 &= 0xff00ff; + + s = ((s & 0xff00) >> 8) | + ((srcp[1] & 0xff00) << 8); + d = ((d & 0xff00) >> 8) | + ((dstp[1] & 0xff00) << 8); + d += (s - d) * alpha >> 8; + d &= 0x00ff00ff; + + *dstp++ = d1 | ((d << 8) & 0xff00) | 0xff000000; + ++srcp; + + s1 = *srcp; + d1 = *dstp; + s1 &= 0xff00ff; + d1 &= 0xff00ff; + d1 += (s1 - d1) * alpha >> 8; + d1 &= 0xff00ff; + + *dstp = d1 | ((d >> 8) & 0xff00) | 0xff000000; + ++srcp; + ++dstp; }, width); srcp += srcskip; dstp += dstskip; @@ -278,9 +537,10 @@ static void BlitRGBtoRGBPixelAlpha(SDL_BlitInfo *info) compositioning used (>>8 instead of /255) doesn't handle it correctly. Also special-case alpha=0 for speed? Benchmark this! */ - if(alpha == SDL_ALPHA_OPAQUE) { + if(alpha) { + if(alpha == SDL_ALPHA_OPAQUE) { *dstp = (s & 0x00ffffff) | (*dstp & 0xff000000); - } else { + } else { /* * take out the middle component (green), and process * the other two in parallel. One multiply less. @@ -294,6 +554,7 @@ static void BlitRGBtoRGBPixelAlpha(SDL_BlitInfo *info) d &= 0xff00; d = (d + ((s - d) * alpha >> 8)) & 0xff00; *dstp = d1 | d | dalpha; + } } ++srcp; ++dstp; @@ -303,6 +564,101 @@ static void BlitRGBtoRGBPixelAlpha(SDL_BlitInfo *info) } } +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) +/* fast (as in MMX with prefetch) ARGB888->(A)RGB888 blending with pixel alpha */ +inline static void BlitRGBtoRGBPixelAlphaMMX3DNOW(SDL_BlitInfo *info) +{ + int width = info->d_width; + int height = info->d_height; + Uint32 *srcp = (Uint32 *)info->s_pixels; + int srcskip = info->s_skip >> 2; + Uint32 *dstp = (Uint32 *)info->d_pixels; + int dstskip = info->d_skip >> 2; + + Uint32 s; + Uint32 alpha; + + __asm__ ( + /* make mm6 all zeros. */ + "pxor %%mm6, %%mm6\n" + + /* Make a mask to preserve the alpha. */ + "pcmpeqb %%mm7, %%mm7\n\t" /* mm7(s) = FF FF FF FF | FF FF FF FF */ + "psrlq $16, %%mm7\n\t" /* mm7(s) = 00 00 FF FF | FF FF FF FF */ + + : ); + + while(height--) { + + DUFFS_LOOP4({ + + __asm__ ( + "prefetch 64(%0)\n" + "prefetch 64(%1)\n" + : : "r" (srcp), "r" (dstp) ); + + s = *srcp; + alpha = s >> 24; + /* FIXME: Here we special-case opaque alpha since the + compositioning used (>>8 instead of /255) doesn't handle + it correctly. Also special-case alpha=0 for speed? + Benchmark this! */ + + if(alpha == SDL_ALPHA_OPAQUE) { + *dstp = (s & 0x00ffffff) | (*dstp & 0xff000000); + } + + else { + __asm__ ( + /* load in the source, and dst. */ + "movd (%0), %%mm0\n" /* mm0(s) = 0 0 0 0 | As Rs Gs Bs */ + "movd (%1), %%mm1\n" /* mm1(d) = 0 0 0 0 | Ad Rd Gd Bd */ + + /* Move the src alpha into mm2 */ + + /* if supporting pshufw */ + /*"pshufw $0x55, %%mm0, %%mm2\n" */ /* mm2 = 0 As 0 As | 0 As 0 As */ + /*"psrlw $8, %%mm2\n" */ + + /* else: */ + "movq %%mm0, %%mm2\n" + "psrld $24, %%mm2\n" /* mm2 = 0 0 0 0 | 0 0 0 As */ + "punpcklwd %%mm2, %%mm2\n" /* mm2 = 0 0 0 0 | 0 As 0 As */ + "punpckldq %%mm2, %%mm2\n" /* mm2 = 0 As 0 As | 0 As 0 As */ + + /* move the colors into words. */ + "punpcklbw %%mm6, %%mm0\n" /* mm0 = 0 As 0 Rs | 0 Gs 0 Bs */ + "punpcklbw %%mm6, %%mm1\n" /* mm0 = 0 Ad 0 Rd | 0 Gd 0 Bd */ + + /* src - dst */ + "psubw %%mm1, %%mm0\n" /* mm0 = As-Ad Rs-Rd | Gs-Gd Bs-Bd */ + + /* A * (src-dst) */ + "pmullw %%mm2, %%mm0\n" /* mm0 = As*As-d As*Rs-d | As*Gs-d As*Bs-d */ + "pand %%mm7, %%mm0\n" /* to preserve dest alpha */ + "psrlw $8, %%mm0\n" /* mm0 = Ac>>8 Rc>>8 | Gc>>8 Bc>>8 */ + "paddb %%mm1, %%mm0\n" /* mm0 = Ac+Ad Rc+Rd | Gc+Gd Bc+Bd */ + + "packuswb %%mm0, %%mm0\n" /* mm0 = | Ac Rc Gc Bc */ + + "movd %%mm0, (%1)\n" /* result in mm0 */ + + : : "r" (srcp), "r" (dstp) ); + + } + ++srcp; + ++dstp; + }, width); + srcp += srcskip; + dstp += dstskip; + } + + __asm__ ( + "emms\n" + : ); +} +#endif + /* 16bpp special case for per-surface alpha=50%: blend 2 pixels in parallel */ /* blend a single 16 bit pixel at 50% */ @@ -410,6 +766,308 @@ static void Blit16to16SurfaceAlpha128(SDL_BlitInfo *info, Uint16 mask) } } +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) +/* fast RGB565->RGB565 blending with surface alpha */ +static void Blit565to565SurfaceAlphaMMX(SDL_BlitInfo *info) +{ + unsigned alpha = info->src->alpha; /* downscale alpha to 5 bits */ + if(alpha == 128) { + Blit16to16SurfaceAlpha128(info, 0xf7de); + } else { + int width = info->d_width; + int height = info->d_height; + Uint16 *srcp = (Uint16 *)info->s_pixels; + int srcskip = info->s_skip >> 1; + Uint16 *dstp = (Uint16 *)info->d_pixels; + int dstskip = info->d_skip >> 1; + Uint32 s, d; + Uint8 load[8]; + + alpha &= ~(1+2+4); /* cut alpha to get the exact same behaviour */ + *(Uint64 *)load = alpha; + alpha >>= 3; /* downscale alpha to 5 bits */ + + movq_m2r(*load, mm0); /* alpha(0000000A) -> mm0 */ + punpcklwd_r2r(mm0, mm0); /* 00000A0A -> mm0 */ + punpcklwd_r2r(mm0, mm0); /* 0A0A0A0A -> mm0 */ + + /* Setup the 565 color channel masks */ + *(Uint64 *)load = 0xF800F800F800F800; + movq_m2r(*load, mm1); /* MASKRED -> mm1 */ + *(Uint64 *)load = 0x07E007E007E007E0; + movq_m2r(*load, mm4); /* MASKGREEN -> mm4 */ + *(Uint64 *)load = 0x001F001F001F001F; + movq_m2r(*load, mm7); /* MASKBLUE -> mm7 */ + while(height--) { + DUFFS_LOOP_QUATRO2( + { + s = *srcp++; + d = *dstp; + /* + * shift out the middle component (green) to + * the high 16 bits, and process all three RGB + * components at the same time. + */ + s = (s | s << 16) & 0x07e0f81f; + d = (d | d << 16) & 0x07e0f81f; + d += (s - d) * alpha >> 5; + d &= 0x07e0f81f; + *dstp++ = d | d >> 16; + },{ + s = *srcp++; + d = *dstp; + /* + * shift out the middle component (green) to + * the high 16 bits, and process all three RGB + * components at the same time. + */ + s = (s | s << 16) & 0x07e0f81f; + d = (d | d << 16) & 0x07e0f81f; + d += (s - d) * alpha >> 5; + d &= 0x07e0f81f; + *dstp++ = d | d >> 16; + s = *srcp++; + d = *dstp; + /* + * shift out the middle component (green) to + * the high 16 bits, and process all three RGB + * components at the same time. + */ + s = (s | s << 16) & 0x07e0f81f; + d = (d | d << 16) & 0x07e0f81f; + d += (s - d) * alpha >> 5; + d &= 0x07e0f81f; + *dstp++ = d | d >> 16; + },{ + movq_m2r((*dstp), mm3);/* 4 dst pixels -> mm3 */ + movq_m2r((*srcp), mm2);/* 4 src pixels -> mm2 */ + + /* RED */ + movq_r2r(mm2, mm5); /* src -> mm5 */ + pand_r2r(mm1 , mm5); /* src & MASKRED -> mm5 */ + psrlq_i2r(11, mm5); /* mm5 >> 11 -> mm5 [000r 000r 000r 000r] */ + + movq_r2r(mm3, mm6); /* dst -> mm6 */ + pand_r2r(mm1 , mm6); /* dst & MASKRED -> mm6 */ + psrlq_i2r(11, mm6); /* mm6 >> 11 -> mm6 [000r 000r 000r 000r] */ + + /* blend */ + psubw_r2r(mm6, mm5);/* src - dst -> mm5 */ + pmullw_r2r(mm0, mm5); /* mm5 * alpha -> mm5 */ + psrlw_i2r(8, mm5); /* mm5 >> 8 -> mm5 */ + paddw_r2r(mm5, mm6); /* mm5 + mm6(dst) -> mm6 */ + psllq_i2r(11, mm6); /* mm6 << 11 -> mm6 */ + pand_r2r(mm1, mm6); /* mm6 & MASKRED -> mm6 */ + + movq_r2r(mm4, mm5); /* MASKGREEN -> mm5 */ + por_r2r(mm7, mm5); /* MASKBLUE | mm5 -> mm5 */ + pand_r2r(mm5, mm3); /* mm3 & mm5(!MASKRED) -> mm3 */ + por_r2r(mm6, mm3); /* save new reds in dsts */ + + /* green */ + movq_r2r(mm2, mm5); /* src -> mm5 */ + pand_r2r(mm4 , mm5); /* src & MASKGREEN -> mm5 */ + psrlq_i2r(5, mm5); /* mm5 >> 5 -> mm5 [000g 000g 000g 000g] */ + + movq_r2r(mm3, mm6); /* dst -> mm6 */ + pand_r2r(mm4 , mm6); /* dst & MASKGREEN -> mm6 */ + psrlq_i2r(5, mm6); /* mm6 >> 5 -> mm6 [000g 000g 000g 000g] */ + + /* blend */ + psubw_r2r(mm6, mm5);/* src - dst -> mm5 */ + pmullw_r2r(mm0, mm5); /* mm5 * alpha -> mm5 */ + psrlw_i2r(8, mm5); /* mm5 >> 8 -> mm5 */ + paddw_r2r(mm5, mm6); /* mm5 + mm6(dst) -> mm6 */ + psllq_i2r(5, mm6); /* mm6 << 5 -> mm6 */ + pand_r2r(mm4, mm6); /* mm6 & MASKGREEN -> mm6 */ + + movq_r2r(mm1, mm5); /* MASKRED -> mm5 */ + por_r2r(mm7, mm5); /* MASKBLUE | mm5 -> mm5 */ + pand_r2r(mm5, mm3); /* mm3 & mm5(!MASKGREEN) -> mm3 */ + por_r2r(mm6, mm3); /* save new greens in dsts */ + + /* blue */ + movq_r2r(mm2, mm5); /* src -> mm5 */ + pand_r2r(mm7 , mm5); /* src & MASKRED -> mm5[000b 000b 000b 000b] */ + + movq_r2r(mm3, mm6); /* dst -> mm6 */ + pand_r2r(mm7 , mm6); /* dst & MASKBLUE -> mm6[000b 000b 000b 000b] */ + + /* blend */ + psubw_r2r(mm6, mm5);/* src - dst -> mm5 */ + pmullw_r2r(mm0, mm5); /* mm5 * alpha -> mm5 */ + psrlw_i2r(8, mm5); /* mm5 >> 8 -> mm5 */ + paddw_r2r(mm5, mm6); /* mm5 + mm6(dst) -> mm6 */ + pand_r2r(mm7, mm6); /* mm6 & MASKBLUE -> mm6 */ + + movq_r2r(mm1, mm5); /* MASKRED -> mm5 */ + por_r2r(mm4, mm5); /* MASKGREEN | mm5 -> mm5 */ + pand_r2r(mm5, mm3); /* mm3 & mm5(!MASKBLUE) -> mm3 */ + por_r2r(mm6, mm3); /* save new blues in dsts */ + + movq_r2m(mm3, *dstp);/* mm3 -> 4 dst pixels */ + + srcp += 4; + dstp += 4; + }, width); + srcp += srcskip; + dstp += dstskip; + } + emms(); + } +} + +/* fast RGB555->RGB555 blending with surface alpha */ +static void Blit555to555SurfaceAlphaMMX(SDL_BlitInfo *info) +{ + unsigned alpha = info->src->alpha; /* downscale alpha to 5 bits */ + if(alpha == 128) { + Blit16to16SurfaceAlpha128(info, 0xfbde); + } else { + int width = info->d_width; + int height = info->d_height; + Uint16 *srcp = (Uint16 *)info->s_pixels; + int srcskip = info->s_skip >> 1; + Uint16 *dstp = (Uint16 *)info->d_pixels; + int dstskip = info->d_skip >> 1; + Uint32 s, d; + Uint8 load[8]; + + alpha &= ~(1+2+4); /* cut alpha to get the exact same behaviour */ + *(Uint64 *)load = alpha; + alpha >>= 3; /* downscale alpha to 5 bits */ + + movq_m2r(*load, mm0); /* alpha(0000000A) -> mm0 */ + punpcklwd_r2r(mm0, mm0); /* 00000A0A -> mm0 */ + punpcklwd_r2r(mm0, mm0); /* 0A0A0A0A -> mm0 */ + + /* Setup the 555 color channel masks */ + *(Uint64 *)load = 0x7C007C007C007C00; + movq_m2r(*load, mm1); /* MASKRED -> mm1 */ + *(Uint64 *)load = 0x03E003E003E003E0; + movq_m2r(*load, mm4); /* MASKGREEN -> mm4 */ + *(Uint64 *)load = 0x001F001F001F001F; + movq_m2r(*load, mm7); /* MASKBLUE -> mm7 */ + while(height--) { + DUFFS_LOOP_QUATRO2( + { + s = *srcp++; + d = *dstp; + /* + * shift out the middle component (green) to + * the high 16 bits, and process all three RGB + * components at the same time. + */ + s = (s | s << 16) & 0x03e07c1f; + d = (d | d << 16) & 0x03e07c1f; + d += (s - d) * alpha >> 5; + d &= 0x03e07c1f; + *dstp++ = d | d >> 16; + },{ + s = *srcp++; + d = *dstp; + /* + * shift out the middle component (green) to + * the high 16 bits, and process all three RGB + * components at the same time. + */ + s = (s | s << 16) & 0x03e07c1f; + d = (d | d << 16) & 0x03e07c1f; + d += (s - d) * alpha >> 5; + d &= 0x03e07c1f; + *dstp++ = d | d >> 16; + s = *srcp++; + d = *dstp; + /* + * shift out the middle component (green) to + * the high 16 bits, and process all three RGB + * components at the same time. + */ + s = (s | s << 16) & 0x03e07c1f; + d = (d | d << 16) & 0x03e07c1f; + d += (s - d) * alpha >> 5; + d &= 0x03e07c1f; + *dstp++ = d | d >> 16; + },{ + movq_m2r((*dstp), mm3);/* 4 dst pixels -> mm3 */ + movq_m2r((*srcp), mm2);/* 4 src pixels -> mm2 */ + + /* RED */ + movq_r2r(mm2, mm5); /* src -> mm5 */ + pand_r2r(mm1 , mm5); /* src & MASKRED -> mm5 */ + psrlq_i2r(10, mm5); /* mm5 >> 10 -> mm5 [000r 000r 000r 000r] */ + + movq_r2r(mm3, mm6); /* dst -> mm6 */ + pand_r2r(mm1 , mm6); /* dst & MASKRED -> mm6 */ + psrlq_i2r(10, mm6); /* mm6 >> 10 -> mm6 [000r 000r 000r 000r] */ + + /* blend */ + psubw_r2r(mm6, mm5);/* src - dst -> mm5 */ + pmullw_r2r(mm0, mm5); /* mm5 * alpha -> mm5 */ + psrlw_i2r(8, mm5); /* mm5 >> 8 -> mm5 */ + paddw_r2r(mm5, mm6); /* mm5 + mm6(dst) -> mm6 */ + psllq_i2r(10, mm6); /* mm6 << 10 -> mm6 */ + pand_r2r(mm1, mm6); /* mm6 & MASKRED -> mm6 */ + + movq_r2r(mm4, mm5); /* MASKGREEN -> mm5 */ + por_r2r(mm7, mm5); /* MASKBLUE | mm5 -> mm5 */ + pand_r2r(mm5, mm3); /* mm3 & mm5(!MASKRED) -> mm3 */ + por_r2r(mm6, mm3); /* save new reds in dsts */ + + /* green */ + movq_r2r(mm2, mm5); /* src -> mm5 */ + pand_r2r(mm4 , mm5); /* src & MASKGREEN -> mm5 */ + psrlq_i2r(5, mm5); /* mm5 >> 5 -> mm5 [000g 000g 000g 000g] */ + + movq_r2r(mm3, mm6); /* dst -> mm6 */ + pand_r2r(mm4 , mm6); /* dst & MASKGREEN -> mm6 */ + psrlq_i2r(5, mm6); /* mm6 >> 5 -> mm6 [000g 000g 000g 000g] */ + + /* blend */ + psubw_r2r(mm6, mm5);/* src - dst -> mm5 */ + pmullw_r2r(mm0, mm5); /* mm5 * alpha -> mm5 */ + psrlw_i2r(8, mm5); /* mm5 >> 8 -> mm5 */ + paddw_r2r(mm5, mm6); /* mm5 + mm6(dst) -> mm6 */ + psllq_i2r(5, mm6); /* mm6 << 5 -> mm6 */ + pand_r2r(mm4, mm6); /* mm6 & MASKGREEN -> mm6 */ + + movq_r2r(mm1, mm5); /* MASKRED -> mm5 */ + por_r2r(mm7, mm5); /* MASKBLUE | mm5 -> mm5 */ + pand_r2r(mm5, mm3); /* mm3 & mm5(!MASKGREEN) -> mm3 */ + por_r2r(mm6, mm3); /* save new greens in dsts */ + + /* blue */ + movq_r2r(mm2, mm5); /* src -> mm5 */ + pand_r2r(mm7 , mm5); /* src & MASKRED -> mm5[000b 000b 000b 000b] */ + + movq_r2r(mm3, mm6); /* dst -> mm6 */ + pand_r2r(mm7 , mm6); /* dst & MASKBLUE -> mm6[000b 000b 000b 000b] */ + + /* blend */ + psubw_r2r(mm6, mm5);/* src - dst -> mm5 */ + pmullw_r2r(mm0, mm5); /* mm5 * alpha -> mm5 */ + psrlw_i2r(8, mm5); /* mm5 >> 8 -> mm5 */ + paddw_r2r(mm5, mm6); /* mm5 + mm6(dst) -> mm6 */ + pand_r2r(mm7, mm6); /* mm6 & MASKBLUE -> mm6 */ + + movq_r2r(mm1, mm5); /* MASKRED -> mm5 */ + por_r2r(mm4, mm5); /* MASKGREEN | mm5 -> mm5 */ + pand_r2r(mm5, mm3); /* mm3 & mm5(!MASKBLUE) -> mm3 */ + por_r2r(mm6, mm3); /* save new blues in dsts */ + + movq_r2m(mm3, *dstp);/* mm3 -> 4 dst pixels */ + + srcp += 4; + dstp += 4; + }, width); + srcp += srcskip; + dstp += dstskip; + } + emms(); + } +} +#endif + /* fast RGB565->RGB565 blending with surface alpha */ static void Blit565to565SurfaceAlpha(SDL_BlitInfo *info) { @@ -500,10 +1158,11 @@ static void BlitARGBto565PixelAlpha(SDL_BlitInfo *info) compositioning used (>>8 instead of /255) doesn't handle it correctly. Also special-case alpha=0 for speed? Benchmark this! */ - if(alpha == (SDL_ALPHA_OPAQUE >> 3)) { + if(alpha) { + if(alpha == (SDL_ALPHA_OPAQUE >> 3)) { *dstp = (s >> 8 & 0xf800) + (s >> 5 & 0x7e0) + (s >> 3 & 0x1f); - } else { + } else { Uint32 d = *dstp; /* * convert source and destination to G0RAB65565 @@ -515,6 +1174,7 @@ static void BlitARGBto565PixelAlpha(SDL_BlitInfo *info) d += (s - d) * alpha >> 5; d &= 0x07e0f81f; *dstp = d | d >> 16; + } } srcp++; dstp++; @@ -543,10 +1203,11 @@ static void BlitARGBto555PixelAlpha(SDL_BlitInfo *info) compositioning used (>>8 instead of /255) doesn't handle it correctly. Also special-case alpha=0 for speed? Benchmark this! */ - if(alpha == (SDL_ALPHA_OPAQUE >> 3)) { + if(alpha) { + if(alpha == (SDL_ALPHA_OPAQUE >> 3)) { *dstp = (s >> 9 & 0x7c00) + (s >> 6 & 0x3e0) + (s >> 3 & 0x1f); - } else { + } else { Uint32 d = *dstp; /* * convert source and destination to G0RAB65565 @@ -558,6 +1219,7 @@ static void BlitARGBto555PixelAlpha(SDL_BlitInfo *info) d += (s - d) * alpha >> 5; d &= 0x03e07c1f; *dstp = d | d >> 16; + } } srcp++; dstp++; @@ -583,7 +1245,8 @@ static void BlitNtoNSurfaceAlpha(SDL_BlitInfo *info) unsigned sA = srcfmt->alpha; unsigned dA = dstfmt->Amask ? SDL_ALPHA_OPAQUE : 0; - while ( height-- ) { + if(sA) { + while ( height-- ) { DUFFS_LOOP4( { Uint32 pixel; @@ -603,6 +1266,7 @@ static void BlitNtoNSurfaceAlpha(SDL_BlitInfo *info) width); src += srcskip; dst += dstskip; + } } } @@ -634,7 +1298,7 @@ static void BlitNtoNSurfaceAlphaKey(SDL_BlitInfo *info) unsigned dG; unsigned dB; RETRIEVE_RGB_PIXEL(src, srcbpp, pixel); - if(pixel != ckey) { + if(sA && pixel != ckey) { RGB_FROM_PIXEL(pixel, srcfmt, sR, sG, sB); DISEMBLE_RGB(dst, dstbpp, dstfmt, pixel, dR, dG, dB); ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB); @@ -686,9 +1350,11 @@ static void BlitNtoNPixelAlpha(SDL_BlitInfo *info) unsigned sA; unsigned dA; DISEMBLE_RGBA(src, srcbpp, srcfmt, pixel, sR, sG, sB, sA); - DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA); - ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB); - ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA); + if(sA) { + DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA); + ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB); + ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA); + } src += srcbpp; dst += dstbpp; }, @@ -719,9 +1385,23 @@ SDL_loblit SDL_CalculateAlphaBlit(SDL_Surface *surface, int blit_index) case 2: if(surface->map->identity) { if(df->Gmask == 0x7e0) + { +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) + if((CPU_Flags()&MMX_CPU)!=0) + return Blit565to565SurfaceAlphaMMX; + else +#endif return Blit565to565SurfaceAlpha; + } else if(df->Gmask == 0x3e0) + { +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) + if((CPU_Flags()&MMX_CPU)!=0) + return Blit555to555SurfaceAlphaMMX; + else +#endif return Blit555to555SurfaceAlpha; + } } return BlitNtoNSurfaceAlpha; @@ -731,7 +1411,14 @@ SDL_loblit SDL_CalculateAlphaBlit(SDL_Surface *surface, int blit_index) && sf->Bmask == df->Bmask && (sf->Rmask | sf->Gmask | sf->Bmask) == 0xffffff && sf->BytesPerPixel == 4) + { +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) + if((CPU_Flags()&MMX_CPU)!=0) + return BlitRGBtoRGBSurfaceAlphaMMX; + else +#endif return BlitRGBtoRGBSurfaceAlpha; + } else return BlitNtoNSurfaceAlpha; @@ -764,7 +1451,19 @@ SDL_loblit SDL_CalculateAlphaBlit(SDL_Surface *surface, int blit_index) && sf->Gmask == df->Gmask && sf->Bmask == df->Bmask && sf->BytesPerPixel == 4) - return BlitRGBtoRGBPixelAlpha; + { +#if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT) + Uint32 f; + f=CPU_Flags(); + if((f&(TDNOW_CPU|MMX_CPU))==(TDNOW_CPU|MMX_CPU)) + return BlitRGBtoRGBPixelAlphaMMX3DNOW; + else + if((f&MMX_CPU)!=0) + return BlitRGBtoRGBPixelAlphaMMX; + else +#endif + return BlitRGBtoRGBPixelAlpha; + } return BlitNtoNPixelAlpha; case 3: diff --git a/src/video/mmx.h b/src/video/mmx.h new file mode 100644 index 000000000..2f2093d18 --- /dev/null +++ b/src/video/mmx.h @@ -0,0 +1,706 @@ +/* mmx.h + + MultiMedia eXtensions GCC interface library for IA32. + + To use this library, simply include this header file + and compile with GCC. You MUST have inlining enabled + in order for mmx_ok() to work; this can be done by + simply using -O on the GCC command line. + + Compiling with -DMMX_TRACE will cause detailed trace + output to be sent to stderr for each mmx operation. + This adds lots of code, and obviously slows execution to + a crawl, but can be very useful for debugging. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT + LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR ANY PARTICULAR PURPOSE. + + 1997-99 by H. Dietz and R. Fisher + + Notes: + It appears that the latest gas has the pand problem fixed, therefore + I'll undefine BROKEN_PAND by default. +*/ + +#ifndef _MMX_H +#define _MMX_H + + +/* Warning: at this writing, the version of GAS packaged + with most Linux distributions does not handle the + parallel AND operation mnemonic correctly. If the + symbol BROKEN_PAND is defined, a slower alternative + coding will be used. If execution of mmxtest results + in an illegal instruction fault, define this symbol. +*/ +#undef BROKEN_PAND + + +/* The type of an value that fits in an MMX register + (note that long long constant values MUST be suffixed + by LL and unsigned long long values by ULL, lest + they be truncated by the compiler) +*/ +typedef union { + long long q; /* Quadword (64-bit) value */ + unsigned long long uq; /* Unsigned Quadword */ + int d[2]; /* 2 Doubleword (32-bit) values */ + unsigned int ud[2]; /* 2 Unsigned Doubleword */ + short w[4]; /* 4 Word (16-bit) values */ + unsigned short uw[4]; /* 4 Unsigned Word */ + char b[8]; /* 8 Byte (8-bit) values */ + unsigned char ub[8]; /* 8 Unsigned Byte */ + float s[2]; /* Single-precision (32-bit) value */ +} __attribute__ ((aligned (8))) mmx_t; /* On an 8-byte (64-bit) boundary */ + + +#if 0 +/* Function to test if multimedia instructions are supported... +*/ +inline extern int +mm_support(void) +{ + /* Returns 1 if MMX instructions are supported, + 3 if Cyrix MMX and Extended MMX instructions are supported + 5 if AMD MMX and 3DNow! instructions are supported + 0 if hardware does not support any of these + */ + register int rval = 0; + + __asm__ __volatile__ ( + /* See if CPUID instruction is supported ... */ + /* ... Get copies of EFLAGS into eax and ecx */ + "pushf\n\t" + "popl %%eax\n\t" + "movl %%eax, %%ecx\n\t" + + /* ... Toggle the ID bit in one copy and store */ + /* to the EFLAGS reg */ + "xorl $0x200000, %%eax\n\t" + "push %%eax\n\t" + "popf\n\t" + + /* ... Get the (hopefully modified) EFLAGS */ + "pushf\n\t" + "popl %%eax\n\t" + + /* ... Compare and test result */ + "xorl %%eax, %%ecx\n\t" + "testl $0x200000, %%ecx\n\t" + "jz NotSupported1\n\t" /* CPUID not supported */ + + + /* Get standard CPUID information, and + go to a specific vendor section */ + "movl $0, %%eax\n\t" + "cpuid\n\t" + + /* Check for Intel */ + "cmpl $0x756e6547, %%ebx\n\t" + "jne TryAMD\n\t" + "cmpl $0x49656e69, %%edx\n\t" + "jne TryAMD\n\t" + "cmpl $0x6c65746e, %%ecx\n" + "jne TryAMD\n\t" + "jmp Intel\n\t" + + /* Check for AMD */ + "\nTryAMD:\n\t" + "cmpl $0x68747541, %%ebx\n\t" + "jne TryCyrix\n\t" + "cmpl $0x69746e65, %%edx\n\t" + "jne TryCyrix\n\t" + "cmpl $0x444d4163, %%ecx\n" + "jne TryCyrix\n\t" + "jmp AMD\n\t" + + /* Check for Cyrix */ + "\nTryCyrix:\n\t" + "cmpl $0x69727943, %%ebx\n\t" + "jne NotSupported2\n\t" + "cmpl $0x736e4978, %%edx\n\t" + "jne NotSupported3\n\t" + "cmpl $0x64616574, %%ecx\n\t" + "jne NotSupported4\n\t" + /* Drop through to Cyrix... */ + + + /* Cyrix Section */ + /* See if extended CPUID level 80000001 is supported */ + /* The value of CPUID/80000001 for the 6x86MX is undefined + according to the Cyrix CPU Detection Guide (Preliminary + Rev. 1.01 table 1), so we'll check the value of eax for + CPUID/0 to see if standard CPUID level 2 is supported. + According to the table, the only CPU which supports level + 2 is also the only one which supports extended CPUID levels. + */ + "cmpl $0x2, %%eax\n\t" + "jne MMXtest\n\t" /* Use standard CPUID instead */ + + /* Extended CPUID supported (in theory), so get extended + features */ + "movl $0x80000001, %%eax\n\t" + "cpuid\n\t" + "testl $0x00800000, %%eax\n\t" /* Test for MMX */ + "jz NotSupported5\n\t" /* MMX not supported */ + "testl $0x01000000, %%eax\n\t" /* Test for Ext'd MMX */ + "jnz EMMXSupported\n\t" + "movl $1, %0:\n\n\t" /* MMX Supported */ + "jmp Return\n\n" + "EMMXSupported:\n\t" + "movl $3, %0:\n\n\t" /* EMMX and MMX Supported */ + "jmp Return\n\t" + + + /* AMD Section */ + "AMD:\n\t" + + /* See if extended CPUID is supported */ + "movl $0x80000000, %%eax\n\t" + "cpuid\n\t" + "cmpl $0x80000000, %%eax\n\t" + "jl MMXtest\n\t" /* Use standard CPUID instead */ + + /* Extended CPUID supported, so get extended features */ + "movl $0x80000001, %%eax\n\t" + "cpuid\n\t" + "testl $0x00800000, %%edx\n\t" /* Test for MMX */ + "jz NotSupported6\n\t" /* MMX not supported */ + "testl $0x80000000, %%edx\n\t" /* Test for 3DNow! */ + "jnz ThreeDNowSupported\n\t" + "movl $1, %0:\n\n\t" /* MMX Supported */ + "jmp Return\n\n" + "ThreeDNowSupported:\n\t" + "movl $5, %0:\n\n\t" /* 3DNow! and MMX Supported */ + "jmp Return\n\t" + + + /* Intel Section */ + "Intel:\n\t" + + /* Check for MMX */ + "MMXtest:\n\t" + "movl $1, %%eax\n\t" + "cpuid\n\t" + "testl $0x00800000, %%edx\n\t" /* Test for MMX */ + "jz NotSupported7\n\t" /* MMX Not supported */ + "movl $1, %0:\n\n\t" /* MMX Supported */ + "jmp Return\n\t" + + /* Nothing supported */ + "\nNotSupported1:\n\t" + "#movl $101, %0:\n\n\t" + "\nNotSupported2:\n\t" + "#movl $102, %0:\n\n\t" + "\nNotSupported3:\n\t" + "#movl $103, %0:\n\n\t" + "\nNotSupported4:\n\t" + "#movl $104, %0:\n\n\t" + "\nNotSupported5:\n\t" + "#movl $105, %0:\n\n\t" + "\nNotSupported6:\n\t" + "#movl $106, %0:\n\n\t" + "\nNotSupported7:\n\t" + "#movl $107, %0:\n\n\t" + "movl $0, %0:\n\n\t" + + "Return:\n\t" + : "=a" (rval) + : /* no input */ + : "eax", "ebx", "ecx", "edx" + ); + + /* Return */ + return(rval); +} + +/* Function to test if mmx instructions are supported... +*/ +inline extern int +mmx_ok(void) +{ + /* Returns 1 if MMX instructions are supported, 0 otherwise */ + return ( mm_support() & 0x1 ); +} +#endif + +/* Helper functions for the instruction macros that follow... + (note that memory-to-register, m2r, instructions are nearly + as efficient as register-to-register, r2r, instructions; + however, memory-to-memory instructions are really simulated + as a convenience, and are only 1/3 as efficient) +*/ +#ifdef MMX_TRACE + +/* Include the stuff for printing a trace to stderr... +*/ + +#include + +#define mmx_i2r(op, imm, reg) \ + { \ + mmx_t mmx_trace; \ + mmx_trace.uq = (imm); \ + printf(#op "_i2r(" #imm "=0x%08x%08x, ", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + __asm__ __volatile__ ("movq %%" #reg ", %0" \ + : "=X" (mmx_trace) \ + : /* nothing */ ); \ + printf(#reg "=0x%08x%08x) => ", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "X" (imm)); \ + __asm__ __volatile__ ("movq %%" #reg ", %0" \ + : "=X" (mmx_trace) \ + : /* nothing */ ); \ + printf(#reg "=0x%08x%08x\n", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + } + +#define mmx_m2r(op, mem, reg) \ + { \ + mmx_t mmx_trace; \ + mmx_trace = (mem); \ + printf(#op "_m2r(" #mem "=0x%08x%08x, ", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + __asm__ __volatile__ ("movq %%" #reg ", %0" \ + : "=X" (mmx_trace) \ + : /* nothing */ ); \ + printf(#reg "=0x%08x%08x) => ", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "X" (mem)); \ + __asm__ __volatile__ ("movq %%" #reg ", %0" \ + : "=X" (mmx_trace) \ + : /* nothing */ ); \ + printf(#reg "=0x%08x%08x\n", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + } + +#define mmx_r2m(op, reg, mem) \ + { \ + mmx_t mmx_trace; \ + __asm__ __volatile__ ("movq %%" #reg ", %0" \ + : "=X" (mmx_trace) \ + : /* nothing */ ); \ + printf(#op "_r2m(" #reg "=0x%08x%08x, ", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + mmx_trace = (mem); \ + printf(#mem "=0x%08x%08x) => ", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + __asm__ __volatile__ (#op " %%" #reg ", %0" \ + : "=X" (mem) \ + : /* nothing */ ); \ + mmx_trace = (mem); \ + printf(#mem "=0x%08x%08x\n", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + } + +#define mmx_r2r(op, regs, regd) \ + { \ + mmx_t mmx_trace; \ + __asm__ __volatile__ ("movq %%" #regs ", %0" \ + : "=X" (mmx_trace) \ + : /* nothing */ ); \ + printf(#op "_r2r(" #regs "=0x%08x%08x, ", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + __asm__ __volatile__ ("movq %%" #regd ", %0" \ + : "=X" (mmx_trace) \ + : /* nothing */ ); \ + printf(#regd "=0x%08x%08x) => ", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + __asm__ __volatile__ (#op " %" #regs ", %" #regd); \ + __asm__ __volatile__ ("movq %%" #regd ", %0" \ + : "=X" (mmx_trace) \ + : /* nothing */ ); \ + printf(#regd "=0x%08x%08x\n", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + } + +#define mmx_m2m(op, mems, memd) \ + { \ + mmx_t mmx_trace; \ + mmx_trace = (mems); \ + printf(#op "_m2m(" #mems "=0x%08x%08x, ", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + mmx_trace = (memd); \ + printf(#memd "=0x%08x%08x) => ", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + __asm__ __volatile__ ("movq %0, %%mm0\n\t" \ + #op " %1, %%mm0\n\t" \ + "movq %%mm0, %0" \ + : "=X" (memd) \ + : "X" (mems)); \ + mmx_trace = (memd); \ + printf(#memd "=0x%08x%08x\n", \ + mmx_trace.d[1], mmx_trace.d[0]); \ + } + +#else + +/* These macros are a lot simpler without the tracing... +*/ + +#define mmx_i2r(op, imm, reg) \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "X" (imm) ) + +#define mmx_m2r(op, mem, reg) \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "X" (mem)) + +#define mmx_r2m(op, reg, mem) \ + __asm__ __volatile__ (#op " %%" #reg ", %0" \ + : "=X" (mem) \ + : /* nothing */ ) + +#define mmx_r2r(op, regs, regd) \ + __asm__ __volatile__ (#op " %" #regs ", %" #regd) + +#define mmx_m2m(op, mems, memd) \ + __asm__ __volatile__ ("movq %0, %%mm0\n\t" \ + #op " %1, %%mm0\n\t" \ + "movq %%mm0, %0" \ + : "=X" (memd) \ + : "X" (mems)) + +#endif + + +/* 1x64 MOVe Quadword + (this is both a load and a store... + in fact, it is the only way to store) +*/ +#define movq_m2r(var, reg) mmx_m2r(movq, var, reg) +#define movq_r2m(reg, var) mmx_r2m(movq, reg, var) +#define movq_r2r(regs, regd) mmx_r2r(movq, regs, regd) +#define movq(vars, vard) \ + __asm__ __volatile__ ("movq %1, %%mm0\n\t" \ + "movq %%mm0, %0" \ + : "=X" (vard) \ + : "X" (vars)) + + +/* 1x32 MOVe Doubleword + (like movq, this is both load and store... + but is most useful for moving things between + mmx registers and ordinary registers) +*/ +#define movd_m2r(var, reg) mmx_m2r(movd, var, reg) +#define movd_r2m(reg, var) mmx_r2m(movd, reg, var) +#define movd_r2r(regs, regd) mmx_r2r(movd, regs, regd) +#define movd(vars, vard) \ + __asm__ __volatile__ ("movd %1, %%mm0\n\t" \ + "movd %%mm0, %0" \ + : "=X" (vard) \ + : "X" (vars)) + + +/* 2x32, 4x16, and 8x8 Parallel ADDs +*/ +#define paddd_m2r(var, reg) mmx_m2r(paddd, var, reg) +#define paddd_r2r(regs, regd) mmx_r2r(paddd, regs, regd) +#define paddd(vars, vard) mmx_m2m(paddd, vars, vard) + +#define paddw_m2r(var, reg) mmx_m2r(paddw, var, reg) +#define paddw_r2r(regs, regd) mmx_r2r(paddw, regs, regd) +#define paddw(vars, vard) mmx_m2m(paddw, vars, vard) + +#define paddb_m2r(var, reg) mmx_m2r(paddb, var, reg) +#define paddb_r2r(regs, regd) mmx_r2r(paddb, regs, regd) +#define paddb(vars, vard) mmx_m2m(paddb, vars, vard) + + +/* 4x16 and 8x8 Parallel ADDs using Saturation arithmetic +*/ +#define paddsw_m2r(var, reg) mmx_m2r(paddsw, var, reg) +#define paddsw_r2r(regs, regd) mmx_r2r(paddsw, regs, regd) +#define paddsw(vars, vard) mmx_m2m(paddsw, vars, vard) + +#define paddsb_m2r(var, reg) mmx_m2r(paddsb, var, reg) +#define paddsb_r2r(regs, regd) mmx_r2r(paddsb, regs, regd) +#define paddsb(vars, vard) mmx_m2m(paddsb, vars, vard) + + +/* 4x16 and 8x8 Parallel ADDs using Unsigned Saturation arithmetic +*/ +#define paddusw_m2r(var, reg) mmx_m2r(paddusw, var, reg) +#define paddusw_r2r(regs, regd) mmx_r2r(paddusw, regs, regd) +#define paddusw(vars, vard) mmx_m2m(paddusw, vars, vard) + +#define paddusb_m2r(var, reg) mmx_m2r(paddusb, var, reg) +#define paddusb_r2r(regs, regd) mmx_r2r(paddusb, regs, regd) +#define paddusb(vars, vard) mmx_m2m(paddusb, vars, vard) + + +/* 2x32, 4x16, and 8x8 Parallel SUBs +*/ +#define psubd_m2r(var, reg) mmx_m2r(psubd, var, reg) +#define psubd_r2r(regs, regd) mmx_r2r(psubd, regs, regd) +#define psubd(vars, vard) mmx_m2m(psubd, vars, vard) + +#define psubw_m2r(var, reg) mmx_m2r(psubw, var, reg) +#define psubw_r2r(regs, regd) mmx_r2r(psubw, regs, regd) +#define psubw(vars, vard) mmx_m2m(psubw, vars, vard) + +#define psubb_m2r(var, reg) mmx_m2r(psubb, var, reg) +#define psubb_r2r(regs, regd) mmx_r2r(psubb, regs, regd) +#define psubb(vars, vard) mmx_m2m(psubb, vars, vard) + + +/* 4x16 and 8x8 Parallel SUBs using Saturation arithmetic +*/ +#define psubsw_m2r(var, reg) mmx_m2r(psubsw, var, reg) +#define psubsw_r2r(regs, regd) mmx_r2r(psubsw, regs, regd) +#define psubsw(vars, vard) mmx_m2m(psubsw, vars, vard) + +#define psubsb_m2r(var, reg) mmx_m2r(psubsb, var, reg) +#define psubsb_r2r(regs, regd) mmx_r2r(psubsb, regs, regd) +#define psubsb(vars, vard) mmx_m2m(psubsb, vars, vard) + + +/* 4x16 and 8x8 Parallel SUBs using Unsigned Saturation arithmetic +*/ +#define psubusw_m2r(var, reg) mmx_m2r(psubusw, var, reg) +#define psubusw_r2r(regs, regd) mmx_r2r(psubusw, regs, regd) +#define psubusw(vars, vard) mmx_m2m(psubusw, vars, vard) + +#define psubusb_m2r(var, reg) mmx_m2r(psubusb, var, reg) +#define psubusb_r2r(regs, regd) mmx_r2r(psubusb, regs, regd) +#define psubusb(vars, vard) mmx_m2m(psubusb, vars, vard) + + +/* 4x16 Parallel MULs giving Low 4x16 portions of results +*/ +#define pmullw_m2r(var, reg) mmx_m2r(pmullw, var, reg) +#define pmullw_r2r(regs, regd) mmx_r2r(pmullw, regs, regd) +#define pmullw(vars, vard) mmx_m2m(pmullw, vars, vard) + + +/* 4x16 Parallel MULs giving High 4x16 portions of results +*/ +#define pmulhw_m2r(var, reg) mmx_m2r(pmulhw, var, reg) +#define pmulhw_r2r(regs, regd) mmx_r2r(pmulhw, regs, regd) +#define pmulhw(vars, vard) mmx_m2m(pmulhw, vars, vard) + + +/* 4x16->2x32 Parallel Mul-ADD + (muls like pmullw, then adds adjacent 16-bit fields + in the multiply result to make the final 2x32 result) +*/ +#define pmaddwd_m2r(var, reg) mmx_m2r(pmaddwd, var, reg) +#define pmaddwd_r2r(regs, regd) mmx_r2r(pmaddwd, regs, regd) +#define pmaddwd(vars, vard) mmx_m2m(pmaddwd, vars, vard) + + +/* 1x64 bitwise AND +*/ +#ifdef BROKEN_PAND +#define pand_m2r(var, reg) \ + { \ + mmx_m2r(pandn, (mmx_t) -1LL, reg); \ + mmx_m2r(pandn, var, reg); \ + } +#define pand_r2r(regs, regd) \ + { \ + mmx_m2r(pandn, (mmx_t) -1LL, regd); \ + mmx_r2r(pandn, regs, regd) \ + } +#define pand(vars, vard) \ + { \ + movq_m2r(vard, mm0); \ + mmx_m2r(pandn, (mmx_t) -1LL, mm0); \ + mmx_m2r(pandn, vars, mm0); \ + movq_r2m(mm0, vard); \ + } +#else +#define pand_m2r(var, reg) mmx_m2r(pand, var, reg) +#define pand_r2r(regs, regd) mmx_r2r(pand, regs, regd) +#define pand(vars, vard) mmx_m2m(pand, vars, vard) +#endif + + +/* 1x64 bitwise AND with Not the destination +*/ +#define pandn_m2r(var, reg) mmx_m2r(pandn, var, reg) +#define pandn_r2r(regs, regd) mmx_r2r(pandn, regs, regd) +#define pandn(vars, vard) mmx_m2m(pandn, vars, vard) + + +/* 1x64 bitwise OR +*/ +#define por_m2r(var, reg) mmx_m2r(por, var, reg) +#define por_r2r(regs, regd) mmx_r2r(por, regs, regd) +#define por(vars, vard) mmx_m2m(por, vars, vard) + + +/* 1x64 bitwise eXclusive OR +*/ +#define pxor_m2r(var, reg) mmx_m2r(pxor, var, reg) +#define pxor_r2r(regs, regd) mmx_r2r(pxor, regs, regd) +#define pxor(vars, vard) mmx_m2m(pxor, vars, vard) + + +/* 2x32, 4x16, and 8x8 Parallel CoMPare for EQuality + (resulting fields are either 0 or -1) +*/ +#define pcmpeqd_m2r(var, reg) mmx_m2r(pcmpeqd, var, reg) +#define pcmpeqd_r2r(regs, regd) mmx_r2r(pcmpeqd, regs, regd) +#define pcmpeqd(vars, vard) mmx_m2m(pcmpeqd, vars, vard) + +#define pcmpeqw_m2r(var, reg) mmx_m2r(pcmpeqw, var, reg) +#define pcmpeqw_r2r(regs, regd) mmx_r2r(pcmpeqw, regs, regd) +#define pcmpeqw(vars, vard) mmx_m2m(pcmpeqw, vars, vard) + +#define pcmpeqb_m2r(var, reg) mmx_m2r(pcmpeqb, var, reg) +#define pcmpeqb_r2r(regs, regd) mmx_r2r(pcmpeqb, regs, regd) +#define pcmpeqb(vars, vard) mmx_m2m(pcmpeqb, vars, vard) + + +/* 2x32, 4x16, and 8x8 Parallel CoMPare for Greater Than + (resulting fields are either 0 or -1) +*/ +#define pcmpgtd_m2r(var, reg) mmx_m2r(pcmpgtd, var, reg) +#define pcmpgtd_r2r(regs, regd) mmx_r2r(pcmpgtd, regs, regd) +#define pcmpgtd(vars, vard) mmx_m2m(pcmpgtd, vars, vard) + +#define pcmpgtw_m2r(var, reg) mmx_m2r(pcmpgtw, var, reg) +#define pcmpgtw_r2r(regs, regd) mmx_r2r(pcmpgtw, regs, regd) +#define pcmpgtw(vars, vard) mmx_m2m(pcmpgtw, vars, vard) + +#define pcmpgtb_m2r(var, reg) mmx_m2r(pcmpgtb, var, reg) +#define pcmpgtb_r2r(regs, regd) mmx_r2r(pcmpgtb, regs, regd) +#define pcmpgtb(vars, vard) mmx_m2m(pcmpgtb, vars, vard) + + +/* 1x64, 2x32, and 4x16 Parallel Shift Left Logical +*/ +#define psllq_i2r(imm, reg) mmx_i2r(psllq, imm, reg) +#define psllq_m2r(var, reg) mmx_m2r(psllq, var, reg) +#define psllq_r2r(regs, regd) mmx_r2r(psllq, regs, regd) +#define psllq(vars, vard) mmx_m2m(psllq, vars, vard) + +#define pslld_i2r(imm, reg) mmx_i2r(pslld, imm, reg) +#define pslld_m2r(var, reg) mmx_m2r(pslld, var, reg) +#define pslld_r2r(regs, regd) mmx_r2r(pslld, regs, regd) +#define pslld(vars, vard) mmx_m2m(pslld, vars, vard) + +#define psllw_i2r(imm, reg) mmx_i2r(psllw, imm, reg) +#define psllw_m2r(var, reg) mmx_m2r(psllw, var, reg) +#define psllw_r2r(regs, regd) mmx_r2r(psllw, regs, regd) +#define psllw(vars, vard) mmx_m2m(psllw, vars, vard) + + +/* 1x64, 2x32, and 4x16 Parallel Shift Right Logical +*/ +#define psrlq_i2r(imm, reg) mmx_i2r(psrlq, imm, reg) +#define psrlq_m2r(var, reg) mmx_m2r(psrlq, var, reg) +#define psrlq_r2r(regs, regd) mmx_r2r(psrlq, regs, regd) +#define psrlq(vars, vard) mmx_m2m(psrlq, vars, vard) + +#define psrld_i2r(imm, reg) mmx_i2r(psrld, imm, reg) +#define psrld_m2r(var, reg) mmx_m2r(psrld, var, reg) +#define psrld_r2r(regs, regd) mmx_r2r(psrld, regs, regd) +#define psrld(vars, vard) mmx_m2m(psrld, vars, vard) + +#define psrlw_i2r(imm, reg) mmx_i2r(psrlw, imm, reg) +#define psrlw_m2r(var, reg) mmx_m2r(psrlw, var, reg) +#define psrlw_r2r(regs, regd) mmx_r2r(psrlw, regs, regd) +#define psrlw(vars, vard) mmx_m2m(psrlw, vars, vard) + + +/* 2x32 and 4x16 Parallel Shift Right Arithmetic +*/ +#define psrad_i2r(imm, reg) mmx_i2r(psrad, imm, reg) +#define psrad_m2r(var, reg) mmx_m2r(psrad, var, reg) +#define psrad_r2r(regs, regd) mmx_r2r(psrad, regs, regd) +#define psrad(vars, vard) mmx_m2m(psrad, vars, vard) + +#define psraw_i2r(imm, reg) mmx_i2r(psraw, imm, reg) +#define psraw_m2r(var, reg) mmx_m2r(psraw, var, reg) +#define psraw_r2r(regs, regd) mmx_r2r(psraw, regs, regd) +#define psraw(vars, vard) mmx_m2m(psraw, vars, vard) + + +/* 2x32->4x16 and 4x16->8x8 PACK and Signed Saturate + (packs source and dest fields into dest in that order) +*/ +#define packssdw_m2r(var, reg) mmx_m2r(packssdw, var, reg) +#define packssdw_r2r(regs, regd) mmx_r2r(packssdw, regs, regd) +#define packssdw(vars, vard) mmx_m2m(packssdw, vars, vard) + +#define packsswb_m2r(var, reg) mmx_m2r(packsswb, var, reg) +#define packsswb_r2r(regs, regd) mmx_r2r(packsswb, regs, regd) +#define packsswb(vars, vard) mmx_m2m(packsswb, vars, vard) + + +/* 4x16->8x8 PACK and Unsigned Saturate + (packs source and dest fields into dest in that order) +*/ +#define packuswb_m2r(var, reg) mmx_m2r(packuswb, var, reg) +#define packuswb_r2r(regs, regd) mmx_r2r(packuswb, regs, regd) +#define packuswb(vars, vard) mmx_m2m(packuswb, vars, vard) + + +/* 2x32->1x64, 4x16->2x32, and 8x8->4x16 UNPaCK Low + (interleaves low half of dest with low half of source + as padding in each result field) +*/ +#define punpckldq_m2r(var, reg) mmx_m2r(punpckldq, var, reg) +#define punpckldq_r2r(regs, regd) mmx_r2r(punpckldq, regs, regd) +#define punpckldq(vars, vard) mmx_m2m(punpckldq, vars, vard) + +#define punpcklwd_m2r(var, reg) mmx_m2r(punpcklwd, var, reg) +#define punpcklwd_r2r(regs, regd) mmx_r2r(punpcklwd, regs, regd) +#define punpcklwd(vars, vard) mmx_m2m(punpcklwd, vars, vard) + +#define punpcklbw_m2r(var, reg) mmx_m2r(punpcklbw, var, reg) +#define punpcklbw_r2r(regs, regd) mmx_r2r(punpcklbw, regs, regd) +#define punpcklbw(vars, vard) mmx_m2m(punpcklbw, vars, vard) + + +/* 2x32->1x64, 4x16->2x32, and 8x8->4x16 UNPaCK High + (interleaves high half of dest with high half of source + as padding in each result field) +*/ +#define punpckhdq_m2r(var, reg) mmx_m2r(punpckhdq, var, reg) +#define punpckhdq_r2r(regs, regd) mmx_r2r(punpckhdq, regs, regd) +#define punpckhdq(vars, vard) mmx_m2m(punpckhdq, vars, vard) + +#define punpckhwd_m2r(var, reg) mmx_m2r(punpckhwd, var, reg) +#define punpckhwd_r2r(regs, regd) mmx_r2r(punpckhwd, regs, regd) +#define punpckhwd(vars, vard) mmx_m2m(punpckhwd, vars, vard) + +#define punpckhbw_m2r(var, reg) mmx_m2r(punpckhbw, var, reg) +#define punpckhbw_r2r(regs, regd) mmx_r2r(punpckhbw, regs, regd) +#define punpckhbw(vars, vard) mmx_m2m(punpckhbw, vars, vard) + + +/* Empty MMx State + (used to clean-up when going from mmx to float use + of the registers that are shared by both; note that + there is no float-to-mmx operation needed, because + only the float tag word info is corruptible) +*/ +#ifdef MMX_TRACE + +#define emms() \ + { \ + printf("emms()\n"); \ + __asm__ __volatile__ ("emms"); \ + } + +#else + +#define emms() __asm__ __volatile__ ("emms") + +#endif + +#endif +