
전자레인지나 TV, 혹은 자동차까지, 어떠한 가전 및 전자 제품을 구매하던 간에 사용 설명서(manual)이 들어 있다. 이는 제조사가 설계 및 구현한 제품의 기능을 사용자가 잘 숙지하게 하기 위해 존재한다. 중요한 점은, 매뉴얼은 사용자 관점에서 쓰여진 글이라는 것이다. 우리가 전자레인지를 사용할 때 전자기파의 어떤 주파수 대역을 이용하는지 알 필요가 전혀 없다. 그저 돌릴 시간을 입력하고 확인 버튼을 누를 줄 알면 오케이다.
ISA(Instruction Set Architecture)란 컴퓨터를 사용하기 위한 매뉴얼을 의미한다. 여기서 컴퓨터란 CPU를 의미하는 것이라고 보아도 좋다. 우리가 매뉴얼을 보는 목적은 컴퓨터가 구체적으로 어떻게 구현되어 있는지 알기 위해서가 아니다. 몇 나노 공정을 했고 캐시 사이즈는 얼마며 최적화를 위한 어떤 기술이 적용되었는지 등은 우리가 알 필요가 없는 것이다. 매뉴얼은 컴퓨터의 사용자, 즉 프로그래머 입장에서 컴퓨터를 의도한대로 동작하기 위해 어떻게 명령해야 하는지를 아주 자세히 설명하고 있어야 한다. 프로그래머 입장에서 컴퓨터에 명령을 내리는 방법은 딱 하나 뿐이다. 명령어(=Instruction)을 입력하는 것이고, 이 때문에 컴퓨터의 매뉴얼을 Instruction Set Architecture라고 부른다.
프로그램은 여러 개의 명령어들이 모여 만들어진 것이고, 컴퓨터에는 이를 저장할 공간이 필요하다. 이를 메모리라고 부른다. 명령어는 메모리에서 실행될 수 없다. 명령어가 실행되는 공간은 CPU고, 따라서 CPU는 메모리로부터 명령어를 가져와야 한다. CPU와 메모리는 Bus로 연결되어, 실제 버스처럼 메모리에 있는 데이터를 CPU로, CPU에 있는 데이터를 메모리로 저장할 수 있게 도와준다.

도식화하면 위 그림과 같이 표현할 수 있다.
- 명령어를 가져옴(Fetch)
- CPU가 명령어를 해독(Decode)
- CPU가 명령어를 실행(Execution)
위 과정을 끊임 없이 반복하는 것이 CPU가 하는 일이며, 이러한 컴퓨터의 구조를 폰 노이만 아키텍쳐라고 부른다. 또한 CPU가 메모리에서 명령어를 읽어오기 때문에 필연적으로 발생하는 병목을 폰 노이만 병목이라고 부른다.

폰 노이만 병목을 줄이기 위해서는 버스를 통해 메모리에 접근하는 횟수를 최대한으로 줄이는 것이 좋을 것이다. 예를 들어 내가 명령어를 어디까지 읽었는지에 대한 정보를 메모리에 저장해놓는 것 보다는 그냥 CPU가 들고 있는게 훨씬 빠를 것이다. 이런 목적을 위해 CPU에 데이터를 저장할 수 있는 작은 메모리를 만들게 되었고, 이를 레지스터(Registers)라고 부른다. 레지스터가 저장된 공간을 흔히 레지스터 파일(Register File)이라고 부른다.

자, 이제 우리에게 게임보이 콘솔 하나가 주어 졌다고 하자. 우리가 게임보이에서 돌아가는 게임을 만들어보고 싶다고 하면, 뭘 읽어봐야 할까? 당연히 ISA를 읽어봐야 한다. 게임보이가 어떤 명령어 체계를 갖고 있는지 숙지하고 그에 맞춰서 프로그램을 짜야 한다. 일반적인 CPU(Intel, AMD, ARM 등)과 달리 닌텐도는 출시 당시 ISA가 공개되지는 않았으나 지금은 쉽게 찾아볼 수 있다(링크). 혹은 ISA는 아니지만 게임보이의 스펙에 대해 위키 형태로 잘 정리된 홈페이지도 존재한다.

위 그림에서 8-bit Microprocessor라고 표기되어 있는 부분이 게임보이의 CPU에 해당하는 부분이다. 위 그림은 게임보이의 구현이 어떻게 되어 있는지를 보여주고 있다. 그러나 프로그래머 입장에서 RAM의 크기가 얼마니 하는 것들은 중요치 않다. 우리는 명령어가 어떻게 되어 있는지, 레지스터는 몇 비트로 몇 개나 있는지가 더 중요하다.

게임보이는 8bit 레지스터(A, B, C, D, E, F, H, L)와 16bit 레지스터(PC, SP) 2가지 종류의 레지스터를 갖는다.
8bit 레지스터들은 하나씩 사용되기도 하며,
ex) ADD A, B
두 레지스터가 묶여서 하나의 16bit 레지스터 처럼 사용되기도 한다.
ex) LD BC, n16
일반적으로 명령어는 3가지로 분류할 수 있다.
- 산술 및 논리 연산 명령어(ADD, SUB, OR, AND, ...)
- 데이터 이동 명령어(LOAD, STORE)
- 분기 명령어(JUMP, BRANCH)
1번의 동작은 자명하다. 2번은 어딘가에 저장된 정보를 가져오거나(=LOAD), 레지스터에 저장된 값을 어딘가에 쓰는 연산(=STORE)를 의미한다. 3번은 명령어를 순차적으로 읽지 않고 임의의 주소로 건너 뛰거나(=JUMP), 조건문에 따라 명령어를 순차적으로 읽을지 아니면 임의의 주소로 분기할지(=BRANCH) 결정하는 명령어를 의미한다. for 문이나 if-else 같은 고수준 문법이 3번 명령어들로 구현된다.
PC는 Program Counter를 의미하며, 현재 읽고 있는 명령어의 주소가 저장되어 있는 레지스터를 의미한다. SP는 Stack Pointer를 의미하는데, 설명이 길어지기 때문에 여기서는 생략하도록 하겠다(참고).
이제 예시로 ISA에서 명령어 하나를 가져와서 분석해보자.

위 그림은 게임보이의 8bit Load Instruction에 대한 부분을 일부 캡쳐한 것이다.
LD r, r' 와 같이 명령어를 작성하고, r' 에 저장된 값을 레지스터 r 에 저장하게 만드는 명령어를 의미한다고 되어 있다. 각 레지스터는 표와 같이 decoding이 되어 있다(ex) LD 111, 000 이면 LD A <- B).
자세히 보면 CY, H, N, Z, CYCL이라는 칸이 존재한다.
먼저 C(Y), H, N, Z는 Flag인데, 산술 연산의 결과를 저장해서 다음 명령어가 이를 보고 명령을 수행할 수 있도록 하는 1비트 레지스터라고 보면 된다. 게임보이에서는 F 레지스터를 비트 단위로 나누어 Flag를 구현하였다.

예를 들어 XOR A, A 명령을 수행하면 A에는 0이 저장되고, 연산 결과가 0이므로 Z flag가 1로 assert된다. 이후 JP Z, a16 명령을 수행하게 되면 if(z == 1) goto a16; 과 같은 기능을 수행하게 된다.
CYCL은 명령어를 실행하는 데 걸리는 사이클 수를 의미하는데, 이는 다음 글에서 살펴보도록 하자.