티스토리 뷰

Application/Assembly

[Assembler] Inline assembler에 대해..

알 수 없는 사용자 2005. 10. 31. 21:00
전산을 시작하고.....벌써......강산이 바뀌었다.
그 사이에 인터넷이라는 것이 발달을 했고,
참, 귀중한 많은 것들을 ....이름 모를 이들에게서.....
얻었다.......

고개숙여 감사를 드리고 싶다...
이제 나도..뭔가를 뱃어 내야할 때가 온 것 같은데....
가진게 없다보니...뱃을게 없다.? 그래도, 할 만큼을 했다는 소린해야 겠기에....
허접하지만 한 번 뱃어 볼라구 한다.

인라인 어셈이란,
고급언어에서 어셈블러 명령을 직접 사용하는 것을 말한다.
새로 시작하시는 분이나.....처음 접하시는 분들에게는 도움이 되리라
생각한다.
인라인 어셈은 필요한가?...........
-----------------------------------------------

2GHz 대의 펜티엄4 CPU 가 일반화 될려는 이....시기에
어셈이 필요할까?

음..............

글세..........

^_^

필자는 인라인어셈을 안 사용하고 게임을 만들어 본 적이 없어서
잘 모르겠다.......

뭐......배워서 버릴게 있겠는가 !!

CPU의 내부 구조.
-----------------------------------------------

여기서 CPU의 자세한 내부 구조를 설명하지는 않겠다.
왜냐하면 하다 보면......자연히 알게 되니깐....
귀챦은데......미리 알아서 외우느라....골아플 필요가 있겠는가..!!

간단하게만 얘기해 보면,

CPU는 어떠한 처리를 할때,
기본적인 방식이..........

1. RAM에 있는 데이타를 CPU 안에 복사
2. 이를 처리(연산)
3. 결과를 다시 RAM에 저장.

이러한 과정을 거친다....

따라서, CPU 내부에는 RAM의 데이타를 읽어 와서.
임시로 저장할 공간이 있다.
이 공간을 Register(레지스터) 라고 한다.
그 크기는 32bit .......즉 integer 값 하나......정도를 저장할
공간 밖에 안된다.
하지만, 이러한 Register가 하나로 부족해서 몇개가 있다..
이들에게 이름을 붙였는데.

EAX, EBX ...

등과 같은 방법으로 붙였다.
레지스터의 종류나.....그 역활에 대해서는 차차 알아 보자.
보다 자세한 내용을 알고 싶은 분들은.... 어셈관련 참고서적을
참고하기 바란다.

//

이 번에는 소스를 한 번 살펴 보자...

void memset ( void* dest, char fill, int num )
{
if (num <= 0) return;

_asm
{
mov?? al, fill
mov?? ecx, num
mov?? edi, dest
rep?? stosb
}

}
일단 얼핏 보기에도... C 코드와 어셈 코드가 섞여 있다.

잘 알 듯이.

_asm { } 로 둘러 싸여 있는 부분이 어셈 코드이다.

그 안을 살펴 보면,

mov, rep, stosb 는 cpu 명령어 이구..
al, ecx, edi 는 register(레지스터) 이다.

fill, num, dest 는 변수 이다.
변수를 CPU가 어찌 알아차리겠는가.........
하지만 고맙게도.. 컴파일러가 변수들을 각 변수들의 주소로....변환해 준다.
이러한 것은 디버깅시에...살펴 보면 알 수 있다.

잠시 위 코드의 뜻을 살펴 보면 (물론 다 알겠지만)

fill 변수의 값을 al 레지스터로 옮겨 오고,
num 변수의 값을 ecx 레지스터로 옮겨 오고,
dest 변수의 값을 edi 레지스터로 옮겨 오고,
(여기서 dest 변수의 값을 타입에서 알수 있듯이 포이터 이다.)

반복을 하라..... (rep)
어떻게 ..
al 레지스터의 값을
edi 레지스터에 있는 주소로
ecx 레지스터의 값 만큼 반복해서 저장해라. (stosb)

아마도.......이런뜻일 것이다.

