1장. 8086/8088 프로세서 (2/2)
상태플래그와 제어 플래그
[그림 1-5-2] 제어 레지스터 (상태플래그, 제어플래그)
플래그
|
설명
|
1 일때
|
0 일때
|
OF(overflow) |
자리 넘침 |
OV(overflow) |
NV(not overflow) |
DF(direction) |
방향 |
DN(down) |
UP(up) |
IF(interrupt) |
가로채기 |
EI(enable) |
DI(disable) |
SF(sign) |
부호 |
NG(negative) |
PL(plus) |
ZF(zero) |
영(0) |
ZR(zero) |
NZ(not zero) |
AF(aux,carry) |
하위 4bit 캐리 |
AC(auc,carry) |
NA(not aux) |
PF(parity) |
패리티 |
PE(parity even) |
PO(parity odd) |
CF(carry) |
자리 올림 |
CY(carry) |
NC(not carry) |
CF(Carry Flag)
연산 명령을 실행한 후에 최상위 비트에서 덧셈에 의한 자리
올림(carry)이나 뺄셈에 의한 자리 내림(borrow)이 있을 경우 Set(해당 bit가 1이 되면 set, 0이 되면 reset)된다.
바이트 단위 연산일 때는 D7비트, 워드 단위일 때는 D15 비트가 대상이 된다. 논리 연산 명령에서는 0으로 reset된다. 로테이토, 쉬프트
명령에서는 해당 데이터의 최상위 또는 최하위 비트가 CF로 전송된다.
MSB (Most Significant Bit) : 최상위 비트
LSB (Least Signficant Bit) : 최하위
비트
PF(Parity Flag)
데이터 전송 등에 따른 결과가 짝수 패리티인 경우에 set되고, 홀수 패리티일 때 reset된다. 이 플래그는 주로 데이터 전송시 에러가
발생 했는지 발생하지 않았는지 조사할 때 쓰인다. 하드웨어가 처리하는 부분이므로 프로그래머 입장에서는 신경쓰지 않아도 된다.
AF(Auxiliary Flag)
산술 연산 도중에 하위 4비트에서 상위 4비트로 자리 올림이 생기거나, 또는 상위 4비트에서 하위 4비트로 자리 내림이 있을 때 1로
set된다. 이 플래그는 10진 연산의 경우 보정하는데 사용된다.
ZF(Zero Flag)
각 연산의 결과가 0이 될 경우 set된다. 비교 명령을 실행하면 비록 레지스터 내용은 변하지 않지만 실행 결과에 따라서 set될 수
있다.
SF(Sign Flag)
연산 결과 최상위 비트가 1이 되면 set된다. SF는 연산 후 그 결과 값의 부호를 나타내는데 사용하며 최상위 비트가 0일 경우 양수,
1일 경우엔 음수가 된다.
OF(Over Flow Flag)
연산결과 오버플로우가 발생했을때 set 된다. 오버플로우가 생기면 원하는 데이터가 망가져서 원하는 데이터를 얻을 수 없으며 프로그램엔
치명적인 에러를 방생한다.0으로 나누었을 때 발생하는 오버플로우에 대해서는 CPU에 의해서 내부적으로 오버플로우 인터럽트가 발생하고, 프로그램은
보통 정지하게 된다.
IF(Interrupt Enable Flag)
하드웨어에 의한 인터럽트를 받아들일 것인가, 받아들이지 않을 것인가를 나타낸다. 1로 set되어 있을 경우 인터럽트를 받아들이고, 0으로
reset되어 있을 경우엔 받아들이지 않는다. 단 NMI(Non-Maskable Interrupt)는 이 플래그의 값에 관계없이
받아들인다.
DF(Direction Flag)
문자열에 관한 명령을 실행할 때, 이 비트에 따라서 소스 또는 데스티네이션 어드레스를 자동적으로 하나씩 증가 또는 감소 시킨다. 0일 때
어드레스가 자동적으로 증가하고, 1일 경우 어드레스가 감소한다.
TF (Trap Flag)
명령 실행 후에 CPU는 이 플래그를 조사하여 1이면 싱글 스텝 인터럽트를 발생한다. 이플래그를 1로 해두면 CPU는 하번에 1개의
명령만 실행한다. 이 기능을 이용하여 디버깅 프로그램을 만들 수 있다.
1.6. 세그먼티드 메모리
1.6.1. 메모리 맵
8086/88의 메모리 공간은 64KB 블록 16개로 분할되어 있다. 16진수 00000h 에서 FFFFFh로 끝나는 메모리 주소를
가지고 있다. 64K의 블록으로 분할되어 있기 때문에 16진으로 64K만큼 하나만 증가 시켜 버리면 블록이 바뀌기 때문에 편리하다. (메모리
주소에서 20000h 는 10000h 보다 65,536바이트/64K 높은 위치에있다.)
[그림 1-6-1]에서 보는 것과 같이 메모리 주소가 일직선으로 연속된 공간이라고 생각하고, 시스템 메모리가 어떻게 할당 되어 있는가를
보이고있는 것을 메모리 맵(memory map)이라고 한다. 예를 들면 RAM영역은 00000h 에서 9FFFFh까지 할당되었고, Video
Display RAM 영역은 A0000h에서 BFFFFh 까지 할당, ROM영역은 C0000h에서 FFFFFh까지 할당된다. 프로그래머는 이를
확실하게 알아야 정확히 메모리의 어느 부분에 안전하게 데이터를 적재할 수 있다.
[그림 1-6-1] 8086/88의 메모리 맵
아래의 [그림 1-6-2]는 MSDOS의 메모리 맵이다. 도스는 8086 CPU에서 설계 되었다. 따라서 메모리의 크기가 1MB로
한정되었다. 호환성의 이유로 1MB의 주소 공간을 아래의 메모리 공간처럼 블록으로 분할 하였다. (메모리의 1MB는 640K의 사용자 메모리와
384K의 시스템 메모리로 구성)
도스 시절부터 컴퓨터를 사용했다면 아마, 게임을 하기위에 640K 의 메모리를 최대한 활용할려고 애를써본 기억이 있을 것이다. 이때의
메모리가 도스의 유저메모리 공간이다.
[그림 1-6-2] MSDOS 의 메모리 맵
1.6.2. 세그먼트 레지스터, 오프셋 레지스터(포인터, 인덱스 레지스터)
1.5. 레지스터의 세그먼트 레지스터를 다시 한번 살펴보자. 64K로 나눈 공간을 세그먼트라고 하였다. 그
세그먼트를 저장하는 레지스터가 세그먼트 레지스터이다. 8086/88의 세그먼트 레지스터는 총 4가지가 있다. (코드 세그먼트(CS), 데이터
세그먼트(DS), 스택 세그먼트(SS), 엑스트라 세그먼트(ES) - [그림 1-5-1] 8086 레지스터의 구성 참고)
여기서 코드 세그먼트는 프로그램 명령어가 저장된 세그먼트 영역의 시작주소를 저장하고, 데이터 세그먼트는 프로그램에 사용되는 데이터가 저장된
세그먼트의 첫 주소를 저장한다. 엑스트라 세그먼트 또한 데이터 세그먼트와 같다.(대개 공유 데이터를 저장하는데 사용된다.) 그리고 스택
세그먼트는 인터럽트와 서브루틴의 반환 주소를 저장하는데 사용된다. (스택에 대해서는 2부 어셈블리 에서 자세하게 소개하겠다.) 또 4개의
세그먼트가 반드시 분리 되서 정의될 필요는 없다. [그림 1-6-3]을 보면 SS영역과 ES이 서로 겹쳐 있는 걸 알 수 있다. 실제로 4개의
세그먼트가 완전히 같은 경우도 허용된다. (CS=DS=ES=SS)
[그림 1-6-3] 각 세그먼트의 시작 주소가 세그먼트 레지스터에 저장되어 있는 것을 표현한 것이다.
세그먼트 레지스터의 값은 64K로 나눈 세그먼트 블록의 첫번 째 주소(base address)이기 때문에, 각 세그먼트 블록 안의
메모리 영역에 접근하기 위해서는 오프셋 레지스터(포인트, 인덱스 레지스터)가 필요하다. 1.5. 레지스터의
포인터 레지스터와 인덱스 레지스터를 다시 자세히 알아보자.
[표 1-6-1] 오프셋 레지스터 - SI, DI, IP, SP, BP
SI(Source Index )
|
DI(Destination Index)
|
IP(Instruction Index )
|
SP(Stack Pointer )
|
BP(Base Pointer )
|
다시 한번 강조하지만 세그먼트 레지스터로는 64KB 크기의 세그먼트 첫 번째 주소밖에 지정하지 못한다. 64KB내에서 원하는 임의의
주소를 지정하기 위해서 16비트 크기의 레지스터가 하나 더 필요한데 이때 사용하는 레지스터가 오프셋 레지스터이다.
SI(Source Index),
DI(Destination Index)
문자열을 다루는 명령에서 소스 데이터의 오프셋 어드레스를 가리킬 때는 SI를
사용하고, 데스티네이션 어드레스를 가리킬 때 DI를 사용한다.
IP(Instruction
Pointer)
명령코드가 들어있는 세그먼트의 오프셋 어드레스를 지정하는데 사용한다. 이때의 세그먼트를 지정하기
위해서는 반드시 CS를 사용해야 한다. 이 포인터는 프로그래머는 사용할 수없다.
SP(Stack
Pointer)
스택 세그먼트의 오프셋 어드레스를 지정하는데 사용된다. 스택 세그먼트를 지정하는 세그먼트
레지스터로는 SS만을 사용해야 한다. 하지만 스택내의 오프셋을 나타내는데는 SP외에 BP로도 사용 할 수
있다.
BP(Base Pointer)
SP와 마찬가지로 스택의 오프셋을 가리키는데 사용한다.
SP와 다른 점은 PUSH와 POP명령에 의해 자동적으로 변하지 않는다는 것이다. 전달받은 인수를 함수 내부에서 가리킬 때 주로
사용한다.
1.6.3. 세그먼트 메모리 기법
세그먼트 내에서 주소는 0에서부터 시작하여 FFFF까지 이다.(64K의 길이, 오프셋 레지스터의크기는 16비트이기 때문이다.) 세그먼트
내에서의 주소는 오프셋(offset) 또는 논리 주소(logical address)라고 한다. 그럼 물리 주소(physical memory)는
무엇인가? 바로 메모리의 실제 주소를 뜻한다.
세그먼트 레지스터는 16비트이기 때문에 실제 메모리 주소(20비트)에 분할된 세그먼트 블록의 첫 주소를 저장하지 못한다. 해결
방법은 실제주소의 하위 비트를 4개를 생략하고 저장을 한다. 반대로 세그먼트 레지스터의 값을 실제 주소로 바꿀 땐 하위비트 뒤에 0000을
추가한다. (이는 레지스터의 내용에 16을 곱한것이다.) 예를 들어 [그림 1-6-4]를 보자. 그림에서 색칠되어 있는 부분을 코드
세그먼트(CS)라고 가정하자. 코드 세그먼트의 논리 주소(오프셋)을 0250h 라면(CS : 8000, Offset : 0250) , 실제
주소(물리주소)는 80000 + 0250으로 80250h 이다.
80000 ( CS의 8000h 의 하위비트에 0 추가 ) -> 16진수이기 때문에 0을 한개 붙였다. 2진수일경우 0 4개를
추가한다.
+)_0250 ( CS의 오프셋 )
---------
80250 (실제 물리 주소)
세그먼트 레지스터와 오프셋 레지스터, 이 두개의 16비트 레지스터를 이용해서 20비트의 메모리에 접근 하는 방법을 세그먼트 메모리
기법이라고 한다.
물리 주소 : 20비트의 길이를 가지고 있고, BIU가 주소 버스 선에 내보내는 실제의 이진 코드
논리 주소 : 주어진 세그먼트의 0위치로 부터의 오프셋
[그림 1-6-4] 세그먼트화된 메모리맵, 실제 주소 모드
두 세그먼트가 겹치는 경우가 있다고 했다. 심지어 4개의 세그먼트까지 중복으로 사용 가능하다. 여기서 발생하는 문제가 있는데, 바로
세그먼트와 오프셋 조합이 중복되는 경우이다. 이는 물리 주소가 중복된다는 말과 동일하다. 이 때 데이터가 서브루틴 스택에 덧쓰기를 하거나
반대로 스택에 저장되는 주소가 데이터를 덧쓰게 되면 큰 문제가 발생한다. 그렇기 때문에 세그먼트가 겹칠때에는 각별히 주의해야한다.
물리 주소가 겹치는 예
ES : 52B9h, offset : D470h -> 52B90 + D470 = 60000h
SS : 5D27h, offset : 2D90h -> 5D270 + 2D90 = 60000h
마지막으로 세그먼트와 오프셋의 표기법을 알아보자.
스택 세그먼트의 논리 주소 2D90h를 표현 할려면 SS:2D90h 라고 쓴다. 이렇게 표현하면 물리 주소는 같더라도 프로그래머가
알아보기가 명확해진다.
각 세그먼트별로 사용해야하는 오프셋 레지스터는 2부 어셈블리에서 자세하게 배운다.