Chapter 3 애플리케이션 인스트럭션 세트(Applications Instruction Set)
이번 장은 보호된 가상주소 모드로 동작중인 80386에서 실행되는 애플리케이션을 제작하는 프로그래머들이 사용할 인스트럭션을 개괄적으로 보여준다. 인스트럭션은 연관 함수들끼리 카테고리로 분류한다.
본 장에서 설명하지 않는 인스트럭션들은 대개 OS 프로그래머들만이 사용하는 것들이다. 파트 II에서 이러한 인스트럭션의 기능을 설명한다.
이 장에서 설명하는 것들은 사실상 32비트 주소의 보호모드에서 동작중인 80386에 관한 내용이다; 하지만 설명하는 모든 인스트럭션은 또한 16비트 주소에서 보호모드, 실주소모드 혹은 가상 8086모드 모두에서 유효하다. 이처럼 다양한 모드에서 차이점은 Chapter 13, 14, 15를 참조하라.
Chapter 17 인스트럭션 사전에서 인코딩, 동작, 타이밍, 플래그 효과를 포함해서 모든 인스트럭션을 보다 상세히 설명한다.
3.1 데이터 이동 인스트럭션(Data Movement Instructions)
이러한 인스트럭션은 바이트, 워드 혹은 더블워드 데이터의 메모리와 기본 레지스터간 이동시에 편리하게 사용되는 방법이다.
1. 일반 목적의 데이터 이동 인스트럭션2. 스택 조작 인스트럭션3. 타입 전환 인스트럭션
3.1.1 일반 목적의 데이터 이동 인스트럭션
MOV (Move) 는 바이트, 워드 혹은 더블워드를 원본 피연산자에서 수신지 피연산자로 전송한다. MOV 인스트럭션은 다음 경로를 통해서 데이터를 전송하는데 유용하다. 또한 세그먼트 레지스터를 조작할 때도 다양한 MOV가 있다. 이러한 것들은 본 장의 마지막 섹션에서 다룬다.:
l 메모리에서 레지스터로l 레지스터에서 메모리로l 범용 레지스터간l 즉시 데이터에서 레지스터로l 즉시 데이터에서 메모리로
MOV 인스트럭션은 메모리에서 메모리로의 이동이 불가능하고 혹은 세그먼트 레지스터에서 세그먼트 레지스터로의 이동은 허용되지 않는다. 메모리에서 메모리로의 이동은 가능하지만 문자열 이동 인스트럭션은 MOVS 이다.
XCHG (Exchange) 는 두 피연산자 간의 내용을 바꾼다. 이 인스트럭션으로 세개의 MOV 인스트럭션을 대신한다. 한 피연산자가 로딩되는 동안에 다른 피연산자의 내용을 저장하기 위한 임시 장소가 필요없다. XCHG 는 특히 세마포어나 동기화 처리에 필요한 이와 비슷한 데이터 구조에 유용하다.
XCHG 인스트럭션은 두 개의 바이트, 두 개의 워드 혹은 두 개의 더블워드 피연산자를 교환할 수 있다. XCHG 인스트럭션에 사용되는 피연산자들은 두 레지스터 피연산자나 레지스터 피연산자와 메모리 피연산자가 될 수 있다. 메모리 피연산자와 사용될 때, XCHG 는 자동으로 LOCK 신호를 활성화시킨다. (버스 잠금에 관해서는 Chapter 11을 참조하라.)
3.1.2 스택 조작 인스트럭션
PUSH (Push) 는 스택 포인터(ESP)를 감소시키고 원본 피연산자를 ESP가 지시하는 스택의 맨 상위에 놓는다(그림 3-1 참조). PUSH는 프로시져를 호출하기 전에 스택에 파라미터를 복사라는 데도 종종 사용된다; 그것은 또한 스택에 임시 변수를 저장하는 기본적인 수단이기도 하다. PUSH 인스트럭션은 메모리 피연산자, 즉시 피연산자, 레지스터 피연산자(세그먼트 레지스터 포함)들간에 사용된다.
PUSHA (Push All Registers)는 8개의 범용 레지스터(그림 3-2 참조) 값들을 스택에 저장한다. 이렇게 해서 프로시져에서 사용할 범용 레지스터 내용들을 얻어 오기 위해 필요한 인스트럭션 수가 감소하고 프로시져 호출이 단순화한다. 프로세서는 다음과 같은 순으로 범용 레지스터 값들을 스택에 넣는다: EAX, ECX, EDX, EBX, EAX를 넣기 전 ESP의 초기 값, EBP, ESI, 그리고 EDI. PUSHA 는 POPA 인스트럭션과 함께 쓰인다.
POP (Pop) 은 현재 취상위 스택(ESP가 가리키는)에서 워드나 더블워드를 가져오고 ESP를 스택의 새로운 상위를 가리키도록 감소시킨다. 그림 3-3 참조. POP은 스택에서 범용 레지스터, 혹은 메모리로 정보를 옮긴다. 세그먼트 레지스터를 조작하는 POP의 변형형태도 또한 많이 있다. 이것은 이 장의 나중에 다룬다.
POPA (Pop All Registers)는 PUSHA에 의해 스택에 저장된 레지스터값들을 ESP값만 제외하고 복원한다. 그림 3-4 참조.
그림 3-1 PUSH
그림 3-2 PUSHA
3.1.3 타입 변환 인스트럭션
타입 변환 인스트럭션은 바이트를 워드로, 워드를 더블워드로, 그리고 더블워드를 64비트 항목(quad-words)으로 변환한다. 이러한 인스트럭션은 특히 부호 있는 정수를 변환하는 데 유용하다. 왜냐하면 부호가 있는 비트가 작은 항목의 부호가 보다 큰 항목의 여분의 비트를 자동으로 채우기 때문이다. 그림 3-5에서처럼 이것을 부호 확장(sign extention)이라 한다.
타입 변환 인스트럭션에는 두 부류가 있다.
1. CWD, CDQ, CBW, 그리고 CWDE 같은 형태로써 오직 EAX 레지스터에 있는 데이터만 조작한다.2. MOVSX 와 MOVZX 로써 하나의 피연산자가 범용 레지스터에 있고 다른 피연산자는 메모리나 레지스터에 있을 수 있다.
CWD(Convert Word to Doubleword)와 CDQ(Convert Doubleword to Quad-Word)는 원본 피연산자의 길이를 두배로 늘린다. CWD는 DX 레지스터를 통해 AX 레지스터의 워드 부호를 확장한다. CDQ는 EDX를 통해서 EAX에 있는 더블워드의 부호를 확장한다. CWD는 워드 나눗셈 전에 워드로 나눈 더블워드를 생성할 때 사용하고 CDQ는 더블워드 나눗셈 전에 더블워드로 나눈 쿼드워드(quad-word)를 만들 때 사용한다.
CBW(Convert Byte to Word)는 AX를 통해서 AL에 있는 부호 있는 바이트를 확장한다.
CWDR(Convert Word to Doubleword Extended)는 EAX 레지스터를 통해서 AX 레지스터에 있는 부호 있는 워드를 생성한다.
MOVSX(Move with Sign Extention)은 8비트 값을 16비트 값으로, 16비트 값을 32비트 값으로 부호를 적용해서 확장한다.
MOVZX(Move with Zero Extention)은 높은 자리를 0으로 삽입해서 8비트 값을 16비트값으로 16비트 값을 32비트 값으로 확장한다.
그림 3-3 POP
그림 3-4 POPA
그림 3-5 부호 확장
3.2 이진 대수 인스트럭션(Binary Arithmetic Instructions)
80386 프로세서의 대수 인스트럭션으로 이진수로 인코딩된 숫자 데이터 조작을 간단히 할 수가 있다. 기능은 표준 덧셈, 뺼셈, 곱셈, 나눗셈 뿐만 아니라 증가, 감소, 비교, 부호 전환도 포함된다. 부호 있는 이진수와 없는 이진 정수 모두 지원된다. 이진 대수 인스트럭션은 또한 십진 정수의 대수를 수행하는 과정에서 첫걸음으로도 사용된다.
많은 대수 인스트럭션은 부호 있는 정수와 없는 정수 모두에 동작한다. 이러한 인스트럭션은 잇따르는 인스트럭션이 부호 있는 혹은 부호가 없는지 대수 연산 결과값을 해석할 수 있도록 ZF, CF, SF 그리고 OF 플래그를 갱신한다. CF에는 부호 없는 정수와 관련한 정보가 포함되어 있다; SF 와 OF는 부호 있는 정수와 관련된 정보를 포함한다. ZF는 부호 있는 정수, 없는 정수 모두에 관여한다; ZF는 결과 값의 모든 비트가 0일 때 셋팅된다.
만일 정수가 부호가 없다면, 이러한 대수 연산 이후에 수신지 피연산자에서 한 비트의 자릿수 올림(carry)할 지 높은 자리에서 빌려(borrow)올 지 결정하기 위해 이러한 대수 연산 이후에 CF 가 테스트될 것이다. CF는 한 비트가 상위 자릿수로 올림될 때(덧셈 인스트럭션 ADD, ADC, AAA 그리고 DAA)나 한 비트가 높은 자리 비트로부터 꿔올 때(뺼셈 인스트력션 SUB, SBB, AAS, DAS, CMP 그리고 NEG) 셋팅된다.
만일 정수가 부호가 있다면, SF와 OF 모두 테스팅된다. SF는 항상 결과 값의 부호로써 동일한 값을 가진다. 부호 있는 정수의 가장 중요한 비트(MSB)는 부호 다음 바이트의 비트 6, 워드의 비트 14, 더블워드의 비트 30이다. OF는 다음의 경우 중의 하나에서 셋팅된다.
l 한 비트가 MSB로부터 자릿수 올림되지만 어느 비트도 부호 비트로 올림은 발생하지 않을 때(덧셈 인스트럭션 ADD, ADC, INC, AAA 그리고 DAA). 다른 말로 하면, 결과 값이 수신지 피연산자에 저장되어야 할 가장 큰 양수보다 더 큰 결과일 경우이다.l 뷰호비트에서 MSB로 한 비트 빌림이 발생하지만 어느 한 비트도 부호 비트로 빌릴 수 없을 때(뺄셈 인스트럭션 SUB, SBB, DEC, AAS, DAS, CMP 그리고 NEG). 다른 말로 하면 결과 값이 수신지에 저장될 값이 가장 작은 음수보다 더 작을 때.
이러한 상태 플래그는 두 조건부 인스트럭션중 하나를 실행함으로써 테스팅된다: Jcc(jimp on condition cc) 나 SETcc (byte set on condition)
3.2.1 덧셈과 뺄셈 인스트럭션
ADD(Add Integers) 는 수신지 피연산자를 원본과 수신지 피연산자의 합으로 교체한다. 오버플로일 경우 CF가 셋팅된다.
ADC(Add Integers with Carry)는 피연산자들을 합하고 CF가 셋팅되어 있으면 하나를 더해서 수신지 피연산자에 결과값으로 교체한다. 만일 CF가 지워지면 ADC는 ADD 인스트럭션과 동일한 기능을 수행한다. ADD 뒤에 여러 개의 ADC 인스트럭션이 뒤따른다면 32비트보다 큰 정수를 더하는 데 사용할 수 있다.
INC(Increment)는 수신지 피연산자에 하나를 더한다. INC는 CF에 영향을 주지 않는다. 캐리(CF)를 업데이트시킬 증가라면 ADD를 사용해서 1을 더하는 방법을 사용하라.
SUB(Subtract Integers)는 수신지 피연산자에서 원본 피연산자를 뺴서 결과값을 수신지 피연산자와 교체하는 것이다. 만일 빌림이 필요하면 CF가 셋팅된다. 피연산자들은 부호가 있을 수도 있고 부호가 없을 수도 있는 바이트, 워드, 더블워드들이다.
SBB(Subtract Integers with Borrow)는 수신지 피연산자로부터 원본 피연산자를 빼서 CF가 셋팅되어 있으면 1을 뺀다음 결과 값을 수신지 피연산자에 반환한다. CF가 비어 있으면 SBB는 SUB와 동일한 기능을 수행한다. SUB에 여러 SBB가 잇따른다면 32비트보다 더 큰 숫자를 뺼셈하는 데 사용할 수 있다.
DEC(Decrement)는 수신지 피연산자에서 1을 감소시킨다. DEC는 CF를 갱신하지 않는다. 캐리에 영향을 주는 감소를 수행하려면 SUB를 사용해서 1을 감소시키는 것이 좋다.
3.2.2 비교와 부호 전환 인스트럭션
CMP(Compare) 는 수신지 피연산자에서 원본 피연산자를 뺀다. 그리고 OF, SF, ZF, AF, PF 그리고 CF를 갱신하지만 원본 피연산자와 수신지 피연산자를 변경하진 않는다. 이어서 Jcc 혹은 SETcc 인스트럭션으로 플래그를 적절히 테스팅할 수 있다.
NEG(Negate)는 0에서 부호가 이쓴 정수 피연산자를 뺀다. NEG는 피연산자를 양수에서 음수, 혹은 음수에서 양수로 부호를 바꾸는 효과가 있다.
3.2.3 곱셈 인스트럭션
80386 에는 피연산자가 부호있을 때와 없을 때 별도의 곱셈 인스트럭션이 있다. MUL 은 부호가 없는 숫자에 사용하고 IMUL은 부호가 있을 때 없을 때 모두 사용한다.
MUL(Unsigned Integer Multiply) 는 원본 피연산자와 누산기(accumulator)에 부호 없는 곱셈을 수행한다. 만일 원본이 바이트라면 프로세서는 AL의 값만큼 곱한 다음에 더블(double) 길이의 결과를 AH와 AL에 반환한다. 만일 소스 피연산자가 워드라면 프로세서는 AX 값만큼 곱해서 더블 데이터 길이의 결과를 DX와 AX에 반환한다. 만일 원본 피연산자가 더블워드라면 EAX의 값을 곱해서 EDX와 EAX에 64비트 결과값을 반환한다. MUL은 상위 절반이 0이 아니면 CF와 OF를 셋팅한다.; 그 외에는 이것들은 비워진다.
IMUL(Signed Integer Multiply)는 부호 있는 곱셈 기능을 수행한다. IMUL은 세가지로 나뉠 수 있다.
1. 피연산자가 한 개인 형태. 피연산자는 바이트, 워드 혹은 더블워드가 될 수 있고 범용 레지스터나 메모리에 상주한다. 이 인스트럭션은 내부적으로 피연산자로써 MUL 인스트럭션과 동일한 방식으로 EAX와 EDX를 사용한다.
2. 두개의 피연산자 형태. 원본 피연산자 중 하나는 임의의 범용 레지스터에 있고, 다른 하나는 메모리나 범용 레지스터에 있을 수 있다. 결과물은 범용 레지스터에 있는 피연산자 값을 교체한다.
3. 세개의 피연산자 형태; 둘은 원본이고 하나는 수신지 피연산자이다. 원본 피연산자 중에 하나는 인스트럭션에 의해서 저장된 즉각적인 값(immediate value)이다; 두 번째 것은 메모리나 임의의 범용 레지스터에 있다. 결과값은 임의의 범용 레지스터에 저장될 것이다. 즉시 피연산자는 부호가 있는 것으로 간주한다. 만일 즉시 피연산자가 바이트라면 프로세서는 곱셈을 하기 전에 자동으로 부호 확장을 통해서 두 번째 피연산자의 크기를 늘릴 것이다.
세 형태는 대부분의 경우에 비슷하다.
l 결과 값의 길이는 피연산자 길이의 두배로 계산된다 .l 중요한 비트가 결과 값의 높은 자리수 절반으로 올림될 때, CF와 OF 플래그가 셋팅된다. 결과 값의 높은 자리수 절반이 낮은 자리수 절반의 부호 확장(sign-extention)일 때, CF와 OF는 비워진다.
그러나 두번 째와 3번째 형식은 결과물이 수신지 피연산자에 저장되기 전에 피연산자들의 길이에 맞게 절단된다는 점에서 다르다. 이러한 절삭 때문에, OF는 중요한 비트가 소실되었는지 확인하기 위해 테스팅된다. (OF를 테스팅하는 방법에 관해서는 INTO와 PUSHF 인스트럭션을 참조하라)
IMUL의 형식 2와 3도 마찬가지로 부호 없는 피연산자들에 사용된다. 왜냐하면 피연산자들이 부호가 있던 없던 간에, 결과 값의 낮은 자릿수 절반이 동일하기 때문이다.
3.2.4 나눗셈 인스트럭션
80386에는 피연산자가 부호가 있느냐 없느냐에 따라서 서로 다른 나눗셈 인스트럭션이 있다. DIV는 부호가 없는 숫자에 사용된다. 반면에 IDIV는 부호가 없는 것뿐만 아니라 부호가 있는 것에도 사용된다. 어느 경우이든 간에 예외(0 인터럽트)는 분모가 0 이거나 몫이 AL, AX 혹은 EAX에 저장하기에 너무 크면 발생한다.
DIV(Unsigned Integer Divide)는 원본 피연산자로 누산기의 부호없는 나눗셈을 수행하는 데 사용한다. 피젯수(dividend, 누산기)는 젯수(원본 피연산자) 길이의 두배이다; 몫과 나머지는 아래처럼 젯수와 동일한 길이를 갖는다.
| 원본 피연산자(젯수)의 길이 | 피젯수 | 나머지 | 몫 |
| 바이트 | AX | AL | AH |
| 워드 | DX:AX | AX | DX |
| 더블워드 | EDX:EAX | EAX | EDX |
정수가 아닌 몫은 0 에 가까운 정수로 절삭된다. 나머지는 항상 젯수보다 작다. 부호 없는 바이트를 나눌 경우, 가장 큰 몫은 255이다. 부호 없는 워드의 경우에 가장 큰 몫은 65,535이다. 부호 없는 더블워드의 경우에 가장 큰 몫은 2^32 - 1이다.
IDIV(Signed Integer Divide)는 원본 피연산자로 누산기를 부호가 있는 나눗셈을 수행한다. IDIV는 DIV인스트럭션이 사용하는 것과 동일한 레지스터를 사용한다.
부호가 있는 바이트 나눗셈의 경우, 가장 큰 양의 몫은 +127이고 최소 음의 몫은 -128이다. 부호가 있는 워드 나눗셈의 경우에 가장 큰 양의 몫은 +32,767이고 가장 작은 음의 몫은 -32768이다. 부호 있는 더블워드의 경우에 가장 큰 양의 몫은 2^31-1이고 가장 작은 음의 몫은 -2^31 이다. 비정수 결과는 0에 가까운 정수로 절삭된다. 나머지는 항상 피젯수와 동일한 부호를 갖고 절대값에 있어서 젯수보다 작다.
3.3 십진 대수 인스트럭션(Decimal Arithmetic Instructions)
십진 대수는 이진 대수 인스트럭션과(이전 섹션에서 설명함) 십진 대수 인스트럭션을 조합해서 수행된다. 십진 대수 인스트럭션은 다음과 같은 방식 중의 하나로 사용된다.
l 이전 이진 대수 연산의 결과값을 유효한 패킹된(packed) 혹은 패킹되지 않은 10진수에 맞추기 위해
l 입력 값을 잇따르는 이진 대수 연산에 맞춰서 결과 값이 유효한 패킹된 혹은 패킹되지 않은 10진수를 만들어낼 수 있도록 할 때
이러한 인스트럭션은 AL이나 AH 레지스터에서만 유효하다. 대부분 AF 플래그를 사용한다.
3.3.1 패킹된 BCD 맞춤 인스트럭션(Adjustment Instructions)
DAA(Decimal Adjust after Addition) 은 두 개의 유효한 패킹된 십진수 피연산자를 더한 결과값을 AL에 맞춘다. DAA 는 유효한 한 쌍의 십진수 결과를 얻기 위해서는 항상 패킹된 두 쌍(한 숫자는 각각의 절반의 바이트에 위치)있는 의 10진수의 덧셈을 따라야 한다. 캐리가 필요하면 캐리 플래그가 셋팅된다.
DAS(Decimal Adjust after Subtraction) 은 두 개의 패킹된 십진수 피연산자의 차를 AL에 맞춘다. DAS는 결과로 한쌍의 패킹된 십진수를 얻기 위해 항상 패킹된 십진수 한 쌍(한 수는 각각의 절반의 바이트에 위치)의 차를 따른다. 캐리 플래그는 빌림(borrow)가 필요하면 세팅된다.
3.3.2 패킹되지 않은 BCD 맞춤 인스트럭션
AAA (ASCII Adjust after Addition)는 AL 레지스터에 있는 내용을 유효한 패킹되지 않은 십진수로 바꾸고 상위 4비트를 0으로 만든다. AAA는 항상 AL에 있는 다른 하나의 값에서 하나의 패킹되지 않은 십진수 피연산자를 뺸 결과를 따른다. 빌림이 필요하면 캐리 플래그가 셋팅되고 AH가 감소한다.
AAM (ASCII Adjust before Multiplication)은 두 개의 패킹되지 않은 십진수 곱셈 결과를 바로 잡는다. AAM은 항상 유효한 십진수 결과값을 산출하기 위해 두 개의 십진수 곱셈 결과를 따라야 한다. 높은 자리 숫자는 AH에 남기고 나증ㄴ 비트는 AL에 남긴다.
AAD (ASCII Adjust before Division)은 두 개의 유효한 패킹되지 않은 십진수 피연산자 나눗셈에 대비해서 AH와 AL에 있는 젯수를 변경해서 나눗셈 결과로 몫은 유효한 패킹되지 않은 십진수가 된다. 이 AH는 높은 자리수가 있어야 하고 AL은 낮은 자리수 숫자가 있어야 한다. 이 인스트럭션은 값을 맞추고 결과 값을 AL에 저장한다. AH는 0이 될 것이다.
3.4 논리 인스트럭션
논리 인스트럭션에 포함되는 것은 다음과 같다.
l Boolean 연산 인스트럭션
l 비트 테스트와 변경 인스트럭션
l 비트 스캔 인스트럭션
l 회전(Rotate)와 시프트 인스트럭션
l 상태에 따른 바이트 셋팅
3.4.1 Boolean 연산 인스트럭션
논리 연산은 AND, OR, XOR, 그리고 NOT 가 있다.
NOT(Not) 은 피연산자의 보수를 만들기 위해 지정된 피연산자에서 비트들을 반전(invert)시킨다. NOT 인스트럭션은 레지스터나 메모리에 있는 단일 피연산자를 사용하는 1진법 연산이다. NOT는 플래그에는 영향을 주지 않는다.
AND, OR, 그리고 XOR 인스트럭션은 표준 논리 연산인 “and”, “(inclusive) or”, 그리고 “exclusive or”이다. 세 인스트럭션은 아래 피연산자의 조합으로 사용할 수 있다.
l 두 레지스터 피연산자.
l 메모리 피연산자와 범용 레지스터 피연산자
l 범용 레지스터 피연산자나 메모리 피연산자와 즉시 피연산자.
AND, OR, 그리고 XOR은 OF와 CF플래그를 비우고 AF는 사용하지 않는다. 그리고 SF, ZF 그리고 PF를 갱신한다.
3.4.2 비트 테스트와 변경 인스트럭션
이 인스트럭션 그룹은 메모리나 범용 레지스터에 있는 단일 비트에 작용한다. 비트의 위치는 피연산자의 낮은 자릿의 끝에서부터의 오프셋으로 명시한다. 오프셋 값은 인스트럭션의 즉시 바이트(immediate byte)로 지정할 수도 있고 범용 레지스터에 있을 수도 있다.
이러한 인스트럭션은 우선 선택한 비트 값을 CF, 즉 캐리 플래그에 할당한다. 다음에 연산된 결과로써, 새로운 값이 선택한 비트에 지정된다. OF, SF, ZF, AF, PF들은 정의되지 않은 상태로 남겨둔다. 테이블 3-1은 이러한 인스트럭션을 정의한다.
테이블 3-1. 비트 테스트와 변경 인스트럭션
| 인스트럭션 | CF에의 영향 | 선택된 비트에의 영향 |
| Bit (Bit Test) | CF <- BIT | (none) |
| BTS (Bit Test and Set) | CF <- BIT | BIT <- 1 |
| BTR (Bit Test and Reset) | CF <- BIT | BIT <- 0 |
| BTC (Bit Test and Complement) | CF <- BIT | BIT <- NOT(BIT) |
3.4.3 비트 스캔 인스트럭션
이 인스트럭션은 워드나 더블워드의 한 비트를 스캔해서 첫 세트 비트 인덱스를 레지스터로 저장한다. 스캔된 비트 문자열은 레지스터일 수도 있고 메모리일 수도 있다. 전체 워드가 0면(어떠한 셋팅된 발견되지 않을 경우), ZF플래그가 셋팅된다. 만일 한 비트가 발견되면 ZF는 비워진다. 만일 셋팅된 비트가 없으면 수신지 레지스터 값은 미지정이다.
BSF(Bit Scan Reverse)는 높은 자릿수에서 낮은 자릿수로 스캔한다(워드에서는 인덱스가 15인 비트에서, 더블워드에서는 31인 곳에서 시작한다).
3.4.4 시프트와 회전 인스트럭션(Shift and Rotate Instructions)
시프트와 회전 인스트럭션은 지정한 피연산자에서 비트들을 재위치시킨다.
이러한 인스트럭션은 아래와 같은 부류이다.
l 시프트 인스트럭션
l 더블 시프트 인스트럭션
l 회전 인스트럭션
3.4.4.1 시프트 인스트럭션
바이트, 워드, 그리고 더블워드의 비트들은 산수적으로 혹은 논리적으로 시프트될 수 있다. 지정된 숫자에 따라서 비트들은 31개의 장소들까지 시프트될 수 있다.
시프트 인스트럭션은 세가지 방법 중의 하나로 숫자를 지정한다. 하나는 단일 시프트로써 내부적으로 숫자를 지정한다. 두 번째는 즉시 값(immediate value)으로 숫자를 지정한다. 세 번째 방법은 CL에 있는 값으로 숫자를 지정한다. 마지막 방식에서 시프트 카운트가 변수가 될 수가 있어서 프로그램이 실행 중에 지정될 수 있다. CL의 낮은 자릿수의 5비트만이 사용된다.
CF는 항상 수신지 피연산자에서 시프트된 마지막 비트값을 포함한다. 단일 비트 시프트에서, 높은 자리수 비트(부호)가 연산으로 인해 바뀌면 OF가 셋팅된다. 그 외에는 OF는 비워진다. 하지만 다중 시프트에서는 OF는 미지정이다.
시프트 인스트럭션은 이진수 멱급수(binary power)로 나눗셈이나 곱셈할 때 편리한 방법을 제공한다. 하지만 부호가 있는 숫자를 우측으로 시프트해서 나눌 경우 IDIV 인스트럭션으로 수행하는 것과 동일한 종류는 아니다.
SAL(Shift Arithmetic Left) 는 수신지 바이트, 워드, 더블워드 피연산자를 하나 혹은 카운트 피연산자(즉시 값 혹은 CL에 있는 값)만큼 왼쪽으로 시프트한다. 프로세서는 피연산자 우측(낮은 자리수)에서 시프트로 빈 곳에 0을 넣고 좌측(높은 자리수)로 빠져 나간다. 그림 3-6 참조
SHL(Shift Logical Right)은 수신지 바이트, 워드, 더블워드 피연산자를 하나 혹은 카운트 피연산자(즉시 값이나 CL에 있는 값)에 지정한 숫자만큼 우측으로 시프트한다. 프로세서는 피연산자 좌측에서부터 0을 채우고 우측으로 빠져 나간다. 그림 3-7 참조
SAR(Shift Arithmetic Right)는 수신지 바이트, 워드, 더블워드 피연산자를 하나 혹은 카운트 피연산자(즉시 값이나 CL에 있는 값)에서 지정한 숫자만큼 우측으로 시프트한다. 프로세서는 양수일 경우에 좌측(높은 자리수)에 0을 시프트하고 음수일 경우 1을 시프트해서 피연산자의 부호를 유지한다. 그림 3-8 참조
이러한 인스트럭션이 비록 2의 멱급수로 정수를 나누는 데 사용된다하더라도 나눗셈 타입은 IDIV인스트럭션으로 하는 방식과는 동일하지 않다. IDIV의 몫은 0에 가깝게 절삭하지만, SAR의 “몫”은 양의 무한대로 절삭된다. 이러한 차이점은 양수일 경우에만 차이가 난다. 예를 들면 IDIV가 –9를 4로 나눌 때 결과는 –2이고 나머지는 –1이다. SAR로 –9를 우측으로 2비트 시프트하면 결과는 –3이다. 여기서 “나머지”는 +3이다; 하지만 SAR인스트럭션은 단지 나머지(CF에 있는)의 높은 자릿수만 저장한다.
그림 3-9에서 일련의 코드는 M = 2^N, 여기서 0 < N < 32인 어떤 M에 대해 IDIV와 동일한 결과를 보여준다. 이러한 과정은 jump가 일어 나느냐에 따라 대략 12에서 18 클락(clock)의 시간이 소요된다; ECX에 M이 있다면, 이에 상당하는 IDIV ECX인스트럭션은 대략 43 클락이 소요된다.
그림 3-6. SAL과 SHL
SHL(SAL과 동의어)는 레지스터나 메모리에 있는 비트들을 비트의 위치를 지정한 숫자만큼 왼쪽으로 사프트한다. CF는 피연산자의 왼쪽에서 시프트되어 나오는 마지막 비트를 저장한다. SHL은 빈 곳에 0을 채워서 시프트한다. 이러한 인스트럭션은 바이트, 워드, 더블워드 피연산자에 유효하다.
그림 3-7. SHR
SHR은 레지스터나 메모리 피연산자의 비트들을 비트 위치를 지정하는 숫자만큼 우측으로 시프트한다. CF는 피연산자의 우측에서 시프트되어 나온 마지막 비트를 저장한다. SHR은 빈 곳에 0을 채워서 시프트한다.
그림 3-8. SAR
SAR은 레지스터나 메모리에 있는 피연산자를 지정한 비트 위치 숫자만큼 우측으로 시프트할 때 부호를 유지한다. CF는 피연산자의 우측에서 시프트되어 나온 마지막 비트를 저장한다.
그림 3-9. IDIV를 모방해서 SAR을 사용할 때.
; N은 ECX에 있고 EAX로 나눈다고 가정
; 클락(CLOCKS)
CMP EAX, 0 ; 부호 플래그를 셋팅 2
JGE NoAdjust ; 부호가 0이면 점프 3 혹은 9
ADD EAX, ECX ; 2
DEC EAX ; EAX := EAX + (N-1) 2
NoAdjust:
SAR EAX, CL ; 3
; TOTAL CLOCKS 12 혹은(18)
3.4.4.2 더블-시프트(Double-Shift) 인스트럭션
이 인스트럭션은 긴 부호가 없는 비트 문자열을 연산할 때 필요한 기본 연산을 제공한다. 더블 시프트는 워드나 더블워드 피연산자에 다음과 같이 작용한다.
1. 두 개의 워드 피연산자를 입력으로 받아서 하나의 워드 결과를 만들어 낸다.2. 두 개의 더블워드 피연산자를 입력으로 받아서 하나의 더블워드 결과를 만들어 낸다.
두 입력 피연산자 중에 하나는 범용 레지스터에 있을 수도 있고 메모리에 있을 수도 있지만, 그 중 하나는 범용 레지스터에 있어야 한다. 결과값은 메모리나 레지스터 피연산자 값을 바꾼다. 시프트할 비트 수는 CL레지스터에 명시될 수도 있고 그 인스트럭션의 즉시 바이트에 명시될 수도 있다.
비트들은 레지스터 피연산자에서 메모리나 레지스터 피연산자로 시프트된다. SF, ZF 그리고 PF는 결과값에 따라 셋팅된다. OF와 AF는 사용되지 않는다.
SHLD(Shift Left Double)은 Reg 필드에서 우측에 있는 R/M필드로 높은 자릿수 비트들을 시프트 하는 동안 R/M의 비트들을 왼쪽으로 시프트된다(그림 3-10 참조). 결과는 R/M 피연산자로 다시 저장된다. Reg 필드는 변경되지 않는다.
SHRD(Shift Right Double)은 Reg 필드에서 왼쪽에 있는 R/M필드로 낮은 자리수의 비트들을 시프트하는 동안, R/M 필드에 있는 비트들을 우측으로 시프트한다.
3.4.4.3 회전 인스트럭션
회전 인스트럭션은 바이트, 워드, 그리고 더블워드가 회전되도록 한다. 피연산자로부터 회전된 비트들은 시프트에서처럼 사라지지 않지만 피연산자의 “끝”으로 다시 “순환(circled)”된다.
회전은 캐리와 오버플로 플래그만 영향을 준다. CF는 한 비트를 고립시켜서 조건부 점프 인스트럭션(JC나 JNC)으로 테스팅하면서 회전 인스트럭션의 두 개에 피연산자의 확장으로 동작한다. CF는 인스트럭션이 회전된 피연산자의 확장으로서 이 비트가 사용되지 않아도 항상 회전되어 나온 마지막 비트값을 저장한다.
단일 비트 회전에서 연산이 수신지 피연산자의 높은자릿수 비트(부호)가 변경되면 OF가 셋팅된다. 부호 비트가 본래 값을 지니고 있으면 OF는 비워진다. 다중비트 회전에서 OF의 값은 항상 사용되지 않는다.
ROL(Rotate Left)는 바이트, 워드, 더블워드 수신지 피연산자를 하나 혹은 카운트 피연산자(즉시 값이나 CL에 있는 값)에서 지정한 비트 수만큼 왼쪽으로 회전한다. 지정한 각각의 회전을 하는 동안 높은 피연산자의 왼쪽에서 나온 높은 자리수 비트는 오른쪽으로 되돌아 가서 피연산자의 새로운 낮은자릿수 비트가 된다. 그림 3-12 참조
ROR(Rotate Right)는 바이트, 워드, 더블워드 수신지 피연산자를 하나 혹은 카운트 피연산자(즉시 값이나 CL에 있는 값)에 지정된 비스수만큼 우측으로 회전한다. 지정한 회전을 하는 동안 낮은 자릿수의 비트는 피연산자의 오른쪽에서 나와서 왼쪽으로 되돌아가서 피연산자의 새로운 높은 자릿수 비트가 된다.
RCL(Rotate Through Carry Left)는 바이트, 워드, 더블워드 수신지 피연산자를 하나 혹은 카운트 피연산자(즉시 값이나 CL에 있는 값)에서 지정한 비트 수만큼 왼쪽으로 회전한다.
이 인스트럭션은 CF를 수신지 피연산자의 높은 자리수 한 비트의 확장으로 다룬다는 점에서 ROL과 다르다. 피연산자의 왼쪽에서 나온 각각의 높은 자리수 비트는 다음 회전 사이클에서 낮은 자리수 비트로 피연산자로 되돌아 가기 전에 CF로 이동한다. 그림 3-14 참조
RCR(Rotate Through Carry Right)는 바이트, 워드, 더블워드 수신지 피연산자를 하나 혹은 카운트 피연산자(즉시 값이나 CL에 포함된 값)에서 지정한 비트 수만큼 우측으로 회전한다.
이 인스트럭션은 CF를 수신지 피연산자의 낮은 자릿수 한 확장으로서 사용한다는 점에서 ROR과 다르다. 피연산자의 우측에서 나오는 각각의 낮은 자릿수의 한 비트는 다음 회전 사이클에서 높은 자릿수 비트로 되돌아가기 전에 CF로 이동한다.
그림 3-10. Shift Left Double
그림 3-11. Shift Right Double
그림 3-12. ROL
그림 3-13. ROR
그림 3-14. RCL
그림 3-15. RCR
3.4.4.4 빠른 “BIT BLT” 는 더블 시프트 인스트럭션 사용
더블 시프트의 한 목적은 정렬되지 않은 비트 문자열의 이동을 구현하는 것이다. 이것을 “bit blt”(BIT BLOCK TRANSFER.) 라 한다. 아래 예는 비트 문자열을 임의의 오프셋에서 더블워드로 정렬된 바이트 문자열로 이동하는 것이다. 왼쪽에서 오른쪽 순서인 문자열은 더블 시프트가 이동 루프 내부에서 사용되면 한번에 32비트 이동한다.
MOV ESI, SrcAddr
MOV EDI, DestAddr
MOV EBX, WordCnt
MOV CL, RelOffset ; relative offset Dest-Src
MOV EDX, [ESI] ; load first word of source
ADD ESI, 4 ; bump source address
BltLoop:
LODS ; new low order part
SHLD EDX, EAX, CL ; EDX overwritten with aligned stuff
XCHG EDX, EAS ; Swap high/low order parts
STOS ; Write out next aligned chunk
JA BltLoop
이 루프는 단순하지만 데이터가 가능한 가장 높은 효율을 위해 32비트 조각들로 이동되도록 한다. 더블 시프트가 없다면 가능한 가장 좋은 방법은 32비트 시프트를 사용해서 루프 반복할 때, 16만큼 이동시키고, 레지스터의 높은 자릿수와 낮은 자릿수 부분을 바꾸기 위해 XCHG를 ROR로 16만큼 회전하는 것이다. 위에 나온 것보다 더 일반적인 루프는 첫번 더블워드를 이동할 때 약간의 추가 마스킹이 필요할 것이다(주 루프에 들어가기 전에). 하지만 기본적으로는 한 루프 절차당 32비트를 이동하는 것은 위의 코드와 기본적으로 같다.
3.4.4.5 빠른 비트-문자열 삽입과 추출
더블 시프트 인스트럭션으로 또한 다음과 같은 것을 할 수가 있다.
l 삽입하는 양쪽 비트 어느 쪽도 방해하지 않고 레지스터에서 비트 문자열을 메모리에 있는 큰 비트 문자열 임의 비트로 빠른 삽입.l 추출하는 양측 어느 쪽도 방해하지 않고 메모리에 있는 큰 비트 문자열의 임의 비트에서 추출해서 레지스터로 삽입
다음 코드 샘플은 다양한 상황에서 비트 삽입과 추출을 수행하는 것을 보여준다.
1. 메모리로 비트 문자열 삽입(비트 문자열은 1-25비트 길이, 즉 4바이트 안팎)
; 레지스터에서 우측에 맞춰진 비트 문자열을 메모리 비트 스트링에 삽입
; 가정:
; 1) 문자열 배열의 기본적으로 dword로 정렬되어 있고,
; 2) 비트 문자열의 길이는 즉시 값(immediate value)이지만 비트 오프셋은 레지스터에 있다.
;
; 레지스트 ESI에는 우측으로 맞춰진 비트 문자열이 있다.
; 레지스터 EDI에는 부분 문자열(substring)의 시작을 나타내는 오프셋이 있다.
; 레지스터 EAX와 ECX는 또한 이러한 “삽입” 연산에 사용된다.
;
MOV ECX, EDI ; preserve original offset for later use
SHR EDI, 3 ; signed divide offset by 8 (byte address)
AND CL, 7H ; isolate low three bits of offset in CL
MOV EAX, [EDI]strg_base ; move string dword into EAX
ROR EAX, CL ; right justify old bit field
SHRD EAX, ESI, length ; bring in new bits
ROL EAX, length ; right justify new bit filed
ROL EAX, CL ; bring to final position
MOV [EDI]strg_base, EAX ; replace dword in memory
2. 메모리로 비트 문자열 삽입 (비트 문자열 길이가 1-31 비트일 때, 즉 5 바이트나 그 이하)
; 레지스터에서 우측으로 맞춰진 비트 문자열을 메모리 비트 문자열로 삽입
;
; 가정:
; 1) 문자열 배열은 ㅣㄱ본적으로 dword로 정렬되어 있고,
; 2) 비트 문자열의 길이는 즉시 값이지만 비트 오프셋은 레지스터에 있다.
;
; 레지스터 ESI에는 오른쪽에 맞춰진 삽입될 비트 문자열이 있다.
; 레지스터 EDI에는 부분 문자열의 시작 주소 비트 오프셋이 있다.
; 레지스터 EAX, EBX, ECX 그리고 EDI는 이러한 “삽입” 연산에 또한 사용된다.
;
MOV ECX, EDI ; temp storage for offset
SHR EDI, 5 ; signed divide offset by 32(dword address)
SHL EDI, 2 ; multiply by 4 (in byte address format)
AND CL, 1FH ; isolate low five bits of offset in CL
MOV EAX, [EDI]strg_base ; move low string dword into EAX
MOV EDX, [EDI]strg_base+4 ; other string dword into EDX
; rotate EDX:EAX right START
MOV EBX, EAX ; temp storage for part of string
SHRD EAX, EDX, CL ; double shift by offset within dword
SHRD EAX, EBX, CL ; double shift by offset within dword
; rotate EDX:EAX right END
SHRD EAX, ESI, length ; bring in new bits
ROL EAX, length ; right justify new bit field
; rotate EDX:EAX left START
MOV EBX, EAX ; temp storage for part of string
SHLD EAX, EDX, CL ; double shift back by offset within word
SHLD EDX, EBX, CL ; double shift back by offset within word
; rotate EDX:EAX left END
MOV [EDI]strg_base, EAX ; replace dword in memory
MOV [EDI]strg_base+4, EDX ; replace dword in memory
3. 비트 문자열 메모리 삽입(비트 문자열이 정확히 32비트일 경우, 즉 메모리 5나 4 타입의 공간)
; 레지스터에서 우측에 맞춰진 비트 문자열 을 메모리 비트 문자열로 삽입
; 가정:
; 1) 문자열 배열은 기본적으로 dword 로 정렬되어 있고,
; 2) 비트 문자열의 길이는 32지만 비트 오프셋은 레지스테 있다.
; 레지스터 ESI에는 삽입될 32비트 문자열이 있다.
;
; 레지스터 EDI에는 부분문자열의 시작 주소 오프셋이 있다.
; 레지스터 EAX, EBX, ECX, 그리고 EDI는 또한 이 “삽입” 연산에서 사용된다.
;
MOV EDX, EDI ; preserve original offset for later use
SHR EDI, 5 ; signed divide offset by 32 (dword address)
SHL EDI, 2 ; multiply by 4 (in byte address format)
AND CL, 1FH ; isolate low five bits of offset in CL
MOV EAX, [EDI]strg_base ; move low string dword into EAX
MOV EDX, [EDI]strg_base+4 ; other string dword into EDX
MOV EBX, EAX ; temp storage for part within dword
SHRD EAX, EDX ; double shift by offset within dword
SHRD EDX, EBX ; double shift by offset within dword
MOV EAX, ESI ; move 32-bit bit field into position
MOV EBX, EAX ; temp storage for part of string
SHLD EAX, EDX ; double shift back by offset within word
SHLD EDX, EBX ; double shift back by offset within word
MOV [EDI]strg_base, EAX ; replace dword in memory
MOV [EDI]strg_base, +4, EDX ; replace dword in memory
4. 메모리에서 비트 문자열 추출(비트 문자열은 1-25 비트 길이, 즉 4바이트 혹은 이하의 간격)
; 메모리 비트 문자열에서 레지스터로 우측 정렬된 비트 문자열 추출
;
; 가정:
; 1) 문자열 배열은 기본적으로 dword 정렬되어 있고,
; 2) 비트 문자열의 길이는 즉시 값이지만 비트 오프셋은 레지스터에 있다.
;
; 레지스터 EAX에는 우측으로 맞춰진, 0으로 채워진 추출된 비트 문자열:이 있다.
; 레지스터 EDI에는 부분 문자열의 시작 주소 오프셋이 있다.
; 레지스터 EDI, 그리고 ECX는 이 “추출”에 또한 사용된다.
;
MOV ECX, EDI ; temp storage for offset
SHR EDI, 3 ; signed divide offset by 8 (byte address)
AND CL, 7H ; isolate low three bits of offset
MOV EAX, [EDI]strg_base ; move string dword into EAX
SHR EAX, CL ; shift by offset within dword
AND EAX, mask ; extracted bit field in EAX
5. 메모리에서 비트 문자열 추출(비트 문자열이 1-32비트 길이, 즉 5바이트나 이하의 범위)
; 메모리 비트 문자열에서 레지스터로 우측으로 맞춰진 레지스터로 추출
;
; 가정:
; 1)문자열 배열은 기본적으로 dword로 정렬되어 있고,
; 2) 비트 문자열의 길이는 즉시 값이지만 비트 오프셋은 레지스터에 있다.
;
; 레지스터 EAX 에는 우측으로 맞춰진, 0으로 채워진 추출된 비트 문자열이 있다.
; 레지스터 EDI에는 부분 문자열의 시작 비트 오프셋이 있다.
; 레지스터 EAX, EBX, 그리고 ECX는 또한 이 “추출”로 사용된다.
MOV ECX, EDI ; temp storage for offset
SHR EDI, 5 ; signed divide offset by 32 (dword address)
SHL EDI, 2 ; multiply by 4 (in byte address format)
AND CL, 1FH ; isolate low five bits of offset in CL
MOV EAX, [EDI]strg_base ; move low string dword into EAX
MOV EDX, [EDI]strg_base+4 ; other string dword into EDX
SHRD EAX, EDX, CL ; double shift right by offset within dword
AND EAX, mask ; extracted bit field in EAX
3.4.5 Byte-Set-On-Condition 인스트럭션
이 인스트럭션 부류는 상태 플래그(status flag)에서 정의되는 16개 조건 중 하나에 따라 한 바이트를 0이나 1로 셋팅한다. 바이트는 메모리에 있을 수도 있고 1바이트 레지스터일 수도 있다. 이러한 인스트럭션은 특히 파스칼(Pascal)같은 고레벨 언어에서 Boolean 으로 표현되는 것을 구현할 때 유용하다.
SETcc (Set Byte on Condition cc) 는 cc가 true일 때 바이트를 1로 셋팅한다; 그 외에는 0으로 셋팅한다. 가능한 조건들에 관한 정의는 Appendix D를 참조하라.
3.4.6 테스트(Test) 인스트럭션
TEST (Test)는 두 피연산자의 논리적 “and”의 기능을 수행하고 OF와 CF를 비우고 AF는 지정하지 않고 SF, ZF 그리고 PF를 갱신한다. 플래그는 조건부로 제어 이동(control transfer) 인스트럭션에 의해 테스트되거나 byte-set-on-condition 인스트럭션에 의해 테스팅된다. 피연산자들은 더블워드, 워드, 바이트일 수 있다.
TEST 와 AND의 차이점은 TEST는 수신지 피연산자를 변경하지 않는다. TEST는 하나의 피연산자에서 다중 여러 비트를 테스팅한다는 점에서 BT와 다르고 반면에 BT는 한 비만 테스트한다.
'IT, 소프트웨어' 카테고리의 다른 글
| 애플리케이션 인스트럭션: II (0) | 2011.01.24 |
|---|---|
| 웹의 단점 (0) | 2011.01.05 |
| 애플리게이션 프로그래밍: I (0) | 2010.11.14 |
| 386 Intel 문서 (0) | 2010.11.10 |
| telnet 접속 암호 관련 - CE 5.0 (0) | 2007.11.01 |