위 함수는.......
자주 사용하는 memset 함수를 한 번 만들어 본 것이다.
다음에는 memcpy 를 한 번 만들어 보자.
%% 오늘의 어셈 명령어
-------------------------------------------------
MOV

이 명령어는 데이타를 옮길때 사용한다,
1. 레지스터에 특정 값을 넣거나,
2. 레지스터에서 레지스터로 값을 옮기거나,
3. 메모리(RAM)에 있는 값을 레지스터로 가져 오거나,
4. 레지스터에서 메모리(RAM)로 데이타를 옮길때....

애석하게도 MOV는 메모리(RAM)에서 메모리(RAM)으로
바로 데이타를 옮기지는 못한다.

ex)? MOV? al, 10??? // al 레지스터에 10이라는 값을 넣어라.
MOV? ebx, eax?? // eax 레지스터의 값을 ecx 레지스터에 넣어라.
MOV? ecx, [edi] // edi 레지스터가 가리키는 주소(RAM)에 있는 값을 ecx로 가져 와라.
MOV? [edi], ecx? // ecx 레지스터에 있는 값을 edi 레지스터가 가리키는 주소(RAM)로 옮겨라.

///
자......약속한 대로....오늘은 memcpy를 한 번 만들어 보자.

void memcpy ( void* dest, void* src, int num )
{
if (num <= 0) return;

_asm
{
mov?? edi, dest
mov?? esi, src
mov?? ecx, num
cld
rep?? movsb
}
}

한 번 분석해 보자.
edi, eax, esi, ecx 는 레지스터 일테고,
mov, cld, rep, movsb 는 cpu 명령어 일것이다.
그리고, dest, src, num은 변수 이다.

edi 에 dest 변수 값을 넣고,
esi 에 src 변수 값을 넣고, ( ...쉽~네~ )
ecx 에 num 변수값을 넣을 테고,
음 cld 는 뭘까? .. 일단 무시하고......
rep는 ecx 값만큼 반복하라는 뚯이고,
(그럼, ecx에 num 값이 있으니, num 만큼 반복하겠군..)
저번에는 stosb 였는데 이번에는 movsb 군,

stosb 는 al 의 값을 edi 주소에 저장하는 것이고,
movsb 는 esi주소의 값을 edi 주소로 옮기는 명령이다.
그리구나서, esi의 값과 edi 값을 하나씩 증가 시킨다.

이를 종합해 보면,
rep movsb 는 esi 주소의 값을 edi 주소로 ecx 갯수만큼 옮기는 역활을 한다.
우리가 알고 있는 memcpy 함수와 동일한 역활을 하는 것이다.

자..... 여기서 우리는
esi 레지스터는 소스 주소,
edi 레지스터는 목적지의 주소를,
ecx 레지스터는 카운트의 용도로 쓰임을 볼 수 있다.

프로그래머가 레지스터를 어떻게 사용할 것이냐는...알아서 결정할 문제이지만,
대부분 위와 같은 역활로 쓰인다.
인제 위에서...지나간 cld 에 대해서 알아 보자.
레지스터에는 여러 종류의 레지스터가 있는데....그 중 플래그레지스터 라는 놈이 있다.
이 놈은 자신의 각 비트가 하나의 상태를 나타낸다.
(하나의 비트 이므로 0 아니면 1 의 값만을 갖는다.)
이 놈은 cpu 의 명령이 실행될때마다.....그 결과에 대한 상태의 변화를 기록한다.

mov?? edi, dest

라는 명령을 실행했다고 하면,
플래그 레지스터의 상태를 나타내는 플래그에는
OF (Overflow가 발생했느냐?)
DF (방향- 어느방향으로 진행할까?)
ZF (Zero- 실행 결과가 0 이냐?)
CF (Carry- 실행 결과가 자리 올림이 발생했느냐?)
....
등등이 있다.
자세한 종류는 어셈 관련 서적을 참고하기 바란다...(죄송)

그 사용예는 앞으로 아주....자주 볼것이다.
그 중 DF만 한 번 보자.

위에서 살펴본 movsb 가 데이타를 옮긴후 esi, edi 값을 하나씩 증가 시킨다고 했다.
사실은, 이 DF 플래그 값에 따라 ...값을 증가시킬것인가 감소시킬것인가를 정한다.

