조립-퀵 가이드

어셈블리 언어 란 무엇입니까?

각 개인용 컴퓨터에는 컴퓨터의 산술, 논리 및 제어 활동을 관리하는 마이크로 프로세서가 있습니다.

각 프로세서 제품군에는 키보드 입력 받기, 화면에 정보 표시 및 기타 다양한 작업 수행과 같은 다양한 작업을 처리하기위한 고유 한 지침 세트가 있습니다. 이러한 명령어 세트를 '기계 언어 명령어'라고합니다.

프로세서는 1과 0의 문자열 인 기계어 명령어 만 이해합니다. 그러나 기계어는 소프트웨어 개발에 사용하기에는 너무 모호하고 복잡합니다. 따라서 저수준 어셈블리 언어는 다양한 명령을 기호 코드와 더 이해하기 쉬운 형식으로 나타내는 특정 프로세서 제품군을 위해 설계되었습니다.

어셈블리 언어의 장점

어셈블리 언어를 이해하면 다음을 알 수 있습니다.

  • 프로그램이 OS, 프로세서 및 BIOS와 상호 작용하는 방법
  • 메모리 및 기타 외부 장치에서 데이터를 표현하는 방법
  • 프로세서가 명령에 액세스하고 실행하는 방법
  • 지침이 데이터에 액세스하고 처리하는 방법
  • 프로그램이 외부 장치에 액세스하는 방법.

어셈블리 언어 사용의 다른 장점은 다음과 같습니다.

  • 더 적은 메모리와 실행 시간이 필요합니다.

  • 더 쉬운 방법으로 하드웨어 특정 복잡한 작업을 허용합니다.

  • 시간이 중요한 작업에 적합합니다.

  • 인터럽트 서비스 루틴 및 기타 메모리 상주 프로그램을 작성하는 데 가장 적합합니다.

PC 하드웨어의 기본 기능

PC의 주요 내부 하드웨어는 프로세서, 메모리 및 레지스터로 구성됩니다. 레지스터는 데이터와 주소를 보유하는 프로세서 구성 요소입니다. 프로그램을 실행하기 위해 시스템은 외부 장치에서 내부 메모리로 프로그램을 복사합니다. 프로세서는 프로그램 명령을 실행합니다.

컴퓨터 스토리지의 기본 단위는 약간입니다. ON (1) 또는 OFF (0) 일 수 있으며 8 개의 관련 비트 그룹은 대부분의 최신 컴퓨터에서 한 바이트를 만듭니다.

따라서 패리티 비트는 바이트의 비트 수를 홀수로 만드는 데 사용됩니다. 패리티가 짝수이면 시스템은 하드웨어 오류 또는 전기적 장애로 인해 발생할 수있는 패리티 오류 (드물지만)가 있다고 가정합니다.

프로세서는 다음 데이터 크기를 지원합니다-

  • 단어 : 2 바이트 데이터 항목
  • 더블 워드 : 4 바이트 (32 비트) 데이터 항목
  • 쿼드 워드 : 8 바이트 (64 비트) 데이터 항목
  • 단락 : 16 바이트 (128 비트) 영역
  • 킬로바이트 : 1024 바이트
  • 메가 바이트 : 1,048,576 바이트

이진수 시스템

모든 숫자 체계는 위치 표기법을 사용합니다. 즉, 숫자가 기록되는 각 위치는 다른 위치 값을 갖습니다. 각 위치는 이진수 시스템의 경우 2 인 밑의 거듭 제곱이며이 거듭 제곱은 0에서 시작하여 1 씩 증가합니다.

다음 표는 모든 비트가 ON으로 설정된 8 비트 2 진수의 위치 값을 보여줍니다.

비트 값 1 1 1 1 1 1 1 1
기수 2의 거듭 제곱으로 위치 값 128 64 32 16 8 4 2 1
비트 번호 7 6 5 4 2 1 0

이진수의 값은 1 비트의 존재와 위치 값을 기반으로합니다. 따라서 주어진 이진수의 값은-

1 + 2 + 4 + 8 +16 + 32 + 64 + 128 = 255

2 8-1 과 같습니다 .

16 진수 시스템

16 진수 시스템은 16 진법을 사용합니다.이 시스템의 숫자 범위는 0에서 15까지입니다. 규칙에 따라 문자 A에서 F는 10 진수 값 10에서 15에 해당하는 16 진수를 나타내는 데 사용됩니다.

계산에서 16 진수는 긴 이진 표현을 축약하는 데 사용됩니다. 기본적으로 16 진수 체계는 각 바이트를 반으로 나누고 각 반 바이트의 값을 표현하여 이진 데이터를 나타냅니다. 다음 표는 10 진수, 2 진수, 16 진수 등가물을 제공합니다.

십진수 이진 표현 16 진수 표현
0 0 0
1 1 1
2 10 2
11
4 100 4
5 101 5
6 110 6
7 111 7
8 1000 8
9 1001 9
10 1010 년
11 1011
12 1100 년
13 1101
14 1110 년 이자형
15 1111 년 에프

이진수를 해당하는 16 진수로 변환하려면 오른쪽에서 시작하여 각각 4 개의 연속 그룹으로 나누고 해당 그룹을 16 진수의 해당 숫자 위에 씁니다.

Example − 이진수 1000 1100 1101 0001은 16 진수와 동일합니다-8CD1

16 진수를 2 진수로 변환하려면 각 16 진수를 4 자리 2 진수로 작성하면됩니다.

Example − 16 진수 FAD8은 2 진수와 동일합니다.-1111 1010 1101 1000

이진 산술

다음 표는 이진 덧셈에 대한 네 가지 간단한 규칙을 보여줍니다.

(나는) (ii) (iii) (iv)
1
0 1 1 1
+0 +0 +1 +1
= 0 = 1 = 10 = 11

규칙 (iii) 및 (iv)는 다음 왼쪽 위치로 1 비트의 캐리를 보여줍니다.

Example

소수 바이너리
60 00111100
+42 00101010
102 01100110

음의 이진 값은 다음과 같이 표현됩니다. two's complement notation. 이 규칙에 따라 이진수를 음수 값으로 변환하는 것은 비트 값반대로하고 1을 더하는 것 입니다.

Example

53 번 00110101
비트 반전 11001010
1 추가 0000000 1
번호 -53 11001011

다른 값에서 한 값을 빼려면 빼는 숫자를 2의 보수 형식으로 변환하고 숫자를 더합니다 .

Example

53에서 42 빼기

53 번 00110101
번호 42 00101010
42의 비트 반전 11010101
1 추가 0000000 1
번호 -42 11010110
53-42 = 11 00001011

마지막 1 비트의 오버플로가 손실됩니다.

메모리의 데이터 주소 지정

프로세서가 명령 실행을 제어하는 ​​프로세스를 fetch-decode-execute cycle 아니면 그 execution cycle. 세 개의 연속 단계로 구성됩니다.

  • 메모리에서 명령어 가져 오기
  • 명령어 디코딩 또는 식별
  • 명령 실행

프로세서는 한 번에 하나 이상의 메모리 바이트에 액세스 할 수 있습니다. 16 진수 0725H를 고려해 보겠습니다. 이 숫자에는 2 바이트의 메모리가 필요합니다. 상위 바이트 또는 최상위 바이트는 07이고 하위 바이트는 25입니다.

프로세서는 데이터를 역방향 바이트 시퀀스로 저장합니다. 즉, 하위 바이트는 하위 메모리 주소에 저장되고 상위 바이트는 상위 메모리 주소에 저장됩니다. 따라서 프로세서가 0725H 값을 레지스터에서 메모리로 가져 오면 먼저 25를 하위 메모리 주소로 전송하고 07을 다음 메모리 주소로 전송합니다.

x : 메모리 주소

프로세서가 메모리에서 레지스터로 숫자 데이터를 가져 오면 다시 바이트를 반전시킵니다. 두 종류의 메모리 주소가 있습니다.

  • 절대 주소-특정 위치를 직접 참조합니다.

  • 세그먼트 주소 (또는 오프셋)-오프셋 값이있는 메모리 세그먼트의 시작 주소입니다.

지역 환경 설정

어셈블리 언어는 프로세서의 명령어 세트와 아키텍처에 따라 다릅니다. 이 자습서에서는 Pentium과 같은 Intel-32 프로세서에 중점을 둡니다. 이 튜토리얼을 따르려면 다음이 필요합니다.

  • IBM PC 또는 이와 동등한 호환 컴퓨터
  • Linux 운영 체제 사본
  • NASM 어셈블러 프로그램 사본

다음과 같은 많은 좋은 어셈블러 프로그램이 있습니다.

  • Microsoft Assembler (MASM)
  • 볼랜드 터보 어셈블러 (TASM)
  • GNU 어셈블러 (GAS)

NASM 어셈블러를 그대로 사용하겠습니다.

  • 비어 있는. 다양한 웹 소스에서 다운로드 할 수 있습니다.
  • 잘 문서화되어 있으며 인터넷에서 많은 정보를 얻을 수 있습니다.
  • Linux와 Windows 모두에서 사용할 수 있습니다.

NASM 설치

Linux를 설치하는 동안 "개발 도구"를 선택하면 Linux 운영 체제와 함께 NASM을 설치할 수 있으며 별도로 다운로드하여 설치할 필요가 없습니다. NASM이 이미 설치되어 있는지 확인하려면 다음 단계를 따르십시오.

  • Linux 터미널을 엽니 다.

  • 유형 whereis nasm ENTER를 누릅니다.

  • 이미 설치되어 있으면 nasm : / usr / bin / nasm 과 같은 줄이 나타납니다. 그렇지 않으면 nasm : 만 표시되면 NASM을 설치해야합니다.

NASM을 설치하려면 다음 단계를 따르십시오.

  • 최신 버전은 NASM (netwide assembler) 웹 사이트를 확인하십시오 .

  • 리눅스 소스 아카이브 다운로드 nasm-X.XX.ta.gz, X.XX아카이브에서 NASM의 버전 번호입니다.

  • 하위 디렉토리를 만드는 디렉토리에 아카이브의 압축을 풉니 다 nasm-X. XX.

  • cd nasm-X.XX및 입력./configure. 이 쉘 스크립트는 그에 따라 Makefile을 사용하고 설정하는 데 가장 적합한 C 컴파일러를 찾습니다.

  • 유형 make nasm 및 ndisasm 바이너리를 빌드합니다.

  • 유형 make install / usr / local / bin에 nasm 및 ndisasm을 설치하고 man 페이지를 설치합니다.

시스템에 NASM을 설치해야합니다. 또는 Fedora Linux 용 RPM 배포를 사용할 수 있습니다. 이 버전은 설치가 더 간단합니다. RPM 파일을 두 번 클릭하기 만하면됩니다.

조립 프로그램은 세 부분으로 나눌 수 있습니다.

  • 그만큼 data 부분,

  • 그만큼 bss 섹션 및

  • 그만큼 text 부분.

데이터 섹션

그만큼 data섹션은 초기화 된 데이터 또는 상수를 선언하는 데 사용됩니다. 이 데이터는 런타임에 변경되지 않습니다. 이 섹션에서는 다양한 상수 값, 파일 이름 또는 버퍼 크기 등을 선언 할 수 있습니다.

데이터 섹션을 선언하는 구문은 다음과 같습니다.

section.data

BSS의

그만큼 bss섹션은 변수 선언에 사용됩니다. bss 섹션을 선언하는 구문은 다음과 같습니다.

section.bss

텍스트 섹션

그만큼 text섹션은 실제 코드를 유지하는 데 사용됩니다. 이 섹션은 선언으로 시작해야합니다.global _start, 프로그램 실행이 시작되는 위치를 커널에 알려줍니다.

텍스트 섹션을 선언하는 구문은 다음과 같습니다.

section.text
   global _start
_start:

코멘트

어셈블리 언어 주석은 세미콜론 (;)으로 시작합니다. 공백을 포함하여 인쇄 가능한 모든 문자를 포함 할 수 있습니다. 다음과 같이 한 줄에 표시 될 수 있습니다.

; This program displays a message on screen

또는 명령과 함께 같은 줄에-

add eax, ebx     ; adds ebx to eax

어셈블리 언어 문

어셈블리 언어 프로그램은 세 가지 유형의 명령문으로 구성됩니다.

  • 실행 가능한 지침 또는 지침,
  • 어셈블러 지시문 또는 의사 작업 및
  • Macros.

그만큼 executable instructions 또는 간단히 instructions처리자에게 무엇을해야하는지 알려주십시오. 각 명령어는operation code(opcode). 실행 가능한 각 명령어는 하나의 기계어 명령어를 생성합니다.

그만큼 assembler directives 또는 pseudo-ops어셈블리 프로세스의 다양한 측면에 대해 어셈블러에게 알립니다. 이들은 실행할 수 없으며 기계어 명령을 생성하지 않습니다.

Macros 기본적으로 텍스트 대체 메커니즘입니다.

어셈블리 언어 문의 구문

어셈블리 언어 문은 한 줄에 하나씩 입력됩니다. 각 문은 다음 형식을 따릅니다.

[label]   mnemonic   [operands]   [;comment]

