어셈블리어

컴퓨팅에서 **어셈블리어**(assembly language, 또는 **어셈블러 언어** 또는 **상징 기계어**)는 흔히 단순히 **어셈블리**라고 불리며 보통 **ASM** 또는 **asm**으로 약칭되는 것으로, 언어의 명령어와 아키텍처의 기계어 명령어 사이에 매우 강력한 대응 관계가 있는 모든 저급 프로그래밍 언어를 말한다. 어셈블리어는 보통 기계어 명령어 하나당 하나의 문을 갖지만 (1:1), 상수, 주석, 어셈블러 지시자, 예컨대 메모리 위치, 레지스터에 대한 상징적 레이블, 그리고 매크로 등도 일반적으로 지원된다.

언어가 기계어 명령어를 나타내는 데 사용된 최초의 어셈블리 코드는 캐슬린 부스와 앤드루 도널드 부스의 1947년 저작인 'Coding for A.R.C.'에서 발견된다. 어셈블리 코드는 어셈블러라고 불리는 유틸리티 프로그램에 의해 실행 가능한 기계어로 변환된다. "어셈블러"라는 용어는 일반적으로 모리스 윌크스, 데이비드 휠러, 스탠리 길이 1951년에 쓴 책 The Preparation of Programs for an Electronic Digital Computer에서 기인한 것으로 간주되나, 이들은 이 용어를 "여러 섹션으로 구성된 다른 프로그램을 하나의 프로그램으로 조립하는 프로그램"이라는 의미로 사용했다. 이 변환 과정을 소스 코드를 조립한다는 의미에서 어셈블리라고 한다. 어셈블러가 프로그램을 처리하는 계산 단계를 어셈블리 시간(assembly time)이라고 한다.

어셈블리는 기계어 명령어에 의존하기 때문에, 각 어셈블리어는 X86이나 ARM과 같은 특정 컴퓨터 구조에 고유하다.

때로는 동일한 아키텍처에 대해 두 개 이상의 어셈블러가 존재하며, 때로는 어셈블러가 특정 운영체제나 특정 운영체제들에 고유하기도 한다. 대부분의 어셈블리어는 운영체제 호출을 위한 특정 구문을 제공하지 않으며, 언어가 모든 시스템 호출 메커니즘이 궁극적으로 의존하는 프로세서의 실제 기능을 제공하므로 대부분의 어셈블리어는 모든 운영체제에서 보편적으로 사용될 수 있다. 어셈블리어와 대조적으로, 대부분의 고급 프로그래밍 언어는 일반적으로 여러 아키텍처에 걸쳐 이식 가능하지만 인터프리팅이나 컴파일이 필요하며, 이는 어셈블보다 훨씬 복잡한 작업이다.

컴퓨팅의 초기 수십 년 동안, 시스템 프로그래밍과 응용 프로그래밍 모두 어셈블리어로만 이루어지는 것이 일반적이었다. 여전히 일부 목적을 위해 대체 불가능하지만, 현재 대부분의 프로그래밍은 더 높은 수준의 인터프리트 및 컴파일 언어로 수행된다. "은빛 총알은 없다"에서 프레더릭 브룩스는 어셈블리어 프로그래밍에서 전환된 효과를 다음과 같이 요약했다: "확실히 소프트웨어 생산성, 신뢰성 및 단순성을 위한 가장 강력한 타격은 프로그래밍을 위한 고급 언어의 점진적인 사용이었다. 대부분의 관찰자들은 그 발전이 생산성을 최소 5배 향상시켰고 신뢰성, 단순성 및 이해 가능성에서 수반되는 이득을 얻었다고 평가한다."

오늘날에는 성능상의 이유나 고급 언어에서 지원하지 않는 방식으로 하드웨어와 직접 상호 작용하기 위해, 고급 언어로 구현된 대규모 시스템 내에서 소량의 어셈블리어 코드를 사용하는 것이 일반적이다. 예를 들어, 리눅스 커널 소스 코드 버전 4.9의 2% 미만이 어셈블리로 작성되었으며, 97% 이상은 C로 작성되었다.

## 어셈블리어 구문
어셈블리어는 예컨대 각 저급 기계 명령어나 명령 코드, 각 지시자, 전형적으로 각 아키텍처 레지스터, 플래그 등을 나타내기 위해 기억술을 사용한다. 일부 기억술은 내장되어 있을 수 있고 일부는 사용자 정의일 수 있다. 많은 작업이 완전한 명령어를 형성하기 위해 하나 이상의 피연산자를 필요로 한다. 대부분의 어셈블러는 프로그램 및 메모리 위치에 대해 이름이 지정된 상수, 레지스터 및 레이블을 허용하며 피연산자에 대한 식을 계산할 수 있다. 따라서 프로그래머는 지루하고 반복적인 계산에서 해방되며 어셈블러 프로그램은 기계어보다 훨씬 가독성이 높다. 아키텍처에 따라 이러한 요소들은 고정 주소뿐만 아니라 오프셋이나 기타 데이터를 사용하여 특정 명령어 또는 주소 지정 방식을 위해 결합될 수 있다. 많은 어셈블러는 프로그램 개발을 용이하게 하고, 어셈블리 과정을 제어하며, 디버깅을 돕기 위해 추가적인 메커니즘을 제공한다.

일부는 열 중심(column oriented)으로, 특정 열에 특정 필드가 있으며 이는 1950년대와 1960년대 초반에 천공 카드를 사용하는 기계에서 매우 일반적이었다. 일부 어셈블러는 구분 기호(예: 구두점, 공백 문자)로 구분된 필드가 있는 자유 형식 구문을 갖는다. 일부 어셈블러는 혼합형으로, 예컨대 레이블은 특정 열에 있고 다른 필드는 구분 기호로 구분되며, 이는 1960년대에 열 중심 구문보다 더 일반적이 되었다.

## 용어
* **매크로 어셈블러**는 (매개변수화된) 어셈블리어 텍스트를 이름으로 나타낼 수 있고, 그 이름을 사용하여 확장된 텍스트를 다른 코드에 삽입할 수 있도록 매크로 명령어 기능을 포함하는 어셈블러이다.
** **오픈 코드**(Open code)는 매크로 정의 외부의 모든 어셈블러 입력을 의미한다.
* **크로스 어셈블러**(참조 크로스 컴파일러)는 결과 코드가 실행될 시스템(대상 시스템)과 다른 유형의 컴퓨터나 운영체제(호스트 시스템)에서 실행되는 어셈블러이다. 크로스 어셈블링은 임베디드 시스템이나 마이크로컨트롤러와 같이 소프트웨어 개발을 지원할 리소스가 없는 시스템을 위한 프로그램 개발을 용이하게 한다. 이러한 경우 결과 목적 파일은 고정 기억 장치(ROM, EPROM 등), 소자 프로그래머(마이크로컨트롤러와 같이 장치에 고정 기억 장치가 통합된 경우), 또는 목적 파일의 정확한 비트 단위 복사본이나 해당 코드의 텍스트 기반 표현(예: Intel hex 또는 Motorola S-record)을 사용하는 데이터 링크를 통해 대상 시스템으로 전송되어야 한다.
* **[[고급 어셈블러]]**는 고급 언어와 더 자주 연관되는 언어 추상화, 예를 들어 고급 제어 구조(IF/THEN/ELSE, DO CASE 등)와 구조체/레코드, 공용체, 클래스, 집합을 포함한 고급 추상 데이터 유형을 제공하는 프로그램이다.
* **[[마이크로어셈블러]]**는 컴퓨터의 저수준 작동을 제어하기 위한 마이크로프로그램을 준비하는 데 도움을 주는 프로그램이다.
* **메타 어셈블러**는 "어셈블리어의 구문론적 및 의미론적 설명을 받아들여 해당 언어를 위한 어셈블러를 생성하는 프로그램"이거나, 어셈블러 소스 파일과 함께 그러한 설명을 받아들여 그 설명에 따라 소스 파일을 어셈블하는 프로그램이다. SDS 9 시리즈 및 SDS 시그마 시리즈 컴퓨터를 위한 "Meta-Symbol" 어셈블러가 메타 어셈블러이다. Sperry Univac 또한 UNIVAC 1100/2200 시리즈를 위한 메타 어셈블러를 제공했다.
* **[[인라인 어셈블러]]**(또는 임베디드 어셈블러)는 고급 언어 프로그램 내에 포함된 어셈블러 코드이다. 이는 하드웨어에 직접 접근해야 하는 시스템 프로그램에서 가장 자주 사용된다.

