Hello x86(2)
“Hello, x86!” 출력하기
/images/Pasted-image-20260307011251.png)
.CODE— 코드 섹션 실제 실행될 명령어(instruction)들이 들어가는 세그먼트다..STARTUP부터END까지가 코드 섹션에 해당한다.코드 섹션 vs 데이터 섹션의 기원
.CODE와.DATA의 분리는 x86/MS-DOS 시절부터 존재했던 개념이다. 이것이 현대 언어까지 그대로 이어진다.
섹션 내용 현대 언어에서 .CODE실행할 명령어 C의 .text섹션.DATA사용할 데이터 C의 .data섹션C언어의 코드/데이터 섹션 개념은 여기서 유래했다.
/images/Pasted-image-20260307012409.png)
.STARTUP— 프로그램 진입점 디렉티브프로그램이 실행될 때 처음 시작되는 지점을 MASM에게 알려주는 디렉티브다.
왜 필요한가 —
.COM파일 구조.MODEL TINY를 사용하면 결과물이.COM파일이 되는데,.COM파일은 첫 번째 명령어가 반드시 오프셋100h위치에 있어야 한다는 규칙이 있다..COM 파일 메모리 구조: 0000h ~ 00FFh : PSP (Program Segment Prefix) — MS-DOS가 사용하는 영역 0100h ~ : ← 실제 코드 시작 위치 (여기서부터 실행)PSP(Program Segment Prefix)는 MS-DOS가 프로그램 실행 전에 앞 256바이트(
100h)를 자신의 관리 정보로 채우기 때문에, 코드는 반드시 그 뒤인100h부터 시작해야 한다.
.STARTUP이 해주는 것.STARTUP바로 다음에 오는 명령어(mov ah, 09h)가 정확히 오프셋100h에 배치되도록 MASM이 자동으로 맞춰준다. 개발자가 직접 오프셋을 계산할 필요가 없다.
/images/Pasted-image-20260307014034.png)
END— 소스 파일 종료 디렉티브MASM에게 “여기까지 어셈블하라”고 알려주는 디렉티브다. 이 모듈(소스 파일)의 마지막을 의미한다.
END이후에 코드가 있어도 MASM은 무시한다.
ENDvs 프로그램 종료END는 어셈블러에게 파일 끝을 알리는 것이고, 실제 프로그램 실행 종료는 그 위의int 21h(함수4Ch)가 담당한다. 둘을 혼동하지 말 것.
/images/Pasted-image-20260307015305.png)
mov— 데이터 이동 명령어오른쪽(src) 값을 왼쪽(dest)에 대입한다.
피연산자가 2개인 이유 어디에 넣을지(dest)를 명시해줘야 한다.
6502는 누산기(A 레지스터) 하나만 있어서 목적지가 항상 A로 고정이었다. x86은 레지스터가 여러 개(AX, BX, CX, DX…)이기 때문에
6502: LDA #$09 ; 목적지는 항상 A로 고정 x86: mov ah, 09h ; 목적지를 직접 지정AX 레지스터 구조
AX (16비트) ├── AH (상위 8비트) └── AL (하위 8비트)
mov ah, 09h는 AX 전체가 아닌 상위 8비트(AH)에만 값을 넣는다.숫자 리터럴 접미사
접미사 진수 예시 h16진수 (hex) 09h,4Chb2진수 (binary) 00001001bo8진수 (octal) 11o없음 10진수 9
/images/Pasted-image-20260307015545.png)
lea— Load Effective Address메모리 주소 자체를 레지스터에 로드하는 명령어다.
lea dx, msg ; dx = msg의 메모리 주소
movvslea차이mov dx, msg ; msg 주소에 있는 값(데이터)을 dx에 로드 lea dx, msg ; msg 주소 자체를 dx에 로드 ← 포인터C언어로 비유하면:
dx = *msg; // mov — 역참조 dx = &msg; // lea — 주소값DX 레지스터
msg는 16비트 주소값이므로 AH(8비트)가 아닌 DX(16비트) 전체에 로드한다. D 레지스터는 Data Register로, 입출력 연산에 주로 사용된다.DX (16비트) ← 주소값 전체가 들어감 ├── DH (상위 8비트) └── DL (하위 8비트)near 포인터 오프셋(16비트)만 로드하는 것이기 때문이다. 세그먼트까지 포함한 32비트 전체 주소는 far 포인터라고 부른다. x86-16 세그먼트 오프셋 주소 읽는 법 참고
슬라이드에서 “near 포인터를 로드”라고 표현한 이유는, 8086의 세그먼트:오프셋 주소 체계에서
/images/Pasted-image-20260307020940.png)
int 21h— MS-DOS 소프트웨어 인터럽트MS-DOS가 제공하는 서비스를 호출하는 명령어다.
21h는 MS-DOS 서비스 인터럽트 번호이며, AH에 넣은 값이 함수 번호가 된다.mov ah, 09h ; 함수 번호 지정 (09h = 문자열 출력) lea dx, msg ; 출력할 문자열 주소를 DX에 전달 int 21h ; MS-DOS 서비스 호출동작 원리
int 21h실행 시 CPU는 인터럽트 벡터 테이블(IVT)에서 21h번 항목을 찾아 MS-DOS 커널 함수로 점프한다. (21h 항목이 MS-DOS 커널 함수이다.) MS-DOS는 AH 값을 보고 어떤 함수를 실행할지 결정한다.AH = 09h → Display String 함수 실행 DS:DX(데이터 세그먼트 + 데이터 오프셋)가 가리키는 문자열을 '$'까지 출력DOS API 함수 번호 (AH 값)
AH 기능 07hDirect console input without echo 08hConsole input without echo 09hDisplay string ← 지금 사용 0AhBuffered keyboard input 4ChTerminate program (프로그램 종료)
번외 — 소프트웨어 인터럽트란?
하드웨어 인터럽트가 외부 장치(키보드, 타이머)가 CPU에 신호를 보내는 것이라면, 소프트웨어 인터럽트는 프로그램이 의도적으로 발생시키는 인터럽트다.
int 21h실행 시 흐름:내 코드 실행 중 → int 21h 명령어 → IVT[21h] 주소로 점프 → MS-DOS 커널 함수 실행 → 내 코드로 복귀OS의 기능을 “요청”하는 메커니즘이며, 현대 OS의 시스템 콜(syscall) 의 원형이다.
/images/Pasted-image-20260307021425.png)
프로그램 종료 —
int 21h함수4ChMS-DOS 서비스
4Ch(Terminate with return code)로 프로그램을 종료한다.mov ah, 4Ch ; 함수 번호: Terminate with return code xor al, al ; 리턴값 = 0 (정상 종료) int 21h ; MS-DOS 종료 서비스 호출종료 스펙
4Ch함수의 스펙상 AL에 리턴값(종료 코드) 을 넣어야 한다. C언어의return 0과 동일한 개념이다.AH = 4Ch → 종료 함수 AL = 00h → 리턴값 (0 = 정상 종료)
xor al, alvsmov al, 0둘 다 AL을 0으로 만들지만xor이 더 효율적이다.
명령어 기계어 크기 mov al, 02바이트 xor al, al2바이트 크기는 같지만
xor은 즉시값(immediate)을 메모리에서 읽어올 필요가 없어서 더 빠르다. 이 관용구는 x86-64에서도 여전히 쓰인다.왜 즉시값 로드가 느리냐면:
mov al, 0→ 명령어 안에 0이 인코딩되어 있음 → 명령어 fetch 시 0도 같이 읽어와야 함xor al, al→ 피연산자가 레지스터 두 개 → 이미 CPU 안에 있는 값끼리 연산 → 메모리 접근 없음
MS-DOS 의 9번 함수는 어떻게 구현되어 있나?
/images/Pasted-image-20260307021958.png)
/images/Pasted-image-20260307022011.png)
화면 출력의 3가지 방법
같은 “문자열 출력”이라도 어느 계층에 접근하느냐에 따라 3가지 방법이 있다.
내 프로그램 ↓ 방법 1 MS-DOS API (int 21h, 함수 09h) ↓ 방법 2 ROM BIOS API (BIOS가 제공하는 함수) ↓ 방법 3 하드웨어 직접 접근 (비디오 메모리 주소에 직접 쓰기)
방법 접근 계층 특징 int 21hMS-DOS 가장 추상화, 이식성 높음 BIOS 함수 직접 호출 ROM BIOS DOS 없이도 동작 직접 접근 하드웨어 그래픽 카드에 직접 write MS-DOS → BIOS → 하드웨어 흐름
int 21h출력 함수도 내부적으로는 BIOS가 제공하는 함수를 호출하고, BIOS는 하드웨어 제조사가 ROM에 구워넣은 코드로 비디오 하드웨어에 접근한다. 즉 방법 1은 방법 2와 3을 내부적으로 거치는 것이다.MS-DOS의