대괄호 안의 필드는 선택 사항입니다. 기본 명령어는 두 부분으로 구성됩니다. 첫 번째는 실행될 명령어 (또는 니모닉)의 이름이고 두 번째는 명령의 피연산자 또는 매개 변수입니다.

다음은 일반적인 어셈블리 언어 명령문의 몇 가지 예입니다.

INC COUNT        ; Increment the memory variable COUNT

MOV TOTAL, 48    ; Transfer the value 48 in the 
                 ; memory variable TOTAL
					  
ADD AH, BH       ; Add the content of the 
                 ; BH register into the AH register
					  
AND MASK1, 128   ; Perform AND operation on the 
                 ; variable MASK1 and 128
					  
ADD MARKS, 10    ; Add 10 to the variable MARKS
MOV AL, 10       ; Transfer the value 10 to the AL register

어셈블리의 Hello World 프로그램

다음 어셈블리 언어 코드는 화면에 'Hello World'문자열을 표시합니다.

section	.text
   global _start     ;must be declared for linker (ld)
	
_start:	            ;tells linker entry point
   mov	edx,len     ;message length
   mov	ecx,msg     ;message to write
   mov	ebx,1       ;file descriptor (stdout)
   mov	eax,4       ;system call number (sys_write)
   int	0x80        ;call kernel
	
   mov	eax,1       ;system call number (sys_exit)
   int	0x80        ;call kernel

section	.data
msg db 'Hello, world!', 0xa  ;string to be printed
len equ $ - msg     ;length of the string

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

Hello, world!

NASM에서 어셈블리 프로그램 컴파일 및 연결

경로를 설정했는지 확인하십시오. nasmldPATH 환경 변수의 바이너리. 이제 위의 프로그램을 컴파일하고 연결하기 위해 다음 단계를 수행하십시오.

  • 텍스트 편집기를 사용하여 위 코드를 입력하고 hello.asm으로 저장합니다.

  • 저장 한 디렉토리와 동일한 디렉토리에 있는지 확인하십시오. hello.asm.

  • 프로그램을 조립하려면 다음을 입력하십시오. nasm -f elf hello.asm

  • 오류가있는 경우이 단계에서 이에 대한 메시지가 표시됩니다. 그렇지 않으면 프로그램의 개체 파일hello.o 생성됩니다.

  • 개체 파일을 연결하고 hello라는 실행 파일을 만들려면 다음을 입력합니다. ld -m elf_i386 -s -o hello hello.o

  • 입력하여 프로그램 실행 ./hello

모든 작업을 올바르게 수행하면 'Hello, world!'가 표시됩니다. 화면에.

우리는 이미 조립 프로그램의 세 부분에 대해 논의했습니다. 이 섹션은 다양한 메모리 세그먼트도 나타냅니다.

흥미롭게도 섹션 키워드를 세그먼트로 바꾸면 동일한 결과를 얻을 수 있습니다. 다음 코드를 시도하십시오-

segment .text	   ;code segment
   global _start    ;must be declared for linker 
	
_start:	           ;tell linker entry point
   mov edx,len	   ;message length
   mov ecx,msg     ;message to write
   mov ebx,1	   ;file descriptor (stdout)
   mov eax,4	   ;system call number (sys_write)
   int 0x80	   ;call kernel

   mov eax,1       ;system call number (sys_exit)
   int 0x80	   ;call kernel

segment .data      ;data segment
msg	db 'Hello, world!',0xa   ;our dear string
len	equ	$ - msg          ;length of our dear string

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

Hello, world!

메모리 세그먼트

세그먼트 메모리 모델은 시스템 메모리를 세그먼트 레지스터에있는 포인터가 참조하는 독립 세그먼트 그룹으로 나눕니다. 각 세그먼트는 특정 유형의 데이터를 포함하는 데 사용됩니다. 한 세그먼트는 명령어 코드를 포함하는 데 사용되고 다른 세그먼트는 데이터 요소를 저장하며 세 번째 세그먼트는 프로그램 스택을 유지합니다.

위의 논의에 비추어 다양한 메모리 세그먼트를 다음과 같이 지정할 수 있습니다.

  • Data segment − 다음과 같이 표현됩니다. .data 섹션 및 .bss. .data 섹션은 프로그램에 대한 데이터 요소가 저장되는 메모리 영역을 선언하는 데 사용됩니다. 이 섹션은 데이터 요소가 선언 된 후에는 확장 될 수 없으며 프로그램 전체에서 정적으로 유지됩니다.

    .bss 섹션은 또한 나중에 프로그램에서 선언 할 데이터에 대한 버퍼를 포함하는 정적 메모리 섹션입니다. 이 버퍼 메모리는 0으로 채워집니다.

  • Code segment − 다음과 같이 표현됩니다. .text부분. 이것은 명령어 코드를 저장하는 메모리 영역을 정의합니다. 이것은 또한 고정 된 영역입니다.

  • Stack −이 세그먼트는 프로그램 내의 기능 및 절차에 전달 된 데이터 값을 포함합니다.

프로세서 작업에는 대부분 데이터 처리가 포함됩니다. 이 데이터는 메모리에 저장되고 거기에서 액세스 할 수 있습니다. 그러나 데이터를 메모리에서 읽고 메모리에 저장하면 제어 버스와 메모리 저장 장치로 데이터 요청을 보내고 동일한 채널을 통해 데이터를 가져 오는 복잡한 프로세스가 포함되므로 프로세서 속도가 느려집니다.

프로세서 작동 속도를 높이기 위해 프로세서에는 다음과 같은 내부 메모리 저장 위치가 포함됩니다. registers.

레지스터는 메모리에 액세스하지 않고도 처리 할 데이터 요소를 저장합니다. 제한된 수의 레지스터가 프로세서 칩에 내장됩니다.

프로세서 레지스터

IA-32 아키텍처에는 10 개의 32 비트 및 6 개의 16 비트 프로세서 레지스터가 있습니다. 레지스터는 세 가지 범주로 분류됩니다.

  • 일반 레지스터,
  • 제어 레지스터 및
  • 세그먼트 레지스터.

일반 레지스터는 다음 그룹으로 더 나뉩니다.

  • 데이터 레지스터,
  • 포인터 레지스터 및
  • 인덱스 레지스터.

데이터 레지스터

산술, 논리 및 기타 연산에 4 개의 32 비트 데이터 레지스터가 사용됩니다. 이 32 비트 레지스터는 세 가지 방법으로 사용할 수 있습니다.

  • 완전한 32 비트 데이터 레지스터 : EAX, EBX, ECX, EDX.

  • 32 비트 레지스터의 아래쪽 절반은 AX, BX, CX 및 DX의 4 개의 16 비트 데이터 레지스터로 사용할 수 있습니다.

  • 위에서 언급 한 4 개의 16 비트 레지스터 중 하위 및 상위 절반은 AH, AL, BH, BL, CH, CL, DH 및 DL의 8 비트 데이터 레지스터로 사용할 수 있습니다.

이러한 데이터 레지스터 중 일부는 산술 연산에서 특정 용도로 사용됩니다.

AX is the primary accumulator; 입력 / 출력 및 대부분의 산술 명령어에 사용됩니다. 예를 들어 곱셈 연산에서는 피연산자의 크기에 따라 하나의 피연산자가 EAX 또는 AX 또는 AL 레지스터에 저장됩니다.

BX is known as the base register인덱싱 된 주소 지정에 사용할 수 있습니다.

CX is known as the count register, ECX로서 CX 레지스터는 반복 작업에 루프 수를 저장합니다.

DX is known as the data register. 입력 / 출력 작업에도 사용됩니다. 또한 큰 값을 포함하는 곱하기 및 나누기 연산을 위해 DX와 함께 AX 레지스터와 함께 사용됩니다.

포인터 레지스터

포인터 레지스터는 32 비트 EIP, ESP 및 EBP 레지스터와 해당 16 비트 오른쪽 부분 IP, SP 및 BP입니다. 포인터 레지스터에는 세 가지 범주가 있습니다.

  • Instruction Pointer (IP)− 16 비트 IP 레지스터는 실행될 다음 명령어의 오프셋 주소를 저장합니다. CS 레지스터와 관련된 IP (CS : IP)는 코드 세그먼트에서 현재 명령어의 전체 주소를 제공합니다.

  • Stack Pointer (SP)− 16 비트 SP 레지스터는 프로그램 스택 내에서 오프셋 값을 제공합니다. SS 레지스터 (SS : SP)와 관련된 SP는 프로그램 스택 내에서 데이터 또는 주소의 현재 위치를 나타냅니다.

  • Base Pointer (BP)− 16 비트 BP 레지스터는 주로 서브 루틴에 전달 된 매개 변수 변수를 참조하는 데 도움이됩니다. SS 레지스터의 주소는 BP의 오프셋과 결합되어 매개 변수의 위치를 ​​가져옵니다. BP는 특수 주소 지정을위한 기본 레지스터로 DI 및 SI와 결합 될 수도 있습니다.

인덱스 레지스터

32 비트 인덱스 레지스터, ESI 및 EDI 및 해당 16 비트 맨 오른쪽 부분. SI 및 DI는 인덱싱 된 주소 지정에 사용되며 때로는 더하기 및 빼기에 사용됩니다. 두 세트의 인덱스 포인터가 있습니다-

  • Source Index (SI) − 문자열 연산을위한 소스 인덱스로 사용됩니다.

  • Destination Index (DI) − 문자열 연산을위한 대상 인덱스로 사용됩니다.

제어 레지스터

32 비트 명령어 포인터 레지스터와 결합 된 32 비트 플래그 레지스터는 제어 레지스터로 간주됩니다.

많은 명령어는 비교 및 ​​수학적 계산을 포함하고 플래그의 상태를 변경하고 일부 다른 조건부 명령어는 제어 흐름을 다른 위치로 가져 가기 위해 이러한 상태 플래그의 값을 테스트합니다.

공통 플래그 비트는 다음과 같습니다.

  • Overflow Flag (OF) − 부호있는 산술 연산 후 데이터의 상위 비트 (가장 왼쪽 비트)의 오버플로를 나타냅니다.

  • Direction Flag (DF)− 문자열 데이터를 이동하거나 비교할 좌우 방향을 결정합니다. DF 값이 0이면 문자열 연산은 왼쪽에서 오른쪽 방향을 취하고 값이 1로 설정되면 문자열 연산은 오른쪽에서 왼쪽 방향을 취합니다.

  • Interrupt Flag (IF)− 키보드 입력 등과 같은 외부 인터럽트를 무시하거나 처리할지 여부를 결정합니다. 값이 0이면 외부 인터럽트를 비활성화하고 1로 설정하면 인터럽트를 활성화합니다.

  • Trap Flag (TF)− 단일 단계 모드에서 프로세서의 작동을 설정할 수 있습니다. 우리가 사용한 DEBUG 프로그램은 트랩 플래그를 설정하므로 한 번에 한 명령 씩 실행할 수 있습니다.

  • Sign Flag (SF)− 산술 연산 결과의 부호를 보여줍니다. 이 플래그는 산술 연산 후 데이터 항목의 부호에 따라 설정됩니다. 부호는 가장 왼쪽 비트의 높은 순서로 표시됩니다. 긍정적 인 결과는 SF 값을 0으로 지우고 부정적인 결과는이를 1로 설정합니다.

  • Zero Flag (ZF)− 산술 또는 비교 연산의 결과를 나타냅니다. 0이 아닌 결과는 0 플래그를 0으로 지우고 0 결과는 1로 설정합니다.

  • Auxiliary Carry Flag (AF)− 산술 연산 후 비트 3에서 비트 4 로의 캐리를 포함합니다. 특수 산술에 사용됩니다. AF는 1 바이트 산술 연산으로 인해 비트 3에서 비트 4로 캐리가 발생할 때 설정됩니다.

  • Parity Flag (PF)− 산술 연산 결과의 총 1 비트 수를 나타냅니다. 1 비트의 짝수는 패리티 플래그를 0으로 지우고 1 비트의 홀수는 패리티 플래그를 1로 설정합니다.

  • Carry Flag (CF)− 산술 연산 후 상위 비트 (가장 왼쪽)에서 0 또는 1의 캐리를 포함합니다. 또한 이동 또는 회전 작업 의 마지막 비트 내용도 저장합니다 .

다음 표는 16 비트 플래그 레지스터에서 플래그 비트의 위치를 ​​나타냅니다.

깃발: 영형 나는 에스
비트 번호 : 15 14 13 12 11 10 9 8 7 6 5 4 2 1 0

세그먼트 레지스터

세그먼트는 데이터, 코드 및 스택을 포함하기 위해 프로그램에 정의 된 특정 영역입니다. 세 가지 주요 세그먼트가 있습니다-

  • Code Segment− 실행할 모든 명령이 포함되어 있습니다. 16 비트 코드 세그먼트 레지스터 또는 CS 레지스터는 코드 세그먼트의 시작 주소를 저장합니다.

  • Data Segment− 여기에는 데이터, 상수 및 작업 영역이 포함됩니다. 16 비트 데이터 세그먼트 레지스터 또는 DS 레지스터는 데이터 세그먼트의 시작 주소를 저장합니다.

  • Stack Segment− 데이터를 포함하고 프로 시저 또는 서브 루틴의 주소를 반환합니다. '스택'데이터 구조로 구현됩니다. 스택 세그먼트 레지스터 또는 SS 레지스터는 스택의 시작 주소를 저장합니다.