## 주요 개념
### 어셈블러
**어셈블러** 프로그램은 작업 및 주소 지정 방식에 대한 기억술과 구문의 조합을 수치적 해당물로 번역하여 목적 파일을 생성한다. 이 표현에는 일반적으로 작업 코드("명령 코드")와 기타 제어 비트 및 데이터가 포함된다. 어셈블러는 또한 상수 식을 계산하고 메모리 위치 및 기타 엔티티에 대한 상징적 이름을 해결한다. 상징적 참조의 사용은 어셈블러의 핵심 기능으로, 프로그램 수정 후 지루한 계산과 수동 주소 업데이트를 줄여준다. 대부분의 어셈블러는 서브루틴을 호출하는 대신 공통된 짧은 명령어 시퀀스를 인라인 확장으로 생성하기 위해 텍스트 대체를 수행하는 매크로 기능도 포함한다.

일부 어셈블러는 특정 명령어 집합 구조에 특화된 몇 가지 간단한 유형의 컴파일러 최적화를 수행할 수도 있다. 이에 대한 구체적인 예로 다양한 벤더의 편재하는 X86 어셈블러가 있다. 점프 크기 조정(jump-sizing)이라 불리는 기능으로, 대부분은 요청에 따라 임의의 횟수의 패스(pass)를 통해 점프 명령어 교체(긴 점프를 짧은 점프나 상대 점프로 교체)를 수행할 수 있다. 다른 것들은 CPU 파이프라인을 가능한 한 효율적으로 활용하기 위해 합리적인 명령어 스케줄링을 최적화하는 데 도움이 되는 RISC 아키텍처용 일부 어셈블러처럼 명령어의 간단한 재배열이나 삽입을 수행할 수도 있다.

어셈블러는 1950년대부터 기계어 바로 위 단계이자 포트란, 알골, 코볼 및 리스프와 같은 고급 프로그래밍 언어 이전의 단계로 사용 가능했다. 또한 Speedcode와 같이 어셈블리와 고급 언어의 특성을 모두 가진 여러 부류의 트랜슬레이터 및 반자동 코드 생성기가 있었다.

특정 중앙 처리 장치나 명령어 집합 구조에 대해 서로 다른 구문을 가진 여러 어셈블러가 있을 수 있다. 예를 들어, x86 제품군 프로세서에서 메모리 데이터를 레지스터에 더하는 명령어는 원래의 인텔 구문에서는 `add eax,[ebx]`일 수 있지만, GNU 어셈블러에서 사용하는 AT&T 구문에서는 `addl (%ebx),%eax`로 작성된다. 외관은 다르지만 서로 다른 구문 형식은 일반적으로 동일한 수치 기계어를 생성한다. 단일 어셈블러는 또한 구문 형식의 변형뿐만 아니라 정확한 의미론적 해석을 지원하기 위해 서로 다른 모드를 가질 수도 있다 (예를 들어 x86 어셈블리 프로그래밍의 특별한 경우인 FASM 구문, TASM 구문, 이상 모드 등).

#### 패스 수
목적 파일을 생성하기 위해 소스를 몇 번 통과해야 하는지(어셈블러가 소스를 몇 번 읽는지)에 따라 두 가지 유형의 어셈블러가 있다.
* **단일 패스 어셈블러**(One-pass assemblers)는 소스 코드를 한 번 처리한다. 정의되기 전에 사용된 기호의 경우, 어셈블러는 최종 정의 후에 정오표를 내보내 링커나 로더에게 아직 정의되지 않은 기호가 사용된 위치를 수정하도록 지시한다.
* **다중 패스 어셈블러**(Multi-pass assemblers)는 첫 번째 패스에서 모든 기호와 그 값을 포함하는 테이블을 생성한 다음, 이후 패스에서 테이블을 사용하여 코드를 생성한다.
두 경우 모두 어셈블러는 후속 기호의 주소를 계산하기 위해 초기 패스에서 각 명령어의 크기를 결정할 수 있어야 한다. 즉, 나중에 정의된 피연산자를 참조하는 작업의 크기가 피연산자의 유형이나 거리에 따라 달라지는 경우, 어셈블러는 해당 작업을 처음 만났을 때 비관적인 추정을 하고 필요한 경우 나중 패스나 정오표에서 하나 이상의 "NOP" 명령어로 채운다. 핍홀 최적화 기능이 있는 어셈블러에서는 비관적인 코드를 대상으로부터의 정확한 거리에 맞춘 코드로 교체할 수 있도록 패스 사이에 주소를 재계산할 수 있다.

단일 패스 어셈블러를 사용한 원래 이유는 메모리 크기와 어셈블리 속도 때문이었다. 종종 두 번째 패스에는 기호 테이블을 메모리에 저장하거나(순방향 참조를 처리하기 위해), 테이프에서 프로그램 소스를 되감아 다시 읽거나, 천공 카드나 천공 종이 테이프 뭉치를 다시 읽어야 했기 때문이다. 나중에 훨씬 더 큰 메모리(특히 디스크 저장 장치)를 갖춘 컴퓨터들은 그러한 재읽기 없이 모든 필요한 처리를 수행할 공간을 갖게 되었다. 다중 패스 어셈블러의 장점은 정오표가 없어 링크 과정(또는 어셈블러가 직접 실행 코드를 생성하는 경우 프로그램 로드)이 더 빠르다는 점이다.

**예시:** 다음 코드 조각에서 단일 패스 어셈블러는 문 S2를 어셈블할 때 후방 참조 BKWD의 주소를 결정할 수 있지만, 분기 문 S1을 어셈블할 때는 전방 참조 FWD의 주소를 결정할 수 없다. 실제로 FWD는 정의되지 않았을 수도 있다. 2패스 어셈블러는 패스 1에서 두 주소를 모두 결정하므로 패스 2에서 코드를 생성할 때 이를 알 수 있다.
    B    
   ...
    EQU *
   ...
   EQU *
   ...
     B   

