C H A P T E R

13
ㅔ

산술 연산

공부할 내용

★ 고정 소숫점 숫자, 부동 소숫점 숫자

★ 부호 있는 수와 부호 없는 수

★ 2의 보수체계와 부호있는 수의 표현

★ 부호있는 수와 부호없는 수를 사용한 조건부 점프

★ 덧셈과 뺄셈

★ 매우 큰 수의 덧셈, 뺄셈

★ 1을 증감하는 명령, 부호있는 수의 부호 변경

★ 곱셈, IMUL을 이용한 즉치 곱셈

★ 나눗셈

고정 소숫점 숫자와 부동 소숫점 숫자

(1) 컴퓨터가 취급하는 숫자의 종류

- 고정 소숫점 숫자(fixed-point number) → 정수

1      .  0.  -99.  47.  8172615.  -12345.

- 부동 소숫점 숫자(floating-point number) → 실수

911.  -43.597  0.  3.14159  .0000070867  -8766.1234

(2) 주프로세서(main processor)는 고정 소숫점 숫자만을 다룬다.

실수를 취급하려면 수학 코프로세서가 있어야 한다.

부호없는 수와 부호있는 수

★  고정 소숫점 숫자를 두 종류로 나누면,

(1) 부호 없는 수(unsigned number): 0, 또는 양수를 표현

3145   1   99999   57

(2) 부호 있는 수(signed number): 양, 음, 또는 0의 값을 표현

+3145  -1  -99999  +57

★ 부호없는 수의 저장

- 그 숫자의 2진법 표현으로 저장된다.

예) 값이 57인 숫자가 1바이트에 저장될 때 → 00111001B

1워드에 저장될 때 → 0000000000111001B

★ 부호없는 수를 저장할 때는 바이트, 워드내의 모든 비트를 사용한다.

★ 부호있는 수를 저장할 때는 비트 패턴 중의 어떤 1개를 골라서 음수를 표현하는데 사용하여야 한다.

	표 13-1			부호없는 수와 부호있는 수의 범위

저장 단위	부호 없는 수	부호 있는 수
바이트 워드	0∼255 0∼65535	-128∼+127 -32768∼+32767

2의 보수 체계

★ 부호 있는 수를 표현하는 방법

(1) 부호를 표현하기 위해 바이트, 워드의 왼쪽 끝에 있는 비트를 사용

예) 1 바이트에 +57과 -57을 저장하는 방법

+57  →	00111001

-57  →	10111001

이 방법의 문제점은 0이 2가지 패턴으로 표현됨

00000000   과 10000000   (하드웨어 상의 문제)

(2) 더 나은 방법: 2의 보수(two's complement)를 사용

★  비트 패턴의 보수는 원래의 비트 패턴의 모든 비트를 반대로하여 만든 비트 패턴을 말한다; 즉 모든 0은 1로, 모든 1은 0으로 된다.

비트패턴 01110 1010101 000 1111111111	보수 10001 0101010 111 0000000000

★  2의 보수를 계산하려면 우선 보수를 구한 후에 1을 더하면 된다.

예) 00111001B (10진수로 57)의 2의 보수 계산

원래의 비트 패턴:	00111001

보수를 만든다   :	11000110

1을 더한다      :	11000111

예) 00000000B을 2의 보수로 만드는 과정을 살펴보자.

원래의 비트 패턴:	00000000

보수를 만든다   :	11111111

1을 더한다      :	100000000

□★ 왼쪽으로 넘친 비트를 무시한다.

★ 따라서 00000000B의 2의 보수는 00000000B이다.

부호있는 수를 표현하는 2의 보수

★ 프로세서는 2의 보수 체계를 사용하여 부호있는 수를 표현한다.

양수: 대응되는 2진수의 값으로 표현

음수: 부호를 제외하고 남은 숫자를 2의 보수로 바꾸어 표현

부호: 0은 양수를, 1은 음수를 표현한다.

10진수의 값 57 -57 127 -127 0	2진수의 값 00111001 11000111 01111111 10000001 00000000	2의 보수 11000111 00111001 10000001 01111111 00000000