DS, CS 및 SS 레지스터 외에도 데이터 저장을위한 추가 세그먼트를 제공하는 ES (추가 세그먼트), FS 및 GS와 같은 다른 추가 세그먼트 레지스터가 있습니다.

어셈블리 프로그래밍에서 프로그램은 메모리 위치에 액세스해야합니다. 세그먼트 내의 모든 메모리 위치는 세그먼트의 시작 주소에 상대적입니다. 세그먼트는 16 또는 16 진수 10으로 균등하게 나눌 수있는 주소에서 시작합니다. 따라서 이러한 모든 메모리 주소에서 가장 오른쪽 16 진수는 0이며 일반적으로 세그먼트 레지스터에 저장되지 않습니다.

세그먼트 레지스터는 세그먼트의 시작 주소를 저장합니다. 세그먼트 내에서 데이터 또는 명령어의 정확한 위치를 얻으려면 오프셋 값 (또는 변위)이 필요합니다. 세그먼트의 메모리 위치를 참조하기 위해 프로세서는 세그먼트 레지스터의 세그먼트 주소를 위치의 오프셋 값과 결합합니다.

어셈블리 프로그래밍에서 레지스터 사용을 이해하려면 다음 간단한 프로그램을 살펴보십시오. 이 프로그램은 간단한 메시지와 함께 화면에 9 개의 별을 표시합니다.

section	.text
   global _start	 ;must be declared for linker (gcc)
	
_start:	         ;tell linker entry point
   mov	edx,len  ;message length
   mov	ecx,msg  ;message to write
   mov	ebx,1    ;file descriptor (stdout)
   mov	eax,4    ;system call number (sys_write)
   int	0x80     ;call kernel
	
   mov	edx,9    ;message length
   mov	ecx,s2   ;message to write
   mov	ebx,1    ;file descriptor (stdout)
   mov	eax,4    ;system call number (sys_write)
   int	0x80     ;call kernel
	
   mov	eax,1    ;system call number (sys_exit)
   int	0x80     ;call kernel
	
section	.data
msg db 'Displaying 9 stars',0xa ;a message
len equ $ - msg  ;length of message
s2 times 9 db '*'

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

Displaying 9 stars
*********

시스템 호출은 사용자 공간과 커널 공간 사이의 인터페이스를위한 API입니다. 우리는 이미 시스템 호출을 사용했습니다. sys_write 및 sys_exit, 각각 화면에 쓰고 프로그램을 종료합니다.

Linux 시스템 호출

어셈블리 프로그램에서 Linux 시스템 호출을 사용할 수 있습니다. 프로그램에서 Linux 시스템 호출을 사용하려면 다음 단계를 수행해야합니다.

  • EAX 레지스터에 시스템 호출 번호를 입력합니다.
  • EBX, ECX 등의 레지스터에 시스템 호출에 대한 인수를 저장합니다.
  • 관련 인터럽트 (80h)를 호출합니다.
  • 결과는 일반적으로 EAX 레지스터에 반환됩니다.

사용 된 시스템 호출의 인수를 저장하는 6 개의 레지스터가 있습니다. 이들은 EBX, ECX, EDX, ESI, EDI 및 EBP입니다. 이러한 레지스터는 EBX 레지스터로 시작하는 연속 인수를 사용합니다. 6 개 이상의 인수가있는 경우 첫 번째 인수의 메모리 위치는 EBX 레지스터에 저장됩니다.

다음 코드 조각은 시스템 호출 sys_exit의 사용을 보여줍니다-

mov	eax,1		; system call number (sys_exit)
int	0x80		; call kernel

다음 코드 조각은 시스템 호출 sys_write의 사용을 보여줍니다-

mov	edx,4		; message length
mov	ecx,msg		; message to write
mov	ebx,1		; file descriptor (stdout)
mov	eax,4		; system call number (sys_write)
int	0x80		; call kernel

모든 syscall은 숫자 (int 80h를 호출하기 전에 EAX에 입력 할 값)와 함께 /usr/include/asm/unistd.h 에 나열됩니다 .

다음 표는이 튜토리얼에서 사용 된 시스템 호출 중 일부를 보여줍니다.

% eax 이름 % ebx % ecx % edx % esx % edi
1 sys_exit int - - - -
2 sys_fork 구조체 pt_regs - - - -
sys_read 서명되지 않은 정수 char * size_t - -
4 sys_write 서명되지 않은 정수 const char * size_t - -
5 sys_open const char * int int - -
6 sys_close 서명되지 않은 정수 - - - -

다음 예제는 키보드에서 숫자를 읽고 화면에 표시합니다.

section .data                           ;Data segment
   userMsg db 'Please enter a number: ' ;Ask the user to enter a number
   lenUserMsg equ $-userMsg             ;The length of the message
   dispMsg db 'You have entered: '
   lenDispMsg equ $-dispMsg                 

section .bss           ;Uninitialized data
   num resb 5
	
section .text          ;Code Segment
   global _start
	
_start:                ;User prompt
   mov eax, 4
   mov ebx, 1
   mov ecx, userMsg
   mov edx, lenUserMsg
   int 80h

   ;Read and store the user input
   mov eax, 3
   mov ebx, 2
   mov ecx, num  
   mov edx, 5          ;5 bytes (numeric, 1 for sign) of that information
   int 80h
	
   ;Output the message 'The entered number is: '
   mov eax, 4
   mov ebx, 1
   mov ecx, dispMsg
   mov edx, lenDispMsg
   int 80h  

   ;Output the number entered
   mov eax, 4
   mov ebx, 1
   mov ecx, num
   mov edx, 5
   int 80h  
    
   ; Exit code
   mov eax, 1
   mov ebx, 0
   int 80h

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

Please enter a number:
1234  
You have entered:1234

대부분의 어셈블리 언어 명령어에는 피연산자가 처리되어야합니다. 피연산자 주소는 처리 할 데이터가 저장되는 위치를 제공합니다. 일부 명령어에는 피연산자가 필요하지 않은 반면 다른 명령어에는 피연산자가 1 개, 2 개 또는 3 개 필요할 수 있습니다.

명령어에 두 개의 피연산자가 필요한 경우 첫 번째 피연산자는 일반적으로 레지스터 또는 메모리 위치의 데이터를 포함하는 대상이고 두 번째 피연산자는 소스입니다. 소스에는 전달할 데이터 (즉시 주소 지정) 또는 데이터 주소 (레지스터 또는 메모리)가 포함됩니다. 일반적으로 소스 데이터는 작업 후에도 변경되지 않습니다.

주소 지정의 세 가지 기본 모드는 다음과 같습니다.

  • 주소 등록
  • 즉각적인 주소 지정
  • 메모리 주소 지정

주소 등록

이 주소 지정 모드에서 레지스터에는 피연산자가 포함됩니다. 명령어에 따라 레지스터는 첫 번째 피연산자, 두 번째 피연산자 또는 둘 다일 수 있습니다.

예를 들면

MOV DX, TAX_RATE   ; Register in first operand
MOV COUNT, CX	   ; Register in second operand
MOV EAX, EBX	   ; Both the operands are in registers

레지스터 간의 데이터 처리에는 메모리가 필요하지 않으므로 데이터 처리 속도가 가장 빠릅니다.

즉각적인 주소 지정

즉치 피연산자에는 상수 값 또는 표현식이 있습니다. 두 개의 피연산자가있는 명령어가 즉시 주소 지정을 사용하는 경우 첫 번째 피연산자는 레지스터 또는 메모리 위치가 될 수 있고 두 번째 피연산자는 즉시 상수입니다. 첫 번째 피연산자는 데이터의 길이를 정의합니다.

예를 들면

BYTE_VALUE  DB  150    ; A byte value is defined
WORD_VALUE  DW  300    ; A word value is defined
ADD  BYTE_VALUE, 65    ; An immediate operand 65 is added
MOV  AX, 45H           ; Immediate constant 45H is transferred to AX

직접 메모리 주소 지정

메모리 주소 지정 모드에서 피연산자가 지정되면 주 메모리 (일반적으로 데이터 세그먼트에 대한 직접 액세스)가 필요합니다. 이러한 주소 지정 방법으로 인해 데이터 처리 속도가 느려집니다. 메모리에서 데이터의 정확한 위치를 찾으려면 일반적으로 DS 레지스터에있는 세그먼트 시작 주소와 오프셋 값이 필요합니다. 이 오프셋 값은effective address.

직접 주소 지정 모드에서 오프셋 값은 일반적으로 변수 이름으로 표시되는 명령어의 일부로 직접 지정됩니다. 어셈블러는 오프셋 값을 계산하고 프로그램에서 사용되는 모든 변수의 오프셋 값을 저장하는 기호 테이블을 유지합니다.

직접 메모리 주소 지정에서 피연산자 중 하나는 메모리 위치를 참조하고 다른 피연산자는 레지스터를 참조합니다.

예를 들면

ADD	BYTE_VALUE, DL	; Adds the register in the memory location
MOV	BX, WORD_VALUE	; Operand from the memory is added to register

직접 오프셋 주소 지정

이 주소 지정 모드는 산술 연산자를 사용하여 주소를 수정합니다. 예를 들어, 데이터 테이블을 정의하는 다음 정의를 살펴보십시오.

BYTE_TABLE DB  14, 15, 22, 45      ; Tables of bytes
WORD_TABLE DW  134, 345, 564, 123  ; Tables of words

다음 작업은 메모리의 테이블에서 레지스터로 데이터에 액세스합니다.

MOV CL, BYTE_TABLE[2]	; Gets the 3rd element of the BYTE_TABLE
MOV CL, BYTE_TABLE + 2	; Gets the 3rd element of the BYTE_TABLE
MOV CX, WORD_TABLE[3]	; Gets the 4th element of the WORD_TABLE
MOV CX, WORD_TABLE + 3	; Gets the 4th element of the WORD_TABLE

간접 메모리 주소 지정

이 주소 지정 모드는 컴퓨터의 Segment : Offset 주소 지정 기능을 활용합니다 . 일반적으로 메모리 참조를 위해 대괄호 안에 코딩 된 기본 레지스터 EBX, EBP (또는 BX, BP) 및 인덱스 레지스터 (DI, SI)가이 용도로 사용됩니다.

간접 주소 지정은 일반적으로 배열과 같은 여러 요소를 포함하는 변수에 사용됩니다. 어레이의 시작 주소는 예를 들어 EBX 레지스터에 저장됩니다.

다음 코드 스 니펫은 변수의 여러 요소에 액세스하는 방법을 보여줍니다.

MY_TABLE TIMES 10 DW 0  ; Allocates 10 words (2 bytes) each initialized to 0
MOV EBX, [MY_TABLE]     ; Effective Address of MY_TABLE in EBX
MOV [EBX], 110          ; MY_TABLE[0] = 110
ADD EBX, 2              ; EBX = EBX +2
MOV [EBX], 123          ; MY_TABLE[1] = 123

MOV 지침

우리는 이미 한 저장 공간에서 다른 저장 공간으로 데이터를 이동하는 데 사용되는 MOV 명령어를 사용했습니다. MOV 명령어는 두 개의 피연산자를 사용합니다.

통사론

MOV 명령어의 구문은 다음과 같습니다.

MOV  destination, source

MOV 명령어는 다음 5 가지 형식 중 하나를 가질 수 있습니다.

MOV  register, register
MOV  register, immediate
MOV  memory, immediate
MOV  register, memory
MOV  memory, register

유의하십시오-

  • MOV 연산의 두 피연산자는 크기가 같아야합니다.
  • 소스 피연산자의 값은 변경되지 않습니다.

MOV 명령어는 때때로 모호성을 유발합니다. 예를 들어, 진술을보십시오-

MOV  EBX, [MY_TABLE]  ; Effective Address of MY_TABLE in EBX
MOV  [EBX], 110	      ; MY_TABLE[0] = 110

해당 바이트 또는 숫자 110에 해당하는 단어를 이동할 것인지 여부는 명확하지 않습니다. 이러한 경우 다음을 사용하는 것이 좋습니다. type specifier.

다음 표는 일반적인 유형 지정자 중 일부를 보여줍니다.

유형 지정자 처리 된 바이트
바이트 1
워드 2
DWORD 4
QWORD 8
TBYTE 10

다음 프로그램은 위에서 설명한 몇 가지 개념을 보여줍니다. 메모리의 데이터 섹션에 'Zara Ali'라는 이름을 저장 한 다음 그 값을 프로그래밍 방식으로 다른 이름 'Nuha Ali'로 변경하고 두 이름을 모두 표시합니다.

section	.text
   global _start     ;must be declared for linker (ld)