#### 고급 어셈블러
더 정교한 고급 어셈블러는 다음과 같은 언어 추상화를 제공한다.
* 고급 프로시저/함수 선언 및 호출
* 고급 제어 구조 (IF/THEN/ELSE, SWITCH)
* 구조체/레코드, 공용체, 클래스, 집합을 포함한 고급 추상 데이터 유형
* 정교한 매크로 처리 (1950년대 후반부터 IBM 700/7000 시리즈 및 IBM 7000 시리즈를 위해, 그리고 1960년대부터 IBM 시스템/360(S/360) 등을 위해 일반 어셈블러에서도 사용 가능했음)
* 객체 지향 프로그래밍 기능인 클래스, 객체, 추상화, 다형성 및 상속
자세한 내용은 아래의 언어 설계를 참조하라.

### 어셈블리어
어셈블리어로 작성된 프로그램은 일련의 기억술 프로세서 명령어와 메타 문(선언적 작업, 지시자, 의사 명령어, 의사 작업 등으로 다양하게 알려짐), 주석 및 데이터로 구성된다. 어셈블리어 명령어는 대개 명령 코드 기억술 뒤에 데이터, 인수 또는 매개변수 목록일 수 있는 피연산자가 오는 형태이다. 일부 명령어는 "암시적"일 수 있는데, 이는 명령어가 작동하는 데이터가 명령어 자체에 의해 암시적으로 정의됨을 의미하며, 이러한 명령어는 피연산자를 취하지 않는다. 결과 문은 어셈블러에 의해 메모리에 로드되어 실행될 수 있는 기계어 명령어로 번역된다.

예를 들어 아래의 명령어는 X86/IA-32 프로세서에게 8비트 즉시값을 레지스터로 이동하라고 지시한다. 이 명령어에 대한 이진 코드는 10110 뒤에 사용할 레지스터를 나타내는 3비트 식별자가 붙는다. AL 레지스터의 식별자는 000이므로, 다음 기계어는 AL 레지스터에 데이터 01100001을 로드한다.
 10110000 01100001
이 이진 컴퓨터 코드는 다음과 같이 십육진법으로 표현함으로써 더 사람이 읽기 쉽게 만들 수 있다.
 B0 61
여기서 `B0`는 "다음 값을 AL에 복사하라"는 의미이며, `61`은 십진법으로 97인 값 01100001의 십육진법 표현이다. 8086 제품군을 위한 어셈블리어는 이와 같은 명령어를 위해 MOV(move의 약자)라는 기억술을 제공하므로, 위의 기계어는 어셈블리어에서 다음과 같이 작성될 수 있으며 필요한 경우 세미콜론 뒤에 설명 주석을 덧붙일 수 있다. 이것이 훨씬 읽기 쉽고 기억하기 쉽다.

```nasm
MOV AL, 61h       ; AL에 십진수 97 (십육진수 61) 로드
```


일부 어셈블리어(이 언어 포함)에서는 MOV와 같은 동일한 기억술이 즉시값, 레지스터의 값, 또는 레지스터의 값이나 즉시(직접) 주소에 의해 가리켜지는 메모리 위치인지에 관계없이 데이터 로드, 복사 및 이동과 관련된 일련의 명령어 군에 사용될 수 있다. 다른 어셈블러는 "메모리를 레지스터로 이동"하기 위해 L, "레지스터를 메모리로 이동"하기 위해 ST, "레지스터를 레지스터로 이동"하기 위해 LR, "즉시 피연산자를 메모리로 이동"하기 위해 MVI 등과 같이 별개의 명령 코드 기억술을 사용할 수도 있다.

서로 다른 명령어에 동일한 기억술이 사용되는 경우, 이는 기억술 뒤에 오는 피연산자에 따라 기억술이 데이터(예: 이 예시의 `61h`)를 제외한 여러 다른 이진 명령어 코드에 대응함을 의미한다. 예를 들어 x86/IA-32 CPU의 경우, 인텔 어셈블리어 구문 `MOV AL, AH`는 레지스터 AH의 내용을 레지스터 AL로 이동하는 명령어를 나타낸다. 이 명령어의 십육진법 형태는 다음과 같다.
 88 E0
첫 번째 바이트 88h는 바이트 크기의 레지스터와 다른 레지스터 또는 메모리 사이의 이동을 식별하며, 두 번째 바이트 E0h는 두 피연산자 모두 레지스터이고 소스는 AH, 대상은 AL임을 지정하도록 (세 개의 비트 필드로) 인코딩된다.

이와 같이 동일한 기억술이 하나 이상의 이진 명령어를 나타낼 수 있는 경우, 어셈블러는 피연산자를 검사하여 생성할 명령어를 결정한다. 첫 번째 예에서 피연산자 `61h`는 유효한 십육진수 수치 상수이며 유효한 레지스터 이름이 아니므로 `B0` 명령어만 적용 가능하다. 두 번째 예에서 피연산자 `AH`는 유효한 레지스터 이름이며 유효한 수치 상수(십육진수, 십진수, 팔진수 또는 이진수)가 아니므로 `88` 명령어만 적용 가능하다.

어셈블리어는 이러한 종류의 모호성 부재가 구문에 의해 보편적으로 강제되도록 항상 설계된다. 예를 들어 인텔 x86 어셈블리어에서 십육진수 상수는 반드시 숫자 숫자로 시작해야 하므로, 십육진수 'A'(십진수 10과 같음)는 `AH`가 아니라 `0Ah` 또는 `0AH`로 작성되어야 하며, 이는 특히 레지스터 AH의 이름처럼 보이지 않도록 하기 위함이다. (동일한 규칙은 레지스터 BH, CH, DH의 이름뿐만 아니라 "BEACH"와 같이 문자로 끝나는 사용자 정의 기호와의 모호성도 방지한다.)

원래 예시로 돌아가서, x86 명령 코드 10110000(`B0`)이 8비트 값을 AL 레지스터에 복사하는 반면, 10110001(`B1`)은 CL로, 10110010(`B2`)은 DL로 이동시킨다. 이에 대한 어셈블리어 예시는 다음과 같다.

```nasm
MOV AL, 1h        ; AL에 즉시값 1 로드
MOV CL, 2h        ; CL에 즉시값 2 로드
MOV DL, 3h        ; DL에 즉시값 3 로드
```

MOV의 구문은 다음 예시처럼 더 복잡해질 수도 있다.

```nasm
MOV EAX, [EBX]	  ; EBX에 담긴 주소의 메모리 4바이트를 EAX로 이동
MOV [ESI+EAX], CL ; CL의 내용을 주소 ESI+EAX의 바이트로 이동
MOV DS, DX        ; DX의 내용을 세그먼트 레지스터 DS로 이동
```

각 경우에서 MOV 기억술은 어셈블러에 의해 명령 코드 88-8C, 8E, A0-A3, B0-BF, C6 또는 C7 중 하나로 직접 번역되며, 프로그래머는 보통 어느 것인지 알거나 기억할 필요가 없다.

