
| [asm編]アセンブラ入門 2005. 5.10 (01版) |
|---|
アセンブラの参考文献としては、Intelの日本語技術資料は入手しておいた方がよいでしょう。
| [asm編] MMX, SSE, SSE2, 3D Now!のサポートCPUの判定 2005. 5.10 (01版) |
|---|
if(!(env->GetCPUFlags() & CPUF_INTEGER_SSE)) {
//--- SSE: pentium III/4
} else if(!(env->GetCPUFlags() & CPUF_MMX)) {
//--- MMX: pentium II
} else {
//--- no MMX
};
|
----------- avisynth.h (抜粋) ----------------------------------------------
// For GetCPUFlags. These are backwards-compatible with those in VirtualDub.
enum {
/* slowest CPU to support extension */
CPUF_FORCE = 0x01, // N/A
CPUF_FPU = 0x02, // 386/486DX
CPUF_MMX = 0x04, // P55C, K6, PII
CPUF_INTEGER_SSE = 0x08, // PIII, Athlon
CPUF_SSE = 0x10, // PIII, Athlon XP/MP
CPUF_SSE2 = 0x20, // PIV, Hammer
CPUF_3DNOW = 0x40, // K6-2
CPUF_3DNOW_EXT = 0x80, // Athlon
CPUF_X86_64 = 0xA0, // Hammer (note: equiv. to 3DNow + SSE2, which only Hammer
// will have anyway)
};
class IScriptEnvironment {
public:
virtual /*static*/ long __stdcall GetCPUFlags() = 0;
------------------------------------------------------------------------------
|
| [asm編]インラインアセンブラを使ってみる(1) C++データへのアクセス 2005. 5.10 (01版) |
|---|
__asm mov eax, 100
__asm mov ebx, 200
__asm mov ecx, 300
または、
__asm {
mov eax, 100
mov ebx, 200
mov ecx, 300
}
|
PVideoFrame __stdcall Sample::GetFrame(int n, IScriptEnvironment *env) {
//*** in-place filtering ***
PVideoFrame src = child->GetFrame(n, env);
env->MakeWritable(&src);
const int pitch = src->GetPitch();
const int rowsize = (src->GetRowSize() + 4) & -8;
const int height = src->GetHeight();
const int modulo = pitch - src->GetRowSize();
const __int64 *srcp = (__int64*)src->GetReadPtr();
__int64 *dstp = (__int64*)src->GetWritePtr();
for (int j=0; j<height; j++) {
for (int i=0; i<rowsize>>3; i++) {
*dstp++ = (*dstp & 0x00ff00ff00ff00ffi64) | 0x8000800080008000i64;
}
dstp += (modulo>>3);
}
return src;
}
|
PVideoFrame __stdcall Sample::GetFrame(int n, IScriptEnvironment* env) {
//*** in-place filtering ***
PVideoFrame src = child->GetFrame(n, env);
env->MakeWritable(&src);
const int pitch = src->GetPitch();
const int rowsize = (src->GetRowSize() + 4) & -8;
const int height = src->GetHeight();
const int modulo = pitch - src->GetRowSize();
const __int64 *srcp = (__int64*)src->GetReadPtr();
__int64 *dstp = (__int64*)src->GetWritePtr();
static const __int64 MASK_UV = 0x00ff00ff00ff00ffi64;
static const __int64 SET_UV_128 = 0x8000800080008000i64;
for (int j=0; j<height; j++) {
for (int i=0; i<rowsize>>3; i++) {
__asm {
mov eax, dstp ;このようにC++の変数(dstp)を参照することができます。
movq mm0, [eax] ;
pand mm0, MASK_UV ;データ定義は_asmブロックの外でしましょう。
por mm0, SET_UV_128
movq [eax], mm0
}
dstp++;
}
dstp += (modulo>>3);
}
__asm { emms } //MMXレジスタはFPUレジスタと共有しているため、
// MMX使用後は必ずFPUモードに戻す必要があります。
//このemms命令はコストが高いので出来るだけ少ない方が望ましい
return src;
}
|
static int var_a = 0;
int var_b = 1;
int array_b[] = { 1, 2, 3, 4 };
struct {
int member_a;
int member_b;
} struct_C = { 10, 20 };
__asm {
mov eax, var_a ; eax = 0
mov eax, [var_a] ; 同上
mov eax, OFFSET var_a ; eax = var_a のアドレス。C言語で言うと &var_a のことです
lea eax, var_b ; eax = var_b のアドレス(動的な変数の場合)lea eax, _var_b$[ebp]
mov eax, array_b[3 * TYPE int] ; eax = array_b[3] = 4
mov eax, [array_b + 3 * TYPE int] ; 同上
mov eax, struct_C.member_b ; eax = 20
}
|
| [asm編]インラインアセンブラを使ってみる(2) レジスタの使用と保護 2005. 5.10 (01版) |
|---|
【処理6】
PVideoFrame __stdcall Sample::GetFrame(int n, IScriptEnvironment* env) {
//*** in-place filtering ***
PVideoFrame src = child->GetFrame(n, env);
env->MakeWritable(&src);
const int pitch = src->GetPitch();
const int rowsize = (src->GetRowSize() + 4) & -8;
const int height = src->GetHeight();
const int modulo = pitch - src->GetRowSize();
const __int64 *srcp = (__int64*)src->GetReadPtr();
__int64 *dstp = (__int64*)src->GetWritePtr();
static const __int64 MASK_UV = 0x00ff00ff00ff00ffi64;
static const __int64 SET_UV_128 = 0x8000800080008000i64;
const int rowsize_by_16 = rowsize>>4;
__asm {
push esi
push ebx
push ecx
movq mm6, MASK_UV
movq mm7, SET_UV_128
mov esi, dstp
mov ebx, height
mov ecx, rowsize_by_16
align 16
LOOP_Y: ; ジャンプ等のために、ラベルも使用できます。
LOOP_X: ;
movq mm0, [esi] ; mm0とmm1に8バイトずつ計16バイトのデータを読み込む
movq mm1, [esi+8]
add esi, 16 ; 次データの読み込みのためにソースポインタを+16する。
pand mm0, mm6 ; データmm0とmm1に交互に
pand mm1, mm6 ; mmx & 0x00ff00ff00ff00ff | 0x8000800080008000
por mm0, mm7 ; を行う
por mm1, mm7
movntq [esi-16],mm0 ; 結果を格納する
movntq [esi- 8],mm1
loopnz LOOP_X ; LOOP_X
add esi, modulo ; ライン末尾のパディングデータをスキップする
dec ebx
mov ecx, rowsize_by_16 ; 次のループのためにXループカウンタを再セット
jnz LOOP_Y ; 高さ(ライン数)数分繰り返す
sfence
emms
pop ecx
pop ebx
pop esi
}
return src;
}
|
| [asm編]インラインアセンブラを使ってみる(3) ジャンプラベル 2005. 5.10 (01版) |
|---|
void func( void )
{
goto C_Dest; /* Legal: correct case */
goto c_dest; /* Error: incorrect case */
goto A_Dest; /* Legal: correct case */
goto a_dest; /* Legal: incorrect case */
__asm
{
jmp C_Dest ; Legal: correct case
jmp c_dest ; Legal: incorrect case
jmp A_Dest ; Legal: correct case
jmp a_dest ; Legal: incorrect case
a_dest: ; __asm label
}
C_Dest: /* C label */
return;
}
int main()
{
}
|
| [asm編]関数呼び出し規約とスタック 2005. 5.10 (01版) |
|---|
| keyword | __cdecl | __stdcall | __fastcall |
|---|---|---|---|
| C++のデフォルトの規約です | |||
| fuction labeling | 関数名の先頭に'_'(アンダースコア)が付けられます | 関数名の先頭に'_'(アンダースコア)が付けられます。 末尾には'@'に続いて引数サイズが10進で付けられます |
関数名の先頭に'@'が付けられます。 末尾には'@'に続いて引数サイズが10進で付けられます |
| Stack cleanup | Caller 引数渡しスタックは呼び出した側でクリアします |
Callee 引数渡しスタックは呼び出された側でクリアします |
Callee 引数渡しスタックは呼び出された側でクリアします |
| Parameter passing | 逆順(右から左)にスタックにプッシュされます | 逆順(右から左)にスタックにプッシュされます | 32bit以下の2つまではレジスタ(ecx,edx)に、それからはスタックにプッシュされます |
| Retern address | パラメタをプッシュ後にcallされるので、戻りアドレスは一番最後にプッシュされます。 つまり、スタックポインタは戻りアドレスを指しています。 |
||
| Retern value | 復帰値はeaxにセットします。32bitより大きい場合は、 一旦別の場所に置いてからそのポインタをeaxにセットします | ||

| [asm編]関数と復帰値 2005. 5.10 (01版) |
|---|
#pragma warning(disable:4035)
int __stdcall Add(int var_a, int var_b) {
__asm {
mov eax, var_a ;実際には、mov eax, dword ptr _var_a$[ebp] と展開されます。
mov edx, var_b ; (_var_a$ = 8, _var_b$ = 12)
add eax, edx ;このように関数の復帰値はeaxレジスタにセットします。
}
return; ;ret 8
}
#pragma warning(default:4035)
void main(void) {
static const char OutputFormat[] = { "%d + %d = %d\n" };
int rc;
int a = 2;
int b = 3;
rc = Add(a, b);
__asm {
mov eax, b
push eax
mov eax, a
push eax
mov eax, OFFSET OutputFormat
push eax
call printf
pop ebx ;スタック位置を元に戻す
pop ebx ;替わりに add esp, 12 でもよい。
pop ebx ;
}
}
|
int __declspec(naked) Func()
{
// Naked functions must provide their own prolog...
__asm {
push ebp
mov esp, ebp
sub esp, __LOCAL_SIZE
}
// ... and epilog
__asm {
mov eax, 1 ;復帰値をeaxにセットする
pop ebp
ret
}
}
|
int __declspec(naked) __stdcall Add(int var_a, int var_b) {
__asm {
mov eax, var_a
mov edx, var_b
add eax, edx
ret 8
}
}
|