INC 명령어는 피연산자를 1만큼 증가시키는데 사용한다. 레지스터나 메모리에 위치한 단일 피연산자를 처리한다.
구문
INC 명령어의 구문은 다음과 같다.
INC destination
destination 피연산자는 8비트, 16비트, 32비트가 될 수 있다.
예제
INC EBX ; 32비트 레지스터를 증가시킨다.
INC DL ; 8비트 레지스터를 증가시킨다.
INC [COUNT] ; count 변수를 증가시킨다.
DEC 명령어는 피연산자를 1만큼 감소시키는데 사용한다. 레지스터나 메모리에 위치한 단일 피연산자를 처리한다.
구문
DEC 명령어의 구문은 다음과 같다.
DEC destination
destination 피연산자는 8비트, 16비트, 32비트가 될 수 있다.
예제
segment .data
count dw 0
value db 15
segment .text
inc [count]
dec [value]
mov ebx, count
inc word [ebx]
mov esi, value
dec byte [esi]
ADD와 SUB 명령어는 byte, word, doubleword 크기 데이터의 단순한 더하기/빼기 연산을 처리한다. 예를 들어, 8비트 피연산자의 합과 차, 16비트 피연산자의 합과 차, 32비트 피연산자의 합과 차.
구문
ADD와 SUB 명령어는 다음과 같다.
ADD/SUB destination, source
ADD/SUB 명령어는 다음의 피연산자를 받는다.
하지만, ADD/SUB 명령어는 다른 명령어와 마찬가지로 메모리에서 메모리로 연산은 불가능하다. ADD 또는 SUB 연산은 오버플로우 플래그와 캐리 플래그를 설정(set)하고 클리어(clear)한다.
예제
다음 예제는 두 자리 수를 유저에게 입력 받고 RAX와 RBX 레지스터에 각각 저장한다. 그리고 값을 더하여 메모리에 있는 res에 저장한 후 결과를 출력한다.
❗️ 64비트 Intel macOS에서 동작하는 코드이다.
SYS_EXIT equ 0x2000001
SYS_READ equ 0x2000003
SYS_WRITE equ 0x2000004
STDIN equ 0
STDOUT equ 1
segment .data
msg1 db "Enter a digit ", 0xA, 0xD
len1 equ $- msg1
msg2 db "Please enter a second digit", 0Xa, 0xD
len2 equ $- msg2
msg3 db "The sum is: "
len3 equ $- msg3
segment .bss
num1 resb 2
num2 resb 3
res resb 1
section .text
global _main
_main:
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, msg1
mov rdx, len1
syscall
mov rax, SYS_READ
mov rdi, STDIN
mov rsi, num1
mov rdx, 2
syscall
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, msg2
mov rdx, len2
syscall
mov rax, SYS_READ
mov rdi, STDIN
mov rsi, num2
mov rdx, 2
syscall
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, msg3
mov rdx, len3
syscall
; 첫 번째 수를 rax 레지스터에 저장하고 두 번째 수를 rbx에 저장한다.
; 아스키 코드 '0'을 빼서 십진수 숫자로 변환한다.
; mov rax, [num1]
mov r10, num1
mov rax, [r10]
sub rax, '0'
; mov rbx, [num2]
mov r11, num2
mov rbx, [r11]
sub rbx, '0'
; rax와 rbx를 add한다.
add rax, rbx
; '0'을 더해서 십진수 수를 아스키 코드로 변환한다.
add rax, '0'
; 메모리 위치의 res 변수에 합을 저장한다.
; mov [res], rax
mov r12, res
mov [r12], rax
; 합을 출력한다.
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, res
mov rdx, 1
syscall
exit:
mov rax, SYS_EXIT
mov rdi, 0 ; exit 함수의 파라미터에 0을 저장한다. 그렇지 않으면 프로그램 종료 시 exit code를 리턴한다.
xor rbx, rbx ; xor를 사용하여 이런 방식으로도 레지스터를 0으로 초기화할 수 있다.
syscall
위의 코드를 컴파일하고 실행하면, 다음의 결과를 얻을 수 있다.
Enter a digit:
3
Please enter a second digit:
4
The sum is:
7
바이너리 데이터를 곱하기 연산하는 명령어는 두 가지가 있다. MUL(Multiply) 명령어는 부호가 없는 데이터를 다루고, IMUL(Integer Multiply) 명령어는 부호가 있는 데이터를 다룬다. 두 명령어는 Carry flag와 Overflow flag를 변화시킨다.
구문
MUL/IMUL 명령어의 구문은 다음과 같다.
MUL/IMUL multiplier
번호 | 시나리오 |
---|---|
1 | 두 개의 바이트가 곱해질 경우 피승수(multiplicant)는 AL 레지스터에 있고, 승수(multiplier)는 메모리나 다른 레지스터에 있다. 곱의 결과는 AX 레지스터에 저장된다. 곱의 상위 8비트는 AH에 저장되고 하위 8비트는 AL에 저장된다. |
2 | 두 개의 워드가 곱해질 경우 피승수는 AX 레지스터에 있고, 승수는 메모리나 다른 레지스터에 있다. 예를 들어, MUL DX와 같은 경우, 반드시 승수는 DX에 저장하고, 피승수는 AX에 저장해야 한다. |
3 | 두 개의 더블워드가 곱해질 경우 피승수는 EA에 있고, 승수는 메모리나 다른 레지스터에 있다. 곱의 결과는 EDX:EAX 레지스터에 저장된다. 예를 들어, 곱의 상위 32비트는 EDX 레지스터에 저장되고 하위 32비트는 EAX 레지스터에 저장된다. |
MOV AL, 10
MOV DL, 25
MUL DL
...
MOV DL, 0FFH ; DL = -1
MOV AL, 0BEH ; AL = -66
IMUL DL
예제
다음 예제는 3 * 2를 계산한다.
❗️ 64비트 Intel macOS에서 동작하는 코드이다.
section .text
global _main
_main:
mov al, '3'
sub al, '0'
mov bl, '2'
sub bl, '0'
mul bl
add al, '0'
mov r10, res
mov [r10], al
mov rsi, msg
mov rdx, len
mov rdi, 1
mov rax, 0x2000004
syscall
mov rsi, res
mov rdx, 1
mov rdi, 1
mov rax, 0x2000004
syscall
mov rax, 0x2000001
mov rdi, 0 ; exit 함수의 파라미터에 0을 저장한다. 그렇지 않으면 프로그램 종료 시 exit code를 리턴한다.
syscall
section .data
msg db "The result is: ", 0xA, 0xD
len equ $- msg
segment .bss
res resb 1
코드를 컴파일하고 실행하면, 다음의 결과를 확인할 수 있다.
The result is:
6
나누기 연산은 몫과 나머지를 생성한다. 곱하기의 경우, 오버플로우가 발생하지 않는다. 곱의 결과를 두배 길이의 레지스터에 저장하기 때문이다. 하지만, 나누기의 경우, 오버플로우가 발생한다. 프로세서는 오버플로우가 발생할 경우, 인터럽트를 발생시킨다.
DIV 명령어는 부호가 없는 데이터에 사용되고 IDIV는 부호가 있는 데이터에 사용된다.
구문
다음은 DIV/IDIV 명령어의 포맷이다.
DIV/IDIV divisor
피제수(dividend)는 누산기(accumulator)에 위치한다. 8비트, 16비트, 32비트 피연산자와 사용할 수 있다. 나누기 연산은 여섯 개의 상태 플래그 모두에 영향을 준다. 다음 표는 다른 피연산자 크기에 따른 세 가지의 나누기 연산을 설명한다.
번호 | 시나리오 |
---|---|
1 | 바이트가 제수(divisor)일 경우 피제수가 AX 레지스터(16비트)에 있다고 가정한다. 나누기 연산 후, 몫은 AL 레지스터에 저장되고 나머지는 AH 레지스터에 저장된다. |
2 | 워드가 제수(divisor)일 경우 피제수가 32비트 크기로 DX:AX 레지스터에 있다고 가정한다. 상위 16비트는 DX에 위치하고, 하위 16비트는 AX에 위치한다. 나누기 연산 후, 16비트 몫은 AX 레지스터에 저장되고, 16비트 나머지는 DX 레지스터에 저장된다. |
3 | 더블워드가 제수(divisor)일 경우 피제수가 64비트 크기로 EDX:EAX레지스터에 있다고 가정한다. 상위 32비트는 EDX에 위치하고, 하위 32비트는 EAX에 위치한다. 나누기 연산 후, 32비트 몫은 EAX 레지스터에 저장되고, 32비트 나머지는 EDX 레지스터에 저장된다. |
예제
다음 예제는 8 / 2를 계산한다. 피제수 8은 16비트 AX 레지스터에 위치하고, 제수 2는 8비트 BL 레지스터에 위치한다.
❗️ 64비트 Intel macOS에서 동작하는 코드이다.
section .text
global _main
_main:
mov ax, '8'
sub ax, '0'
mov bl, '2'
sub bl, '0'
div bl
add ax, '0'
mov r10, res
mov [r10], ax
mov rsi, msg
mov rdx, len
mov rdi, 1
mov rax, 0x2000004
syscall
mov rsi, res
mov rdx, 1
mov rdi, 1
mov rax, 0x2000004
syscall
mov rax, 0x2000001
mov rdi, 0 ; exit 함수의 파라미터에 0을 저장한다. 그렇지 않으면 프로그램 종료 시 exit code를 리턴한다.
syscall
section .data
msg db "The result is: ", 0xA, 0xD
len equ $- msg
segment .bss
res resb 1
코드를 컴파일하고 실행하면, 다음의 결과를 확인할 수 있다.
The result is:
4