어셈블리어를 기계어로 변환하는 것은 어셈블러의 몫이며, 그 반대는 역어셈블러에 의해 최소한 부분적으로 달성될 수 있다. 고급 프로그래밍 언어와 달리, 많은 단순한 어셈블리 문과 기계어 명령어 사이에는 일대일 대응 관계가 있다. 그러나 어떤 경우에는 어셈블러가 흔히 필요한 기능을 제공하기 위해 여러 기계어 명령어로 확장되는 의사 명령어(본질적으로 매크로)를 제공할 수도 있다. 예를 들어 "크거나 같으면 분기" 명령어가 없는 기계의 경우, 어셈블러는 기계의 "작으면 설정"과 "0이면 분기(설정 명령어의 결과에 따라)"로 확장되는 의사 명령어를 제공할 수 있다. 대부분의 모든 기능을 갖춘 어셈블러는 벤더와 프로그래머가 더 복잡한 코드 및 데이터 시퀀스를 생성하는 데 사용하는 풍부한 매크로 언어(아래에서 논의됨)를 제공한다. 어셈블러 환경에서 정의된 의사 명령어와 매크로에 대한 정보는 목적 프로그램에 존재하지 않으므로, 역어셈블러는 매크로와 의사 명령어 호출을 재구성할 수 없으며 어셈블러가 해당 추상 어셈블리어 엔티티로부터 생성한 실제 기계 명령어만 역어셈블할 수 있다. 마찬가지로, 어셈블리어 소스 파일의 주석은 어셈블러에 의해 무시되고 생성되는 목적 파일에 아무런 영향을 미치지 않으므로, 역어셈블러는 항상 소스 주석을 복구할 수 없다.

각 컴퓨터 구조는 고유한 기계어를 가진다. 컴퓨터는 지원하는 작업의 수와 유형, 레지스터의 크기와 수, 저장소의 데이터 표현 방식이 서로 다르다. 대부분의 범용 컴퓨터는 본질적으로 동일한 기능을 수행할 수 있지만 그 방식은 다르며, 대응하는 어셈블리어는 이러한 차이를 반영한다.

하나의 명령어 집합에 대해 여러 세트의 기억술이나 어셈블리어 구문이 존재할 수 있으며, 이는 전형적으로 서로 다른 어셈블러 프로그램으로 구현된다. 이러한 경우 가장 대중적인 것은 대개 CPU 제조업체가 공급하고 설명서에서 사용하는 것이다.

두 개의 서로 다른 기억술 세트를 가진 CPU의 두 예로는 인텔 8080 제품군과 인텔 8086/8088이 있다. 인텔이 자사의 어셈블리어 기억술에 대해 저작권을 주장했기 때문에(적어도 1970년대와 1980년대 초에 발행된 설명서의 각 페이지에서), 인텔 명령어 집합과 호환되는 CPU를 독립적으로 생산한 일부 회사들은 자신들만의 기억술을 발명했다. 인텔 8080A를 개선한 자일로그 Z80 CPU는 모든 8080A 명령어에 더해 더 많은 명령어를 지원한다. 자일로그는 새로운 명령어뿐만 아니라 모든 8080A 명령어를 위해 완전히 새로운 어셈블리어를 발명했다. 예를 들어 인텔이 다양한 데이터 전송 명령어를 위해 MOV, MVI, LDA, STA, LXI, LDAX, STAX, LHLD, SHLD라는 기억술을 사용하는 곳에서 Z80 어셈블리어는 그들 모두에 대해 LD라는 기억술을 사용한다. 유사한 사례로 인텔 8086과 8088의 개선된 복제판인 NEC V20 및 NEC V30 CPU가 있다. Z80의 자일로그와 마찬가지로 NEC는 인텔의 저작권 침해 비난을 피하기 위해 모든 8086 및 8088 명령어를 위한 새로운 기억술을 발명했다. (그러한 저작권이 유효할 수 있는지는 의문이며, 나중에 AMD 및 사이릭스와 같은 CPU 회사들은 허가나 법적 처벌 없이 인텔의 x86/IA-32 명령어 기억술을 그대로 재발행했다.) 실제로 V20과 V30을 프로그래밍한 많은 사람들이 NEC의 어셈블리어가 아닌 인텔의 언어로 작성했는지는 의문이다. 동일한 명령어 집합 구조에 대한 두 어셈블리어는 서로 동형이므로(영어와 피그 라틴과 다소 비슷함), 제조업체의 제품에 제조업체 고유의 어셈블리어를 사용해야 할 요구 사항은 없다.

### x86 리눅스에서의 "Hello, world!"
X86 프로세서 기반 리눅스용 32비트 어셈블리어에서 "Hello, world!"는 다음과 같이 출력될 수 있다.

```nasm
section	.text       ; 코드 세그먼트의 시작
   global _start    ; 생성된 목적 파일에서 _start가 보이도록 선언

_start:
   mov	edx,len     ; 문자열의 길이, write()의 세 번째 인수
   mov	ecx,msg     ; 문자열의 주소, write()의 두 번째 인수
   mov	ebx,1       ; 파일 디스크립터 (표준 출력), write()의 첫 번째 인수
   mov	eax,4       ; write()를 위한 시스템 호출 번호
   int	0x80        ; 시스템 호출 트랩

   mov	ebx,0       ; 종료 코드, exit()의 첫 번째 인수
   mov	eax,1       ; exit()를 위한 시스템 호출 번호
   int	0x80        ; 시스템 호출 트랩

section	.data       ; 데이터 세그먼트의 시작
msg db 'Hello, world!', 0xa  ; 출력할 문자열
len equ $ - msg     ; 어셈블 시 상수로 계산된 해당 문자열의 길이
```


## 언어 설계
### 기본 요소
어셈블러 저자들이 문(statement)을 분류하는 방식과 사용하는 용어에는 상당한 다양성이 있다. 특히 일부는 기계 기억술이나 확장 기억술이 아닌 다른 모든 것을 의사 작업(pseudo-op)으로 설명한다. 일반적인 어셈블리어는 프로그램 작동을 정의하는 데 사용되는 3가지 유형의 명령어 문으로 구성된다.

* 명령 코드 기억술
* 데이터 정의
* 어셈블리 지시자

#### 명령 코드 기억술 및 확장 기억술
어셈블리어의 명령어(문)는 고급 프로그래밍 언어의 명령어와 달리 일반적으로 매우 단순하다. 일반적으로 기억술은 단일 실행 가능한 기계어 명령어(명령 코드)에 대한 상징적 이름이며, 각 기계어 명령어에 대해 최소한 하나의 명령 코드 기억술이 정의되어 있다. 각 명령어는 전형적으로 작업 또는 명령 코드와 0개 이상의 피연산자로 구성된다. 대부분의 명령어는 단일 값이나 한 쌍의 값을 참조한다. 피연산자는 즉시값(명령어 자체에 코딩된 값), 명령어에 지정되거나 암시된 레지스터, 또는 저장소의 다른 곳에 위치한 데이터의 주소일 수 있다. 이는 기저에 있는 프로세서 아키텍처에 의해 결정되며, 어셈블러는 단순히 이 아키텍처가 작동하는 방식을 반영할 뿐이다. 확장 기억술은 명령 코드와 특정 피연산자의 조합을 지정하기 위해 자주 사용된다. 예를 들어 시스템/360 어셈블러는 마스크가 15인 에 대한 확장 기억술로 를 사용하고, 마스크가 0인 에 대해 ("NO OPeration" – 한 단계 동안 아무것도 하지 않음)를 사용한다.

확장 기억술은 명령어 이름에서 명확하지 않은 목적으로 명령어를 특수하게 사용하는 것을 지원하기 위해 종종 사용된다. 예를 들어, 많은 CPU에는 명시적인 NOP 명령어가 없지만 그 목적으로 사용될 수 있는 명령어가 있다. 8086 CPU에서 명령어 는 를 위해 사용되며, 는  명령어를 인코딩하기 위한 의사 명령 코드이다. 일부 역어셈블러는 이를 인식하여  명령어를 로 디코딩한다. 마찬가지로 시스템/360 및 시스템/370용 IBM 어셈블러는 제로 마스크가 있는  및 에 대해 확장 기억술  및 을 사용한다. SPARC 아키텍처의 경우 이들은 합성 명령어(synthetic instructions)로 알려져 있다.