2의 보수로 표현된 숫자를 해석하는 방법

★  2의 보수 체계로 표현된 음수는 읽기가 힘들다.

예) +2 = 00000010B,    -2 = 11111110B

예) 현재 AX 레지스터에 아래의 비트 패턴이 들어 있다고 하자.

1001010110100010B

- 첫비트가 1이므로 이는 음수

- 숫자의 값을 알기 위해 남은 비트들의 2의 보수를 계산함

원래의 비트 패턴:	1001010110100010

보수를 만든다   :	0110101001011101

1을 더한다      :	0110101001011110

십진수로 바꾼다: 27230,  따라서 원래의 값은 -27230 이다.

★ 디버거로 보는 레지스터의 값은 16진수임.

예) AX 레지스터에 C04AH가 저장되어 있다면,

C     0     4    A

1100  0000  0100 1010

따라서 AX의 값은 1100000001001010B이다.

원래의 비트 패턴:	1100000001001010

보수를 만든다   :	0011111110110101

1을 더한다      :	0011111110110110

10진수로 바꾼다:  절대값이 16310이다.

따라서, AX에 있던 값은 -16310이다.

★ 메모리의 자료항목을 볼 때:

DS:10A0H --- 5CH

DS:10A1H --- B7H

(1) DS:10A0H의 값이 부호있는 1바이트 크기의 수라면,

5     C

0101  1100     ===> 양수 92

(2) DS:10A0H의 값이 부호있는 1 워드 크기의 수라면,

DS:10A0H       DS:10A1H

5C		B7

★ 프로세서가 두 바이트에 워드를 저장할 때는 역순으로 저장

즉, 워드의 값은 B75CH 이다.

B     7    5     C

1011  0111 0101  1100  => 음수

원래의 비트 패턴:	1011011101011100

보수를 만든다   :	0100100010100011

1을 더한다      :	0100100010100100

10진수로 변환   : 18596

따라서, 주소가 DS:10A0H인 워드의 값은 -18596이다.

	그림 13-1				16개의 16진수 자릿수와 그에 대응되는 2진수 값

0       1       2       3       4       5       6       7

0000    0001    0010    0011    0100    0101    0110    0111

8       9       A       B       C       D       E       F

1000    1001    1010    1011    1100    1101    1110    1111

★ 부호있는 16진수의 값은 첫째 자리수가 0-7이면 양수, 8-F이면 음수

부호있는 수와 부호없는 수의 선택

★ 부호없는 수와 부호있는 수는 모두 바이트나 워드에 저장될 수 있다.

★  음수를 취급하려면 부호있는 수를 사용하고, 그렇지 않으면 부호없는 수를 사용한다.

	표 13-2			부호없는 수와 부호있는 수의 범위

저장 단위	부호 없는 수	부호 있는 수
바이트 워드	0∼255 0∼65535	-128∼+127 -32768∼+32767

부호없는 수와 부호있는 수를 사용한 조건부 점프

(1) 어떤 형의 숫자를 사용하고 있는가를 기억하고 관리해야 하는 것은 프로세서가 아니라 프로그래머이다.

(2)  조건부 점프 - 부호없는 수에 사용되는 조건부 점프의 집합과 부호있는 수에 사용되는 조건부 점프의 집합이 따로 분리되어 있다.

예 1) 부호없는 수에 오버플로우가 발생하였는가를 테스트하는 명령

JC	부호없는 수의 값이 오버플로우 되었으면 점프하라

JNC	부호없는 수의 값이 오버플로우 되지 않았으면 점프하라

예 2) 부호있는 수에 오버플로우가 발생하였는가를 테스트하는 명령

JO	부호있는 수의 값이 오버플로우 되었으면 점프하라

JNO	부호있는 수의 값이 오버플로우 되지 않았으면 점프하라

예 3) AX 레지스터에 VALUE의 값을 더한 후, 그 결과값이 너무

커서 저장될 수 없는 상황인지를 테스트하여 보려고 한다.

