어셈블리 조건분기
- 작성자
- 고친과정
2003년 9월 30일 : 처음씀
2. 개요
- 이 문서의 내용은 MASM, TASM의 명령어 기준으로 OP의 순서(부등호조건)를 가정합니다.
- AT&T 문법에서는 부등호 조건이 (방향만)반대라고 생각하시면 됩니다.
3. 조건분기 명령어 요약표
- 아래의 표에서 SF와 OF를 비교하는 조건은 부호를 식별한다는 것으로 생각하시면 됩니다.
- ZF는 상호 뺄셈을 하였을때 0인경우를 1로 봅니다. 즉, 값이 같을때 1이 됩니다.
- OF(Overflow)와 CF(Carry)의 차이를 혼돈하시는 분이 계실거 같은데 CF는 자리빌림/자리올림을 하는 경우이고 OF는 계산결과가 Sign bit가 반전될때 발생합니다.
어셈블리 조건분기 명령어 부등호 조건 Flag 조건 JMP 1(True) JA > CF=0 & ZF=0 JNBE > CF=0 & ZF=0 JAE >= CF=0 | ZF=1 JNB >= CF=0 | ZF=1 JB < CF=1 JNAE < CF=1 JBE <= CF=1 | ZF=1 JNA <= CF=1 | ZF=1 JCXZ CX == 0 . JE == ZF=1 JZ == ZF=1 JG > ZF=0 & SF == OF JNLE > ZF=0 & SF == OF JGE >= SF == OF JNL >= SF == OF JL < SF != OF JGNE < SF != OF JLE <= ZF == 1 | SF != OF JNG <= ZF == 1 | SF != OF JNE != ZF == 0 JNZ != ZF == 0 JNO No overflow OF == 0 JNP No parity (홀수) PF == 0 JPO No parity (홀수) PF == 0 JNS No sign (양수) SF == 0 JO Overflow OF == 1 JP Parity (작수) PF == 1 JS Sign (음수) SF == 1
4. 플래그 레지스터(Flag register)
- 16bits : FLAGS
- 32bits : EFLAGS
- 64bits : RFLAGS
플래그 레지스터(Flag register) Bit 플래그 기호 이름 의미 0 CF 캐리 플래그 (Carry flag) 부호 없는 숫자의 연산 결과가 비트 범위를 넘어섰을 때 참이 됩니다. (1=Carry, 0=No Carry) 1 - EFLAGS인 경우는 항상 1로 예약 2 PF 패리티 플래그 (Parity flag) 연산 결과에서 1로된 비트의 수가 짝수일 경우 참이 됩니다. (1=Even, 0=Odd) 3 - 예약 4 AF 보조 캐리 플래그 (Adjust flag) 연산 결과 하위 니블(4bits)에서 비트 범위를 넘어섰을 때 참이 된다. 이진화 십진법(BCD) 연산에 사용됩니다. (1=Auxiliary Carry, 0=No Auxiliary Carry) 5 - 예약 6 ZF 제로 플래그 (Zero flag) 연산 결과가 0일 경우에 참이 됩니다. (1=Zero, 0=Not Zero) 7 SF 사인 플래그 (Sign flag) 연산 결과가 음수일 때 참이 됩니다. (1=Negative, 0=Positive) 8 TF 트랩 플래그 (Trap flag) 참일 경우 한 명령이 실행할 때마다 인터럽트가 발생합니다. 디버깅시에 사용합니다. 9 IF 인터럽트 플래그 (Interrupt flag) 이 플래그가 참일 경우에만 인터럽트 요구를 받아들입니다. 일반적으로 관리자 모드에서만 값을 변경 할 수 있습니다. (1=Enable Interrupt, 0=Disable Interrupt) 10 DF 디렉션 플래그 (Direction flag) 문자열 조작에서 참일 경우 주소 레지스터 값이 자동으로 감소하고, 거짓일 경우 자동으로 증가합니다. (1=Down, 0=Up) 11 OF 오버플로 플래그 (Overflow flag) 부호 있는 숫자의 연산 결과가 비트 범위를 넘어섰을 때 참이 된다. (1=Overflow, 0=Not Overflow) 12 IOPL I/O privilege level (286+ only), always 1[clarification needed] on 8086 and 186
13 14 NT 8086과 186은 항상 1, 286+는 Nested task flag 15 - 8086과 186은 항상 1, 그 이후의 모델은 항상 0 16 RF Resume flag (386+ only) 17 VM Virtual 8086 mode flag (386+ only) 18 AC Alignment check/Access control (486SX+ only) 19 VIF Virtual interrupt flag (Pentium+) 20 VIP Virtual interrupt pending (Pentium+) 21 ID Able to use CPUID instruction (Pentium+) 22 - 예약 23 24 25 26 27 28 29 30 31 32 ~ 63 - 예약
5. 참고: SUB의 Carry 동작
질문
cmp 명령은 sub 명령과 거의 흡사하고 차이점이라면 cmp는 목적지 피연산자를 수정하지 않는다는 점이라고 알고 있고 뺄셈연산은 피연산자를 2의 보수로 취하여 덧셈하는것과 같다고 알고 있었다는 전제하에 아래의 경우는 모두 같은 결과를 내놓을줄 알았는데 문제는 Carry flag 가 CMP 그리고 SUB 와 ADD가 서로 다른 결과를 보이는 이유는 무었일까요?
첫번째, cmp 명령의 결과 Carry flag 는 0, AL=4
두번째, sub 명령의 결과 cmp와 동일하게 Carry flag 는 0, AL=3
세번째, add 명령의 결과 cmp, sub 와는 반대의 결과인 Carry flag는 1, AL=3
답변(해설)
SUB의 경우는 다음과 같이 계산되어 최상위 비트(MSB)에서는 자리내림이 발생하지 않아 Carry flag 는 0인 상태를 유지합니다.
하지만 ADD의 경우 다음과 같이 계산되어 최상위 비트(MSB)에서 자리올림이 발생하고 Carry flag가 1이 됩니다.
근데 이유는 간단합니다.
회로를 설계할때 보통 가산회로는 있으나 감산회로는 가산회로를 이용하여 설계를 하게 됩니다. (모든 회로가 그런것은 아니지만...)
그래서 SUB는 다음과 같이 구현하여 비교해 보아도 동일한 결과값을 출력하게 됩니다.
이것을 보정하기 위해서 덧셈연산방법에는 다음과 같은 구현조건을 회로에 추가하여 뺄셈시의 Carry flag 와 같도록 추가하였습니다.
즉, SUB는 다음과 같은 구현과 동일합니다. (SUB를 ADD연산으로 대체하여 표현할경우 Carry의 특성)
cmp 명령은 sub 명령과 거의 흡사하고 차이점이라면 cmp는 목적지 피연산자를 수정하지 않는다는 점이라고 알고 있고 뺄셈연산은 피연산자를 2의 보수로 취하여 덧셈하는것과 같다고 알고 있었다는 전제하에 아래의 경우는 모두 같은 결과를 내놓을줄 알았는데 문제는 Carry flag 가 CMP 그리고 SUB 와 ADD가 서로 다른 결과를 보이는 이유는 무었일까요?
첫번째, cmp 명령의 결과 Carry flag 는 0, AL=4
MOV AL, 4 CMP AL, 1
MOV AL, 4 SUB AL, 1
MOV AL, 4 ADD AL, 0FFH
SUB의 경우는 다음과 같이 계산되어 최상위 비트(MSB)에서는 자리내림이 발생하지 않아 Carry flag 는 0인 상태를 유지합니다.
0000 0100 - 0000 0001 ------------- 0 0000 0011
0000 0100 + 1111 1111 (1의 2의 보수) ------------- 1 0000 0011
회로를 설계할때 보통 가산회로는 있으나 감산회로는 가산회로를 이용하여 설계를 하게 됩니다. (모든 회로가 그런것은 아니지만...)
그래서 SUB는 다음과 같이 구현하여 비교해 보아도 동일한 결과값을 출력하게 됩니다.
- 소스 피연산자를 2의 보수로 취한다.
- 목적지 피연산자와 소스 피연산자를 덧셈한다.
이것을 보정하기 위해서 덧셈연산방법에는 다음과 같은 구현조건을 회로에 추가하여 뺄셈시의 Carry flag 와 같도록 추가하였습니다.
- 결과저장후 Carry flag는 반전시킨다.
즉, SUB는 다음과 같은 구현과 동일합니다. (SUB를 ADD연산으로 대체하여 표현할경우 Carry의 특성)
MOV AL, 4 /* 목적지 피연산자 */ MOV CL, 1 /* 소스 피연산자 */ NEG CL /* 2의 보수 */ ADD AL, CL /* 덧셈 */ CMC /* Carry flag 반전 */
6. 참고자료
- https://en.wikipedia.org/wiki/Status_register
- https://en.wikipedia.org/wiki/FLAGS_register
- https://thinkpro.tistory.com/137
- Carry flag는 unsigned integer overflow.
- Overflow flag는 signed integer overflow.
- https://m.blog.naver.com/PostView.nhn?blogId=window__han&logNo=80166632434&proxyReferer=https%3A%2F%2Fwww.google.com%2F
- Carry flag 가 설정되는 경우 2가지
- 두 수의 합으로 발생한 Carry가 부호비트보다 앞 비트에 더해질 때 set 됩니다.
- 두 수의 차로 발생한 Carry로 인해 부호비트 앞 비트로부터 borrow가 발생해야 할 때 set 됩니다.
- 나머지의 경우는 Carry flag는 clear 됨.
- Overflow flag 가 설정되는 경우 2가지
- 두 수의 합으로 부호비트가 set 되었을 때 Overflow flag 가 set 됩니다.
- 두 수의 합으로 부호비트가 clear 되었을 때 Overflow flag 가 set 됩니다.
- 나머지의 경우는 Overflow flag는 clear 됨.
- 음수와 양수의 덧셈은 결코 Overflow flag를 set 하지 않습니다.
- 두 양수를 더하여 음수가 되거나 두 음수를 더하여 양수가 되는 경우 set 됩니다.
- Carry flag 가 설정되는 경우 2가지