C H A P T E R

13
ㅔ

산술 연산

공부할 내용

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

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

★ 2의 보수체계

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

★ 덧셈과 뺄셈

★ 매우 큰 수의 덧셈, 뺄셈

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

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

★ 나눗셈

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

★ 컴퓨터가 취급하는 숫자의 종류

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

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

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

911.  -43.597  0.  3.14159  .0000070867  -8766.1234

부호없는 수와 부호있는 수

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

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

3145   1   99999   57

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

+3145  -1  -99999  +57

★ 부호없는 수의 저장

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

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

1워드에 저장될 때   → 0000000000111001B

★ 부호있는 수를 저장 ∀ 2의 보수로 표현

2의 보수 체계

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

(1) 부호 표현을 위해 왼쪽 끝 비트(MSB)를 사용(1's complement)

→ 문제점 : 0이 2가지 패턴으로 표현됨(00000000, 10000000)

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

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

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

원래의 비트 패턴 :	00111001

보수를 만든다    :	11000110

1을 더한다       :	11000111

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

★ 부호 없는 수와 부호 있는 수에 따른 조건부 점프 집합이 따로 분리되어 있다.

예) AX 레지스터에 VALUE의 값을 더한 후, 그 결과값이 너무 커서 저장될 수 없는 상황인지를 테스트하여 보려고 한다.

부호없는 수인 경우

ADD     AX, VALUE

JC       ERROR

부호있는 수인 경우

ADD     AX, VALUE

JO       ERROR

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

예) 부호있는 수가 한 바이트에 저장될 때 그 값이 +127보다 크거나-128보다 작으면 OF가 1로 세트된다.

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

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이 아닌 다른 레지스터에는 사용될 수 없다.

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

더블워드

부호없는 수	:	0∼4294967295

부호있는 수	:	-2147483648∼2147483647

쿼드워드

부호없는 수 :  0∼18446744073709551615

부호있는 수 :  -922337203685477808∼922337203685477807

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

DFIRST	DD	987654321 (∀ 3ADE68B1)

DSECOND	DD	123456789 (∀ 075BCD15)

- 더블 워드에 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

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

QFIRST	DQ	543210987654321

QSECOND	DQ	123456789012345

; 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

예)	INC	AX

DEC	AL

예)	; 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

예)	NEG	AX

NEG	VALUE

NEG	SI

NEG	CL

★ NEG 명령이 하는 내부적으로 하는 일은 오퍼랜드를 2의 보수로 만들고 나서 이를 0으로부터 빼는 것이다.

곱셈: MUL과 IMUL

★ 곱셈 명령의 종류

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

부호있는 수를 위한 곱셈 : IMUL 명령

(형식)	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 명령

(형식)	DIV     operand

IDIV    operand

→ 워드를 바이트로 나누거나, 더블워드를 워드로 나눌 수 있다.

★ 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