부호없는 수인 경우

ADD     AX, VALUE

JC       ERROR

부호있는 수인 경우

ADD     AX, VALUE

JO       ERROR

★ 부호있는 수는 양수의 방향으로도 오버플로우가 발생하지만 음수의 방향으로도 오버플로우가 발생한다.

예) 부호있는 수가 한 바이트에 저장될 때 그 값이 +127보다 크거나

-128보다 작으면 OF가 1로 세트된다.

(3) 두 숫자의 상대적 크기를 테스트하는 비교 명령 다음에 사용되는 조건부 점프 명령

	표 13-3			부호없는 수에 사용되는 조건부 점프

연산 코드	의미
JC JNC JB (JNAE) JA (JNBE) JBE (JNA) JAE (JNB) JE JNE	오버플로우이면 점프하라 오버플로우가 아니면 점프하라 아래이면 점프하라 (위거나 같지 않으면) 위이면 점프하라 (아래이거나 같지 않으면) 아래이거나 같으면 점프하라 (위가 아니면) 위이거나 같으면 점프하라 (아래가 아니면) 같으면 점프하라 같지 않으면 점프하라

	표 13-4			부호있는 수에 사용되는 조건부 점프

연산 코드	의미
JO JNO JL (JNGE) JG (JNLE) JLE (JNG) JGE (JNL) JE JNE JS JNS	오버플로우이면 점프하라 오버플로우가 아니면 점프하라 작으면 점프하라 (크거나 같지 않으면) 크면 점프하라 (작거나 같지 않으면) 작거나 같으면 점프하라 (크지 않으면) 크거나 같으면 점프하라 (작지 않으면) 같으면 점프하라 같지 않으면 점프하라 음수이면 점프하라 양수이거나 0 이면 점프하라

예) 부호 없는 수의 경우

CMP     AX, TOTAL

JA      L1

예) 부호 있는 수의 경우

CMP     AX, TOTAL

JG      L1

(4) 부호 있는 수에만 해당되는 조건부 점프 명령

JS	SF가 1로 세트되면 점프하라

JNS	SF가 0으로 클리어되면 점프하라

예)     SUB     AX, VALUE

JS      NEG_RESULT

덧셈과 뺄셈: ADD와 SUB

(형식)   ADD     operand1, operand2

SUB     operand1, operand2

(설명)

두 번째 오퍼랜드(operand2)가 첫째 오퍼랜드(operand1)에 더해지거나 빼지는 것이다. 결과는 첫째 오퍼랜드에 있는 원래의 값이 대치되면서 저장된다.

(예제)   ADD	AX, VALUE

SUB      AX, VALUE

(주의) -  두 오퍼랜드는 동일한 형이어야 한다.

-  첫째 오퍼랜드: 레지스터, 또는 자료 항목

-  둘째 오퍼랜드: 레지스터, 자료 항목, 또는 즉치

ADD     AL, VALUE

SUB     BX, SI

ADD     CH, 5

SUB     VALUE, AX

ADD     VALUE, 5

예) 두 자료 항목의 덧셈

; add VALUE1+VALUE2

MOV	AX, VALUE2

ADD	VALUE1, AX

워드와 바이트의 덧셈과 뺄셈: CBW

★  워드와 바이트를 더하고 빼는 것도 유용할 때가 많다.

이 때, 바이트를 확장하여 워드 길이로 만들 필요가 있다.

(할 일)

1.	바이트의 값을 AX의 오른쪽 반쪽(AL)에 복사한다.

2.	AX의 왼쪽 반쪽(AH)을 적당히 세트한다.

부호없는 수의 경우: AH ← 0

부호있는 수의 경우: AH ← 부호가 유지되도록 해야 함

3.	바이트 대신에 AX를 사용하여 계산한다.

예) ; unsigned numbers: add a word to a byte

MOV     AL, BYTE_VALUE

MOV     AH, 0

ADD     AX, WORD_VALUE

예) ; signed numbers: add a word to a byte

MOV     AL, BYTE_VALUE

CBW

ADD     AX, WORD_VALUE