일부 어셈블러는 두 개 이상의 기계 명령어를 생성하는 간단한 내장 매크로 명령어도 지원한다. 예를 들어 일부 Z80 어셈블러에서는  명령어가  다음에 를 생성하는 것으로 인식된다. 이들은 때때로 의사 명령 코드로 알려져 있다.

기억술은 임의의 기호이다. 1985년에 IEEE는 모든 어셈블러가 사용할 균일한 기억술 세트에 대한 표준 694를 발표했다. 이 표준은 이후 철회되었다.

#### 데이터 지시자
데이터와 변수를 담을 데이터 요소를 정의하는 데 사용되는 명령어가 있다. 이들은 데이터의 유형, 길이 및 데이터 구조 정렬을 정의한다. 이러한 명령어는 또한 데이터가 외부 프로그램(별도로 어셈블된 프로그램)에서 사용 가능한지 아니면 데이터 섹션이 정의된 프로그램 내에서만 사용 가능한지를 정의할 수 있다. 일부 어셈블러는 이를 의사 작업으로 분류한다.

#### 어셈블리 지시자
의사 명령 코드, 의사 작업 또는 pseudo-ops라고도 불리는 어셈블리 지시자는 어셈블러에게 "명령어를 어셈블하는 것 이외의 작업을 수행하도록 지시"하는 명령이다. 지시자는 어셈블러가 작동하는 방식에 영향을 미치며 "목적 파일, 기호 테이블, 리스팅 파일 및 내부 어셈블러 매개변수의 값에 영향을 미칠 수 있다". 때때로 의사 명령 코드라는 용어는 데이터를 생성하는 것과 같이 목적 코드를 생성하는 지시자를 위해 예약된다.

의사 작업의 이름은 종종 기계 명령어와 구별하기 위해 점으로 시작한다. 의사 작업은 프로그램의 어셈블리를 프로그래머가 입력한 매개변수에 의존하게 만들 수 있으므로, 하나의 프로그램을 서로 다른 응용 프로그램을 위해 서로 다른 방식으로 어셈블할 수 있다. 또는 의사 작업을 사용하여 프로그램을 읽고 유지 관리하기 쉽게 만들기 위해 프로그램의 표현을 조작할 수 있다. 의사 작업의 또 다른 일반적인 용도는 런타임 데이터를 위한 저장 영역을 예약하고 선택적으로 그 내용을 알려진 값으로 초기화하는 것이다.

상징적 어셈블러는 프로그래머가 임의의 이름(레이블 또는 기호)을 메모리 위치 및 다양한 상수와 연관시킬 수 있게 한다. 보통 모든 상수와 변수에 이름이 부여되어 명령어가 이름으로 해당 위치를 참조할 수 있게 함으로써 자기 문서화 코드를 촉진한다. 실행 코드에서 각 서브루틴의 이름은 해당 진입점과 연관되므로 서브루틴에 대한 모든 호출은 그 이름을 사용할 수 있다. 서브루틴 내부에서 GOTO 대상에는 레이블이 부여된다. 일부 어셈블러는 일반 기호와 어휘적으로 구별되는 로컬 기호를 지원한다(예: GOTO 대상으로 "10$" 사용).

NASM과 같은 일부 어셈블러는 유연한 기호 관리를 제공하여 프로그래머가 서로 다른 이름공간을 관리하고, 자료 구조 내의 오프셋을 자동으로 계산하며, 리터럴 값이나 어셈블러가 수행한 간단한 계산 결과를 참조하는 레이블을 할당할 수 있게 한다. 레이블은 재배치 가능한 주소로 상수와 변수를 초기화하는 데에도 사용될 수 있다.

어셈블리어는 대부분의 다른 컴퓨터 언어와 마찬가지로 프로그램 소스 코드에 어셈블리 중에 무시될 주석을 추가할 수 있게 한다. 이진 기계 명령어 시퀀스의 의미와 목적을 결정하기 어려울 수 있으므로 어셈블리어 프로그램에서 신중한 주석 처리는 필수적이다. 컴파일러나 역어셈블러에 의해 생성된 "가공되지 않은"(주석 없는) 어셈블리어는 변경이 필요할 때 읽기가 매우 어렵다.

### 매크로
많은 어셈블러는 사전 정의된 매크로를 지원하며, 다른 어셈블러는 변수와 상수가 포함된 텍스트 줄 시퀀스를 포함하는 프로그래머 정의(및 반복적으로 재정의 가능한) 매크로를 지원한다. 매크로 정의는 가장 일반적으로 어셈블러 문(예: 지시자, 상징적 기계 명령어 및 어셈블러 문을 위한 템플릿)의 혼합이다. 이 텍스트 줄 시퀀스에는 명령 코드나 지시자가 포함될 수 있다. 매크로가 정의되면 그 이름이 기억술 대신 사용될 수 있다. 어셈블러가 그러한 문을 처리할 때, 문을 해당 매크로와 연관된 텍스트 줄로 교체한 다음, 마치 소스 코드 파일에 존재하는 것처럼 처리한다(일부 어셈블러에서는 교체 텍스트에 존재하는 모든 매크로의 확장 포함). 이러한 의미에서의 매크로는 1950년대 IBM autocoder로 거슬러 올라간다.

**매크로 어셈블러**는 전형적으로 매크로 정의, 변수 정의, 산술, 논리 또는 문자열 식의 결과로 변수 설정, 반복, 조건부 코드 생성 등을 위한 지시자를 갖는다. 이러한 지시자 중 일부는 매크로 정의 내에서만 사용되도록 제한될 수 있는 반면(예: HLASM의 **MEXIT**), 다른 것들은 오픈 코드(매크로 정의 외부) 내에서 허용될 수 있다(예: HLASM의 **AIF** 및 **COPY**).

어셈블리어에서 "매크로"라는 용어는 C 프로그래밍 언어의 전처리기와 같은 일부 다른 맥락에서보다 더 포괄적인 개념을 나타내며, C의 #define 지시자는 대개 짧은 단일 행 매크로를 만드는 데 사용된다. PL/I 및 일부 다른 언어의 매크로와 마찬가지로 어셈블러 매크로 명령어는 그 자체로 긴 "프로그램"일 수 있으며, 어셈블리 중에 어셈블러에 의해 해석되어 실행된다.

매크로는 '짧은' 이름을 가질 수 있지만 여러 줄 또는 실제로 많은 줄의 코드로 확장될 수 있으므로, 어셈블리어 프로그램을 고급 언어처럼 훨씬 짧게 보이게 하고 더 적은 소스 코드 줄을 필요로 하게 만드는 데 사용될 수 있다. 또한 어셈블리 프로그램에 더 높은 수준의 구조를 추가하고, 선택적으로 매개변수를 통해 내장된 디버깅 코드를 도입하는 등의 유사한 기능을 수행하는 데 사용될 수 있다.

