─━ IT ━─

MASM32 어셈블러 입문 : 파트2

DKel 2021. 8. 16. 01:04
반응형
읽으시기 전에 본고의 내용은, 독자의 환경에 MASM32 가 인스톨 되고 있는 것을 전제로 하고 있습니다.아직 인스톨 하지 않은 경우는,http://www.masm32.com/에서 입수하세요. 처음에 시리즈 제1회에서는 Microsoft Assembler를 사용하여 어셈블러 파일을 컴파일할 수 있도록 Visual Studio를 셋업 하는 방법을 설명했습니다. 제2회에서는 어셈블러라는 언어 그 자체라고, 어셈블러에 포함된 몇가지 명령에 대해서 해설하고 싶습니다. 변수 어셈블러에는 변수는 없습니다. 적어도 C++로 쓰이는 것 같은 의미의 변수는 없습니다. 어셈블러는 레지스터와 메모리 주소를 사용합니다. 즉, 어셈블러를 쓴다는 것은 프로세서와 같은 언어를 말한다는 것입니다. 이 점은 꼭 머리에 넣어 두세요. 예를 들면, 프로세서는 nMyInteger는 정수 값이 있음을 모르시네요응. CMyClass라고 하는 클래스가 있는 것도 모릅니다. 프로세서가 인식하고 있는 것은 레지스터 뿐이고 프로세서가 주어진 주소의 메모리에 액세스 합니다. 그럼 레지스터란 도대체 무엇일까요. 그리고 어떻게 하면 메모리에 액세스 할 수 있는 것일까요. 레지스터 레지스터 레지스터는 변수와 비슷하지만 프로세서만이 사용한다고 하는 점이 다릅니다. 방금 전에 「고등언어와 같은 의미에서의 변수는 존재합니다.」 레지스터 레지스터 레지스터 레지스터 레지스터 레지스터는 변수와 비슷합니다.수에 의해서 결정됩니다. 즉, 32비트 프로세서의 경우는 32비트가 됩니다. C++용어에서는, 이 일정 사이즈의 수치를 DWORD라고 부릅니다. 이 수치에는 부호는 붙어 있지 않습니다. 음수를 나타내지 않으면 안 될 때는, 「0x1000000+ 음수」로 표현합니다. 따라서,-1은 0xFFFFF 프로세서만,-2는 0xFFFFInFFE로 표현됩니다.(이하 같습니다.) (아프와 같습니다.)
ebx - 베이스 레지스터
ecx - 카운터 레지스터
edx - 데이터 레지스터
esi - 메모리 조작용 소스 레지스터
edi - 메모리 조작용의 데스티네이션 레지스터 eax, ebx, ecx, edx 는, 참조 방법을 변경하는 것으로, 그 레지스터를 구성하는 바이트로 분할할 수 있습니다.예를 들어 어큐믈레이터 레지스터(A 레지스터라고도 불립니다)의 경우는 다음과 같습니다.al: eax 레지스터 내 하위 단어 하위(lower) 바이트
ah: eax 레지스터 내 하위 단어 상위(higher) 바이트
ax: eax 레지스터 하위 단어(즉 2바이트)
사용 예: (ah < 8) + al
eax: 레지스터 전체 (4바이트) ebx, ecx, edx에 대해서도 다음 그림과 같이 같은 명명 규칙이 적용됩니다(esi 및 edi에는 해당되지 않습니다).이들 이름은 프로세서의 발전경위에 따릅니다.레지스터 이름 선두의 e는 확장(extended) 레지스터를 나타냅니다.즉, 16비트 프로세서에서 32비트 프로세서로 이행했을 때 각 레지스터의 32비트 버전이라는 의미입니다.16비트 프로세서에서 사용할 수 있는 레지스터는 ax, bx, cx, dx 등 뿐이었습니다.32비트 프로세서가 등장했을 때에, 새롭게 사용 가능하게 된 16비트분을 「e」라고 하는 접두사로 나타내게 되었습니다.단, 레지스터의 상위 16비트에 직접 액세스 하는 방법은 없습니다.mov(이동) 명령 우선, 가장 단순한 mov(이동) 명령부터 살펴보도록 하겠습니다.mov 명령은, 프로세서 내부에서 값을 이동하는 방법을 나타냅니다.다음은 예를 제시하겠습니다.mov eax, 100개의 명령에서는, 100이라고 하는 값을 eax 레지스터로 「이동」하고 있습니다.이것은 eax=100 과 같은 것입니다.mov명령의 구문은 다음과 같습니다.mov (destination), (source)source와 destination은 같은 크기(비트 수)여야 합니다.그 다음에 mov 명령의 예를 몇 개 나타내겠습니다.mov al , bl ; move the lower byte of ebx into the lower byte
; of eax
mov al , 0 ffh ; move 0 xFF into the lower byte of eax
mov ah , 0 ffh ; move 0 xFF into the high byte of the low word
; ( 2 - bytes ) of eax
mov ax , 0 ffffh ; move 0 xFFFF into the low word of eax
mov eax, 0fffh ; move 0xFFFF into eax 메모리의 내용을 레지스터로 이동하거나 반대로 이동하실 수도 있습니다.그런 경우에는 메모리의 내용을 나타내기 위해서 큰 괄호를 사용합니다.이동되는 바이트 수는 레지스터명에 따라 결정됩니다.mov al , [ esi ] ; move the byte contained in the memory address
; in register esi into the lower byte of eax
mov [ edi ] , bl ; move the byte value in the lowest byte of ebx
; into the memory address in register edi
mov cx , [ esi ] ; move the word ( 2 - byte ) value contained in the
; memory address of register esi into the lower
; word of ecx
mov [ edi ] , edx ; move the dword ( 4 - byte ) value contained in edx
; into the memory address contained in register edi 또한 대괄호의 연산자를 사용할 때 오프셋을 포함할 수도 있습니다.mov al , [ esi + 3 ] ; move the byte contained in the memory address
; in register esi + 3 into the lower byte of eax
mov [ edi + 2 ] , dx ; move the lower word ( 2 - bytes ) contained in
; edx into the memory address contained in the
; register edi + 2 함수는 다음 형식으로 선언합니다.TestProc proc dwValue 1 : DWORD , wValue 2 : WORD , bValue 3 : BYTE

