필요할 때만 점프하기

jmp 명령이 무조건으로 점프를 하는데 어떤 상태가 되었을 때만 점프를 하는 명령어도 있습니다. 3종류가 있는데 하나는 프로세서의 상태 비트를 확인해서 점프를 하는 것이고 두번째는 부호가 있는 숫자를 비교해서 점프를 하는 것이고 마지막은 부호없는 숫자의 비교 결과로 점프를 하는 것입니다.

플래그 상태를 보고 점프하기

다음 표는 프로세서 상태 플래그에 따라서 점프를 하는 명령어들의 표입니다. 그런데 어떤 명령어들의 설명을 보면 0일때 혹은 같을때라는 설명이 있습니다. 같은 것과 0인게 무슨 관계이길래 이거나 저거나 같다는 설명을 할까요?

몇번째인지도 모르겠지만 또 프로세서는 모든걸 숫자로만 본다는 이야기를 하게됩니다. 숫자가 같다와 0이다를 보면 딱 생각나는게 있지 않나요? 바로 빼기입니다. 숫자의 비교라는 것은 결국 두 숫자를 뺀다는 것입니다. 예를 들어 루프를 10번 하려고할때 cx에 0을 넣고 cx를 하나씩 증가시킵니다. 그리고 cx값이 10인지 확인하는대 빼기 연산을 한다는 것입니다. 빼기를 하다보면 zf 플래그가 1이 되는 순간이 있겠지요 그러면 cx 값이 10이라는걸 알 수 있게 됩니다.

물론 sub cx, 10 명령으로 빼기를 하면 cx 값이 바뀌겠지요 그래서 빼기는 하되 값은 안바꾸는 cmp 명령도 있습니다. 어쨌든 sub, xor 같이 레지스터 값을 0으로 만드는 연산을 하게되면 zf 플래그가 1이 됩니다. 그래서 루프를 다 돌았는지, 원하는 결과값이 나왔는지 안나왔는지 등을 확인할 수 있게 됩니다. 그리고 그런 상황에 따라 점프를 하게 되는 것이지요. 루프를 다 안돌았으면 다시 루프의 시작 명령으로 점프를 하면 되고, 원하는 값이 안나왔으면 에러 처리로 점프하면 되는 것입니다.

명령어 설명 조건 반대명령어
jz, je 0일때(같을때) 점프 zf=1 jnz,jne
jc, jb, jnae 각각 캐리가 생겼을때, 작을때, 크거나 같지 않을때 점프 cf=1 jnc, jnb, jae
js 계산 결과 부호 비트가 켜졌을 때 점프 sf=1 jns
jo 오버플로우가 발생했을 때 점프 of=1 jno
jpe, jp Parity Even 상태가 되었을 때 점프 pf=1 jpo
jnz, jne 0이 아니거나 같지 않을 때 점프 zf=0 jz, je
jnc, jnb, jae 캐리가 없을때, 작지 않을때, 크거나 같을 때 점프 cf=0 jc, jb, jnae
jns 부호 비트가 0일 때 점프 sf=0 js
jno 오버플로우가 아닐 때 점프 of=0 jo
jpo, jnp Parity Odd 상태일 때 점프 pf=0 jpe, jp

크거나 같지 않다는 것은 작다는 것이고 작지 않다는 것은 크거나 같다는 것입니다. 필요에 따라 어떤 상황을 쓸것인지 결정하면 됩니다.

또 jz, je 같이 이름은 다르지만 같은 일을 하는 명령어가 있습니다. 이런 명령어들은 기계어도 같습니다. 그래서 내가 코드에 jz를 써놨어도 에뮬레이터를 보면 je로 표시될 수도 있고 그 반대일 수도 있습니다. jc와 jb도 같은 명령이지요. 이렇게 같은 일을 하지만 다른 명령어들을 만들면 프로그래머가 코드를 이해하기 쉬워집니다. 하지만 에뮬레이터나 디스어셈블러 같은 툴들은 같은 기계 코드를 만나므로 같은 명령어중에 어떤걸 의도했는지 모릅니다.

