;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; お約束 .586 .mmx .model flat _TEXT64 segment page public use32 'CODE' align 8 ;------------------------------------------------------------------- ; 定数 ;------------------------------------------------------------------- _0_R_G_B dq 000026464b230e98h _G_B_R_G dq 4b230e9826464b23h _R_G_B_0 dq 26464b230e980000h _pd32767 dq 00007fff00007fffh _pw1 dq 0001000100010001h ;------------------------------------------------------------------- ; 0x0e98 - 3736 (YB) ; 0x4b23 - 19235 (YG) ; 0x2646 - 9798 (YR) ; 0x7fff - 32767 切り上げ用 ;------------------------------------------------------------------- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ifdef BCC public block_RGB_to_Y block_RGB_to_Y PROC else public _block_RGB_to_Y@12 _block_RGB_to_Y@12 PROC endif ;------------------------------------------------------------------- ; 関数のスタート - 次の C の関数と対応 ; ; void __stdcall block_RGB_to_Y( ; unsigned char *rgb, /* [esp+ 4] */ ; short *y, /* [esp+ 8] */ ; int step /* [esp+12] */ ; ) ; ; stdcall 命名規則 - BCC - 特に修飾無し ; VC - 先頭に _ と 最後に @ + 引数サイズが追加 ; 引数の配置 - 引数は右から順にスタックに push される ; ;------------------------------------------------------------------- ; 実装の方針 ; ; block_RGB_to_Y をそのまま MMX 化 ; *rgb に 8 byte アライメントを期待できないので 4 並列演算 ;------------------------------------------------------------------- ; レジスタの退避 ; ; __stdcall 呼び出し規約では、各レジスタは全て生きた状態で呼び出さ ; れる。関数開始時と終了時でレジスタが等しくなければいけない ; 注:戻り値を返す場合の EAX レジスタは別として ;------------------------------------------------------------------- push esi ; 入力元アドレス用 push edi ; 出力先アドレス用 push ecx ; ループカウンタ用 ;------------------------------------------------------------------- ; 32bit x 3 で 12 byte スタックを消費 ; ; 現在のスタック状況 ; ; [esp+ 0] 次の push 用の空き領域 ; [esp+ 4] ecx の退避データ ; [esp+ 8] edi の退避データ ; [esp+12] esi の退避データ ; [esp+12+ 4] rgb(最初の引数) ; [esp+12+ 8] y (2つ目の引数) ; [esp+12+12] step(3つ目の引数) ;------------------------------------------------------------------- ; 下準備 ; ; 計算に必要な変数をレジスタにセットする(今回は殆ど必要ない) ;------------------------------------------------------------------- mov esi, [esp+12+ 4] ; rgb mov edi, [esp+12+ 8] ; y mov ecx, 8 ; ブロックの高さ pxor mm3, mm3 ; 0 に初期化 ;------------------------------------------------------------------- ; ループ時のジャンプ用ラベル ;------------------------------------------------------------------- block_RGB_to_Y_next_line: ;------------------------------------------------------------------- ; 行処理開始 - 最初の4画素 ;------------------------------------------------------------------- movd mm0, [esi] ; 00_00_00_00_B2_R1_G1_B1 movd mm1, [esi+4] ; 00_00_00_00_G3_B3_R2_G2 movd mm2, [esi+8] ; 00_00_00_00_R4_G4_B4_R3 punpcklbw mm0, mm3 ; 00B2_00R1_00G1_00B1 punpcklbw mm1, mm3 ; 00G3_00B3_00R2_00G2 punpcklbw mm2, mm3 ; 00R4_00G4_00B4_00R3 movq mm4, mm0 ; 00B2_00R1_00G1_00B1 movq mm5, mm2 ; 00R4_00G4_00B4_00R3 psrlq mm4, 48 ; 0000_0000_0000_00B2 psllq mm5, 32 ; 00B4_00R3_0000_0000 por mm4, mm5 ; 00B4_00R3_0000_00B2 pmaddwd mm0, _0_R_G_B ; 00000YR1_0000YGB1 pmaddwd mm1, _G_B_R_G ; 0000YGB3_0000YRG2 pmaddwd mm2, _R_G_B_0 ; 0000YRG4_00000YB4 pmaddwd mm4, _0_R_G_B ; 00000YR3_00000YB2 movq mm5, mm0 ; 00000YR1_0000YGB1 movq mm6, mm2 ; 0000YRG4_00000YB4 psllq mm0, 32 ; 0000YGB1_00000000 psrlq mm2, 32 ; 00000000_0000YRG4 paddd mm1, mm4 ; 000YRGB3_000YRGB2 paddd mm0, mm5 ; 000YRGB1_0000YGB1 paddd mm2, mm6 ; 0000YRG4_000YRGB4 punpckhdq mm0, mm1 ; 000YRGB3_000YRGB1 punpckldq mm1, mm2 ; 000YRGB4_000YRGB2 paddd mm0, _pd32767 ; 切り上げ paddd mm1, _pd32767 ; 〃 psrld mm0, 15 ; 000000Y3_000000Y1 psrld mm1, 15 ; 000000Y4_000000Y2 psllq mm1, 16 ; 00Y4_0000_00Y2_0000 por mm0, mm1 ; 00Y4_00Y3_00Y2_00Y1 movq [edi], mm0 ; Y を出力 ;------------------------------------------------------------------- ; 次の4画素 ;------------------------------------------------------------------- movd mm0, [esi+12] ; 00_00_00_00_B6_R5_G5_B5 movd mm1, [esi+16] ; 00_00_00_00_G7_B7_R6_G6 movd mm2, [esi+20] ; 00_00_00_00_R8_G8_B8_R7 punpcklbw mm0, mm3 ; 00B6_00R5_00G5_00B5 punpcklbw mm1, mm3 ; 00G7_00B7_00R6_00G6 punpcklbw mm2, mm3 ; 00R8_00G8_00B8_00R7 movq mm4, mm0 ; 00B6_00R5_00G5_00B5 movq mm5, mm2 ; 00R8_00G8_00B8_00R7 psrlq mm4, 48 ; 0000_0000_0000_00B6 psllq mm5, 32 ; 00B8_00R7_0000_0000 por mm4, mm5 ; 00B8_00R7_0000_00B6 pmaddwd mm0, _0_R_G_B ; 00000YR5_0000YGB5 pmaddwd mm1, _G_B_R_G ; 0000YGB7_0000YRG6 pmaddwd mm2, _R_G_B_0 ; 0000YRG8_00000YB8 pmaddwd mm4, _0_R_G_B ; 00000YR7_00000YB6 movq mm5, mm0 ; 00000YR5_0000YGB5 movq mm6, mm2 ; 0000YRG8_00000YB8 psllq mm0, 32 ; 0000YGB5_00000000 psrlq mm2, 32 ; 00000000_0000YRG8 paddd mm1, mm4 ; 000YRGB7_000YRGB6 paddd mm0, mm5 ; 000YRGB5_0000YGB5 paddd mm2, mm6 ; 0000YRG8_000YRGB8 punpckhdq mm0, mm1 ; 000YRGB7_000YRGB5 punpckldq mm1, mm2 ; 000YRGB8_000YRGB6 paddd mm0, _pd32767 ; 切り上げ paddd mm1, _pd32767 ; 〃 psrld mm0, 15 ; 000000Y7_000000Y5 psrld mm1, 15 ; 000000Y8_000000Y6 psllq mm1, 16 ; 00Y8_0000_00Y6_0000 por mm0, mm1 ; 00Y8_00Y7_00Y6_00Y5 movq [edi+8], mm0 ; Y を出力 ;------------------------------------------------------------------- ; 次の行へ ;------------------------------------------------------------------- add esi, [esp+12+12] ; *rgb を次の行へ add edi, 16 ; 出力先アドレスを進める dec ecx ; ループカウンタを減らす test ecx, ecx ; 0 になったかチェック jnz block_RGB_to_Y_next_line ; なってなければ次の行へ ;------------------------------------------------------------------- ; 後始末 ;------------------------------------------------------------------- ; MMX 使用時の後始末 ; ; emms を呼んで MMX state を消去する ; ; M4C の場合浮動小数点演算を使用せず、また VFAPI プラグインを呼ぶ ; 前に _clearfp() 関数(オリジナルでは __asm{ finit};)を使用して ; いるため呼ばなくても問題は発生しない ; ; M4C が _clearfp() を呼んでいるのは mpg4c32.dll が emms してない ; からと思われる ; ; ただし、安全のためには呼んでいた方が良い ;------------------------------------------------------------------- ; emms ; block_compare_Y で呼ぶ ;------------------------------------------------------------------- ; レジスタの復元 ; ; 最初に退避したレジスタを(push と逆順に pop して)復元する ;------------------------------------------------------------------- pop ecx pop edi pop esi ret 12 ;------------------------------------------------------------------- ; 引数のクリア(スタック領域) ; ret で指定バイト分のスタック領域をクリアしてから呼び出し元に帰る ;------------------------------------------------------------------- ifdef BCC block_RGB_to_Y ENDP else _block_RGB_to_Y@12 ENDP endif ;------------------------------------------------------------------- ; 関数の終了 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ifdef BCC public block_compare_Y block_compare_Y PROC else public _block_compare_Y@16 _block_RGB_to_Y@16 PROC endif ;------------------------------------------------------------------- ; 関数のスタート - 次の C の関数と対応 ; ; void __stdcall block_compare_Y( ; short *block1, /* [esp+ 4] */ ; short *block2, /* [esp+ 8] */ ; int diff /* [esp+12] */ ; int pixel /* [esp+16] */ ; ) ;------------------------------------------------------------------- ; 実装の方針 ; ; block_compare_Y をそのまま MMX 化 ; 16 bit x 4 x 2 の8並列演算 ;------------------------------------------------------------------- ; レジスタの退避 ;------------------------------------------------------------------- push esi ; block1 アドレス用 push edi ; block2 アドレス用 push ecx ; ループカウンタ用 ;------------------------------------------------------------------- ; 32bit x 3 で 12 byte スタックを消費 ; ; 現在のスタック状況 ; ; [esp+ 0] 次の push 用の空き領域 ; [esp+ 4] ecx の退避データ ; [esp+ 8] edi の退避データ ; [esp+12] esi の退避データ ; [esp+12+ 4] block1(最初の引数) ; [esp+12+ 8] block2(2つ目の引数) ; [esp+12+12] diff (3つ目の引数) ; [esp+12+16] pixel (4つ目の引数) ;------------------------------------------------------------------- ; 下準備 ;------------------------------------------------------------------- mov esi, [esp+12+ 4] ; block1 mov edi, [esp+12+ 8] ; block2 mov ecx, 8 ; 8 * 8 / 8 pxor mm0, mm0 ; 0 に初期化 movd mm7, [esp+12+12] ; 0000_0000_0000_diff punpcklwd mm7, mm7 ; 0000_0000_diff_diff punpcklwd mm7, mm7 ; diff_diff_diff_diff psubw mm7, _pw1 ; > を >= と同じにするため ;------------------------------------------------------------------- ; ループ時のジャンプ用ラベル ;------------------------------------------------------------------- block_compare_Y_next_8_pixel: ;------------------------------------------------------------------- ; 比較用コア ;------------------------------------------------------------------- movq mm1, [esi] ; 00Y4_00Y3_00Y2_00Y1 movq mm2, [esi+8] ; 00Y8_00Y7_00Y6_00Y5 movq mm3, [edi] ; 00y4_00y3_00y2_00y1 movq mm4, [edi+8] ; 00y8_00y7_00y6_00y5 lea esi, [esi+16] ; アドレスインクリメント lea edi, [edi+16] ; 〃 movq mm5, mm1 ; 00Y4_00Y3_00Y2_00Y1 movq mm6, mm2 ; 00Y8_00Y7_00Y6_00Y5 psubusw mm1, mm3 ; 飽和演算で差を取る psubusw mm2, mm4 ; psubusw mm3, mm5 ; 逆方向でも差を取る psubusw mm4, mm6 ; por mm1, mm3 ; 差の絶対値にする por mm2, mm4 ; pcmpgtw mm1, mm7 ; diff との比較 pcmpgtw mm2, mm7 ; 大きい場合 -1 (FFFF) paddw mm0, mm1 ; 結果を足していく paddw mm0, mm2 ;------------------------------------------------------------------- ; 次へ ;------------------------------------------------------------------- dec ecx test ecx, ecx jnz block_compare_Y_next_8_pixel ;------------------------------------------------------------------- ; 最終評価 ;------------------------------------------------------------------- pxor mm1, mm1 ; 0 で初期化 pxor mm2, mm2 psubw mm1, mm0 ; 正負反転 movq mm3, mm1 punpcklwd mm1, mm2 ; 0000cnt2_0000cnt1 punpckhwd mm3, mm2 ; 0000cnt4_0000cnt3 paddd mm1, mm3 ; count4+2_count3+1 movq mm0, mm1 psrlq mm0, 32 ; 00000000_count4+2 paddd mm0, mm1 ; count4+2_c4+3+2+1 movd ecx, mm0 ; c4+3+2+1 xor eax, eax cmp ecx, [esp+12+16] ; pixel と比較 setge al ; 大きければ al をセット ;------------------------------------------------------------------- ; 後始末 ;------------------------------------------------------------------- ;block_compare_Y_last: ;------------------------------------------------------------------- ; MMX 使用時の後始末 ;------------------------------------------------------------------- emms ;------------------------------------------------------------------- ; レジスタの復元 ;------------------------------------------------------------------- pop ecx pop edi pop esi ret 16 ;------------------------------------------------------------------- ; 引数のクリア(スタック領域) ;------------------------------------------------------------------- ifdef BCC block_compare_Y ENDP else _block_compare_Y@12 ENDP endif ;------------------------------------------------------------------- ; 関数の終了 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;------------------------------------------------------------------- ; _TEXT64 セグメントの最後をあらわす END を書く ;------------------------------------------------------------------- END