즉, DF 가 0 이면 esi, edi 의 값을 증가 시키고,
DF 가 1 이면 esi, edi 의 값을 감소 시킨다.

cld 라는 명령어는 이 DF 플래그의 값을 0으로 세팅한다.
std 라는 명령도 있느데 이는 DF 플래그를 1로 세팅한다.




자...그럼.....위의 명령들을 한 번 확장해 보자.

movsb 라는 명령어에서 마지막 b는 byte 만큼씩 옮기라는 뜻이다.
그렇다면 movsw 도 있고, movsd도 있다.
여기서 w = word 를 d = double word 를 말한다.

실제 우리가 사용하는 memcpy 코드는 아래와 같다.
한 번 고민해 보면 좋지 않을까...싶다.

void memcpy ( void* dest, void* src, int num )
{
if (num <= 0) return;

_asm
{
mov?? edi, dest
mov?? eax, edi
mov?? esi, src
mov?? ecx, num
mov?? edx, ecx
shr?? ecx, 1
shr?? ecx, 1
cld
rep?? movsd
mov?? ecx, edx
and?? ecx, 0x03
rep?? movsb
}
}

///

오늘은.....저번에 배운 플래그 레지스터가 어떻게 활용되는지를
한 번 살펴 보자.

void?? strcpy (char* dest, char* src)
{
_asm
{
mov?? edi, src
mov?? ecx, 0xffffffff
xor?? al, al
cld
repnz scasb
not?? ecx

...
...
...
}

}

자...... 팔을 겉어 붙이고 한번 분석해 보자.

호호~~... 막 기분이 좋다....거의 다 아는 것들이 눈에 보인다.

xor al, al? (잉?.....) 왜 자신을 xor 했을까?

이 코드는 mov al, 0 과 같다....
근데.....위와 같은 코드를 레지스터의 값을 0으로 세팅할 때
아주....자주 사용하는 방법이다.
아니다.....거의 이렇게 사용한다.....
이유는 ?
아마도? mov al, 0 보다 속도가 빠르나 부다....
// 거의 이렇게 사용하는 코드
//-----------------------------------------------------------
레지스터 값을 0으로 세팅할때 xor 연산을 한다.

그리구,
아주 반가운 rep (repeat,반복하라),
근데, 이번에는 뒤에 nz 가 붙었다.
그 뜻은 (not zero)...이고, 이를 Repeat와 합해 보면,

repnz = Repeat While not zero?? ( 0 이 아닐 동안 반복하라 )
여기서 Not Zero 라는 것은
앞서 실행한 명령의 결과가 0 이 아닐때,
만약 실행한 결과가 0이면
플래그레지스터의 ZF(zero flag)가 1로 세팅되어 있을 것이다.
0 이 아닐때이므로
플래그레지스터의 ZF(zero flag)가 0일때라는 뜻이다.

rep는 반복할때마다 ecx 값을 하나씩 빼나간다....그러다 ecx = 0 이 되면
반복을 중단한다.
그러면 repnz 는 ecx = 0 일때도 중단하고, ZF = 1 일때도 중단한다.

카운트 만큼 반복하는 것은 C에서
for(int i=0;i<cnt;i++ ) { }

특정조건동안 반복하는 것은
while (a != 0) { }

뭐 이런 구문들을 생각나게 한다.

오~~~ 드디어 우리가 바야흐로....어셈으로 조건문을 배우기 시작하는가??
그 다음,
scasb 는 Scan String의 뜻으로

al - [edi] (edi가 가리키는 주소의 값)

연산한 결과를 가지고 플래그 레지스터의 값들을 바꾼고
edi 값을 하나 증가 시킨다.
물론, 그 결과값이 0 이면 ZF 플래그를 1 로 세팅한다.

바로 이놈이 반복을 멈추게 하는 놈이다.

즉, 조건절을 만들어 내는 부류의 명령이다.