_start:             ;tell linker entry point
	
   ;writing the name 'Zara Ali'
   mov	edx,9       ;message length
   mov	ecx, name   ;message to write
   mov	ebx,1       ;file descriptor (stdout)
   mov	eax,4       ;system call number (sys_write)
   int	0x80        ;call kernel
	
   mov	[name],  dword 'Nuha'    ; Changed the name to Nuha Ali
	
   ;writing the name 'Nuha Ali'
   mov	edx,8       ;message length
   mov	ecx,name    ;message to write
   mov	ebx,1       ;file descriptor (stdout)
   mov	eax,4       ;system call number (sys_write)
   int	0x80        ;call kernel
	
   mov	eax,1       ;system call number (sys_exit)
   int	0x80        ;call kernel

section	.data
name db 'Zara Ali '

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

Zara Ali Nuha Ali

NASM은 다양한 define directives변수에 대한 저장 공간을 예약합니다. 정의 어셈블러 지시문은 스토리지 공간 할당에 사용됩니다. 하나 이상의 바이트를 예약하고 초기화하는 데 사용할 수 있습니다.

초기화 된 데이터를위한 저장 공간 할당

초기화 된 데이터에 대한 저장 할당 문 구문은 다음과 같습니다.

[variable-name]    define-directive    initial-value   [,initial-value]...

여기서 variable-name 은 각 저장 공간의 식별자입니다. 어셈블러는 데이터 세그먼트에 정의 된 각 변수 이름에 대한 오프셋 값을 연결합니다.

정의 지시어에는 다섯 가지 기본 형식이 있습니다.

지령 목적 저장 공간
DB 바이트 정의 1 바이트 할당
DW 단어 정의 2 바이트 할당
DD 더블 워드 정의 4 바이트 할당
DQ 쿼드 워드 정의 8 바이트 할당
DT 10 바이트 정의 10 바이트 할당

다음은 정의 지시문을 사용하는 몇 가지 예입니다-

choice		DB	'y'
number		DW	12345
neg_number	DW	-12345
big_number	DQ	123456789
real_number1	DD	1.234
real_number2	DQ	123.456

유의하십시오-

  • 문자의 각 바이트는 16 진수의 ASCII 값으로 저장됩니다.

  • 각 10 진수 값은 해당 16 비트 2 진 값으로 자동 변환되어 16 진수로 저장됩니다.

  • 프로세서는 리틀 엔디안 바이트 순서를 사용합니다.

  • 음수는 2의 보수 표현으로 변환됩니다.

  • 짧은 부동 소수점 숫자와 긴 부동 소수점 숫자는 각각 32 비트 또는 64 비트를 사용하여 표현됩니다.

다음 프로그램은 정의 지시어의 사용을 보여줍니다-

section .text
   global _start          ;must be declared for linker (gcc)
	
_start:                   ;tell linker entry point
   mov	edx,1		  ;message length
   mov	ecx,choice        ;message to write
   mov	ebx,1		  ;file descriptor (stdout)
   mov	eax,4		  ;system call number (sys_write)
   int	0x80		  ;call kernel

   mov	eax,1		  ;system call number (sys_exit)
   int	0x80		  ;call kernel

section .data
choice DB 'y'

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

y

초기화되지 않은 데이터를위한 저장 공간 할당

예약 지시문은 초기화되지 않은 데이터를위한 공간을 예약하는 데 사용됩니다. 예약 지시문은 예약 할 공간 단위 수를 지정하는 단일 피연산자를 사용합니다. 각 정의 지시문에는 관련 예약 지시문이 있습니다.

예비 지침에는 다섯 가지 기본 형식이 있습니다.

지령 목적
RESB 바이트 예약
RESW 단어 예약
RESD 더블 워드 예약
RESQ 쿼드 워드 예약
쉬다 10 바이트 예약

다중 정의

프로그램에 여러 데이터 정의 명령문이있을 수 있습니다. 예를 들면-

choice	  DB 	'Y' 		 ;ASCII of y = 79H
number1	  DW 	12345 	 ;12345D = 3039H
number2    DD  12345679  ;123456789D = 75BCD15H

어셈블러는 여러 변수 정의에 대해 연속 메모리를 할당합니다.

다중 초기화

TIMES 지시문은 동일한 값에 대한 여러 초기화를 허용합니다. 예를 들어, 크기가 9 인 mark라는 배열은 다음 문을 사용하여 정의하고 0으로 초기화 할 수 있습니다.

marks  TIMES  9  DW  0

TIMES 지시문은 배열과 테이블을 정의하는 데 유용합니다. 다음 프로그램은 화면에 9 개의 별표를 표시합니다.

section	.text
   global _start        ;must be declared for linker (ld)
	
_start:                 ;tell linker entry point
   mov	edx,9		;message length
   mov	ecx, stars	;message to write
   mov	ebx,1		;file descriptor (stdout)
   mov	eax,4		;system call number (sys_write)
   int	0x80		;call kernel

   mov	eax,1		;system call number (sys_exit)
   int	0x80		;call kernel

section	.data
stars   times 9 db '*'

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

*********

상수를 정의하는 NASM에서 제공하는 몇 가지 지시문이 있습니다. 이전 장에서 이미 EQU 지시문을 사용했습니다. 특히 세 가지 지침에 대해 논의 할 것입니다.

  • EQU
  • %assign
  • %define

EQU 지침

그만큼 EQU지시문은 상수를 정의하는 데 사용됩니다. EQU 지시문의 구문은 다음과 같습니다.

CONSTANT_NAME EQU expression

예를 들면

TOTAL_STUDENTS equ 50

그런 다음 코드에서이 상수 값을 다음과 같이 사용할 수 있습니다.

mov  ecx,  TOTAL_STUDENTS 
cmp  eax,  TOTAL_STUDENTS

EQU 문의 피연산자는 표현식이 될 수 있습니다.

LENGTH equ 20
WIDTH  equ 10
AREA   equ length * width

위의 코드 세그먼트는 AREA를 200으로 정의합니다.

다음 예제는 EQU 지시어의 사용을 보여줍니다-

SYS_EXIT  equ 1
SYS_WRITE equ 4
STDIN     equ 0
STDOUT    equ 1
section	 .text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point
   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg1         
   mov edx, len1 
   int 0x80                
	
   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg2         
   mov edx, len2 
   int 0x80 
	
   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg3         
   mov edx, len3 
   int 0x80
   
   mov eax,SYS_EXIT    ;system call number (sys_exit)
   int 0x80            ;call kernel

section	 .data
msg1 db	'Hello, programmers!',0xA,0xD 	
len1 equ $ - msg1			

msg2 db 'Welcome to the world of,', 0xA,0xD 
len2 equ $ - msg2 msg3 db 'Linux assembly programming! ' len3 equ $- msg3

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

Hello, programmers!
Welcome to the world of,
Linux assembly programming!

% assign 지시어

그만큼 %assign지시문은 EQU 지시문과 같은 숫자 상수를 정의하는 데 사용할 수 있습니다. 이 지시문은 재정의를 허용합니다. 예를 들어, 상수 TOTAL을 다음과 같이 정의 할 수 있습니다.

%assign TOTAL 10

나중에 코드에서 다음과 같이 재정의 할 수 있습니다.

%assign  TOTAL  20

이 지시문은 대소 문자를 구분합니다.

% define 지시어

그만큼 %define지시문을 사용하면 숫자 및 문자열 상수를 모두 정의 할 수 있습니다. 이 지시문은 C의 #define과 유사합니다. 예를 들어 상수 PTR을 다음과 같이 정의 할 수 있습니다.

%define PTR [EBP+4]

위 코드는 PTR 을 [EBP + 4]로 대체합니다 .

이 지시문은 또한 재정의를 허용하며 대소 문자를 구분합니다.

INC 지침

INC 명령어는 피연산자를 1 씩 증가시키는 데 사용됩니다. 레지스터 또는 메모리에있을 수있는 단일 피연산자에서 작동합니다.

통사론

INC 명령어는 다음과 같은 구문을 가지고 있습니다.

INC destination

피연산자 대상 은 8 비트, 16 비트 또는 32 비트 피연산자 일 수 있습니다.

INC EBX	     ; Increments 32-bit register
INC DL       ; Increments 8-bit register
INC [count]  ; Increments the count variable

DEC 지침

DEC 명령어는 피연산자를 1 씩 감소시키는 데 사용됩니다. 레지스터 또는 메모리에있을 수있는 단일 피연산자에서 작동합니다.

통사론

DEC 명령어는 다음과 같은 구문을 갖습니다.

DEC 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 지침

ADD 및 SUB 명령어는 바이트, 워드 및 더블 워드 크기로 이진 데이터의 간단한 더하기 / 빼기를 ​​수행하는 데 사용됩니다. 즉, 각각 8 비트, 16 비트 또는 32 비트 피연산자를 더하거나 빼는 데 사용됩니다.

통사론

ADD 및 SUB 명령어에는 다음과 같은 구문이 있습니다.

ADD/SUB	destination, source

ADD / SUB 명령어는-

  • 등록하려면 등록
  • 등록 할 메모리
  • 메모리에 등록
  • 상수 데이터에 등록
  • 일정한 데이터에 대한 메모리

그러나 다른 명령어와 마찬가지로 ADD / SUB 명령어를 사용하여 메모리 대 메모리 작업을 수행 할 수 없습니다. ADD 또는 SUB 작업은 오버플로 및 캐리 플래그를 설정하거나 지 웁니다.

Example

The following example will ask two digits from the user, store the digits in the EAX and EBX register, respectively, add the values, store the result in a memory location 'res' and finally display the result.

SYS_EXIT  equ 1
SYS_READ  equ 3
SYS_WRITE equ 4
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 2 
   res resb 1    

section	.text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point
   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg1         
   mov edx, len1 
   int 0x80                

   mov eax, SYS_READ 
   mov ebx, STDIN  
   mov ecx, num1 
   mov edx, 2
   int 0x80            

   mov eax, SYS_WRITE        
   mov ebx, STDOUT         
   mov ecx, msg2          
   mov edx, len2         
   int 0x80

   mov eax, SYS_READ  
   mov ebx, STDIN  
   mov ecx, num2 
   mov edx, 2
   int 0x80        

   mov eax, SYS_WRITE         
   mov ebx, STDOUT         
   mov ecx, msg3          
   mov edx, len3         
   int 0x80

   ; moving the first number to eax register and second number to ebx
   ; and subtracting ascii '0' to convert it into a decimal number
	
   mov eax, [num1]
   sub eax, '0'
	
   mov ebx, [num2]
   sub ebx, '0'

   ; add eax and ebx
   add eax, ebx
   ; add '0' to to convert the sum from decimal to ASCII
   add eax, '0'

   ; storing the sum in memory location res
   mov [res], eax

   ; print the sum 
   mov eax, SYS_WRITE        
   mov ebx, STDOUT
   mov ecx, res         
   mov edx, 1        
   int 0x80

exit:    
   
   mov eax, SYS_EXIT   
   xor ebx, ebx 
   int 0x80

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

Enter a digit:
3
Please enter a second digit:
4
The sum is:
7

The program with hardcoded variables −

section	.text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point
   mov	eax,'3'
   sub     eax, '0'
	
   mov 	ebx, '4'
   sub     ebx, '0'
   add 	eax, ebx
   add	eax, '0'
	
   mov 	[sum], eax
   mov	ecx,msg	
   mov	edx, len
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	ecx,sum
   mov	edx, 1
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	eax,1	;system call number (sys_exit)
   int	0x80	;call kernel
	
section .data
   msg db "The sum is:", 0xA,0xD 
   len equ $ - msg   
   segment .bss
   sum resb 1

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

The sum is:
7

MUL / IMUL 명령어

이진 데이터를 곱하는 데는 두 가지 지침이 있습니다. MUL (Multiply) 명령어는 부호없는 데이터를 처리하고 IMUL (Integer Multiply)은 부호있는 데이터를 처리합니다. 두 명령어 모두 Carry 및 Overflow 플래그에 영향을줍니다.

통사론

MUL / IMUL 명령어의 구문은 다음과 같습니다.

MUL/IMUL multiplier

두 경우 모두 승수는 승수 및 승수의 크기에 따라 누산기에 있으며 생성 된 제품도 피연산자의 크기에 따라 두 개의 레지스터에 저장됩니다. 다음 섹션에서는 세 가지 다른 경우에 MUL 명령어를 설명합니다.

Sr. 아니. 시나리오
1

When two bytes are multiplied −

승수는 AL 레지스터에 있고 승수는 메모리 또는 다른 레지스터의 바이트입니다. 제품은 AX에 있습니다. 제품의 상위 8 비트는 AH에 저장되고 하위 8 비트는 AL에 저장됩니다.

2

When two one-word values are multiplied −

승수는 AX 레지스터에 있어야하며 승수는 메모리 또는 다른 레지스터의 단어입니다. 예를 들어, MUL DX와 같은 명령어의 경우 승수를 DX에 저장하고 승수를 AX에 저장해야합니다.

결과 제품은 두 개의 레지스터가 필요한 더블 워드입니다. 상위 (가장 왼쪽) 부분은 DX에 저장되고 하위 (가장 오른쪽) 부분은 AX에 저장됩니다.

When two doubleword values are multiplied −

