ARM 어셈블리에서 CMP 명령은 어떻게 동작할까?
ARM에서 CMP 명령은 두 값을 비교하기 위해 사용되는 명령어입니다.
비교는 내부적으로 두 값을 뺄셈하여 결과의 상태를 확인하는 방식으로 이루어집니다.
예를 들어, 5 - 3 = 2는 양수이므로 5가 3보다 크다는 것을 알 수 있고, 3 - 5 = -2는 음수이므로 3이 5보다 작다는 것을 알 수 있습니다.
또한 5 - 5 = 0이므로 두 값이 동일하다는 것도 확인할 수 있습니다.
CMP 명령은 이러한 산술 원리를 이용하여 실제 계산 결과 대신, 결과의 상태만 CPSR에 기록합니다.
CPU 연산과 CPSR의 관계
ARM에서는 모든 산술 및 논리 연산의 상태가 CPSR에 저장됩니다.
CPSR의 최상위 4비트에는 N, Z, C, V 플래그가 위치하며, 계산 결과를 정리합니다.
연산 결과값 자체는 레지스터에 저장되며,
CPSR에는 그 결과의 상태만 저장됩니다.
CPSR에 대한 자세한 내용은 이 포스팅을 참고해 주세요.
CMP 명령
cmp Rn, Operand2
여기서 Rn은 반드시 레지스터여야 하며, Operand2는 시프트 피연산자(Shifter Operand)입니다.
앞서 설명한 것처럼 CMP는 실질적으로 뺄셈 연산(Rn - Operand2) 을 수행합니다.
하지만, 그 결과값을 Rn에 저장하지 않고, CPSR 플래그만 갱신합니다.
즉, cmp Rn, Operand2는 subs Rn, Rn, Operand2와 동일한 연산을 수행하지만 레지스터 값은 변경되지 않습니다.
CMP를 비롯한 몇몇 비교 명령어는 접미사s없이도CPSR을 자동으로 갱신하는 특별한 명령어입니다.
예제 코드
.text
.global _start
_start:
mov r0, #5
mov r1, #5
cmp r0, r1
beq equal
equal:
mov r2, #0xff
cmp r0, r1 실행 후 CPSR의 상태는 다음과 같습니다.
| CPSR 플래그 | 값 | 이유 |
|---|---|---|
| N | 0 | 5 - 5 ≥ 0 |
| Z | 1 | 5 - 5 = 0 |
| C | 1 | 빌림 없음 |
| V | 0 | 오버플로우 없음 |
CMP에서C플래그(Bit[29])는 부호 없는(unsigned) 비교용입니다.
빌림이 없으면C=1, 빌림이 발생하면C=0입니다.
디버깅
(gdb) i r cpsr
0x60000010
GDB에서 i r cpsr 명령을 실행하면 CMP로 인해 갱신된 CPSR값을 확인할 수 있습니다.
CPSR은 32비트 전체가 출력되지만, 우리가 관심 있는 영역은 최상위 4비트(N, Z, C, V) 입니다.
파이썬을 사용해 이 비트들을 쉽게 확인할 수 있습니다.
cpsr = 0x60000010
flags = (cpsr >> 28) & 0xF
print("{:04b}".format(flags))