다음 코드를 에물레이터로 실행해보면 모두 jnb 명령으로 어셈블되는 것을 볼 수 있습니다. jnb 명령의 기계 코드는 2바이트로 제한되어있는데 jnb 명령 자체가 73h로 1바이트입니다. 따라서 주소를 표현할 수 있는 자리가 1바이트만 남으므로 점프할 수 있는 범위가 1바이트가 됩니다. 1바이트라면 부호를 고려했을 때 -128~127이므로 명령어가 있는 위치로부터 위로 128바이트까지 되돌아 올라갈 수 있고 127바이트까지 아래로 내려갈 수 있게 됩니다.

즉 이번에 설명된 점프 명령들은 16비트 점프가 아닌 8비트 점프들입니다.

jnc a  
   jnb a  
   jae a

mov ax, 4  
   a: mov ax, 5  
   ret

만약 프로그램이 너무 길어져서 128바이트 범위를 벗어난 지점으로 점프하고 싶으면 어떻게 해야될까요? 이 예제를 실행해서 에물레이터에 어떻게 어셈블되었는지 확인해보면 알 수 있습니다. 현대 프로그래밍 환경에서는 전혀 필요없는 내용이므로 따로 설명하지는 않겠습니다. 궁금하면 한번 돌려보세요. 참고로 ARM같은 최신 프로세서도 RISC라는 특성때문에 점프할 수 있는 범위가 작습니다. 그래서 다양한 해결 방법들이 있습니다만 컴파일러가 알아서 해주는 것이지요. 운영체제나 드라이버를 개발해야할때만 그런 특성을 고려하게됩니다.

jz a  
jb a  
jnc a  
c DB 128 DUP\(9\)  
a:  
ret

부호있는 숫자를 비교해서 점프하기

1바이트의 숫자를 비교할 때 255와 10중에 어느게 더 큰가는 부호를 따지느냐 아니냐에 따라 다릅니다. 부호를 고려한다면 255는 -1이고 10은 10이므로 10이 더 큰 것이고 부호를 뺀다면 255가 더 크게 됩니다. 따라서 점프 명령도 빼기를 하므로 부호가 있냐 없냐가 중요하게 됩니다.

부호있는 숫자들을 비교해서 점프하는 명령어는 다음 테이블에 있습니다.

명령어 설명 조건 반대 명령어
JE , JZ Jump if Equal (=). Jump if Zero. ZF = 1 JNE, JNZ
JNE , JNZ Jump if Not Equal (<>). Jump if Not Zero. ZF = 0 JE, JZ
JG , JNLE Jump if Greater (>). Jump if Not Less or Equal (not <=). ZF = 0 and SF = OF JNG, JLE
JL , JNGE Jump if Less (<). Jump if Not Greater or Equal (not >=). SF <> OF JNL, JGE
JGE , JNL Jump if Greater or Equal (>=). Jump if Not Less (not <). SF = OF JNGE, JL
JLE , JNG Jump if Less or Equal (<=). Jump if Not Greater (not >). ZF = 1 or SF <> OF JNLE, JG

<>는 같지 않다는 표시입니다. 별로 설명하게 없습니다. 크냐 작냐 뿐이니까요. 참고로 크지 않다는 작거나 같다는 것이고 작지 않다는 크거나 같다는 것입니다. > 의 반대는 <= 이라는거 당연하지만 가끔 =을 빼먹으면 골치아픈 버그가 생깁니다.

부호없는 숫자를 비교해서 점프하기

명령어 설명 조건 반대 명령어
JE , JZ Jump if Equal (=). Jump if Zero. ZF = 1 JNE, JNZ
JNE , JNZ Jump if Not Equal (<>). Jump if Not Zero. ZF = 0 JE, JZ
JA , JNBE Jump if Above (>). Jump if Not Below or Equal (not <=). CF = 0 and ZF = 0 JNA, JBE
JB , JNAE, JC Jump if Below (<). Jump if Not Above or Equal (not >=). Jump if Carry. CF = 1 JNB, JAE, JNC
JAE , JNB, JNC Jump if Above or Equal (>=). Jump if Not Below (not <). Jump if Not Carry. CF = 0 JNAE, JB
JBE , JNA Jump if Below or Equal (<=). Jump if Not Above (not >). CF = 1 or ZF = 1 JNBE, JA

이정도로 점프 명령어는 마무리하겠습니다.

results matching ""

    No results matching ""