// 이러한 조건문은
//----------------------------------------------------------------
1. 어떤 명령이....실행결과로써 플래그 레지스터를 건든다. (값을 바꾼다)
2. 뒤에서 이 플래그의 조건 (플래그의 값)에 따라 분기를 하거나 반복을 한다.

의 순을 따른다.

^_^
차차....더 알아 보자..

위 소스를 정리하면,
ecx에 0xffffffff값을 넣고,
al에 NULL을 넣고,
NULL을 찿을때까지...반복해서 ecx값을 빼 나간다.
NULL을 찿고 난뒤...ecx 값을 not 연산을 한다.
그러면, ecx에 src의 문자열에서 NULL까지의 문자수가 나온다.

ecx값을 return 하면 바로 strlen 함수가 된다.
그리구 ... 부분에 memcpy 기능을 붙여 넣으면 (ecx 만큼 memcpy를 하면)
바로 strcpy 함수가 완성되는 것이다.
여기서도.....비일비재한 코드를 하나 볼 수 있다.

// 비일비재한 코드
//------------------------------------------------------------------
ecx에 0xffffffff 값을 넣고 빼 나가다가 이를 not 연산하면 원하는 카운트를
얻는 코드.
strcpy 의 나머지 ... 부분은 여러분이 완성하기 바란다.

////

저번 시간에 조건문을 두드리기 시작했으니....
내친김에 조건문에 대해서 한 번 알아 보자.

for (int i=0; i<10; i++)
{
...
}

우리가 C 에서 보는 가장 흔한 문장 중에 하나이다.

for 문을 살펴보면,
1. 초기값 설정? ( int i=0; )
2. 조건부?????? ( i < 10; )
3. 반복부분???? ( i++; )

이렇게 나눌 수 있다.

먼저, 초기값 설정부문 (int i=0;) 은

mov? eax, i
xor? eax, eax

로 바꾸면 될 듯 하다.

다음 조건부 ( i < 10; )...
음. 복잡할 것 같으니 나중에 보자.

다음. 반복부분 (i++;)

inc? i

대충.. 합한 코드를 보면서 조건부분을 살펴보자.
mov eax, i????? // 초기값 세팅 부분 (i=0;)
xor eax,eax???? //
mov i,eax?????? //

loop_part:? <-- 이게 뭔지는 알겠지? (위치를 표시하는 라벨)

...
...
...

inc i?????????? // 반복부분? (i++;)

mov edx,i?????? // 조건부분? (i<10;)
cmp edx, 0x0a?? //
jl? loop_part?? //
cmp 라는 명령어가 보인다.
cmp = Compare (비교하라)
cmp edx, 10? ==? (edx - 10) 연산을 해서 플레그 값들을 세팅하라.
따라서 (edx-10)이 0보다 작으면 SF(Sign Flag,부호플래그)=1 이 될 것이다.

jl = Jump Less (작으면 분기하라)

cmp edx, 0x0a
jl? loop_part ==? edx 가 0x0a 보다 작으면 loop_part로 이동하라.

결국 loop_part 부분을 반복하게 될것이다.

여기서 조건관련 수식어들을 살펴 보자.

JE? = Jump Equal???? ( 같으면 분기하라)
JNE = Jump Not Equal (같지 않으면 ...)
JL? =????? Less????? ( 작으면 ... )
JNL =????? Not Less
JG? =????? Greater?? ( 크면 ... )
JS? =????? Sign????? ( (-)마이너스 이면 ... )
JZ? =????? Zero????? ( 0 이면... )
...
...

별거 아니다. (^_^)
=====================================================================

while 문도 한 번 살펴 보자....
While 문과 For 문과 비교해 보면 초기값설정부와 반복부분이 없다.
그 외는 아마도 동일할 것이다.

while (i>0)
{
...
...
}

이를 코드로 바꾸면...

loop_part: (라벨)
...
...
mov?? eax, i
test? eax,eax
jnle? loop_part

별루 설명할 것도 없지만,
test = Logical Compare
test eax,eax 는 eax 와 eax를 논리적 AND 연산하여 그 결과로 플래그들을 세팅한다.

근데, 문득 이런 생각이 든다.

jnle = 작지도 않고 같지도 않을때 JUMP 하라.
jg?? = 클때 JUMP 하라.