★ CBW는 AH,  AL이 아닌 다른 레지스터에는 사용될 수 없다.

예) CBW의 작동 예제

★ 57의 2진수 표현: 111001

★ 57이 1 바이트에 저장되는 경우: 00111001

★ -57의 값을 갖는 바이트: 11000111

★ 57이 1 워드에 저장되는 경우: 0000000000111001

★ -57의 값을 갖는 워드: 1111111111000111

매우 큰 수의 덧셈과 뺄셈: ADC와 SBB

더블워드

부호없는 수	:	0∼4294967295

부호있는 수	:	-2147483648∼2147483647

쿼드워드

부호없는 수 :  0∼18446744073709551615

부호있는 수 :  -922337203685477808∼922337203685477807

예제) 더블워드를 사용하는 예이다. 123456789를 987654321에 더하려고 한다. (ADC : add with carry)

(1) 자료의 정의

DFIRST    DD      987654321

DSECOND  DD      123456789

(2) 더블 워드에  DFIRST와 DSECOND가 저장되어 있는 모습

DFIRST	B168	DE3A

DSECOND	15CD	5B07

(우리의 계획)

□  DSECOND의 왼쪽 워드를 DFIRST의 왼쪽 워드에 더한다.

□  DSECOND의 오른쪽 워드를 DFIRST의 오른쪽 워드에 더한다.

; add doublewords:   DFIRST <-- DFIRST+DSECOND

MOV     AX, WORD PTR DSECOND

ADD     WORD PTR DFIRST, AX

MOV     AX, WORD PTR DSECOND+2

ADC     WORD PTR DFIRST+2, AX

예제) 쿼드워드를 사용하는 예제

(1) 자료의 정의

QFIRST     DQ      543210987654321

QSECOND   DQ      123456789012345

(2) 실제 덧셈

; add quadwords:   QFIRST <-- QFIRST+QSECOND

MOV     AX, WORD PTR QSECOND

ADD     WORD PTR QFIRST, AX

MOV     AX, WORD PTR QSECOND+2

ADC     WORD PTR QFIRST+2, AX

MOV     AX, WORD PTR QSECOND+4

ADC     WORD PTR QFIRST+4, AX

MOV     AX, WORD PTR QSECOND+6

ADC     WORD PTR QFIRST+6, AX

★ 뺄셈의 예제

- SBB (subtract with borrow)

; subtract doublewords:   DFIRST <-- DFIRST-DSECOND

MOV     AX, WORD PTR DSECOND

SUB     WORD PTR DFIRST, AX

MOV     AX, WORD PTR DSECOND+2

SBB     WORD PTR DFIRST+2, AX

; subtract quadwords:   QFIRST <-- QFIRST-QSECOND

MOV     AX, WORD PTR QSECOND

SUB     WORD PTR QFIRST, AX

MOV     AX, WORD PTR QSECOND+2

SBB     WORD PTR QFIRST+2, AX

MOV     AX, WORD PTR QSECOND+4

SBB     WORD PTR QFIRST+4, AX

MOV     AX, WORD PTR QSECOND+6

SBB     WORD PTR QFIRST+6, AX

숫자에서 1을 증감하는 명령: INC와 DEC

(형식)   INC     operand

DEC     operand

(설명) 오퍼랜드(operand)는 레지스터이거나 (메모리에 있는) 자료 항목이다. INC는 오퍼랜드에 1을 더하고, DEC는 오퍼랜드에서 1을 뺀다.

(예제)   INC     AX

DEC     AL

INC     CH

DEC     DI

INC     VALUE

DEC     TABLE[BX]+4

예제) INC와 DEC의 루프안에서의 사용

; repeat

;   call READ_CHARACTER

; until (CF is set to 1)

; COUNT <-- number of times READ_CHARACTER is called

CLC

MOV     COUNT, 0

L1:

CALL    READ_CHARACTER

INC     COUNT

JNC     L1

부호있는 수의 부호 변경: NEG

★ 양수는 음수로, 음수는 양수로 바꾸려면 NEG 명령을 사용한다.