두 개의 더블 워드 값을 곱할 때 곱셈은 EAX에 있어야하며 곱셈기는 메모리 또는 다른 레지스터에 저장된 더블 워드 값입니다. 생성 된 제품은 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를 곱하고 결과를 표시합니다.

section	.text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point

   mov	al,'3'
   sub     al, '0'
	
   mov 	bl, '2'
   sub     bl, '0'
   mul 	bl
   add	al, '0'
	
   mov 	[res], al
   mov	ecx,msg	
   mov	edx, len
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	ecx,res
   mov	edx, 1
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	eax,1	;system call number (sys_exit)
   int	0x80	;call kernel

section .data
msg db "The result is:", 0xA,0xD 
len equ $- msg   
segment .bss
res resb 1

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

The result is:
6

DIV / IDIV 지침

나누기 작업은 두 가지 요소를 생성합니다. quotient 그리고 remainder. 곱셈의 경우 제품을 유지하기 위해 2 배 길이 레지스터를 사용하므로 오버플로가 발생하지 않습니다. 단, 분할의 경우 오버 플로우가 발생할 수 있습니다. 오버플로가 발생하면 프로세서가 인터럽트를 생성합니다.

DIV (나누기) 명령어는 부호없는 데이터에 사용되며 IDIV (정수 나누기)는 부호있는 데이터에 사용됩니다.

통사론

DIV / IDIV 명령어의 형식-

DIV/IDIV	divisor

배당금은 누산기에 있습니다. 두 명령어 모두 8 비트, 16 비트 또는 32 비트 피연산자와 함께 작동 할 수 있습니다. 이 작업은 6 개의 상태 플래그 모두에 영향을줍니다. 다음 섹션에서는 피연산자 크기가 다른 세 가지 분할 사례를 설명합니다.

Sr. 아니. 시나리오
1

When the divisor is 1 byte −

피제수는 AX 레지스터 (16 비트)에 있다고 가정합니다. 나눗셈 후 몫은 AL 레지스터로 이동하고 나머지는 AH 레지스터로 이동합니다.

2

When the divisor is 1 word −

피제수는 길이가 32 비트이고 DX : AX 레지스터에 있다고 가정합니다. 상위 16 비트는 DX에 있고 하위 16 비트는 AX에 있습니다. 나눗셈 후 16 비트 몫은 AX 레지스터로 이동하고 16 비트 나머지는 DX 레지스터로 이동합니다.

When the divisor is doubleword −

피제수는 64 비트 길이이며 EDX : EAX 레지스터에 있다고 가정합니다. 상위 32 비트는 EDX에 있고 하위 32 비트는 EAX에 있습니다. 나눗셈 후 32 비트 몫은 EAX 레지스터로 이동하고 32 비트 나머지는 EDX 레지스터로 이동합니다.

다음 예에서는 8을 2로 나눕니다. dividend 8 에 저장됩니다 16-bit AX register 그리고 divisor 2 에 저장됩니다 8-bit BL register.

section	.text
   global _start    ;must be declared for using gcc
	
_start:             ;tell linker entry point
   mov	ax,'8'
   sub     ax, '0'
	
   mov 	bl, '2'
   sub     bl, '0'
   div 	bl
   add	ax, '0'
	
   mov 	[res], ax
   mov	ecx,msg	
   mov	edx, len
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	ecx,res
   mov	edx, 1
   mov	ebx,1	;file descriptor (stdout)
   mov	eax,4	;system call number (sys_write)
   int	0x80	;call kernel
	
   mov	eax,1	;system call number (sys_exit)
   int	0x80	;call kernel
	
section .data
msg db "The result is:", 0xA,0xD 
len equ $- msg   
segment .bss
res resb 1

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

The result is:
4

프로세서 명령어 세트는 프로그램의 필요에 따라 비트를 테스트, 설정 및 지우는 명령어 AND, OR, XOR, TEST 및 NOT 부울 논리를 제공합니다.

이 지침의 형식-

Sr. 아니. 교수 체재
1 AND 피연산자 1, 피연산자 2
2 또는 OR 피연산자 1, 피연산자 2
XOR XOR 피연산자 1, 피연산자 2
4 테스트 TEST 피연산자 1, 피연산자 2
5 아니 피연산자 1 아님

모든 경우의 첫 번째 피연산자는 레지스터 또는 메모리에있을 수 있습니다. 두 번째 피연산자는 레지스터 / 메모리 또는 즉치 (상수) 값에있을 수 있습니다. 그러나 메모리 대 메모리 작업은 불가능합니다. 이러한 명령어는 피연산자의 비트를 비교하거나 일치시키고 CF, OF, PF, SF 및 ZF 플래그를 설정합니다.

AND 명령어

AND 명령어는 비트 AND 연산을 수행하여 논리식을 지원하는 데 사용됩니다. 비트 AND 연산은 두 피연산자의 일치하는 비트가 1이면 1을 반환하고 그렇지 않으면 0을 반환합니다. 예를 들면 다음과 같습니다.

Operand1: 	0101
             Operand2: 	0011
----------------------------
After AND -> Operand1:	0001

AND 연산은 하나 이상의 비트를 지우는 데 사용할 수 있습니다. 예를 들어, BL 레지스터에 0011 1010이 포함되어 있다고 가정합니다. 상위 비트를 0으로 지워야하는 경우 0FH로 AND합니다.

AND	BL,   0FH   ; This sets BL to 0000 1010

다른 예를 들어 보겠습니다. 주어진 숫자가 홀수인지 짝수인지 확인하려는 경우 간단한 테스트는 숫자의 최하위 비트를 확인하는 것입니다. 이것이 1이면 숫자는 홀수이고 그렇지 않으면 숫자는 짝수입니다.

숫자가 AL 레지스터에 있다고 가정하면 다음과 같이 쓸 수 있습니다.

AND	AL, 01H     ; ANDing with 0000 0001
JZ    EVEN_NUMBER

다음 프로그램은 이것을 설명합니다-

section .text
   global _start            ;must be declared for using gcc
	
_start:                     ;tell linker entry point
   mov   ax,   8h           ;getting 8 in the ax 
   and   ax, 1              ;and ax with 1
   jz    evnn
   mov   eax, 4             ;system call number (sys_write)
   mov   ebx, 1             ;file descriptor (stdout)
   mov   ecx, odd_msg       ;message to write
   mov   edx, len2          ;length of message
   int   0x80               ;call kernel
   jmp   outprog

evnn:   
  
   mov   ah,  09h
   mov   eax, 4             ;system call number (sys_write)
   mov   ebx, 1             ;file descriptor (stdout)
   mov   ecx, even_msg      ;message to write
   mov   edx, len1          ;length of message
   int   0x80               ;call kernel

outprog:

   mov   eax,1              ;system call number (sys_exit)
   int   0x80               ;call kernel

section   .data
even_msg  db  'Even Number!' ;message showing even number
len1  equ  $ - even_msg odd_msg db 'Odd Number!' ;message showing odd number len2 equ $ - odd_msg

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

Even Number!

ax 레지스터의 값을 홀수로 변경하십시오.

mov  ax, 9h                  ; getting 9 in the ax

프로그램은 다음을 표시합니다.

Odd Number!

마찬가지로 전체 레지스터를 지우려면 00H로 AND 할 수 있습니다.

OR 명령

OR 명령어는 비트 OR 연산을 수행하여 논리식을 지원하는 데 사용됩니다. 비트 OR 연산자는 피연산자 중 하나 또는 모두에서 일치하는 비트가 1 인 경우 1을 반환합니다. 두 비트가 모두 0이면 0을 반환합니다.

예를 들면

Operand1:     0101
             Operand2:     0011
----------------------------
After OR -> Operand1:    0111

OR 연산은 하나 이상의 비트를 설정하는 데 사용할 수 있습니다. 예를 들어 AL 레지스터에 0011 1010이 포함되어 있다고 가정 해 보겠습니다. 하위 비트 4 개를 설정해야하며 값 0000 1111, 즉 FH로 OR 할 수 있습니다.

OR BL, 0FH                   ; This sets BL to  0011 1111

다음 예는 OR 명령어를 보여줍니다. 값 5와 3을 각각 AL 및 BL 레지스터에 저장 한 다음 명령어,

OR AL, BL

AL 레지스터에 7을 저장해야합니다-

section .text
   global _start            ;must be declared for using gcc
	
_start:                     ;tell linker entry point
   mov    al, 5             ;getting 5 in the al
   mov    bl, 3             ;getting 3 in the bl
   or     al, bl            ;or al and bl registers, result should be 7
   add    al, byte '0'      ;converting decimal to ascii
	
   mov    [result],  al
   mov    eax, 4
   mov    ebx, 1
   mov    ecx, result
   mov    edx, 1 
   int    0x80
    
outprog:
   mov    eax,1             ;system call number (sys_exit)
   int    0x80              ;call kernel
	
section    .bss
result resb 1

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

7

XOR 명령어

XOR 명령어는 비트 XOR 연산을 구현합니다. XOR 연산은 피연산자의 비트가 다른 경우에만 결과 비트를 1로 설정합니다. 피연산자의 비트가 같으면 (둘 다 0 또는 둘 다 1) 결과 비트는 0으로 지워집니다.

예를 들면

Operand1:     0101
             Operand2:     0011
----------------------------
After XOR -> Operand1:    0110

XORing 자체가있는 피연산자는 피연산자를 다음과 같이 변경합니다. 0. 레지스터를 지우는 데 사용됩니다.

XOR     EAX, EAX

테스트 지침

TEST 명령어는 AND 연산과 동일하게 작동하지만 AND 명령어와 달리 첫 번째 피연산자를 변경하지 않습니다. 따라서 레지스터의 숫자가 짝수인지 홀수인지 확인해야하는 경우 원래 숫자를 변경하지 않고 TEST 명령어를 사용하여이를 수행 할 수도 있습니다.

TEST    AL, 01H
JZ      EVEN_NUMBER

NOT 명령

NOT 명령어는 비트 NOT 연산을 구현합니다. NOT 연산은 피연산자의 비트를 반전시킵니다. 피연산자는 레지스터 나 메모리에있을 수 있습니다.

예를 들면

Operand1:    0101 0011
After NOT -> Operand1:    1010 1100

어셈블리 언어의 조건부 실행은 여러 루프 및 분기 명령으로 수행됩니다. 이러한 명령어는 프로그램의 제어 흐름을 변경할 수 있습니다. 조건부 실행은 두 가지 시나리오에서 관찰됩니다.

Sr. 아니. 조건부 지침
1

Unconditional jump

이것은 JMP 명령에 의해 수행됩니다. 조건부 실행에는 종종 현재 실행중인 명령어를 따르지 않는 명령어의 주소로의 제어 전송이 포함됩니다. 제어 이전은 새로운 명령 세트를 실행하기 위해 앞으로, 또는 동일한 단계를 다시 실행하기 위해 뒤로 할 수 있습니다.

2

Conditional jump

이것은 조건에 따라 점프 명령 j <condition> 세트에 의해 수행됩니다. 조건부 명령어는 순차 흐름을 중단하여 제어를 전송하고 IP의 오프셋 값을 변경하여 수행합니다.

조건부 명령어를 논의하기 전에 CMP 명령어를 살펴 보겠습니다.

CMP 명령

CMP 명령어는 두 피연산자를 비교합니다. 일반적으로 조건부 실행에 사용됩니다. 이 명령어는 기본적으로 피연산자가 같은지 여부를 비교하기 위해 다른 피연산자에서 하나의 피연산자를 뺍니다. 대상 또는 소스 피연산자를 방해하지 않습니다. 의사 결정을위한 조건부 점프 명령과 함께 사용됩니다.

통사론

CMP destination, source

CMP는 두 개의 숫자 데이터 필드를 비교합니다. 대상 피연산자는 레지스터 또는 메모리에있을 수 있습니다. 소스 피연산자는 상수 (즉시) 데이터, 레지스터 또는 메모리 일 수 있습니다.

CMP DX,	00  ; Compare the DX value with zero
JE  L7      ; If yes, then jump to label L7
.
.
L7: ...

CMP는 카운터 값이 루프를 실행해야하는 횟수에 도달했는지 비교하는 데 자주 사용됩니다. 다음과 같은 일반적인 조건을 고려하십시오.

INC	EDX
CMP	EDX, 10	; Compares whether the counter has reached 10
JLE	LP1     ; If it is less than or equal to 10, then jump to LP1

무조건 점프

앞서 언급했듯이 이것은 JMP 명령어에 의해 수행됩니다. 조건부 실행에는 종종 현재 실행중인 명령어를 따르지 않는 명령어의 주소로의 제어 전송이 포함됩니다. 제어 이전은 새로운 명령 세트를 실행하기 위해 앞으로, 또는 동일한 단계를 다시 실행하기 위해 뒤로 할 수 있습니다.

통사론

JMP 명령은 제어 흐름이 즉시 전송되는 레이블 이름을 제공합니다. JMP 명령어의 구문은 다음과 같습니다.

JMP	label

다음 코드 스 니펫은 JMP 명령어를 보여줍니다.

