요약

화면에 글자 찍기, 키보드 읽기 같은 일은 컴퓨터에 이미 만들어져 있는 코드가 하는데, 프로그램은 그 코드가 메모리 어디에 있는지 몰라도 된다. 단축 번호만 누르면 전화가 걸리는 것처럼, 번호를 말하면 프로세서가 메모리 맨 앞의 주소록에서 그 번호의 주소를 찾아 그리로 갔다가 일이 끝나면 부른 자리로 돌아온다. 다만 주소록이 누구나 고칠 수 있는 자리에 놓여 있다 보니, 한 칸이 잘못 바뀌면 그 번호를 부르는 모든 프로그램이 엉뚱한 곳으로 간다.

예시로 보기

프로시저는 call 이름으로 부른다. 어셈블러가 이름을 주소로 바꿔 주기 때문이다: 프로시저와 CALL·RET. 그런데 BIOS와 MS-DOS의 코드는 내 프로그램과 따로 만들어졌고, 컴퓨터마다 놓인 주소가 다르다. 주소를 프로그램에 박아 넣을 수 없다.

그래서 주소 대신 번호를 쓴다. 화면 서비스는 10h번이다. 프로그램은 이렇게만 쓴다.

mov ...
int 10h        ; 10h번 서비스를 부른다
add ...

프로세서가 int 10h를 만나면 다음 순서로 움직인다.

 부르는 프로그램                                  인터럽트 처리 코드
+-------------+                               F000:F065  sti    (3)
| mov...      |                                    F066  cld
| int 10h  ---+--(1)--+        +-----(2)----->     F067  push es
| add...   <--+--+    |        |                   F068  ...
+-------------+  |    v        |                         IRET --+
                 |  +------+-----------+-----------+            |
                 |  | 3069 | F000:F065 | F000:AB62 |            |
                 |  +------+-----------+-----------+            |
                 |      인터럽트 벡터 표 (가운데가 INT 10h의 칸)|
                 +---------------------(4)----------------------+
  1. 번호 10h로 인터럽트 벡터 표에서 칸을 찾는다.
  2. 그 칸에 적힌 주소 F000:F065로 건너간다.
  3. 거기 있는 코드가 일을 한다.
  4. 코드 끝의 IRET가 int 10h 바로 다음 명령(add)으로 돌려보낸다.

주소 F000:F065는 예일 뿐이다. 아래 표에서는 INT 10h의 칸에 C000:0CD7이 적혀 있다. 컴퓨터마다 다르기 때문에 번호로 부르는 것이다.

검증: 오프셋 = 번호 × 4(아래 표의 다섯 줄, INT 9h → 0024h, INT 10h → 0040h, INT 21h → 0084h), 그 표의 주소를 리틀 엔디언으로 넣었을 때의 바이트, INT와 IRET 뒤에 CS·IP·플래그·SP가 원래대로 돌아오는지, 카드 C2 — 50_interrupt-vector-table_verify.py. 16비트 실주소 모드를 실제로 돌린 것이 아니라 파이썬 흉내로 확인했다.

정의

INT 명령은 소프트웨어 인터럽트를 일으킨다. 프로그램이 스스로 “지금 이 번호의 처리 코드를 불러 달라”고 하는 것이다. 번호는 0부터 FFh까지 256가지다.

INT number      ; number = 0..FFh

인터럽트를 처리하는 코드를 인터럽트 핸들러(interrupt handler)라 부른다. 인터럽트 서비스 루틴(ISR)도 같은 것의 다른 이름이다.

인터럽트 벡터 표(IVT)는 번호마다 핸들러의 주소를 하나씩 적어 둔 표다. 메모리 맨 앞 00000h에서 시작한다. 한 칸은 32비트(4바이트)이고 세그먼트와 오프셋이 들어 있다.

기호로 쓰면. 인터럽트 번호를 \(n\)(\(0 \le n \le 255\))이라 하면, 그 칸이 시작하는 주소는 번호에 4를 곱한 값이다.

\[\text{칸의 시작 주소} = n \times 4\]

INT 10h라면 \(10\text{h} \times 4 = 40\text{h}\)다. 16진수 10h는 십진수 16이라 \(16 \times 4 = 64 = 40\text{h}\)다. 칸 4바이트 가운데 낮은 주소 2바이트가 오프셋, 높은 주소 2바이트가 세그먼트다1.

