명령어 집합

번지 지정 방식(Addressing mode)

데이터를 전송하거나 특정 코드로 실행제어를 변경하려면 주소를 지정해야 한다. 기계어 속에서 여러가지 방식으로 주소를 지정한다.
명령어에서 번지를 지정하는 방식은 다양하게 여러가지가 존재한다. CPU마다 존재하는 것도 다르고, 어셈블리어에서 표현하는 방식도 다르다. 번지는 데이터의 위치를 지정할 때와 다음 실행할 명령어가 있는 위치를 지정하는 경우가 있다.

즉시 모드 (immediate mode)

명령어에 대상 데이터가 포함된다.

68000: moveq.l #$10,d0  ; 0x10을 D0에 넣는다.
x86:   mov  eax,10H     ; 0x10을 EAX에 넣는다.

   +------+-----+-----+----------------+
   | add  | reg1| reg2|    constant    |    reg1 := reg2 + constant;
   +------+-----+-----+----------------+

직접 지정(direct)

유효주소가 기계어의 OP 코드 다음에 연속되어 있다. 이 값을 주소로 지정하여 데이터의 위치를 결정한다.

   +------+-----+--------------------------------------+
   | load | reg |         address                      | 
   +------+-----+--------------------------------------+
   Effective address = address

x86:  mov  eax, [0x00040222h]

간접 지정(indirect)

기계어의 OP 코드 다음에 연속되어 오는 주소는 데이터의 주소 포인터 값이 된다. 따라서 OP 코드 다음의 주소를 읽어 주소 포인터를 결정하고, 다시 그 값을 주소값으로 한번 더 데이터를 읽는다.

   +------+-----+--------------------------------------+
   | load | reg |         address1                     | 
   +------+-----+-----------------+--------------------+
   Effective address = [address]  |
                                  |
                +-----------------v--------------------+
       address1 |            address2                  | 
                +-----------------+--------------------+
                                  |
                +-----------------v--------------------+
       address2 |              data                    | 
                +--------------------------------------+
x86 등 많은 CPU에서 지원하지 않는다.

레지스터 직접 지정 (register direct)

레지스터의 값이 데이터이다.

   +------+-----+-----+-----+
   | add  | reg1| reg2| reg3|      reg1 := reg2 + reg3;
   +------+-----+-----+-----+

레지스터 간접 지정 (register indirect)

레지스터의 값이 데이터의 위치인 포인터 주소값이다. 따라서 레지스터값을 주소값으로 하여 읽으면 대상 데이터가 된다.

   +------+------+-----+
   | load | reg1 | base| 
   +------+------+-----+
   Effective address = 베이스 레지스터(base register)

절대 주소 지정 (absolute/direct)

점프명령의 OP 코드 다음에 점프할 주소값 절대 주소값이다. 따라서 OP코드 다음에 주소를 읽어 PC에 넣으면 흐름이 변경된다.

   +-------+--------------------------------------+
   |  JMP  |         address                      | 
   +-------+--------------------------------------+
   Effective address = address(instruction 안에 존재)

프로그램 카운터 상대 지정 (Program Counter relative with displacement)

현재 PC에서 상태 옵셋을 더하여 주소를 결정한다. 따라서 현재 위치로 부터 상대적 위치가 된다.

   +----+------------------------------+
   |jump|           offset             |    jump relative
   +----+------------------------------+
   Effective PC address = next instruction address + offset (offset는 상대적을 음수일수도 있다)

레지스터 간접 지정 (register indirect)

+-------+-----+
   |jumpVia| reg | 
   +-------+-----+
   Effective PC address = 레지스터 reg의 값

ALU에 의한 연산 명령어

거의 모든 CPU는 산술연산은 사칙연산의 4가지를 지원하며, ALU가 정수형 계산을 한다. 나누기와 곱하기의 경우 계산 결과가 한개의 레지스터에 들어가는 것이 불가능하므로 2개의 레지스터를 사용하는 것이 일반적이다. 나누기의 경우는 결과가 몫과 나머지의 정수값으로 2개의 레지스터에 저장된다.
ALU을 통해 연산이 끝나면 CPU내의 FLAG 레지스터에 FLAG(C,Z,N,O) 결과 값이 저장되고 다음 명령어에 적용 시킬수 있다. 조건 점프 명령은 FLAG에 이미 설정된 값에 따라 조건이 판단된다. ALU와 관련된 명령어는 FLAG에 영향을 미친다.

   ; i386 경우

   mov   eax,10   ; EAX 레지스터에 10을 저장
   add   eax,2    ; EAX+2->EAX - ALU을 통해 연산하고 결과와 FLAG 저장
                  ; EAX는 10+2=>12으로 변경되고, FLAG Z=0, C=0, N=0, O=0으로 설정
   mov   ebx,eax

   xor   eax,eax  ; EAX을 클리어. 12 XOR 12 -> 0, FLAG Z=1, C=0, N=0, O=0으로 설정

더하기와 나누기

더하기와 나누기 명령어는 정수형 연산이다. 부호를 고려하여 signed와 unsigned 명령어가 존재한다. 연산을 할 때, 플래그 중 C(Cary)을 사용하는 경우도 있고 사용하지 않고 레지스터 값 만으로 연산하는 경우도 있다.

곱하기와 나누기

; i386 경우
   imul  bl     ; ax <- al * bl;                 8비트 signed
   imul  bx     ; dx:ax <- ax * bx;              16비트 signed
   imul  ebx    ; edx:eax <- eax * ebx;          32비트 signed
   mul   ebx    ; edx:eax <- eax * ebx;          32비트 unsigned
   idiv  ebx    ; edx <- eax % ebx; eax <-  eax / ebx; 32비트 signed
   div   ebx    ; edx <- eax % ebx; eax <-  eax / ebx; 32비트 unsigned

비교 명령어

연산 명령어 중 CMP(어셈블리어에 따라 표현은 다름) 명령어는 ALU 통해 계산을 하고, FLAG 결과 만을 FLAG레지스터에 저장하고 결과값을 버린다.
다음과 같은 개념으로 컴파일러 될 수도 있다(다음 코드는 컴파일러의 환경과 상황에 따라 다르므로 차이가 있을 수 있다):

 // C 언어
   int a;
   a = 10;

   if (a == 10)
       a++
   //...

이 경우 다음이 같은 개념으로 변환될 수 있다.
; x86 경우
   mov   eax,10   ; EAX 레지스터에 10을 저장
   cmp   eax,10   ; EAX-10, FLAG 저장 - ALU을 통해 비교를 위해 연산
                  ; EAX 가 10일 경우 10-10의 결과는 Z=1, C=0, N=0, O=0으로 FLAG 설정
                  ;   EAX  값 변경은 없음
   jne   JumpNext ; NE(Not Equal, Z=0일 경우 같지 않음으로 판단), CMP결과가 Z=1이므로 점프하지 않음.
   inc   eax      ; EAX  증가, EAX =11 -  a++ 실행 a = 11.
JumpNext:
   ...

; ARM의 경우
   CMP R1,#10
   BNE skipNext
   ADD R1, R1, #1  ; R1+1=>R1
skipNext:

또는
   CMP    R1,#10
   ADDEQ  R1, R1, #1

논리 연산 명령어

논리연산은 각 비트별로 AND, OR, NOT, XOR 등 논리연산을 수행한다.

시프트 명령어

레지스터의 값을 왼쪽 또는 오른쪽으로 비트 시프트한다. 플래그 중 C(Cary)을 사용하는 경우도 있고, 레지스터 값 만으로 연산하는 경우도 있다.
부호를 고려하여 왼쪽으로 시프트할 때, 부호 비트가 유지되는 경우도 있다.

조건 점프 명령어

ALU에 의한 연산결과에 따라 FLAG 레지스터 값에 따라 조건을 판단하고, 조건이 만족하면 점프한다. 따라서 조건명령어가 실행되기 전에 FLAG의 레지스터에 설정이 되어 있어야 한다.

특권 레벨 제어

CPU의 실행 권한을 설정한다. 저사양의 CPU에는 없는 기능이지만 고사양의 경우 CPU마다 여러 수준의 실행 상태를 유지한다. 실행 권한에 따라 실행될 수 있는 기계어가 제한되는 경우도 있다. 운영 체제에서 커널과 응용 프로그램 실행 시 설정된다.

x86 : 4레벨
68K : 2레벌 : 유저 레벨,  슈퍼바이저(supervisor) 레벨