MOV  AX, 00    ; Initializing AX to 0
MOV  BX, 00    ; Initializing BX to 0
MOV  CX, 01    ; Initializing CX to 1
L20:
ADD  AX, 01    ; Increment AX
ADD  BX, AX    ; Add AX to BX
SHL  CX, 1     ; shift left CX, this in turn doubles the CX value
JMP  L20       ; repeats the statements

조건부 점프

조건부 점프에서 지정된 조건이 충족되면 제어 흐름이 대상 명령으로 전달됩니다. 조건 및 데이터에 따라 다양한 조건부 점프 명령이 있습니다.

다음은 산술 연산에 사용되는 부호있는 데이터에 사용되는 조건부 점프 명령어입니다.

교수 기술 테스트 된 플래그
JE / JZ 동등 점프 또는 제로 점프 ZF
JNE / JNZ 같지 않은 점프 또는 0이 아닌 점프 ZF
JG / JNLE 더 크게 또는 덜 / 같지 않게 점프 OF, SF, ZF
JGE / JNL 더 크게 / 동등하게 또는 더 적게 점프 OF, SF
JL / JNGE 덜 점프하거나 크거나 같지 않게 점프 OF, SF
JLE / JNG 덜 / 동등하게 또는 더 크지 않게 점프 OF, SF, ZF

다음은 논리 연산에 사용되는 부호없는 데이터에 사용되는 조건부 점프 명령어입니다.

교수 기술 테스트 된 플래그
JE / JZ 동등 점프 또는 제로 점프 ZF
JNE / JNZ 같지 않은 점프 또는 0이 아닌 점프 ZF
JA / JNBE 위로 점프 또는 아래로 점프 / 같지 않음 CF, ZF
JAE / JNB 위 / 같음 또는 아래로 점프 CF
JB / JNAE 아래로 점프 또는 위 / 같지 않음으로 점프 CF
JBE / JNA 아래로 / 동등하거나 위로 점프하지 않음 AF, CF

다음 조건부 점프 명령은 특별한 용도로 사용되며 플래그 값을 확인합니다.

교수 기술 테스트 된 플래그
JXCZ CX가 0이면 점프 없음
JC 운반하면 점프 CF
JNC 캐리가 없으면 점프 CF
JO 넘치면 점프
JNO 오버플로가 없으면 점프
JP / JPE 점프 패리티 또는 점프 패리티 짝수 PF
JNP / JPO 패리티 없음 또는 패리티 홀수 점프 PF
JS 점프 기호 (음수) SF
JNS 부호 없음 (양수 값) SF

J <condition> 명령어 세트에 대한 구문-

예,

CMP	AL, BL
JE	EQUAL
CMP	AL, BH
JE	EQUAL
CMP	AL, CL
JE	EQUAL
NON_EQUAL: ...
EQUAL: ...

다음 프로그램은 세 변수 중 가장 큰 변수를 표시합니다. 변수는 두 자리 변수입니다. 세 변수 num1, num2 및 num3은 각각 47, 22 및 31 값을 갖습니다.

section	.text
   global _start         ;must be declared for using gcc

_start:	                 ;tell linker entry point
   mov   ecx, [num1]
   cmp   ecx, [num2]
   jg    check_third_num
   mov   ecx, [num2]
   
	check_third_num:

   cmp   ecx, [num3]
   jg    _exit
   mov   ecx, [num3]
   
	_exit:
   
   mov   [largest], ecx
   mov   ecx,msg
   mov   edx, len
   mov   ebx,1	;file descriptor (stdout)
   mov   eax,4	;system call number (sys_write)
   int   0x80	;call kernel
	
   mov   ecx,largest
   mov   edx, 2
   mov   ebx,1	;file descriptor (stdout)
   mov   eax,4	;system call number (sys_write)
   int   0x80	;call kernel
    
   mov   eax, 1
   int   80h

section	.data
   
   msg db "The largest digit is: ", 0xA,0xD 
   len equ $- msg 
   num1 dd '47'
   num2 dd '22'
   num3 dd '31'

segment .bss
   largest resb 2

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

The largest digit is: 
47

JMP 명령어는 루프를 구현하는 데 사용할 수 있습니다. 예를 들어 다음 코드 조각은 loop-body를 10 번 실행하는 데 사용할 수 있습니다.

MOV	CL, 10
L1:
<LOOP-BODY>
DEC	CL
JNZ	L1

그러나 프로세서 명령어 세트에는 반복을 구현하기위한 루프 명령어 그룹이 포함되어 있습니다. 기본 LOOP 명령어는 다음과 같은 구문이 있습니다.

LOOP 	label

여기서 label 은 점프 명령에서와 같이 대상 명령을 식별하는 대상 레이블입니다. LOOP 명령어는ECX register contains the loop count. 루프 명령이 실행되면 ECX 레지스터가 감소하고 컨트롤이 ECX 레지스터 값, 즉 카운터가 값 0에 도달 할 때까지 대상 레이블로 점프합니다.

위의 코드 조각은 다음과 같이 작성할 수 있습니다.

mov ECX,10
l1:
<loop body>
loop l1

다음 프로그램은 화면에 1에서 9까지의 숫자를 인쇄합니다.

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   mov ecx,10
   mov eax, '1'
	
l1:
   mov [num], eax
   mov eax, 4
   mov ebx, 1
   push ecx
	
   mov ecx, num        
   mov edx, 1        
   int 0x80
	
   mov eax, [num]
   sub eax, '0'
   inc eax
   add eax, '0'
   pop ecx
   loop l1
	
   mov eax,1             ;system call number (sys_exit)
   int 0x80              ;call kernel
section	.bss
num resb 1

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

123456789:

수치 데이터는 일반적으로 이진 시스템으로 표현됩니다. 산술 명령어는 이진 데이터에서 작동합니다. 화면에 숫자를 표시하거나 키보드로 입력하면 ASCII 형식입니다.

지금까지 우리는 산술 계산을 위해 ASCII 형식의이 입력 데이터를 바이너리로 변환하고 그 결과를 다시 바이너리로 변환했습니다. 다음 코드는 이것을 보여줍니다-

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   mov	eax,'3'
   sub     eax, '0'
	
   mov 	ebx, '4'
   sub     ebx, '0'
   add 	eax, ebx
   add	eax, '0'
	
   mov 	[sum], eax
   mov	ecx,msg	
   mov	edx, len
   mov	ebx,1	         ;file descriptor (stdout)
   mov	eax,4	         ;system call number (sys_write)
   int	0x80	         ;call kernel
	
   mov	ecx,sum
   mov	edx, 1
   mov	ebx,1	         ;file descriptor (stdout)
   mov	eax,4	         ;system call number (sys_write)
   int	0x80	         ;call kernel
	
   mov	eax,1	         ;system call number (sys_exit)
   int	0x80	         ;call kernel
	
section .data
msg db "The sum is:", 0xA,0xD 
len equ $ - msg   
segment .bss
sum resb 1

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

The sum is:
7

그러나 이러한 변환에는 오버 헤드가 있으며 어셈블리 언어 프로그래밍을 사용하면 이진 형식으로보다 효율적인 방식으로 숫자를 처리 할 수 ​​있습니다. 십진수는 두 가지 형태로 표현할 수 있습니다.

  • ASCII 형식
  • BCD 또는 이진 코드 십진수 형식

ASCII 표현

ASCII 표현에서 10 진수는 ASCII 문자열로 저장됩니다. 예를 들어 10 진수 값 1234는 다음과 같이 저장됩니다.

31	32	33	34H

여기서 31H는 1의 ASCII 값이고 32H는 2의 ASCII 값입니다. ASCII 표현으로 숫자를 처리하기위한 네 가지 명령이 있습니다.

  • AAA − 추가 후 ASCII 조정

  • AAS − 빼기 후 ASCII 조정

  • AAM − 곱셈 후 ASCII 조정

  • AAD − 분할 전 ASCII 조정

이러한 명령어는 피연산자를 사용하지 않으며 필수 피연산자가 AL 레지스터에 있다고 가정합니다.

다음 예제는 AAS 명령어를 사용하여 개념을 보여줍니다.

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   sub     ah, ah
   mov     al, '9'
   sub     al, '3'
   aas
   or      al, 30h
   mov     [res], ax
	
   mov	edx,len	        ;message length
   mov	ecx,msg	        ;message to write
   mov	ebx,1	        ;file descriptor (stdout)
   mov	eax,4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	edx,1	        ;message length
   mov	ecx,res	        ;message to write
   mov	ebx,1	        ;file descriptor (stdout)
   mov	eax,4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	eax,1	        ;system call number (sys_exit)
   int	0x80	        ;call kernel
	
section	.data
msg db 'The Result is:',0xa	
len equ $ - msg			
section .bss
res resb 1

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

The Result is:
6

BCD 표현

BCD 표현에는 두 가지 유형이 있습니다.

  • 압축 해제 된 BCD 표현
  • 패킹 된 BCD 표현

압축이 풀린 BCD 표현에서 각 바이트는 10 진수에 해당하는 이진을 저장합니다. 예를 들어, 숫자 1234는 다음과 같이 저장됩니다.

01	02	03	04H

이 번호를 처리하기위한 두 가지 지침이 있습니다.

  • AAM − 곱셈 후 ASCII 조정

  • AAD − 분할 전 ASCII 조정

4 개의 ASCII 조정 명령어 인 AAA, AAS, AAM 및 AAD도 압축 해제 된 BCD 표현과 함께 사용할 수 있습니다. 압축 된 BCD 표현에서 각 숫자는 4 비트를 사용하여 저장됩니다. 두 개의 십진수가 바이트로 압축됩니다. 예를 들어, 숫자 1234는 다음과 같이 저장됩니다.

12	34H

이 번호를 처리하기위한 두 가지 지침이 있습니다.

  • DAA − 추가 후 소수점 조정

  • DAS − 빼기 후 소수점 조정

패킹 된 BCD 표현에서는 곱셈과 나눗셈을 지원하지 않습니다.

다음 프로그램은 두 개의 5 자리 십진수를 더하고 합계를 표시합니다. 위의 개념을 사용합니다-

section	.text
   global _start        ;must be declared for using gcc

_start:	                ;tell linker entry point

   mov     esi, 4       ;pointing to the rightmost digit
   mov     ecx, 5       ;num of digits
   clc
add_loop:  
   mov 	al, [num1 + esi]
   adc 	al, [num2 + esi]
   aaa
   pushf
   or 	al, 30h
   popf
	
   mov	[sum + esi], al
   dec	esi
   loop	add_loop
	
   mov	edx,len	        ;message length
   mov	ecx,msg	        ;message to write
   mov	ebx,1	        ;file descriptor (stdout)
   mov	eax,4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	edx,5	        ;message length
   mov	ecx,sum	        ;message to write
   mov	ebx,1	        ;file descriptor (stdout)
   mov	eax,4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	eax,1	        ;system call number (sys_exit)
   int	0x80	        ;call kernel

section	.data
msg db 'The Sum is:',0xa	
len equ $ - msg			
num1 db '12345'
num2 db '23456'
sum db '     '

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

The Sum is:
35801

이전 예제에서 이미 가변 길이 문자열을 사용했습니다. 가변 길이 문자열은 필요한만큼의 문자를 가질 수 있습니다. 일반적으로 두 가지 방법 중 하나로 문자열의 길이를 지정합니다.

  • 명시 적으로 문자열 길이 저장
  • 센티넬 캐릭터 사용

위치 카운터의 현재 값을 나타내는 $ 위치 카운터 기호를 사용하여 문자열 길이를 명시 적으로 저장할 수 있습니다. 다음 예에서-

msg  db  'Hello, world!',0xa ;our dear string
len  equ  $ - msg            ;length of our dear string

$는 문자열 변수 msg 의 마지막 문자 뒤의 바이트를 가리 킵니다 . 따라서,$-msg문자열의 길이를 제공합니다. 우리는 또한 쓸 수 있습니다

msg db 'Hello, world!',0xa ;our dear string
len equ 13                 ;length of our dear string

또는 문자열 길이를 명시 적으로 저장하는 대신 문자열을 구분하기 위해 후행 센티널 문자로 문자열을 저장할 수 있습니다. 센티넬 문자는 문자열 내에 나타나지 않는 특수 문자 여야합니다.

예를 들면-

message DB 'I am loving it!', 0

문자열 지침

각 문자열 명령어에는 소스 피연산자, 대상 피연산자 또는 둘 다 필요할 수 있습니다. 32 비트 세그먼트의 경우 문자열 명령어는 ESI 및 EDI 레지스터를 사용하여 각각 소스 및 대상 피연산자를 가리 킵니다.

그러나 16 비트 세그먼트의 경우 SI 및 DI 레지스터는 각각 소스와 대상을 가리키는 데 사용됩니다.