ret

Test Procendp 이 코드 예는 빈 함수이지만 기본적인 형식은 어느 함수든 동일합니다.우선 함수명을 지정하고 proc로 지정합니다.함수의 파라미터는 proc 뒤에 :이라는 형식으로 필요한 수만큼 지정합니다.파라미터의 DWORD, WORD, BYTE의 형태입니다.함수의 끝에는 함수명과 endp를 포함하는 행을 기술합니다.ret 문은 반환하는 글이죠.다시 말하면, 이 문장에서 함수가 종료됩니다.ret문은 함수의 마지막에 기술을 하셔야 합니다.이 함수를 C++에서 호출할 경우에는 함수에서 돌아오기 전에 레지스터 ebx, esi, edi의 원래 값을 복원해야 합니다.이것을 실시하려면 , 통상은 push 와 pop 를 사용합니다(자세한 것은 후술).함수의 반환값은 eax 레지스터에 저장됩니다.대부분의 명령에서는 함수의 파라미터에 이름으로 액세스할 수 있습니다.다음은 예를 제시하겠습니다.TestProc proc dwValue 1 : DWORD , dwValue 2 : DWORD

mov eax , dwValue 1
add eax , dwValue 2
ret

Test Procendp 이 함수에서는 dwValue1과 dwValue2를 더하여 결과를 반환합니다.C++에서 어셈블러 함수에 접속하려면 같은 이름과 파라미터를 갖는 함수를 선언해야 합니다.C++ 에서의 파라미터 사이즈는 어셈블러 코드 내에서 정의한 파라미터 사이즈와 같아야 합니다.또한 extern ′C′로 선언하고 stdcall 호출 규칙을 사용할 필요가 있습니다.예를 들어 위의 어셈블러 함수에 대한 C++ 선언은 다음과 같습니다.extern ′ C ′ unsigned int __ stdcall TestProc ( unsigned int dwValue 1 ,
unsigned int dwValue2); 포인터를 건네는 경우는 그 포인터를 어셈블러 함수의 DWORD 파라미터로 선언합니다(32비트 운영체제에서는 포인터의 사이즈가 32비트이므로).마찬가지로 char는 BYTE로 주고, WCHAR는 WORD로 주도록 합니다.어셈블러 함수를 정적 DLL에서 내보내는 설계로 했을 경우에는 C++ 내에서 함수를 선언할 필요가 없습니다.DLL의 .def 파일에 함수 이름만 포함시키면 이 방법으로 선언한 다른 C++ 함수와 마찬가지로 사용할 수 있습니다.스택과 push명령, pop명령프로세서에는 「스택」이라고 불리는 기구가 있습니다.push 명령과 pop 명령을 사용하면 이 스택에 대해 레지스터, 상수, 메모리 내용을 푸시/팝할 수 있습니다.스택은 사용 가능한 레지스터 수의 적음을 보충하기 위해 준비된 기구입니다.스택을 사용하면 레지스터 내용을 효율적이고 신속한 방법으로 저장/취득할 수 있습니다.간단히 말해서, 스택은 퍼스트 인-라스트 아웃 방식의 값 큐입니다.push 명령에서는 큐의 맨 위에 값을 추가하고 pop 명령에서는 큐의 맨 위에 있는 값을 삭제하여 레지스터 또는 메모리 주소에 저장합니다.다음은 예를 제시하겠습니다.TestFunction proc

mov eax , 100
push eax ; Stack now contains { 100 }

mov eax , 200
push eax ; Stack now contains { 200 , 100 }

mov eax , 300
push eax ; Stack now contains ( 300 , 200 , 100 }

pop eax ; eax = 300 , stack = { 200 , 100 }
pop eax ; eax = 200 , stack = { 100 }
pop eax ; eax = 100 , stack = { }

ret

Test Function endp 스택의 일반적인 용도는 함수를 종료하기 전에 레지스터 ebx, esi, edi의 값을 복원하는 것입니다.다음은 예를 제시하겠습니다.TestFunction proc

push ebx
push esi
push edi

; code goes in here

pop edi
pop esi
pop ebx
ret

TestFunction endp 정말이라면 사용할 예정인 레지스터 값만 저장해두면 되는데 여기에서는 push 명령과 pop 명령의 사용법을 나타내기 위해서 이렇게 했습니다.여기에서 주목해 주었으면 하는 것은 함수를 종료할 때의 스택 상태가 함수에 들어간 시점의 것과 같아야 한다는 것입니다.바꿔 말하면 push문을 기술한 경우에는 그 함수가 돌아오기 전에 각각의 push문에 대응하는 pop문을 기술해야 합니다.플래그와 플래그와 관련된 명령 플래그는 프로세서 내에서 true 또는 false 값을 가질 수 있는 설정입니다.프로세서에는 다양한 처리의 종료상태를 나타내는 일련의 플래그가 포함되어 있습니다.다양한 플래그가 있는데 본 글에서는 ′제로 플래그′를 다루기로 하겠습니다.이 플래그는 어떤 종류의 처리에서 레지스터가 제로가 된 것을 나타내기 위해 세팅됩니다.그 외의 처리에서는 등가임을 나타내기 위해 이 플래그가 세팅됩니다.예를 들어, 감분을 하는 dec 명령을 생각해 봅시다.이 명령은 지정된 레지스터 또는 값을 1씩 줄입니다.결과가 제로가 되었을 경우는, 제로 플래그가 세트 됩니다.다음은 예를 제시하겠습니다.TestFunction proc

mov eax , 2
dec eax ; eax == 1
dec eax ; eax == 0 , zero flag is set
ret

Test Function endp 그 외에 특정 플래그 상태에 따라 동작이 달라지는 처리도 있습니다.그러한 처리의 일례가 점프입니다.가장 기본적인 jmp 명령에서는 프로그램 실행을 메모리 내의 지정 장소로 점프시킵니다.(점프처는 보통 라벨로 지정합니다.이것은 C++ goto 문장과 비슷합니다).점프에는 다양한 형식이 있으며 예를 들어 jz명령(제로일 경우 점프), jnz명령(제로가 아닌 경우 점프)등이 있습니다.이러한 명령 및 제로 플래그에 대한 지식을 사용하면 루프를 기술할 수 있습니다.LoopFunction proc

xor eax , eax ; efficient way of saying eax = 0
mov ecx , 5 ; ecx is the register generally used for counters

LoopStart : ; this is a label , used for labelling code positions
inc eax
dec ecx
jnz LoopStart

; eax now equals 5

ret

Loop Function endp 정리 본고에서는 어셈블러의 기본적인 명령을 몇 가지 소개하고 사용법을 설명했습니다.또, 레지스터란 무엇인가라고 어셈블러내에 존재하는 레지스터에 대해서도 해설했습니다.또한 어셈블러에서 파라미터가 있는 함수를 정의하는 방법과 어셈블러 함수를 C++ 내에서 선언하는 방법을 제시했습니다.다음 차시에서는 산술연산과 어셈블러 코드의 개발을 용이하게 하는 MASM의 매크로에 대해 설명하겠습니다.파트3로→
반응형