매크로 어셈블러는 종종 매크로가 매개변수를 취할 수 있게 한다. 일부 어셈블러는 선택적 매개변수, 상징적 변수, 조건문, 문자열 조작 및 산술 연산과 같은 고급 언어 요소를 통합하여 주어진 매크로의 실행 중에 모두 사용할 수 있고 매크로가 컨텍스트를 저장하거나 정보를 교환할 수 있게 하는 매우 정교한 매크로 언어를 포함한다. 따라서 매크로는 매크로 인수를 기반으로 수많은 어셈블리어 명령어 또는 데이터 정의를 생성할 수 있다. 이는 예를 들어 레코드 스타일의 데이터 구조나 "언롤링된" 루프를 생성하거나 복잡한 매개변수를 기반으로 전체 알고리즘을 생성하는 데 사용될 수 있다. 예를 들어 "정렬" 매크로는 복잡한 정렬 키의 사양을 수락하고 일반적인 프로시저에서 사양을 해석하는 데 필요한 런타임 테스트 없이 해당 특정 키에 맞게 제작된 코드를 생성할 수 있다. 그러한 매크로 제품군을 사용하여 대폭 확장된 어셈블리어를 사용하는 조직은 그러한 프로그래머들이 컴퓨터의 가장 낮은 수준의 개념적 요소로 작업하지 않기 때문에 고급 언어로 작업하는 것으로 간주될 수 있다. 이 점을 강조하듯, 매크로는 가상 머신을 위한 어셈블리어인 SIL(SNOBOL Implementation Language)로 작성된 SNOBOL4(1967)에서 초기 가상 머신을 구현하는 데 사용되었다. 대상 기계는 매크로 어셈블러를 사용하여 이를 기본 코드로 번역한다. 이는 당시로서는 높은 수준의 이식성을 가능하게 했다.

매크로는 메인프레임 시대에 특정 고객을 위한 대규모 소프트웨어 시스템을 맞춤화하는 데 사용되었으며, 제조업체 운영체제의 특정 버전을 만들어 고용주의 요구를 충족시키기 위해 고객 인력에 의해서도 사용되었다. 이는 예를 들어 IBM의 Conversational Monitor System / Virtual Machine(VM/CMS)과 IBM의 "실시간 트랜잭션 처리" 애드온인 CICS, 그리고 1970년대에 시작되어 오늘날에도 많은 대형 컴퓨터 예약 시스템(CRS)과 신용카드 시스템을 구동하는 항공/금융 시스템인 ACP/TPF를 다루는 시스템 프로그래머들에 의해 수행되었다.

또한 어셈블러의 매크로 처리 능력만을 사용하여 완전히 다른 언어로 작성된 코드를 생성하는 것도 가능하다. 예를 들어, 어셈블러에게 임의의 코드를 생성하도록 지시하는 어셈블리 시간 연산자 내에 코볼 코드 줄을 포함하는 순수 매크로 어셈블러 프로그램을 사용하여 코볼 버전의 프로그램을 생성할 수 있다. IBM OS/360은 시스템 생성을 수행하기 위해 매크로를 사용한다. 사용자는 일련의 어셈블러 매크로를 코딩하여 옵션을 지정한다. 이러한 매크로를 어셈블하면 작업 제어 언어 및 유틸리티 제어 문을 포함하여 시스템을 빌드하기 위한 작업 스트림이 생성된다.

이는 1960년대에 인식된 바와 같이 "매크로 처리"의 개념이 "어셈블리"의 개념과 독립적이기 때문이며, 전자는 현대적 용어로 목적 코드를 생성하는 것보다 워드 프로세싱, 텍스트 처리에 더 가깝다. 매크로 처리의 개념은 변수를 설정하고 그 값에 대해 조건부 테스트를 수행하기 위해 "전처리기 지시자"를 지원하는 C 프로그래밍 언어에 나타났고 지금도 나타난다. 어셈블러 내부의 일부 이전 매크로 프로세서와 달리, C 전처리기는 루프나 "go to" 기능이 없기 때문에 튜링 완전하지 않으며, 후자는 프로그램이 루프를 돌 수 있게 해준다.

매크로 처리의 위력에도 불구하고, 어셈블러에게는 영원한 것이었지만 많은 고급 언어(주요 예외는 C, C++ 및 PL/I)에서는 사용되지 않게 되었다.

매크로 매개변수 대체는 엄격하게 이름에 의한 것이다. 매크로 처리 시에 매개변수의 값은 그 이름으로 텍스트적으로 대체된다. 결과적으로 발생하는 가장 유명한 부류의 버그는 매크로 작성자가 이름을 기대했을 때 매개변수 자체가 식이고 단순한 이름이 아닌 경우에 발생했다. 매크로에서:

 foo: macro a
 load a*b

의도는 호출자가 변수의 이름을 제공하고, "전역" 변수 또는 상수 b가 "a"를 곱하는 데 사용되는 것이었다. foo가 매개변수 `a-c`와 함께 호출되면, `load a-c*b`의 매크로 확장이 발생한다. 모호성을 피하기 위해 매크로 프로세서 사용자는 매크로 정의 내부의 형식 매개변수에 괄호를 치거나, 호출자가 입력 매개변수에 괄호를 칠 수 있다.

### 구조적 프로그래밍 지원
실행 흐름을 인코딩하기 위해 구조적 프로그래밍 요소를 제공하는 매크로 패키지들이 작성되었다. 이 접근 방식의 가장 초기 사례는 Concept-14 매크로 세트로, 원래 할란 밀스(1970년 3월)가 제안하고 IBM 연방 시스템 부문의 마빈 케슬러가 구현했으며, OS/360 어셈블러 프로그램을 위해 IF/ELSE/ENDIF 및 유사한 제어 흐름 블록을 제공했다. 이는 어셈블리 언어에서 스파게티 코드를 유발하는 주요 요인 중 하나인 GOTO 작업의 사용을 줄이거나 없애는 방법이었다. 이 접근 방식은 1980년대 초반(대규모 어셈블리어 사용의 마지막 시기)에 널리 받아들여졌다. IBM의 High Level Assembler Toolkit은 그러한 매크로 패키지를 포함한다.

또 다른 설계는 Whitesmiths Ltd.(유닉스와 유사한 Idris 운영체제와 최초의 상업용 C 컴파일러로 보고된 것의 개발사)에서 8080/Z80 프로세서를 위해 만든 "스트림 중심" 어셈블러인 A-Natural이었다. 이 언어는 명령 코드, 레지스터 및 메모리 참조와 같은 가공되지 않은 기계 요소로 작업했기 때문에 어셈블러로 분류되었으나, 실행 순서를 나타내기 위한 식 구문을 통합했다. 괄호 및 기타 특수 기호는 블록 중심의 구조적 프로그래밍 구성체와 함께 생성된 명령어의 순서를 제어했다. A-Natural은 직접 코딩하기보다는 C 컴파일러의 목적 언어로 빌드되었으나 그 논리적 구문은 일부 팬들을 확보했다.

대규모 어셈블리어 개발이 쇠퇴한 이후 더 정교한 어셈블러에 대한 수요는 거의 없었다. 그럼에도 불구하고 리소스 제약이나 대상 시스템 아키텍처의 특이성으로 인해 고급 언어를 효과적으로 사용할 수 없는 경우에는 여전히 개발 및 적용되고 있다.

강력한 매크로 엔진을 갖춘 어셈블러는 Masm32 패키지와 함께 제공되는 switch 매크로와 같은 매크로를 통해 구조적 프로그래밍을 허용한다(이 코드는 완전한 프로그램이다).