문자열 처리를위한 5 가지 기본 지침이 있습니다. 그들은-

  • MOVS −이 명령어는 데이터의 1 바이트, 워드 또는 더블 워드를 메모리 위치에서 다른 위치로 이동합니다.

  • LODS−이 명령어는 메모리에서로드됩니다. 피연산자가 1 바이트이면 AL 레지스터에로드되고, 피연산자가 1 워드이면 AX 레지스터에로드되고 더블 워드가 EAX 레지스터에로드됩니다.

  • STOS −이 명령어는 레지스터 (AL, AX 또는 EAX)의 데이터를 메모리에 저장합니다.

  • CMPS−이 명령어는 메모리의 두 데이터 항목을 비교합니다. 데이터는 바이트 크기, 워드 또는 더블 워드 일 수 있습니다.

  • SCAS −이 명령어는 레지스터 (AL, AX 또는 EAX)의 내용을 메모리에있는 항목의 내용과 비교합니다.

위의 각 명령어에는 바이트, 워드 및 더블 워드 버전이 있으며 문자열 명령어는 반복 접두사를 사용하여 반복 할 수 있습니다.

이러한 명령어는 ES : DI 및 DS : SI 쌍의 레지스터를 사용합니다. 여기서 DI 및 SI 레지스터에는 메모리에 저장된 바이트를 참조하는 유효한 오프셋 주소가 포함됩니다. SI는 일반적으로 DS (데이터 세그먼트)와 연결되고 DI는 항상 ES (추가 세그먼트)와 연결됩니다.

DS : SI (또는 ESI) 및 ES : DI (또는 EDI) 레지스터는 각각 소스 및 대상 피연산자를 가리 킵니다. 소스 피연산자는 DS : SI (또는 ESI)에 있고 대상 피연산자는 메모리의 ES : DI (또는 EDI)에 있다고 가정합니다.

16 비트 주소의 경우 SI 및 DI 레지스터가 사용되며 32 비트 주소의 경우 ESI 및 EDI 레지스터가 사용됩니다.

다음 표는 다양한 버전의 문자열 명령어와 피연산자의 가정 된 공간을 제공합니다.

기본 지침 피연산자 바이트 연산 워드 연산 더블 워드 연산
MOVS ES : DI, DS : SI MOVSB MOVSW MOVSD
LODS AX, DS : SI LODSB LODSW LODSD
STOS ES : DI, AX STOSB STOSW STOSD
CMPS DS : SI, ES : DI CMPSB CMPSW CMPSD
SCAS ES : DI, AX SCASB SCASW SCASD

반복 접두사

REP 접두사를 문자열 명령어 앞에 설정하면 (예 : REP MOVSB) CX 레지스터에 배치 된 카운터를 기반으로 명령어가 반복됩니다. REP는 명령을 실행하고 CX를 1 씩 줄인 다음 CX가 0인지 확인합니다. CX가 0이 될 때까지 명령 처리를 반복합니다.

방향 플래그 (DF)는 작업의 방향을 결정합니다.

  • CLD (Clear Direction Flag, DF = 0)를 사용하여 작업을 왼쪽에서 오른쪽으로 만듭니다.
  • STD (Set Direction Flag, DF = 1)를 사용하여 작업을 오른쪽에서 왼쪽으로 만듭니다.

REP 접두어에는 다음과 같은 변형도 있습니다.

  • REP : 무조건적인 반복입니다. CX가 0이 될 때까지 작업을 반복합니다.

  • REPE 또는 REPZ : 조건부 반복입니다. 제로 플래그가 같음 / 제로를 나타내는 동안 작업을 반복합니다. ZF가 같지 않음 / 0이 아님을 나타내거나 CX가 0 일 때 중지됩니다.

  • REPNE 또는 REPNZ : 또한 조건부 반복입니다. 제로 플래그가 같지 않음 / 제로임을 나타내는 동안 작업을 반복합니다. ZF가 같음 / 0을 나타내거나 CX가 0으로 감소하면 중지됩니다.

어셈블러에 대한 데이터 정의 지시문이 변수에 대한 저장소를 할당하는 데 사용된다는 점을 이미 논의했습니다. 변수는 특정 값으로 초기화 될 수도 있습니다. 초기화 된 값은 16 진수, 10 진수 또는 2 진 형식으로 지정할 수 있습니다.

예를 들어, 다음 방법 중 하나로 'months'라는 단어 변수를 정의 할 수 있습니다.

MONTHS	DW	12
MONTHS	DW	0CH
MONTHS	DW	0110B

데이터 정의 지시문은 1 차원 배열을 정의하는데도 사용할 수 있습니다. 1 차원 숫자 배열을 정의하겠습니다.

NUMBERS	DW  34,  45,  56,  67,  75, 89

위의 정의는 각각 34, 45, 56, 67, 75, 89의 숫자로 초기화 된 6 개의 단어 배열을 선언합니다. 이는 2x6 = 12 바이트의 연속 메모리 공간을 할당합니다. 첫 번째 숫자의 기호 주소는 NUMBERS가되고 두 ​​번째 숫자의 기호 주소는 NUMBERS + 2가됩니다.

다른 예를 들어 보겠습니다. 인벤토리 크기가 8 인 배열을 정의하고 모든 값을 0으로 초기화 할 수 있습니다.

INVENTORY   DW  0
            DW  0
            DW  0
            DW  0
            DW  0
            DW  0
            DW  0
            DW  0

다음과 같이 축약 될 수 있습니다-

INVENTORY   DW  0, 0 , 0 , 0 , 0 , 0 , 0 , 0

TIMES 지시문은 동일한 값에 대한 여러 초기화에도 사용할 수 있습니다. TIMES를 사용하면 INVENTORY 배열을 다음과 같이 정의 할 수 있습니다.

INVENTORY TIMES 8 DW 0

다음 예제는 2, 3, 4의 세 값을 저장하는 3 요소 배열 x를 정의하여 위의 개념을 보여줍니다. 배열에 값을 더하고 합계 9를 표시합니다.

section	.text
   global _start   ;must be declared for linker (ld)
	
_start:	
 		
   mov  eax,3      ;number bytes to be summed 
   mov  ebx,0      ;EBX will store the sum
   mov  ecx, x     ;ECX will point to the current element to be summed

top:  add  ebx, [ecx]

   add  ecx,1      ;move pointer to next element
   dec  eax        ;decrement counter
   jnz  top        ;if counter not 0, then loop again

done: 

   add   ebx, '0'
   mov  [sum], ebx ;done, store result in "sum"

display:

   mov  edx,1      ;message length
   mov  ecx, sum   ;message to write
   mov  ebx, 1     ;file descriptor (stdout)
   mov  eax, 4     ;system call number (sys_write)
   int  0x80       ;call kernel
	
   mov  eax, 1     ;system call number (sys_exit)
   int  0x80       ;call kernel

section	.data
global x
x:    
   db  2
   db  4
   db  3

sum: 
   db  0

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

9

어셈블리 언어 프로그램의 크기가 큰 경향이 있으므로 프로 시저 또는 서브 루틴은 어셈블리 언어에서 매우 중요합니다. 절차는 이름으로 식별됩니다. 이 이름 뒤에는 잘 정의 된 작업을 수행하는 절차의 본문이 설명됩니다. 프로 시저의 끝은 return 문으로 표시됩니다.

통사론

다음은 절차를 정의하는 구문입니다-

proc_name:
   procedure body
   ...
   ret

프로시 저는 CALL 명령어를 사용하여 다른 함수에서 호출됩니다. CALL 명령어는 호출 된 프로 시저의 이름을 아래와 같이 인수로 가져야합니다.

CALL proc_name

호출 된 프로시 저는 RET 명령어를 사용하여 호출 프로 시저에 제어를 반환합니다.

우리는 매우 간단한 절차라는 쓰자 ECX와 EDX에 저장되어있는 변수를 등록하고 EAX 레지스터 반환 합 추가 -

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   mov	ecx,'4'
   sub     ecx, '0'
	
   mov 	edx, '5'
   sub     edx, '0'
	
   call    sum          ;call sum procedure
   mov 	[res], eax
   mov	ecx, msg	
   mov	edx, len
   mov	ebx,1	        ;file descriptor (stdout)
   mov	eax,4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	ecx, res
   mov	edx, 1
   mov	ebx, 1	        ;file descriptor (stdout)
   mov	eax, 4	        ;system call number (sys_write)
   int	0x80	        ;call kernel
	
   mov	eax,1	        ;system call number (sys_exit)
   int	0x80	        ;call kernel
sum:
   mov     eax, ecx
   add     eax, edx
   add     eax, '0'
   ret
	
section .data
msg db "The sum is:", 0xA,0xD 
len equ $- msg   

segment .bss
res resb 1

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

The sum is:
9

스택 데이터 구조

스택은 스택의 '상단'이라는 위치에서 데이터를 저장하고 제거 할 수있는 메모리의 배열과 유사한 데이터 구조입니다. 저장해야하는 데이터는 스택으로 '푸시'되고 검색 할 데이터는 스택에서 '팝업'됩니다. 스택은 LIFO 데이터 구조입니다. 즉, 처음 저장된 데이터가 마지막에 검색됩니다.

어셈블리 언어는 스택 작업에 ​​대해 PUSH 및 POP의 두 가지 지침을 제공합니다. 이 명령어에는 다음과 같은 구문이 있습니다.

PUSH    operand
POP     address/register

스택 세그먼트에 예약 된 메모리 공간은 스택 구현에 사용됩니다. 레지스터 SS 및 ESP (또는 SP)는 스택 구현에 사용됩니다. 스택에 삽입 된 마지막 데이터 항목을 가리키는 스택의 맨 위는 SS : ESP 레지스터로 가리키며, 여기서 SS 레지스터는 스택 세그먼트의 시작을 가리키고 SP (또는 ESP)는 오프셋을 스택 세그먼트.

스택 구현에는 다음과 같은 특성이 있습니다.

  • words 또는 doublewords 바이트가 아닌 스택에 저장할 수 있습니다.

  • 스택은 역방향, 즉 하위 메모리 주소로 증가합니다.

  • 스택의 맨 위는 스택에 삽입 된 마지막 항목을 가리 킵니다. 삽입 된 마지막 단어의 하위 바이트를 가리 킵니다.

레지스터 값을 사용하기 전에 스택에 저장하는 것에 대해 논의했듯이; 다음과 같은 방식으로 수행 할 수 있습니다.

; Save the AX and BX registers in the stack
PUSH    AX
PUSH    BX

; Use the registers for other purpose
MOV	AX, VALUE1
MOV 	BX, VALUE2
...
MOV 	VALUE1, AX
MOV	VALUE2, BX

; Restore the original values
POP	BX
POP	AX

다음 프로그램은 전체 ASCII 문자 집합을 표시합니다. 기본 프로그램 은 ASCII 문자 세트를 표시하는 display 라는 프로 시저를 호출합니다 .

section	.text
   global _start        ;must be declared for using gcc
	
_start:	                ;tell linker entry point
   call    display
   mov	eax,1	        ;system call number (sys_exit)
   int	0x80	        ;call kernel
	
display:
   mov    ecx, 256
	
next:
   push    ecx
   mov     eax, 4
   mov     ebx, 1
   mov     ecx, achar
   mov     edx, 1
   int     80h
	
   pop     ecx	
   mov	dx, [achar]
   cmp	byte [achar], 0dh
   inc	byte [achar]
   loop    next
   ret
	
section .data
achar db '0'

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}
...
...

재귀 프로시 저는 자신을 호출하는 프로 시저입니다. 재귀에는 직접 및 간접의 두 가지 종류가 있습니다. 직접 재귀에서 프로시 저는 자신을 호출하고 간접 재귀에서는 첫 번째 프로 시저가 두 번째 프로 시저를 호출하고 차례로 첫 번째 프로 시저를 호출합니다.

재귀는 수많은 수학적 알고리즘에서 관찰 될 수 있습니다. 예를 들어 숫자의 계승을 계산하는 경우를 생각해보십시오. 숫자의 계승은 방정식으로 주어집니다-

Fact (n) = n * fact (n-1) for n > 0

예를 들어, 5의 계승은 1 x 2 x 3 x 4 x 5 = 5 x 계승 4이며 이것은 재귀 절차를 보여주는 좋은 예가 될 수 있습니다. 모든 재귀 알고리즘에는 종료 조건이 있어야합니다. 즉, 조건이 충족되면 프로그램의 재귀 호출을 중지해야합니다. 계승 알고리즘의 경우 n이 0 일 때 종료 조건에 도달합니다.

다음 프로그램은 팩토리얼 n이 어셈블리 언어로 구현되는 방법을 보여줍니다. 프로그램을 간단하게 유지하기 위해 계승 3을 계산합니다.

section	.text
   global _start         ;must be declared for using gcc
	
_start:                  ;tell linker entry point

   mov bx, 3             ;for calculating factorial 3
   call  proc_fact
   add   ax, 30h
   mov  [fact], ax
    
   mov	  edx,len        ;message length
   mov	  ecx,msg        ;message to write
   mov	  ebx,1          ;file descriptor (stdout)
   mov	  eax,4          ;system call number (sys_write)
   int	  0x80           ;call kernel

   mov   edx,1            ;message length
   mov	  ecx,fact       ;message to write
   mov	  ebx,1          ;file descriptor (stdout)
   mov	  eax,4          ;system call number (sys_write)
   int	  0x80           ;call kernel
    
   mov	  eax,1          ;system call number (sys_exit)
   int	  0x80           ;call kernel
	