아래 표는 실제 벡터 표의 앞부분을 한 줄에 네 칸씩 적은 예다. 한 줄이 16바이트(10h)라서 줄마다 주소가 10h씩 커진다.

인터럽트 번호 줄의 시작 주소 네 칸에 적힌 주소
00~03 0000 02C1:5186 0070:0C67 0DAD:2C1B 0070:0C67
04~07 0010 0070:0C67 F000:FF54 F000:837B F000:837B
08~0B 0020 0D70:022C 0DAD:2BAD 0070:0325 0070:039F
0C~0F 0030 0070:0419 0070:0493 0070:050D 0070:0C67
10~13 0040 C000:0CD7 F000:F84D F000:F841 0070:237D

INT 9h의 칸은 08~0B 줄의 두 번째라 0DAD:2BAD다. 주소로는 \(9 \times 4 = 24\text{h}\)에서 시작한다.

자주 쓰는 번호는 다음과 같다.

번호 하는 일
INT 10h 화면: BIOS 비디오 INT 10h와 속성 바이트
INT 16h 키보드: BIOS 키보드 입력 INT 16h
INT 17h 프린터
INT 1Ah 시각
INT 1Ch 사용자가 쓰라고 비워 둔 타이머 인터럽트
INT 21h MS-DOS 서비스: INT 21h 입출력 함수

CALL·RET와 다른 점

INT와 IRET는 CALL과 RET의 짝과 닮았다. 다른 점은 둘이다1.

  • 갈 곳을 명령에 적지 않고 표에서 찾는다.
  • 돌아올 주소뿐 아니라 플래그 레지스터도 스택에 넣는다. INT는 플래그, CS, IP 세 워드를 차례로 넣고, IRET는 거꾸로 IP, CS, 플래그를 꺼낸다. 그래서 핸들러가 플래그를 바꿔도 부른 쪽으로 돌아오면 플래그가 원래대로다.

핸들러는 프로그램이 INT로 부를 수도 있고, 키보드 같은 장치가 신호를 보내서 불릴 수도 있다. 뒤쪽이 하드웨어 인터럽트다. 두 경우 모두 같은 표를 쓴다.

활용

  • INT 21h, INT 16h, INT 10h 같은 서비스 호출이 모두 이 틀이다: 레지스터에 원하는 일의 번호와 값을 넣고 int 번호를 부른다.
  • 표의 칸을 내 코드의 주소로 바꾸면 그 번호가 불릴 때마다 내 코드가 먼저 돈다: 인터럽트 핸들러 바꿔 끼우기.
  • 여기서 다룬 표와 주소 계산은 16비트 실주소 모드의 것이다.

연결

확인 문제

C1 인터럽트 벡터 표는 메모리 어디에 있고, 한 칸에 무엇이 몇 바이트로 들어 있는가?

답: 메모리 맨 앞 00000h에서 시작한다. 한 칸은 4바이트이고, 그 번호의 핸들러 주소(세그먼트와 오프셋)가 들어 있다. 칸이 256개라 전체가 1,024바이트다.

C2 INT 16h와 INT 1Ch의 칸은 각각 몇 번지에서 시작하는가?

답: 0058h, 0070h.
이유: 번호 × 4다. \(16\text{h} = 22\)라 \(22 \times 4 = 88 = 58\text{h}\), \(1\text{Ch} = 28\)이라 \(28 \times 4 = 112 = 70\text{h}\).
흔한 오답: 16h × 4를 십진수 16 × 4 = 64로 계산하는 것. 16h는 십진수 22다.

C3 BIOS의 화면 코드를 call F000:F065처럼 주소로 부르지 않고 int 10h로 부르는 이유는?

답: 그 코드가 놓인 주소는 컴퓨터마다 다르다. 번호로 부르면 프로세서가 그 컴퓨터의 표에서 주소를 찾으므로, 같은 프로그램이 어느 컴퓨터에서나 돈다.
이유: 이 문서의 두 예에서도 INT 10h의 주소가 F000:F065와 C000:0CD7로 다르다.

  1. Intel 64 and IA-32 Architectures Software Developer’s Manual의 INT n과 IRET 설명(실주소 모드)을 따랐다. ↩ ↩2