음.....같은거 아닐까???? ^_^

/////
저 번 시간 마지막 의문에 대한 정답은?? 예. 이다.

G? = NLE
LE = NG
NL = GE
NGE = L
PO = NP
P = PE
NBE = A
NA = BE
NZ = NE
E = Z
AE = NB
NAE = B

는 서로 같다...
자...

저 번 시간에 if 문과 while문을 살펴 보았다.
이 번 시간에는 switch 문을 한 번 살펴 보자.

c 로 된 아래와 같은 코드가 있다고 생각해 보자.

switch (x)
{
case 0:
printf? ("강태공 0");
break;
case 2:
printf ("강태공 2");
break;
case 3:
printf ("강태공 3");
break;
case 8:
printf ("강태공 8");
break;
default:
printf ("강태공 default");
break;
};
이를 어셈으로 바꾸면 아래와 같이 된다.

mov?? edx, x
sub?? edx, 0x01
jb??? case_0
dec?? edx
jz??? case_2
dec?? edx
jz??? case_3
sub?? edx,0x05
jz??? case_8
jmp?? case_default

case_0: (라벨)
...

case_2: (라벨)
...

case_3: (라벨)
...

case_8: (라벨)
...

case_default: (라벨)
...
^_^

설명하지 않아도 ....잘....알것이다.

switch 문이라서 별다를것 같지만, 막상 코드를 보면
별 다른것이 없다.

if/while/switch 문을 어셈으로 표현해 보면서
반대로 C 에서의 이들 구문의 특성을 알 수 있다.
요즈음과 같이 펜티엄 4가 대세인 시대에 이들 구문들을
속도때문에 구분해서 사용한다는 것은 별 의미가 없을 수 있다.
하지만, 혹시라도 속도를 고려해야할 때가 있다면
이제까지의 공부가 도움이 될 것이다.

---------------------------------------------------------------

본 강좌와는 무관하지만,
덧붙여서 지금까지 코드를 보면서 C 언어에 대해서 언급해 본다면,

어셈블리어가 기계어(CPU명령어)와 1:1 매치가 된다.
C 언어는 몇개의 CPU명령어를 조합해서 하나의 구문을 만든다.
결국, 어셈으로 구현할때 조금 손이 많이 가는 부분을 간단하게
표현할 수 있도록 한 정도로 볼 수 있다.

그래서, C 언어를 저급(보다 기계어에 가까운) 언어라고 한다.
상식적으로 생각해 봐도, 최적화만 잘해 준다면 거의 어셈으로
코딩한 것과 비슷할 것이다.
속도가 중요한 운영체제 같은 프로그램을 만드는데는
아주....딱.... 적합한 언어인 것이다.

반대로 생각해 본다면,
아주 마음에 안드는 언어이다.
도대체 자동으로 해 주는게 없다.
어셈으로 코딩하는것보다.....조금 나은 정도 인것이다.
아주 많은 로직과 방대한 분량의 코드를 작성해야하는
대형 시스템에게는 악몽과 같은 언어 이다.
혹시나 C 언어로 작업하는 대형 프로젝트가 있다면,
본 강좌를 읽고 있는 독자라면, 참여하지 말 것을 권하고 싶다.
요즈음 세상에는 사람을 죽이는 도구에? 칼이나 약만 있는것이 아니다.

이렇게 본다면,
향후 개발자가 되고 싶은 독자라면,
최소한 저급언어와 고급언어 하나씩은 익히고 있어야 할 것 같다.
고급언어는 기계의 관점보다 인간의 사고 관점에서
언어가 구성되어 있어서 훨씬 배우기 쉽우므로 크게 걱정하지
않아도 될 것이다.

---------------------------------------------------------------

이 쯤에서, 정리할 겸해서
CPU 명령들을 한 번 전체적으로 살펴 봐야할 것 같다.
필자가 아래에 리스트를 나열하면 독자들이 댓글로
그 명령들의 의미를 적어 주면 좋겠다.
1. 데이타 전송 명령들