(형식)      NEG     operand

(설명) 오퍼랜드(operand)는 레지스터이거나 (메모리 공간의) 자료 항목일 수 있다.

(예제)   NEG     AX

NEG     VALUE

NEG     SI

NEG     CL

★ 기술적인 면에서 볼 때, NEG 명령이 하는 일은 오퍼랜드를 2의 보수로 만들고 나서 이를 0으로부터 빼는 것이다.

곱셈: MUL과 IMUL

★ 곱셈 명령의 종류

부호없는 수를 위한 곱셈: MUL 명령

부호있는 수를 위한 곱셈: IMUL 명령  (integer multiply)

(형식)  MUL     operand

IMUL    operand

(설명) 오퍼랜드(operand): 레지스터, 자료 항목

자료형: BYTE나 WORD

★ 곱셈을 하려면 프로세서는 2개의 오퍼랜드를 필요로 한다. BYTE형의 오퍼랜드를 지정하면 프로세서는 또 다른 오퍼랜드는 AL에 있다고 가정한다. WORD형의 오퍼랜드를 지정하면, 프로세서는 또 다른 오퍼랜드는 AX에 있다고 가정한다.

예) BVALUE가 BYTE형의 자료 항목이라면 아래의 명령은 BVALUE를 AL에 곱한다.

MUL     BVALUE

IMUL    BVALUE

예)  WVALUE가 WORD형의 자료 항목이라면 아래의 명령은

WVALUE를 AX에 곱한다.

MUL     WVALUE

IMUL    WVALUE

★ 곱셈을 하면 그 결과 값은 오퍼랜드의 원래의 길이의 두 배만큼 커질 수 있다.

★ 1 바이트 * 1 바이트 → 2 바이트  (AH, AL)

예) AL = 100(64H), BVALUE = 10(0AH),

곱셈전의 AX의 값

AH   AL

??	64	← AX 레지스터

곱셈의 결과 : 1000(3E8H)이며, AX는 아래와 같은 값을 갖는다.

AH   AL

03	E8	← AX 레지스터

★ 1 워드 * 1 워드 → 2 워드 (DX, AX)

예) WVALUE = 1000(3E8H), AX = 55555(D903H)가 있다고 하자.

곱셈하기 전의 DX:AX의 쌍에는 다음의 값이 있다.

DX  AX

????	D903	← DX:AX

곱셈의 결과: 55555000(34FB3B8H)이며, DX:AX는 아래처럼

DX   AX

034F	B3B8	← DX:AX

★ 곱셈의 결과는 원래의 오퍼랜드에 그 값이 저장될 정도로 작은 경우가 많다. 결과의 왼쪽 반을 무시하는 것이 괜찮으면 MUL과 IMUL은  캐리 플래그와 오버플로우(CF와 OF)를 0으로 클리어한다.

예)      MUL     CX

MOV	RESULT1, AX

JNC	L1

MOV      RESULT2, DX

L1:

IMUL을 사용한 즉치 곱셈

★  숫자를 즉치와 곱할 때는 IMUL 명령의 변형된 형태를 사용한다.

이 명령은 부호없는 수와 부호있는 수에 모두 사용 가능하다.

(형식)   IMUL    operand, immediate-value

IMUL    destination, operand, immediate-value

(설명)  오퍼랜드(operand)와 데스티네이션(destination)은 다음 중의 한 레지스터일 수 있다.

AX  BX  CX  DX  SP  BP  SI  DI

★ 프로세서는 즉치를 오퍼랜드에 곱한다. 데스티네이션이 지정되어 있으면 곱셈의 결과는 데스티네이션에 복사된다. 그렇지 않으면 결과는 원래의 오퍼랜드 값을 대치한다.

예)     IMUL    AX, 10

IMUL    SI, BX, -5

★ 이 명령은 결과의 오른편의 16비트만을 저장한다. 따라서 이 명령을 사용할 때는 그 결과가 1개의 워드에 저장가능할 것이라는 확신이 있어야 한다. 그렇지 않으면 즉치를 레지스터에 복사한 후에 MUL이나 원래의 IMUL을 사용하여야 한다.