proc_fact:
   cmp   bl, 1
   jg    do_calculation
   mov   ax, 1
   ret
	
do_calculation:
   dec   bl
   call  proc_fact
   inc   bl
   mul   bl        ;ax = al * bl
   ret

section	.data
msg db 'Factorial 3 is:',0xa	
len equ $ - msg			

section .bss
fact resb 1

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

Factorial 3 is:
6

매크로 작성은 어셈블리 언어로 모듈 식 프로그래밍을 보장하는 또 다른 방법입니다.

  • 매크로는 이름으로 지정된 일련의 명령이며 프로그램의 어느 곳에서나 사용할 수 있습니다.

  • NASM에서 매크로는 다음과 같이 정의됩니다. %macro%endmacro 지시.

  • 매크로는 % macro 지시문으로 시작하고 % endmacro 지시문으로 끝납니다.

매크로 정의 구문-

%macro macro_name  number_of_params
<macro body>
%endmacro

여기서 number_of_params 는 숫자 매개 변수를 지정하고 macro_name 은 매크로의 이름을 지정합니다.

매크로는 필요한 매개 변수와 함께 매크로 이름을 사용하여 호출됩니다. 프로그램에서 일련의 명령어를 여러 번 사용해야하는 경우 해당 명령어를 매크로에 넣고 항상 명령어를 작성하는 대신 사용할 수 있습니다.

예를 들어, 프로그램에 대한 매우 일반적인 요구 사항은 화면에 문자열을 쓰는 것입니다. 문자열을 표시하려면 다음과 같은 일련의 지침이 필요합니다.

mov	edx,len	    ;message length
mov	ecx,msg	    ;message to write
mov	ebx,1       ;file descriptor (stdout)
mov	eax,4       ;system call number (sys_write)
int	0x80        ;call kernel

위의 문자열 표시 예에서 레지스터 EAX, EBX, ECX 및 EDX는 INT 80H 함수 호출에 사용되었습니다. 따라서 화면에 표시해야 할 때마다 이러한 레지스터를 스택에 저장하고 INT 80H를 호출 한 다음 스택에서 레지스터의 원래 값을 복원해야합니다. 따라서 데이터 저장 및 복원을 위해 두 개의 매크로를 작성하는 것이 유용 할 수 있습니다.

IMUL, IDIV, INT 등과 같은 일부 명령어는 일부 특정 레지스터에 저장하고 일부 특정 레지스터에 값을 반환하기 위해 일부 정보가 필요하다는 것을 확인했습니다. 프로그램이 중요한 데이터를 유지하기 위해 이미 이러한 레지스터를 사용하고있는 경우 이러한 레지스터의 기존 데이터를 스택에 저장하고 명령이 실행 된 후 복원해야합니다.

다음 예제는 매크로 정의 및 사용을 보여줍니다-

; A macro with two parameters
; Implements the write system call
   %macro write_string 2 
      mov   eax, 4
      mov   ebx, 1
      mov   ecx, %1
      mov   edx, %2
      int   80h
   %endmacro
 
section	.text
   global _start            ;must be declared for using gcc
	
_start:                     ;tell linker entry point
   write_string msg1, len1               
   write_string msg2, len2    
   write_string msg3, len3  
	
   mov eax,1                ;system call number (sys_exit)
   int 0x80                 ;call kernel

section	.data
msg1 db	'Hello, programmers!',0xA,0xD 	
len1 equ $ - msg1 msg2 db 'Welcome to the world of,', 0xA,0xD len2 equ $- msg2 

msg3 db 'Linux assembly programming! '
len3 equ $- msg3

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

Hello, programmers!
Welcome to the world of,
Linux assembly programming!

시스템은 입력 또는 출력 데이터를 바이트 스트림으로 간주합니다. 세 가지 표준 파일 스트림이 있습니다-

  • 표준 입력 (stdin),
  • 표준 출력 (stdout) 및
  • 표준 오류 (stderr).

파일 설명자

file descriptor파일 ID로 파일에 할당 된 16 비트 정수입니다. 새 파일이 생성되거나 기존 파일이 열리면 파일 설명자가 파일에 액세스하는 데 사용됩니다.

표준 파일 스트림의 파일 설명자- stdin, stdoutstderr 각각 0, 1 및 2입니다.

파일 포인터

file pointer파일에서 후속 읽기 / 쓰기 작업의 위치를 ​​바이트 단위로 지정합니다. 각 파일은 일련의 바이트로 간주됩니다. 열려있는 각 파일은 파일 시작을 기준으로 오프셋을 바이트 단위로 지정하는 파일 포인터와 연결됩니다. 파일이 열리면 파일 포인터가 0으로 설정됩니다.

파일 처리 시스템 호출

다음 표는 파일 처리와 관련된 시스템 호출을 간략하게 설명합니다.

% eax 이름 % ebx % ecx % edx
2 sys_fork 구조체 pt_regs - -
sys_read 서명되지 않은 정수 char * size_t
4 sys_write 서명되지 않은 정수 const char * size_t
5 sys_open const char * int int
6 sys_close 서명되지 않은 정수 - -
8 sys_creat const char * int -
19 sys_lseek 서명되지 않은 정수 off_t 서명되지 않은 정수

시스템 호출을 사용하는 데 필요한 단계는 앞에서 설명한 것처럼 동일합니다.

  • EAX 레지스터에 시스템 호출 번호를 입력합니다.
  • EBX, ECX 등의 레지스터에 시스템 호출에 대한 인수를 저장합니다.
  • 관련 인터럽트 (80h)를 호출합니다.
  • 결과는 일반적으로 EAX 레지스터에 반환됩니다.

파일 생성 및 열기

파일을 만들고 열려면 다음 작업을 수행하십시오.

  • 시스템 호출 sys_creat () 번호 8을 EAX 레지스터에 넣습니다.
  • EBX 레지스터에 파일 이름을 입력합니다.
  • ECX 레지스터에 파일 권한을 입력합니다.

시스템 호출은 생성 된 파일의 파일 디스크립터를 EAX 레지스터에 반환합니다. 오류가 발생한 경우 오류 코드는 EAX 레지스터에 있습니다.

기존 파일 열기

기존 파일을 열려면 다음 작업을 수행하십시오.

  • EAX 레지스터에 시스템 호출 sys_open () 번호 5를 입력합니다.
  • EBX 레지스터에 파일 이름을 입력합니다.
  • ECX 레지스터에 파일 액세스 모드를 설정합니다.
  • EDX 레지스터에 파일 권한을 넣으십시오.

시스템 호출은 생성 된 파일의 파일 디스크립터를 EAX 레지스터에 반환합니다. 오류가 발생한 경우 오류 코드는 EAX 레지스터에 있습니다.

파일 액세스 모드 중 가장 일반적으로 사용되는 모드는 읽기 전용 (0), 쓰기 전용 (1) 및 읽기-쓰기 (2)입니다.

파일에서 읽기

파일을 읽으려면 다음 작업을 수행하십시오.

  • 시스템 호출 sys_read () 번호 3을 EAX 레지스터에 넣습니다.

  • EBX 레지스터에 파일 설명자를 넣습니다.

  • ECX 레지스터의 입력 버퍼에 포인터를 놓습니다.

  • 버퍼 크기, 즉 읽을 바이트 수를 EDX 레지스터에 넣습니다.

시스템 호출은 EAX 레지스터에서 읽은 바이트 수를 반환합니다. 오류가 발생한 경우 오류 코드는 EAX 레지스터에 있습니다.

파일에 쓰기

파일에 쓰려면 다음 작업을 수행하십시오.

  • 시스템 호출 sys_write () 번호 4를 EAX 레지스터에 넣습니다.

  • EBX 레지스터에 파일 설명자를 넣습니다.

  • ECX 레지스터의 출력 버퍼에 포인터를 놓습니다.

  • 버퍼 크기, 즉 쓸 바이트 수를 EDX 레지스터에 넣으십시오.

시스템 호출은 EAX 레지스터에 기록 된 실제 바이트 수를 반환합니다. 오류의 경우 오류 코드는 EAX 레지스터에 있습니다.

파일 닫기

파일을 닫으려면 다음 작업을 수행하십시오.

  • 시스템 호출 sys_close () 번호 6을 EAX 레지스터에 넣습니다.
  • EBX 레지스터에 파일 설명자를 넣습니다.

시스템 호출은 오류가 발생한 경우 EAX 레지스터의 오류 코드를 반환합니다.

파일 업데이트

파일을 업데이트하려면 다음 작업을 수행하십시오.

  • EAX 레지스터에 시스템 호출 sys_lseek () 번호 19를 입력합니다.
  • EBX 레지스터에 파일 설명자를 넣습니다.
  • ECX 레지스터에 오프셋 값을 입력합니다.
  • 오프셋의 기준 위치를 EDX 레지스터에 넣습니다.

참조 위치는 다음과 같습니다.

  • 파일 시작-값 0
  • 현재 위치-값 1
  • 파일 끝-값 2

시스템 호출은 오류가 발생한 경우 EAX 레지스터의 오류 코드를 반환합니다.

다음 프로그램은 myfile.txt 라는 파일을 만들고 열고이 파일에 'Welcome to Tutorials Point'라는 텍스트를 씁니다. 다음으로 프로그램은 파일에서 읽고 info 라는 버퍼에 데이터를 저장합니다 . 마지막으로 info에 저장된 텍스트를 표시합니다 .

section	.text
   global _start         ;must be declared for using gcc
	
_start:                  ;tell linker entry point
   ;create the file
   mov  eax, 8
   mov  ebx, file_name
   mov  ecx, 0777        ;read, write and execute by all
   int  0x80             ;call kernel
	
   mov [fd_out], eax
    
   ; write into the file
   mov	edx,len          ;number of bytes
   mov	ecx, msg         ;message to write
   mov	ebx, [fd_out]    ;file descriptor 
   mov	eax,4            ;system call number (sys_write)
   int	0x80             ;call kernel
	
   ; close the file
   mov eax, 6
   mov ebx, [fd_out]
    
   ; write the message indicating end of file write
   mov eax, 4
   mov ebx, 1
   mov ecx, msg_done
   mov edx, len_done
   int  0x80
    
   ;open the file for reading
   mov eax, 5
   mov ebx, file_name
   mov ecx, 0             ;for read only access
   mov edx, 0777          ;read, write and execute by all
   int  0x80
	
   mov  [fd_in], eax
    
   ;read from file
   mov eax, 3
   mov ebx, [fd_in]
   mov ecx, info
   mov edx, 26
   int 0x80
    
   ; close the file
   mov eax, 6
   mov ebx, [fd_in]
   int  0x80 
	
   ; print the info 
   mov eax, 4
   mov ebx, 1
   mov ecx, info
   mov edx, 26
   int 0x80
       
   mov	eax,1             ;system call number (sys_exit)
   int	0x80              ;call kernel

section	.data
file_name db 'myfile.txt'
msg db 'Welcome to Tutorials Point'
len equ  $-msg

msg_done db 'Written to file', 0xa
len_done equ $-msg_done

section .bss
fd_out resb 1
fd_in  resb 1
info resb  26

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

Written to file
Welcome to Tutorials Point

그만큼 sys_brk()커널이 시스템 호출을 제공하여 나중에 이동할 필요없이 메모리를 할당합니다. 이 호출은 메모리의 애플리케이션 이미지 바로 뒤에 메모리를 할당합니다. 이 시스템 기능을 사용하면 데이터 섹션에서 사용 가능한 가장 높은 주소를 설정할 수 있습니다.

이 시스템 호출은 설정해야하는 가장 높은 메모리 주소 인 하나의 매개 변수를 사용합니다. 이 값은 EBX 레지스터에 저장됩니다.

오류가 발생하면 sys_brk ()는 -1을 반환하거나 음의 오류 코드 자체를 반환합니다. 다음 예제는 동적 메모리 할당을 보여줍니다.

다음 프로그램은 sys_brk () 시스템 호출을 사용하여 16kb의 메모리를 할당합니다.

section	.text
   global _start         ;must be declared for using gcc
	
_start:	                 ;tell linker entry point

   mov	eax, 45		 ;sys_brk
   xor	ebx, ebx
   int	80h

   add	eax, 16384	 ;number of bytes to be reserved
   mov	ebx, eax
   mov	eax, 45		 ;sys_brk
   int	80h
	
   cmp	eax, 0
   jl	exit	;exit, if error 
   mov	edi, eax	 ;EDI = highest available address
   sub	edi, 4		 ;pointing to the last DWORD  
   mov	ecx, 4096	 ;number of DWORDs allocated
   xor	eax, eax	 ;clear eax
   std			 ;backward
   rep	stosd            ;repete for entire allocated area
   cld			 ;put DF flag to normal state
	
   mov	eax, 4
   mov	ebx, 1
   mov	ecx, msg
   mov	edx, len
   int	80h		 ;print a message

exit:
   mov	eax, 1
   xor	ebx, ebx
   int	80h
	
section	.data
msg    	db	"Allocated 16 kb of memory!", 10
len     equ	$ - msg

위의 코드가 컴파일되고 실행되면 다음과 같은 결과가 생성됩니다.

Allocated 16 kb of memory!