CMOVE/CMOVZ
CMOVNE/CMOVNZ
CMOVA/CMOVNBE
CMOVAE/CMOVNB
CMOVB/CMOVNAE
CMOVBE/CMOVNA
CMOVG/CMOVNLE
CMOVGE/CMOVNL
CMOVL/CMOVNGE
CMOVLE/CMOVNG
CMOVC
CMOVNC
CMOVO
CMOVNO
CMOVS
CMOVNS
CMOVP/CMOVPE
CMOVNP/CMOVPO
XCHG
BSWAP
XADD
CMPXCHG
CMPXCHG8B
PUSH
POP
PUSHA/PUSHAD
POPA/POPAD
IN
OUT
CWD/CDQ
CBW/CWDE
MOVSX
MOVZX

^_^

별루 많지 않을 것이다.
//////

안녕하세요
하얀 고양이 입니다.
자주 쓰이고 눈에 거슬리는(?) 함수나 매크로등을 인라인 어셈으로 바꾸어 보자 라는 취지로 하게 되었습니다. 그 첫타는

ZeroMemory

이 함수 굳이 설명을 하지 않아도 memset과 자주 쓰이는 0으로의 초기화 함수중에 하나 이지요 :D 이것을 이번에 인라인 어셈으로 바꾸어 보고자 합니다.
이유인즉 아무리 CPU 속도가 좋아져도 역시 눈에 거슬리는 것은 거슬리는것
(ㅠㅠ)

push ebp
mov esp,ebp
..
..
pop ebp
ret

그리고 여기에 쓰이는 최소한의 스택 2개들 그리고
수를 알수 없는 cmp 명령어의 사용

위의 코드는 함수의 전형적인 코드 입니다.
이것들을 없에고 좀더 간단하게 코드를 줄여 보자!! 입니다.
아! 루프안이라면 아무래도 함수 보단 매크로 쪽이 좋을 듯 합니다..
(왜 인지는 한번 생각해 보심이 제 생각은 밑에 적겠습니다.)

우선 4의 배수의 크기를 가진 메모리를 초기화 하도록 하죠
타입을 2가지로 나누어야 합니다.
우선 포인터인것과 일반 적인 변수 형태 입니다.
코드 상에는 크게 차이는 없습니다. 그럼 적어 보도록 하죠
크기가 4의 배수 라면

xor eax,eax
mov ecx,크기
shr ecx,2
mov edi,주소 or lea edi,변수
rep stosd

이것 입니다.
코드를 설명 하면

xor eax,eax
eax에는 메모리에 넣어 줄 값입니다. 우리는 0으로 메모리를 채니까, 위처럼 했습니다. 사실 이 부분을 mov eax,0으로 해주어도 되지만 xor쪽이 조금 빠릅니다... :D

mov ecx,크기

이곳에는 크기를 넣어 주시면 됩니다. 직접 수를 적어 주셔도 되고 변수로도 가능합니다. 만약 변수의 이름이 length 라면
mov ecx,length 이런 식으로 해주셔도 됩니다.

shr ecx,2
이것은 4로 나누어 주기 위한 코드 입니다. 오른쪽으로 2번 쉬프트는.. 4로 나눈 효과이지요 :D 왜 인지는 밑에 설명이 갑니다...

mov edi,주소 or lea edi,변수

edi 에는 변수의 주소가 꼭 들어 가야 합니다. 즉 0으로 채워질 메모리 주소 입니다. mov는 뭔지 아시리라 보고 설명을 하지 않겠습니다, lea는 그 변수의 주소를 넣어 줍니다. 만약 메모리 변수 이름이 buff 라면(포인터 변수는 아닙니다.) lea edi,buff 로 해주시면 됩니다. 변수 타입에 따라 2개 중에 하나를 쓰시기 바랍니다.