```nasm
include \masm32\include\masm32rt.inc	; Masm32 라이브러리 사용

.code
demomain:
  REPEAT 20
	switch rv(nrandom, 9)	; 0에서 8 사이의 숫자 생성
	mov ecx, 7
	case 0
		print "case 0"
	case ecx				; 대부분의 다른 프로그래밍 언어와 달리,
		print "case 7"		; Masm32 switch는 "변수 케이스"를 허용함
	case 1 .. 3
		.if eax==1
			print "case 1"
		.elseif eax==2
			print "case 2"
		.else
			print "cases 1 to 3: other"
		.endif
	case 4, 6, 8
		print "cases 4, 6 or 8"
	default
		mov ebx, 19		     ; 별 20개 출력
		.Repeat
			print "*"
			dec ebx
		.Until Sign?		 ; 사인 플래그가 설정될 때까지 루프
	endsw
	print chr$(13, 10)
  ENDM
  exit
end demomain
```


## 어셈블리어의 사용
프로그램 내장식 컴퓨터가 도입되었을 때 프로그램은 기계어로 작성되었으며, 천공 종이 테이프에서 컴퓨터로 로드되거나 콘솔 스위치에서 메모리로 직접 입력되었다. 캐슬린 부스는 1947년 버크벡 칼리지에서 ARC2를 작업하는 동안 시작한 이론적 연구를 바탕으로 "어셈블리어를 발명한 공로를 인정받고 있으며", 이는 앤드루 부스(나중에 그녀의 남편)가 프린스턴 고등연구소에서 수학자 존 폰 노이만 및 물리학자 허먼 골드스타인과 상담한 이후였다.

1948년 말, EDSAC(Electronic Delay Storage Automatic Calculator)은 부트스트랩 프로그램에 통합된 어셈블러("initial orders"라는 이름)를 가졌다. 이는 IEEE 컴퓨터 협회로부터 최초의 "어셈블러" 창시자로 인정받는 데이비드 휠러가 개발한 한 글자 기억술을 사용했다. EDSAC에 관한 보고서는 필드를 명령어 단어로 결합하는 과정을 위해 "어셈블리"라는 용어를 도입했다. SOAP(Symbolic Optimal Assembly Program)은 1955년 스탠 폴리가 IBM 650 컴퓨터를 위해 작성한 어셈블리어였다.

어셈블리어는 초기 컴퓨터에서 필요했던 오류가 발생하기 쉽고 지루하며 시간이 많이 걸리는 1세대 프로그래밍의 상당 부분을 제거하여 프로그래머를 숫자 코드 기억 및 주소 계산과 같은 지루함에서 해방시켰다. 한때 모든 종류의 프로그래밍에 널리 사용되었다. 1950년대 후반까지 향상된 프로그래밍 생산성을 추구하는 과정에서 그 사용은 상당 부분 고급 언어로 대체되었다. 오늘날 어셈블리어는 여전히 직접적인 하드웨어 조작, 특수 프로세서 명령어에 대한 접근, 또는 중요한 성능 문제를 해결하기 위해 사용된다. 전형적인 용도는 장치 드라이버, 저수준 임베디드 시스템 및 실시간 시스템이다 ( 참조).

수많은 프로그램이 완전히 어셈블리어로 작성되었다. Burroughs MCP(1961)는 운영체제가 어셈블리어로만 개발되지 않은 최초의 컴퓨터였다. 그것은 알골 방언인 ESPOL(Executive Systems Problem Oriented Language)로 작성되었다. 대기업에서 개발한 대량의 IBM 메인프레임 소프트웨어를 포함하여 많은 상업용 응용 프로그램도 어셈블리어로 작성되었다. 코볼, 포트란 및 일부 PL/I이 결국 어셈블리어를 밀어냈지만, 많은 대규모 조직은 1990년대까지 어셈블리어 응용 프로그램 인프라를 유지했다.

어셈블리어는 애플 II, 아타리 8비트 제품군, ZX 스펙트럼 및 코모도어 64와 같은 8비트 홈 컴퓨터의 주요 개발 언어였다. 이러한 시스템에서 인터프리트되는 베이직은 최대 실행 속도를 제공하지 못했고 사용 가능한 하드웨어를 최대한 활용하기 위한 시설도 부족했다. 어셈블리어는 아타리 2600 및 패밀리컴퓨터와 같은 8비트 콘솔 프로그래밍을 위한 기본 선택이었다.

MS-DOS, 터보 파스칼, 로터스 1-2-3 스프레드시트와 같은 IBM PC 호환기종의 핵심 소프트웨어는 어셈블리어로 작성되었다. 컴퓨터 속도가 기하급수적으로 빨라지면서 어셈블리어는 지배적인 개발 언어라기보다 둠의 렌더링과 같이 프로그램의 일부를 가속화하기 위한 도구가 되었다. 1990년대에 어셈블리어는 세가 새턴과 같은 시스템에서 성능을 극대화하기 위해 사용되었으며, 모탈 컴뱃 및 NBA 잼과 같이 TMS34010 통합 CPU/GPU를 사용하는 아케이드 하드웨어의 주요 언어로 사용되었다.

### 현재의 용도
고급 언어와 비교하여 어셈블리어의 유용성과 성능에 대한 논쟁이 있어 왔다.

어셈블리어가 중요한 특정 틈새 용도가 있지만(아래 참조), 최적화를 위한 다른 도구들도 있다.

 기준으로, 프로그래밍 언어 인기도를 측정하는 TIOBE 지수에서 어셈블리어는 예를 들어 비주얼 베이직보다 앞선 11위를 차지하고 있다. 어셈블러는 속도 최적화 또는 크기 최적화를 위해 사용될 수 있다. 속도 최적화의 경우, 일부 반례가 있음에도 불구하고 현대의 최적화 컴파일러는 고급 언어를 직접 작성한 어셈블리만큼 빠르게 실행될 수 있는 코드로 번역한다고 주장된다. 현대 프로세서와 메모리 하위 시스템의 복잡성은 컴파일러와 어셈블리 프로그래머 모두에게 효과적인 최적화를 점점 더 어렵게 만들고 있다. 프로세서 성능이 향상되면서 대부분의 CPU는 캐시 미스, I/O 작업 및 페이징과 같이 예측 가능한 병목 현상으로 인해 발생하는 지연과 함께 대부분의 시간 동안 유휴 상태에 있게 되어, 가공되지 않은 코드 실행 속도는 많은 프로그래머에게 문제가 되지 않게 되었다.

어셈블리 프로그래밍의 사용이 더 일반적인 특정 컴퓨터 프로그래밍 영역은 여전히 존재한다.

