본문 바로가기
Computer Science/language

[assembly] Assembly 기본 문법

by yeong-yi 2025. 5. 24.
Contents

mov

데이터 복사/이동  명령어

mov <destination> , <source>

 

destination : 값을 저장할 목적지(레지스터나 메모리)

source: 이동할 원본(레지스터, 메모리, 혹은 즉시값)

 

위와 같은 문법 사용시, source의 값을 destination에 대입

source나 destination에 메모리 피연산자가 나온다면, mov 명령어는 그 메모리에 직접 접근해 값을 넣거나 가져옴

간혹 immediate: 즉시값. (=상수) 레지스터나 메모리 피연산자가 아닌 숫자 그대로 들어감

mov rdi, rax  ; rax 레지스터에 담긴 값을 rdi 레지스터에 복사함
mov rsi, rdx  ; rdx 레지스터에 담긴 값을 rsi 레지스터에 복사함
mov rsp, rdi  ; rdi 레지스터에 담긴 값을 rsp 레지스터에 복사함

<레지스터 간 데이터 이동>

mov rdi, 1337        ; 10진수 숫자 1337이라는 값을 rdi 레지스터에 담음
mov rax, 0           ; 10진수 숫자 0이라는 값을 rax 레지스터에 담음
mov edi, 0xdeadbeef  ; 16진수 숫자 0xdeadbeef라는 값을 edi 레지스터에 담음

<레지스터에 값 이동>

mov DWORD PTR [address], eax  ; eax 레지스터에 담긴 값을, 주소 값 address가 가리키는 메모리에 DWORD(4 바이트) 크기로 복사함
mov BYTE PTR [var], 0x10      ; 16진수 숫자 0x10이라는 값을, 주소 값 var가 가리키는 메모리에 BYTE(1 바이트) 크기로 복사함
mov QWORD PTR [address], rsp  ; rsp 레지스터에 담긴 값을, 주소값 address가 가리키는 메모리에 QWORD(8 바이트) 크기로 복사함

<메모리에 값 혹은 레지스터를 이동>

mov rbp, [rbp + 16]  ; rbp 레지스터에 16을 더한 주소 값이 가리키는 메모리의 값을, rbp 레지스터에 복사함
mov rax, [rdi]       ; rdi 레지스터에 담긴 주소 값이 가리키는 메모리의 값을, rax 레지스터에 복사함

<레지스터에 메모리 값 이동>

 

 

lea

메모리에 실제 접근을 하지 않고, 유효 주소(Effective Address, EA)를 저장하기 위해 사용

유효 주소: 연산의 대상이 되는 데이터가 저장된 위치. C언어의 포인터 역할

int arr[5] = {10, 20, 30, 40, 50};
int *ptr = &arr[2]; // 유효 주소

&arr[2]는 배열의 세 번째 원소의 주소를 가리킴. 이 주소를 계산하기 위해 어셈 블리어에선 다음과 같이 계산

mov rbx, &arr  ; 배열 arr의 시작 주소를 rbx 레지스터에 복사
mov rcx, 2     ; 배열의 인덱스 값을 rcx 레지스터에 복사
lea rax, [rbx + rcx * 4]  ; 배열의 세 번째 원소를 가리키는 주소 값 rax = rbx + (rcx * 4) = 1008

mov명령어는 메모리에 직접 접근해 값을 가져오지만 lea 명령어는 단순히 메모리 주소를 계산

 

add, sub, mul, div

add: 덧셈

sub: 뺄셈

mul, imul: 곱셈

div, idiv: 나눗셈

부호 없는 연산(mul, div) / 부호 있는 연산(imul, idiv)

 

add - 덧셈

add 명령어는 두 피연산자를 더한다.

 

add <destination>, <source>

 

 

<destination>에 <source>를 더한 결과를 <destination>에 대입한다.

mov rax, 5      ; rax = 5
mov rbx, 10     ; rbx = 10
add rax, rbx    ; rax = rax + rbx = 15

<레지스터 간의 덧셈>

mov rax, 100
add rax, 50     ; rax = 150

<레지스터와 값끼리 덧셈>

 

sub - 뺄셈

 

sub <destination>, <source>

 

 

<destination>에 <source>를 뺀 결과를 <destination>에 대입한다

 

mul / imul - 부호 있는 곱셈 / 부호 없는 곱셈

mul / imul 명령어는 EAX 레지스터에 보관된 값과 피연산자를 곱하는 명령어이다.

두 숫자를 곱했을 때 연산 결과의 크기가 피연산자의 크기와 다르기 때문에 두 개의 레지스터를 이용해 연산 결과 처리.

 