rep stosd
가장 중요한 코드 인데..
굳이 풀자면 rep(eat) sto(re)s(tring)d(word)입니다.
반복해서(repeat) 저장해라(store) 문자열을(string) Dword로(Dword)..
어색하죠 ㅡㅡa
주 요는 edi에 eax를 ecx 만큼 저장 하라는 말입니다. Dword는 4바이트 입니다. 그러니까 eax전체가 edi의 주소로 복사가 됩니다.(eax는 4바이트이기 때문입니다.) 좀 부실 한것 같아서 재차 설명을 드리자면 eax는 4바이트 입니다
그런데 지금 위에서는 4바이트로 저장을 하죠? 그러니까.. eax전체가 저장이 되는 것입니다. 만약 eax가 0x00FF00FF라면 그대로 0x00FF00FF가 저장이 되는 것입니다.

xor eax,eax
mov ecx,크기
mov edi,주소 or lea edi,변수

이부분은

rep stosd

이 부분을 실행하기 위해 초기화 해준다고 생각 하시면 될 듯 합니다... ^^;;
자 이제는 나머지 4의 배수가 아닌 부분을 생각 해보죠. 이것도 보시면 아하 하실 정도로 간단 합니다.

mov eax,0
mov ecx,크기
mov edx,ecx
shr ecx,2
mov edi,주소 or lea edi,변수
rep stosd

mov ecx,edx
and ecx,03
rep stosb

입니다.
약간의 차이는 있지만 거의 비슷 하죠?
우선 추가된 코드를 보죠
mov edx,ecx 임시적으로 크기를 저장한것입니다.
잠시 후에 다시 설명을

mov ecx,edx
and ecx,03
이것은 4로 나눈 나머지를 구하는 것입니다.
어떻게 되는지는 생각 해보세요 :D(무책임 한가 ㅡㅡa)

rep stosb
이것을 또 풀죠
rep(eat) sto(re)s(tring)b(yte)입니다. 대충 무슨 말인지 알겠죠.
부실하지만 여기까지 입니다...
이것을 매크로로 만들어 쓰시면 괜찮을듯 합니다...

으음 위에서 했던 말인데.. 매크로와 함수의 차이를 아시리라 봅니다.
매크로는 사용한곳에 코드가 붙죠.. 그러나 함수는 그렇지 않고요.. 하지만
같은 것이 계속 반복 되는 것이라면 2개의 차이는 별 반 없습니다.
하 지만 매크로는 그대로 코드가 붙는 것입니다. 즉 함수 보다는 좀 더 코드가 없다는 것이지요 스택을 쓸 일도 없고.. +_+ ...하지만 이것도.. 3,4번 쓰일때 이지요.. 만약 수가 5개 10이상이라면 차라리 함수를 쓰길 권장하겠습니다......
각자의 장단 점이겠지요...
///////

안녕하세요.. 하얀 고양이 입니다.
이번엔 저번것을 확장판(?)을 해보려고 합니다..
이번엔 문자열 복사...
보통은 memcpy 같은 함수가 이것을 합니다.
물론 이것을 쓰셔도 되지만.. 앞에 서와 같은 이유(?) 로 인해.. :D
인라인으로 바꾸어 보도록 하죠..

mov ecx,복사 길이
shr ecx,2
lea edi, 대상
lea esi, 원본
rep movsd

간단 하죠... 확장이라고 한것이 부끄럽네요.. ( ..)

mov ecx,복사 길이
shr ecx,2

것은 아시리라 봅니다... 물론 4로 나누어 준것 입니다....
4바이트씩 복사가 되게 했으니 이렇게 설정을 해 주었습니다...
만약에 4바이트로 계산이 되어 있다면.. shr 명령은 없에 주셔도 되겠지요.

그리고.. 밑에 edi에는 주소 값만 들어 가면 됩니다. 만약 "대상"이라는 변수가
포인터라면 mov edi,대상 이런 식으로 해주셔야겠지요..
esi도 마찬가지로 edi와 같습니다 이것도 주소로...

그리고 많이 본
rep(eat) mov(e)s(tring)d(word)로 4바이트씩 복사를 합니다. 물론 ecx 수 만큼 복사를 수행 합니다.

만약 4의 배수가 아니라면...
전의 했던 방식으로 해주시면 됩니다. 다시 하자면..

mov ecx,복사 길이
mov edx,ecx
shr ecx,2
lea edi, 대상
lea esi, 원본
rep movsd
mov ecx,edx
and ecx,2
rep movsb
입니다.