어셈블리 조건분기

개요

  • 이 문서의 내용은 MASM, TASM의 명령어 기준으로 OP의 순서(부등호조건)를 가정합니다.
  • AT&T 문법에서는 부등호 조건이 (방향만)반대라고 생각하시면 됩니다.

조건분기 명령어 요약표

  • 아래의 표에서 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

플래그 레지스터(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 (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 - 예약

참고: SUB의 Carry 동작

질문

cmp 명령은 sub 명령과 거의 흡사하고 차이점이라면 cmp는 목적지 피연산자를 수정하지 않는다는 점이라고 알고 있고 뺄셈연산은 피연산자를 2의 보수로 취하여 덧셈하는것과 같다고 알고 있었다는 전제하에 아래의 경우는 모두 같은 결과를 내놓을줄 알았는데 문제는 Carry flag 가 CMP 그리고 SUB 와 ADD가 서로 다른 결과를 보이는 이유는 무었일까요?

첫번째, cmp 명령의 결과 Carry flag 는 0, AL=4
MOV AL, 4
CMP AL, 1


두번째, sub 명령의 결과 cmp와 동일하게 Carry flag 는 0, AL=3
MOV AL, 4
SUB AL, 1


세번째, add 명령의 결과 cmp, sub 와는 반대의 결과인 Carry flag는 1, AL=3
MOV AL, 4
ADD AL, 0FFH


답변(해설)

SUB의 경우는 다음과 같이 계산되어 최상위 비트(MSB)에서는 자리내림이 발생하지 않아 Carry flag 는 0인 상태를 유지합니다.
  0000 0100
- 0000 0001
-------------
0 0000 0011


하지만 ADD의 경우 다음과 같이 계산되어 최상위 비트(MSB)에서 자리올림이 발생하고 Carry flag가 1이 됩니다.
  0000 0100
+ 1111 1111 (1의 2의 보수)
-------------
1 0000 0011


근데 이유는 간단합니다.

회로를 설계할때 보통 가산회로는 있으나 감산회로는 가산회로를 이용하여 설계를 하게 됩니다. (모든 회로가 그런것은 아니지만...)

그래서 SUB는 다음과 같이 구현하여 비교해 보아도 동일한 결과값을 출력하게 됩니다.
  • 소스 피연산자를 2의 보수로 취한다.
  • 목적지 피연산자와 소스 피연산자를 덧셈한다.

결과값으로는 동일한 결과를 수행하기는 했습니다. 하지만 Carry flag 는 두 결과가 반대의 결과를 보이게 됩니다.

이것을 보정하기 위해서 덧셈연산방법에는 다음과 같은 구현조건을 회로에 추가하여 뺄셈시의 Carry flag 와 같도록 추가하였습니다.
  • 결과저장후 Carry flag는 반전시킨다.

이 조건구현으로 감산회로를 가산회로의 조합으로 구현할수 있게 되었답니다.

즉, SUB는 다음과 같은 구현과 동일합니다. (SUB를 ADD연산으로 대체하여 표현할경우 Carry의 특성)
MOV AL, 4 /* 목적지 피연산자 */
MOV CL, 1 /* 소스 피연산자 */
NEG CL /* 2의 보수 */
ADD AL, CL /* 덧셈 */
CMC /* Carry flag 반전 */


참고자료




/*
[ FrontPage | PrintView | RawView | RSS ]

Copyright ⓒ MINZKN.COM
All Rights Reserved.

MINZKN
*/