mul 레지스터(부호 있는)

 

mul <source>

 

 

암시적으로 RAX 레지스터를 첫 번째 피연산자로 사용

<source>에 레지스터/메모리 주소를 두 번째 피연산자로 사용

 

ex. EAX 와 ESI 두 레지스터를 곱한다고 가정했을 때, 32비트와 32비트의 곱셈 결과는 최대 64비트까지 나옴. 32비트 레지스터 하나에 결과 보관하기엔 부족하므로 32비트 레지스터를 하나 더 사용해 상위 32비트, 하위 32비트를 나눠서 보관한다.

 

imul 레지스터(부호 없는)

 

imul <source> ; 피연산자 1개
imul <destination>, <source> ; 피연산자 2개
imul <destination>, <source>, <immediate> ; 피연산자 3개

 

 

mul 명령어와 동일한 동작 수행하지만, 부호 없는 정수에 대해 곱셈을 수행하는 명령어이다.

mul 명령어는 피연산자가 1개만 올 수 있지만 imul 명령어는 최대 3개까지 올 수 있다.

 

피연산자 1개: RAX, EAX등 레지스터에 <source>레지스터를 곱한 결과를, EDX, RDX 같은 레지스터에 보관

피연산자 2개: destination에 source를 곱한 결과를 destination에 대입

피연산자 3개: source와 immediate(즉시값)을 곱한 결과를 destination에 대입

 

부호 없는 곱셈을 수행하기에, 연산 결과가 비트 수를 넘는다면 넘은만큼 그대로 버리고 상태 플래그 업데이트X

 

div / idiv - 부호 없는 나눗셈 / 부호 있는 나눗셈

두 피연산자에 나눗셈을 수행하는 명령어

나누기 연산을 수행하고 몫과 나머지를 특정 레지스터에 저장하는 방식으로 진행

 

div / idiv 제수

 

 

피제수의 크기에 따라 AX, DX:AX, EDX:EAX, RDX:RAX에 저장, 수행한 결과인 몫과 나머지를 각 크기에 따라 AL:AH, AX:DX, RAX:RDX에 저장

 

 

and, or, xor, not

논리 연산은 컴퓨터가 비트 단위로 데이터를 처리할때 사용

비트 마스킹, 암호화, 비트 토글, 패리티 체크등에 사용

논리 연산은 진리표에 따라서 결과가 나옴.

 

and

AND 연산은 두 개의 비트가 모두 1일 때 결과 1 주로 특정 비트를 선택적으로 유지하는 비트마스킹 등에 사용

 

AND destination, source

 

 

or

OR 연산은 하나 이상의 비트가 1이면 결과 1

특정 비트를 설정(set)하는 데 사용

 

OR destination, source

 

 

xor

XOR 연산은 두 개의 비트가 다를때만 결과 1

주로 비트 토글 및 암호화 사용

 

 

XOR destination, source

 

 

not

NOT 연산은 비트 반전

 

 

NOT destination

 

 

inc, dec

레지스터나 메모리에 저장된 값 1씩 증가/감소 시키는 명령어

 

inc (1 증가)

mov rax, 10
inc rax ; rax=11

dec (1 감소)

mov rax, 10
dec rax ; rax=9

비교 연산

비교 명령어는 두 피연산자의 값을 비교하고 플래그를 설정

cmp

cmp <destination>, <source>

cmp 명령어는 내부적으로 두 피연산자를 빼본후 플래그 갱신

[Code]
1: mov rax, 0xA
2: mov rbx, 0xA
3: cmp rax, rbx ; ZF=1

<destination>이 <source>와 같은 값이면 뺐을 때 0이 되므로 ZF (Zero Flag)를 활성화

→ CPU는 ZF 플래그를 보고 rax 레지스터에 담긴 값과 rbx 레지스터에 담긴 값이 서로 같았다는 사실 판단 가능

 

test

test <destination>, <source>

test 명령어는 내부적으로 <destination>과 <source>를 AND 연산 수행하고, 결과를 버린 후 플래그만 갱신

: 특정 비트가 0인지 아닌지 확인하거나, 레지스터가 0인지 검사하기 위함.

→ 하나가 0이면 AND한 결과는 무조건 0이므로 ZF를 활성화

[Code]
1: xor rax, rax
2: test rax, rax  ; ZF=1

xor rax, rax를 통해 rax 레지스터에 0을 담은 후 test rax, rax 수행 시 내부적으로 AND 연산을 수행한 결과가 0이므로 ZF 플래그 설정

→ CPU는 ZF 플래그를 보고 rax 레지스터에 담긴 값이 0이라는 사실 판단