* 아타리 2600, 코모도어 64 및 그래프 계산기와 같이 고급 언어 선택지가 제한된 시스템을 위한 코드 작성. 이러한 1970년대와 1980년대 컴퓨터를 위한 프로그램은 종종 데모씬이나 레트로게이밍 하위 문화의 맥락에서 작성된다.
* 하드웨어와 직접 상호 작용해야 하는 코드, 예를 들어 장치 드라이버 및 인터럽트 핸들러.
* 임베디드 프로세서나 DSP에서, 초당 1000번 또는 10000번 발생하는 인터럽트와 같이 인터럽트당 최소한의 사이클 수를 필요로 하는 높은 반복 횟수의 인터럽트.
* 컴파일러에 구현되지 않은 프로세서 고유의 명령어를 사용해야 하는 프로그램. 일반적인 예로는 많은 암호화 알고리즘의 핵심인 비트 단위 회전 명령어뿐만 아니라 바이트의 패리티나 덧셈의 4비트 캐리를 쿼리하는 것이 있다.
* 전화기, 자동차 연료 및 점화 시스템, 에어컨 제어 시스템, 보안 시스템의 펌웨어와 같이 고급 언어와 연관된 런타임 시스템 구성 요소나 라이브러리에 의존하지 않고 실행되어야 하는 독립 실행형 실행 파일.
* 성능에 민감한 내부 루프를 가진 프로그램으로, 어셈블리어가 고급 언어에서는 달성하기 어려운 최적화 기회를 제공하는 경우. 예를 들어 BLAS를 사용한 선형대수학이나 이산 코사인 변환(예: X264의 SIMD 어셈블리 버전).
* C와 같은 고급 언어의 프로그램을 위해 벡터화된 함수를 생성하는 프로그램. 고급 언어에서는 때때로 SIMD 기억술에 직접 매핑되는 컴파일러 내장 함수의 도움을 받지만, 그럼에도 불구하고 주어진 벡터 프로세서에 특화된 일대일 어셈블리 변환 결과를 낳는다.
* 시뮬레이션, 비행 내비게이션 시스템, 의료 장비와 같은 실시간 프로그램. 예를 들어 플라이 바이 와이어 시스템에서 텔레메트리는 엄격한 시간 제약 내에 해석되고 조치되어야 한다. 이러한 시스템은 인터프리트 언어, 자동 쓰레기 수집, 페이징 작업 또는 선점형 멀티태스킹에 의해 발생할 수 있는 예측 불가능한 지연 원인을 제거해야 한다. 이러한 시스템에 대해 어셈블리나 저급 프로그래밍 언어를 선택하면 프로그래머에게 처리 세부 사항에 대한 더 큰 가시성과 제어권을 제공한다.
* 항상 엄격하게 동일한 실행 시간을 가져야 하여 타이밍 공격을 방지해야 하는 암호 알고리즘.
* rav1e(AV1용 인코더) 및 dav1d(AV1용 참조 디코더)와 같은 비디오 인코더 및 디코더는 가능한 경우 AVX2 및 ARM Neon 명령어를 활용하기 위해 어셈블리를 포함한다.
* IBM 메인프레임 컴퓨터를 위해 작성된 레거시 코드를 수정하고 확장하는 경우.
* 극도로 높은 보안 상황에서 환경에 대한 완전한 통제가 필요하여 아무것도 당연하게 여길 수 없는 경우.
* 컴퓨터 바이러스, 부트로더, 특정 장치 드라이버, 또는 하드웨어나 저수준 운영체제에 매우 밀접한 기타 항목들.
* 추가 오버헤드를 최소한으로 유지해야 하는 모니터링, 추적 및 디버깅을 위한 명령어 집합 시뮬레이터.
* 크로스 컴파일러가 없는 새로운 또는 특수 프로세서에서 고급 언어가 존재하지 않는 상황.
* 다음과 같은 프로그램 파일의 역공학 및 수정:
** 원래 고급 언어로 작성되었을 수도 있고 아닐 수도 있는 기존 이진 파일, 예를 들어 소스 코드를 사용할 수 없거나 분실된 프로그램을 재현하려는 경우, 또는 독점 소프트웨어의 복제 방지를 해제하려는 경우.
** 여러 방법으로 가능한 비디오 게임(롬 해킹이라고도 함). 가장 널리 쓰이는 방법은 어셈블리어 수준에서 프로그램 코드를 변경하는 것이다.

어셈블리어는 여전히 대부분의 컴퓨터 과학 및 전자공학 교육 과정에서 가르치고 있다. 오늘날 어셈블리어를 도구로서 정기적으로 다루는 프로그래머는 적지만, 그 기저에 있는 개념은 여전히 중요하다. 이진 산술, 메모리 할당, 스택 처리, 문자 집합 인코딩, 인터럽트 처리 및 컴파일러 설계와 같은 기본 주제는 컴퓨터가 하드웨어 수준에서 어떻게 작동하는지 파악하지 않고는 상세히 연구하기 어려울 것이다. 컴퓨터의 동작은 근본적으로 명령어 집합에 의해 정의되므로, 그러한 개념을 배우는 논리적인 방법은 어셈블리어를 공부하는 것이다. 대부분의 현대 컴퓨터는 유사한 명령어 집합을 가지고 있다. 따라서 단일 어셈블리어를 공부하는 것만으로도 기본 개념을 배우고, 어셈블리어를 사용하는 것이 적절한 상황을 인식하며, 고급 언어로부터 얼마나 효율적인 실행 코드가 생성될 수 있는지 확인하는 데 충분하다.

### 전형적인 응용 프로그램
* 어셈블리어는 일반적으로 시스템의 부팅 코드에서 사용된다. 부팅 코드는 운영체제를 부팅하기 전에 시스템 하드웨어를 초기화하고 테스트하는 저수준 코드로, 종종 ROM에 저장된다. (IBM PC 호환기종 시스템의 바이오스 및 CP/M이 그 예이다.)
* 어셈블리어는 종종 저수준 코드, 예를 들어 기존 시스템 호출의 가용성에 의존할 수 없고 시스템이 실행될 특정 프로세서 아키텍처를 위해 실제로 이를 구현해야 하는 운영체제 커널을 위해 사용된다.
* 일부 컴파일러는 완전히 컴파일하기 전에 고급 언어를 먼저 어셈블리로 번역하여, 디버깅 및 최적화 목적으로 어셈블리 코드를 볼 수 있게 한다.
* 파스칼이나 C와 같은 상대적으로 저수준인 언어의 일부 컴파일러는 프로그래머가 소스 코드에 어셈블리어를 직접 포함할 수 있게 한다(소위 인라인 어셈블리). 이러한 기능을 사용하는 프로그램은 각 하드웨어 플랫폼에서 서로 다른 어셈블리어를 사용하여 추상화를 구축할 수 있다. 시스템의 이식 가능한 코드는 균일한 인터페이스를 통해 이러한 프로세서 고유의 구성 요소를 사용할 수 있다.
* 어셈블리어는 역공학에 유용하다. 많은 프로그램이 기계어 형태로만 배포되는데, 이는 역어셈블러에 의해 어셈블리어로 번역하는 것은 간단하지만 역컴파일러를 통해 고급 언어로 번역하는 것은 더 어렵다. 인터랙티브 디스어셈블러와 같은 도구는 이러한 목적을 위해 역어셈블리를 광범위하게 사용한다. 이 기술은 해커가 상용 소프트웨어를 크랙하거나, 경쟁사가 경쟁 회사와 유사한 결과를 내는 소프트웨어를 생산하는 데 사용된다.
* 어셈블리어는 특히 처리 능력과 RAM이 제한된 초기 개인용 컴퓨터에서 실행 속도를 향상시키기 위해 사용된다.
* 어셈블러는 다른 코드에서 사용할 수 있도록 서식이 지정되고 주석이 달린 소스 코드로부터 고급 언어 오버헤드 없이 데이터 블록을 생성하는 데 사용될 수 있다.

## 같이 보기
* 어셈블러의 비교

## 내용주

## 각주

## 외부 링크
* 
*  리눅스 어셈블리어
*  NASM


분류:프로그래밍 언어 구현
분류:임베디드 시스템
분류:프로그래밍 언어 구현
분류:저급 프로그래밍 언어