조건부 명령어들 (cmp 명령어, 조건부 jump, loopz)

고급 언어의 조건부 문장이 저수준으로 구현된 코드로 어떻게 변환되는 지를 알아보자.

 

논리구조 특성을 구현할 때 어셈블리 언어를 어떻게 사용하는지 살펴볼건데,

cpu가 cmp 명령어와 프로세서 상태 플래그를 사용해서 명령어의 피연산자를 비교하므로, 전에 배웠던 상태 플래그를 잠깐 상기해보자.

 

 

 

 

cpu 플래그

Zero 플래그는 연산 결과가 0일 때 set 설정

 

Carry 플래그는 명령어가 대상 피연산자에 비해 너무 크거나  작을 때 set

 

Sign 플래그는 대상 피연산자가 음수이면 set, 대상 피연산자가 양수이면 clear상태로 set

 

명령어가 유효하지 않은 부호 있는 결과를 생성할 때 오버플로 플래그를 set
ex) 비트 7 캐리가 비트 6 캐리와 XOR됨

 

Parity 플래그는 명령어가 대상 피연산자의 하위 바이트에 짝수 개의 1비트를 생성할 때 set

 

Auxiliary Carry 플래그는 연산이 비트 3에서 비트 4로 가는 행동을 생성할 때 set

 

 

이러한 상태 플래그들은 bool 명령어를 사용할 때 영향을 받는다.

기본적인 복습을 했으니, 이제 각각의 명령어들이 수행될 때 어떤 작업이 일어나는지 알아보자.

 

 

 

명령어 소개

 

AND 명령어

 

두 피연산자에 대응되는 각 비트 쌍에 대해 bool AND 연산을 하고, 목적지 피연산자에 결과를 저장한다.

AND destination, source

 

 

두 비트가 모두 1이면 결과 비트는 1이고, 그렇지 않으면 결과 비트는 0이다.

 

AND 명령어는 다른 비트들에 영향을 주지 않고, 해당 비트들을 0으로 해제한다.

이런 기법을 비트 마스킹 masking이라고 한다.

페인트칠을 할 때 칠하지 말아야 할 부분에 마스킹 테이프를 붙이는 것과 같다.

 

 

예를 들어 아스키 코드를 비교해보면 대문자 A와 소문자 a는 비트 5만 다르다.

0 1 1 0 0 0 0 1 = 61h ('a')
0 1 0 0 0 0 0 1 = 41h ('A')

여기에 11011111 비트와 AND시킨다면 문자를 간단하게 대문자로 변환해줄 수 있다.

 

 

 

 

OR 명령어

 

두 피연산자에 대응되는 각 비트 쌍에 대해 bool OR 연산을 하고, 목적지 피연산자에 결과를 저장한다.

OR destination, source

 

두 피연산자의 대응되는 각 비트에 대해서 입력 비트 중 적어도 하나가 1일 때 출력 비트가 1이다.

 

OR 명령어는 다른 비트들에 영향을 주지 않고 1개 이상의 비트들을 1로 설정할 때 유용하다.

 

예를 들어 zero와 sign 플래그의 값을 OR 연산하여 AL의 값을 파악할 수 있다.,

zero 플래그 sign 플래그 AL의 값
0 0 0보다 크다
1 0 0과 같다
0 1 0보다 작다

 

 

 

 

XOR 명령어

 

두 피연산자에 대응되는 각 비트 쌍에 대해 비트단위로 XOR 연산을 하고, 목적지 피연산자에 결과를 저장한다.

 XOR destination, source

 

두 비트가 같으면 결과는 0이고, 그렇지 않으면 결과는 1이다.

 

XOR 명령어는 항상 overflow 와 carry 플래그를 0으로 설정한다.

 

 

 

 

NOT 명령어

 

피연산자의 모든 비트를 반대로 바꾼다. 결과는 1의 보수라고 부른다.

NOT destination

 

 

 

 

 

TEST 명령어

 

묵시적으로 두 피연산자에 대응되는 각 비트 쌍에 대해 AND 연산을 하고, 결과에 따라서 sign, zero, parity 플래그를 설정한다.

 

AND와 달리 TEST 명령어의 경우 목적지 피연산자를 수정하지 않는다. 하지만 플래그는 여전히 영향받는다.

