어셈블리 언어를 보통 가장 낮은 레벨의 언어라고 말합니다. 여기에서 높은 언어란 사람들끼리 사용하는 자연어를 말합니다. 그리고 낮을 수록 기계에 가깝다는 뜻입니다. 어셈블리 언어는 인간이 사용할 수 있는 언어중에서는 가장 기계에 가깝다는 말이 됩니다. 따라서 어셈블리 언어로 뭔가를 해보려면 기계를 먼저 알아야합니다. 어셈블리 언어보다 먼저 수와 기계를 설명하는게 그런 이유 때문입니다. 이번 글에서는 8086이 어떤 구조로 되어있고 어셈블리 언어로 조종할 수 있는 기능들이 뭐가 있나 알아볼 것입니다.
어셈블리 언어보다 더 기계에 가까운게 있을까요? 숫자가 있습니다. 모든 어셈블리 언어는 기계가 이해할 수 있는 또 기계를 직접 동작시키는 숫자로 변환됩니다. 그렇게 변환하는 것을 어셈블러 assembler 변환하는 작업 자체를 어셈블링 assembling 이라고 합니다. 숫자의 모음은 기계어라고도 합니다. 사람의 언어가 아니라는 것이지요. 그래서 인간이 사용할 수 있는 최소한의 언어가 어셈블리 언어입니다.
참고로 어셈블리 언어는 숫자와 1대1로 매칭됩니다. 아직 배우지는 않았지만 mov라는 명령어가 있는데 mov라는 명령이 나오면 이 명령은 항상 특정 숫자로 변환됩니다. 이 값을 123h라고 가정해보면 어셈블러는 코드에서 mov를 만나면 그대로 123h라고 변환하는 것입니다. 그리고 mov ax, bx라는 명령을 만나면 항상 12345h라고 변환한다고 해봅시다. 어셈블러는 컴파일러에 비하면 굉장히 간단한 프로그램입니다.
이 그림은 컴퓨터를 아주아주 많이 간단하게 표현한 것입니다. 메모리와 프로세서, 장치들이 버스로 연결되어있습니다. 우리는 파일을 하드디스크에 저장합니다. 그리고 더블클릭을 하면 프로세서가 더블클릭이라는 명령을 이해하고 파일을 하드디스크에서 메모리로 읽어옵니다. 그리고 메모리를 조금씩 읽어서 파일에 써있는 명령어들을 실행합니다. 그 명령어가 특정 이미지를 장치로 보내라는 명령이면 영상 출력이 되는 것이고 특정한 데이터를 스피커로 보내라는 것이면 음악 출력이 되는 것입니다.
우리가 어셈블러로 우리 어셈블리 언어 코드를 어셈블링해서 저장한 실행파일이 있다면 이 파일은 하드디스크 장치에 저장될 것입니다. 그리고 프로세서는 실행파일을 메모리로 옮기고 메모리에서 명령들을 읽어서 실행할 것입니다. 8086 프로세서의 구조를 간단한게 생각해보면 이렇습니다. (주1)(주2)
레지스터라는 것은 프로세서 안에 있는 16비트의 저장공간입니다. 메모리와 역할이 같지요. 하지만 메모리와 프로세서는 프로세서보다 훨씬 느린 버스라는 전선으로 연결되어있어서(주3) 메모리에 있는 데이터를 프로세서가 읽으려면 프로세서는 한참을 기다려야 합니다. 하지만 레지스터는 프로세서 내부에 있기 때문에 아주 빠르게 읽을 수 있습니다. 속도 차이는 최소한 만배 이상입니다. 마치 서울에서 대전에가서 물건을 사오는 것과 주머니 속에있는 물건을 꺼내오는 것의 차이와 같습니다. 대전에 가서 큰 물건을 사올 수는 장점이 있지만 시간이 오래 걸린다는 단점이 있지요. 주머니속 물건도 빨리 꺼낼 수는 있지만 큰 물건을 넣을 수는 없다는 단점이 있습니다.
그래서 계산할 때 최대한 많은 데이터를 메모리에서 레지스터로 읽어오고 최대한 레지스터만 가지고 계산을 합니다. 그리고 최종 데이터만 메모리에 기록하도록 하면 프로그램 속도를 높일 수 있습니다. 그렇게 계산 중간 중간에 사용할 임시 데이터를 저장하는 공간을 레지스터라고 생각하면 됩니다.
8086에는 8개의 범용 레지스터가 있습니다.
AX: 누산 레지스터라고 불리거나 연산 레지스터라고 불립니다. 계산에 주로 사용됩니다.
BX: 베이스 주소 레지스터라고 불립니다. 메모리 주소를 계산할 때 사용됩니다.
CX: 카운터 레지스터라고 불립니다. 반복문에서 지금 몇번째로 반복하고 있는지를 기억할 때 사용됩니다.