워드와 바이트의 곱셈

(1) 바이트를 먼저 워드로 변환하여 길이를 같도록 만들어야 한다.

예) ; unsigned multiplication of a byte by a word

MOV     AL, UBYTE

MOV     AH, 0

MUL     UWORD

예) ; signed multiplication of a byte by a word

MOV     AL, SBYTE

CBW

IMUL    SWORD

나눗셈: DIV와 IDIV

★ 종류

(1) 부호없는 수를 위한 DIV 명령

(2) 부호 있는 수를 위한 IDIV  (integer division)

(형식)   DIV     operand

IDIV    operand

(설명) 워드를 바이트로 나누거나, 더블워드를 워드로 나눌 수 있다. 나눗셈의 결과는 몫(quotient) 과 나머지(remainder)이다. 이 두 결과는 모두 정수이다.

★ IDIV 명령을 실행하여 얻은 결과인 몫과 나머지는 그 부호가 서로 같도록 설계되어 있다.

예) -53 / 7 =  몫이 -7이고 나머지가 -1이다.

★ 오퍼랜드는 제수(divisor)이고,

피제수(dividend): AX 레지스터에 있거나 DX:AX에 있다고 가정

★ 오퍼랜드가 BYTE 형이면

AX: 워드 크기의 피제수가 있다고 가정한다.

나눗셈 결과인 몫 → AL,  나머지 → AH에 저장된다.

★ 오퍼랜드가 WORD형이면

DX:AX: 더블워드 크기의 피제수가 있다고 가정한다.

나눗셈 결과인 몫 → AX,  나머지 → DX에 저장된다.

(예) ; unsigned division of a word by a byte: 53/BVALUE

MOV     AX, 53

DIV      BVALUE

결과: AL에는 몫인 5가 남고, AH에는 나머지인 3이 저장된다.

(예)

; signed division of a doubleword by a word: DVALUE/WVALUE

MOV     DX, WORD PTR DVALUE+2

MOV     AX, WORD PTR DVALUE

IDIV      WVALUE

결과: AX에는 몫인 -5000이 남고 DX에는 나머지인 -3이 저장된다.

★  DIV와 IDIV 명령은 오버플로우를 나타내기 위하여 플래그를 세트하지 않는다. 그 대신에 인터럽트를 건다.

인터럽트의 결과로 아래의 메시지가 화면에 표시되고,

Division overflow

프로그램의 실행이 중단된다.

(바람직한 예)

; division that tests for a zero divisor

CMP     BVALUE, 0

JE        L1

MOV     AX, WVALUE

DIV      BVALUE

L1:

바이트를 바이트로 나누거나 워드를 워드로 나눌 때 사용하는 명령: CWD

★ 바이트를 바이트로 나누거나 워드를 워드로 나누려면 피제수를 먼저 올바른 길이로 확장시켜야 한다.

(방법) 피제수를 AL(또는 AX)에 복사하고나서 AH(또는 DX)를 조정

- 부호없는 수인 경우:  AH(또는 DX) ← 0

- 부호있는 수인 경우: CBW(또는 CWD)를 사용

★ CBW: AL의 부호있는 값을 동일한 값의 워드로 바꾸어 AX에 저장

★ CWD: AX의 부호있는 워드를 동일한 값의 더블워드로 확장하여

DX:AX에 저장한다.

예)  ; unsigned division of a byte by a byte: UBYTE1/UBYTE2

MOV     AL, UBYTE1

MOV     AH, 0

DIV      UBYTE2

예)  ; signed division of a byte by a byte: SBYTE1/SBYTE2

MOV     AL, SBYTE1

CBW

DIV      SBYTE2

예)  ; unsigned division of a word by a word: UWORD1/UWORD2

MOV     AX, UWORD1

MOV     DX, 0

DIV      UWORD2

예) ; signed division of a word by a word: SWORD1/SWORD2

MOV     AX, SWORD1

CWD

IDIV     SWORD2