test al,00000011b
 jnz  ValueFound

 

 

 

 

 

CMP 명령어

 

목적지 피연산자와 소스 피연산자를 비교한다. 대상 피연산자는 변경되지 않는다.

CMP destination, source

 

 

몇몇 예시를 알아보자.

 

목적지와 소스가 서로 같은 경우이다.

 mov al,5
 cmp al,5	; Zero flag set

 

 

목적지가 소스보다 작은 경우이다.

mov al,4
cmp al,5	; Carry flag set

 

 

destination이 source보다 큰 경우이다.

 mov al,6
 cmp al,5	; ZF = 0, CF = 0

 

 

아래 예시에서는 부호있는 정수를 사용한다.

목적지가 소스보다 큰 경우이다.

 mov al,5
 cmp al,-2	; Sign flag == Overflow flag

 

 

목적지가 소스보다 작은 경우이다.

 

 mov al,-1
 cmp al,5	; Sign flag != Overflow flag

 

 

 

 

 

조건부 Jump

 

위에서 소개했던 명령어들의 집합에는 고급 논리 구조는 없지만, 비교와 점프 명령어를 조합해서 고급 논리 구조를 구현할 수 있다. 

Jcond destination

 

Jcond 명령어는 특정 레지스터 또는 플래그 조건이 충족되는 경우 해당 레이블로 분기된다.

 

다음은 carry, zero, sign, overflow, parity 플래그에 따라 동작하는 명령어다.

 

ex) 더블워드 메모리 edi가 짝수일 경우 라벨 L2로 점프

 test DWORD PTR [edi],1
 jz   L2

 

 

 

아래는 플래그가 아닌 등호 결과에 따라서 점프하는 명령어다.

 

ex) ESI가 가리키는 메모리 워드가 0일 경우 라벨 L1으로 점프

 cmp WORD PTR [esi],0
 je  L1

 

 

 

부호 없는 비교에 따른 점프

 

ex) 부호없는 EAX가 EBX보다 큰 경우

cmp eax,ebx
 ja  Larger

 

 

 

부호 있는 비교에 따른 점프

 

ex) 부호 있는 EAX가 Val1보다 작거나 같으면 레이블 L1으로 이동

cmp eax,Val1
jle L1

 

 

배운 것을 바탕으로 다음은 루프와 XOR 명령어를 사용하여 문자열의 모든 문자를 암호화 시킬 수 있는 프로그램이다.

 KEY = 239		 ; can be any byte value
 BUFMAX = 128
 .data
 buffer  BYTE BUFMAX+1 DUP(0)
 bufSize DWORD BUFMAX
 .code	; loop counter
 mov ecx,bufSize	 ; index 0 in buffer
 mov esi,0
 L1:
 xor buffer[esi],KEY	 ; translate a byte
 inc esi		 ; point to next byte
 loop L1

 

 

 

 

 

조건부 Loop 명령어

 

LOOPZ 와 LOOPE 명령어

LOOPE destination
LOOPZ destination

이 명령어들은 ECX가 하나씩 줄어들며, ECX가 0보다 크고 ZF가 1이면 목적지로 점프한다.

주어진 값과 일치 하지 않는 첫 번째 요소에 대한 배열을 검색할 때 유용하다.

 

 

 

LOOPNZ  와 LOOPNE 명령어

LOOPNZ destination
LOOPNE destination

이 명령어들은 ECX가 하나씩 줄어들며, ECX가 0보다 크고 ZF가 0이면 목적지로 점프한다.

주어진 값과 일치하는 첫 번째 요소에 대해 배열을 검색할 때 유용하다.

 

 

LOOPNZ 예시)  배열에서 첫 번째 양수 값 찾기

 .data
 array SWORD -3,-6,-1,-10,10,30,40,4
 sentinel SWORD 0
 .code
 	mov esi,OFFSET array
 	mov ecx,LENGTHOF array
 next:
 	test WORD PTR [esi],8000h 	; test sign bit
 	pushfd				; push flags on stack
 	add esi,TYPE array
 	popfd				; pop flags from stack
 	loopnz next			 ; continue loop
 	jnz quit			; none found
 	sub esi,TYPE array	 	; ESI points to value
 quit: