cmp 와 반복문

1 minute read

comparison-based jump

cmp는 비교 연산이다.

cmp vleft, vright

이는 실제

sub vleft, vright

연산을 실행한 후, EFLAG 만 바꾸고 결과값을 저장하지 않는다.
우리는 이를 통해서 두 수를 비교할 수 있다.

unsigned 정수에서의 ZF 와 CF


왼쪽 == 오른쪽 이라면 ZF = 1, CF = 0 이다.
왼쪽 > 오른쪽이라면, ZF = 0, CF = 0 이다.
왼쪽 < 오른쪽이라면 ZF = 0, CF = 1 이다.

signed 정수에서의 ZF, OF, SF


왼쪽 == 오른쪽이라면 ZF = 1 이고 나머진 0 이다.
왼쪽 > 오른쪽이라면 ZF = 0 이고 OF 는 SF 와 같다.
왼쪽 < 오른쪽이라면 ZF = 0 이고 둘은 다르다.

이는 양쪽이 둘 다 음수거나 양수거나 아니면 다르거나, 4가지 경우의 수로 따져볼 수 있다.

비교 결과를 이용한 점프

앞서 cmp 명령어를 이용한 후 그에 따른 EFLAGS 에 따라 jump 를 하거나 안 할 수 있다.

예컨대, JE 는 둘이 같은 경우 jump 한다.
JNE 는 둘이 다른 경우 jump 한다.
JL 는 왼쪽이 더 작을 때, JG 는 왼쪽이 더 클 때 jump 한다. (L : Less than, G : Greater than)
JL 은 JNGE 와 같다. 역시 JG 는 JNLE 와 같다.

위는 signed 에서의 비교이고, unsigned 는 약간 다르다.
unsigned 에선 JE - JNE 대신 JZ - JNZ 를 쓴다.
Less 대신 Below 라는 표현을, Greater 대신 Above 를 쓴다.
당연한 이유인 것이, signed 냐 unsigned 냐에 따라 취급하는 EFLAG 들이 다르기 때문이다.

cmp, jmp 를 이용한 분기

if (~~~~)
  zzzz;

else 
  xxxx;

위와 같은 분기문을 nasm 으로 작성하면,

      cmp AAA, BBB
      jne ELSE
      ZZZZZZZ
      .....
ELSE: 
      XXXXXXX

와 같이 될 것이다.

또한 while 같은 분기문도

while:  
        cmp AAA, BBB
        je  endwhile
        ~~~~
        cmp AAA, BBB
        jne while
        
endwhile: ...

와 같이 cmp 에 while 문을 종결할 수 있는 조건을 물어보고
아니라면 위로 점프하고 아니면 안 할 수도 있다.

do while 을 구현한다면 위에 cmp ~ je endwhile 을 지우면 될 것이다.

또한 범용 레지스터 ECX 의 본래 목적에 맞게 이를 iterator 로 사용한 loop 명령어도 존재한다.

       MOV  ECX, 10
LABEL: 
       ADD  EAX, EBX
       LOOP LABEL

loop 명령어는 ECX 를 1씩 감소시키면서 0 이 아니라면 해당 라벨로 점프한다.
따라서 위 코드는 EAX에 EBX * 10 을 더하는 코드가 된다.

추가로, loope 는 ZF 가 1 이어야 한다는 조건이 추가되고,
loopne 는 ZF 가 0 이어야 한다는 조건이 추가된다.