디스크에서 추가 데이터 로드하는 방법에 대한 설명입니다.
This explains how to load additional data from the disk.
👉️ 디스크는 플로피 디스크입니다.
The disk is a floppy disk.
👉️ 전체코드(NASM) / Full Code(NASM)
✔️ boot.asm
— 첫 번째 섹터(512바이트)만 담당하는 코드입니다.
This code handles only the first sector (512 bytes).
[org 0x7c00]
bits 16
start:
mov ax, 0
mov ds, ax
mov es, ax
mov si, msg_boot
call print_string
; 디스크 읽기 (2번 섹터부터 1개 섹터를 0x8000에 로드)
; Disk read (load 1 sector starting from sector 2 to 0x8000)
mov ah, 0x02
mov al, 1
mov ch, 0
mov cl, 2
mov dh, 0
mov bx, 0x8000
int 0x13
jc disk_error
mov si, msg_success
call print_string
; 로드된 2번 섹터 코드로 점프
; Jump to loaded sector 2 code
jmp 0x8000
disk_error:
mov si, msg_error
call print_string
jmp hang
hang:
jmp $
print_string:
lodsb
cmp al, 0
je .done
mov ah, 0x0e
int 0x10
jmp print_string
.done:
ret
msg_boot db '1. Booting MBR...', 13, 10, 0
msg_success db '2. Disk read success!', 13, 10, 0
msg_error db 'Disk read failed!', 13, 10, 0
times 510 - ($ - $$) db 0
dw 0xaa55
— 메모리 0x8000에 로드되어 실행될 독립된 코드 파일입니다. 똑같이 512바이트 크기를 맞춰줍니다.
This is a standalone code file designed to be loaded into memory at address 0x8000 and executed. It is also sized to exactly 512 bytes.
[org 0x8000]
bits 16
start:
mov si, msg_next_sector
call print_string
; 대기모드 / Standby mode
jmp $
print_string:
lodsb
cmp al, 0
je .done
mov ah, 0x0e
int 0x10
jmp print_string
.done:
ret
msg_next_sector db '3. Hello from Sector 2 (0x8000)!', 13, 10, 0
times 512 - ($ - $$) db 0
👉️컴파일 및 실행 방법 (QEMU 기준)
How to compile and run (using QEMU)
✔️ Xubuntu 터미널에서 다음 명령어를 순서대로 실행합니다.
Run the following commands in order in the Xubuntu terminal.
✔️ 두 코드를 각각 빌드한 뒤 리눅스의 cat과 dd 명령어로 완벽한 가상 디스크 이미지를 만듭니다.
After building each piece of code, create a complete virtual disk image using the Linux cat and dd commands.
# 1. 각각의 어셈블리 파일을 바이너리로 컴파일
# Compile each assembly file into binary
nasm -f bin boot.asm -o boot.bin
nasm -f bin sector2.asm -o sector2.bin
# 2. 두 파일을 하나로 합침 (총 1024바이트)
# Merged two files into one (total 1,024 bytes)
cat boot.bin sector2.bin > temp.bin
# 3. 가상 머신 BIOS가 오인하지 않도록 1.44MB 플로피 디스크 규격으로 빈 공간 채우기
# Pad the empty space to the 1.44MB floppy disk specification
# to prevent the virtual machine BIOS from misinterpreting it.
dd if=temp.bin of=os.img bs=512 count=2880 conv=sync
# 4. QEMU 실행
# Run QEMU
qemu-system-x86_64 -drive file=os.img,format=raw,if=floppy -boot a
👉️코드 설명 / Code Explanation
✔️ boot.asm
— 레지스터를 0으로 초기화 합니다.
Initialize the register to zero.
mov ax, 0
mov ds, ax
mov es, ax
1) 세그먼트 레지스터는 직접 값을 대입할 수 없습니다.
You cannot directly assign values to segment registers.
2) 그래서 범용레지스터에 0을 대입하고 이 범용 레지스터를 세그먼트레지스터에 다시 대입합니다.
So, we assign 0 to a general-purpose register and then assign that register to a segment register.
📓16비트 컴퓨터 세그먼트레지스터 / 16-bit computer segment register
ds (Data Segment):
코드에서 사용할 문자열, 변수 등의 데이터 주소 기준점을 가리킵니다.
It points to the reference point for data addresses—such as strings and variables—used in the code.
es (Extra Segment):
디스크에서 데이터를 읽어오거나 메모리를 복사할 때 쓰는 추가 목적지 주소 기준점입니다.
This is an additional destination address reference point used when reading data from a disk or copying memory.
cs (Code Segment):
CPU가 지금 실행하고 있는 기계어 코드의 주소 기준점입니다.
It is the reference address for the machine code currently being executed by the CPU.
ss (Stack Segment):
함수 호출이나 임시 데이터 저장에 쓰는 스택 메모리의 주소 기준점입니다
It is the reference address for the stack memory used for function calls or temporary data storage.
— mov si, msg_boot 여기 si레지스터에 msg_boot에 저장된 값의 주소를 저장합니다.mov si, msg_boot stores the address of the value held in msg_boot into the si register.
1)msg_boot변수에는 msg_boot db ‘1. Booting MBR…’, 13, 10, 0 이렇게 저장되어 있습니다.
The msg_boot variable stores the following: msg_boot db '1. Booting MBR…', 13, 10, 0.
mov si, msg_boot
— 함수를 실행합니다.
Executes the function.
1)파일 아래에 print_string: 라벨이있습니다.
There is a print_string: label at the bottom of the file.
2) call print_string하면 돌아올 주소를 스택에 저장하고 점프합니다.
Calling print_string saves the return address onto the stack and then jumps.
3) 형태는 라벨이지만 이것을 기능적으로 함수라고 합니다.
Although it takes the form of a label, functionally it is referred to as a function.
call print_string
— 디스크 읽기 / Disk read
1)디스크 섹터를 메모리로 읽어오겠다(Read Sectors)”는 기능 번호(0x02)를 선택하는 명령입니다.
This is a command to select function number 0x02, which reads disk sectors into memory.
mov ah, 0x02
2)디스크에서 몇 개의 섹터를 읽을지 지정합니다. 여기서는 1 섹터(512바이트)만큼 읽겠다는 뜻입니다.
Specifies how many sectors to read from the disk. In this case, it means reading one sector (512 bytes).
mov al, 1
3) (실린더 번호)디스크 트랙의 위치를 나타내는 실린더(Cylinder) 번호를 0번으로 지정합니다. 디스크의 맨 바깥쪽 가장자리를 뜻합니다.
(Cylinder Number) Specifies the cylinder number—representing the position of the disk track—as 0. This refers to the outermost edge of the disk.
mov ch, 0
4)(섹터 번호)디스크의 몇 번째 섹터부터 읽기 시작할지 지정합니다. 부트 로더(MBR)가 1번 섹터에 들어있으므로, 그 바로 다음 영역인 2번 섹터를 지정한 것입니다.
(Sector Number) Specifies the starting sector on the disk for the read operation. Since the boot loader (MBR) is located in sector 1, sector 2—the area immediately following it—has been specified.
mov cl, 2
5)(헤드 번호)디스크의 앞뒷면을 결정하는 헤드(Head) 번호를 0번으로 지정합니다. (단면 또는 첫 번째 면을 뜻함)
(Head Number) Specifies the head number—which determines the front or back side of the disk—as 0 (indicating the single side or the first side).
mov dh, 0
6)(저장할 메모리 주소)디스크에서 읽어온 512바이트 데이터를 메모리의 몇 번지 자리에 저장할지 지정하는 목적지 주소입니다.
(Memory address for storage) This is the destination address specifying where in memory the 512 bytes of data read from the disk should be stored.
앞서 데이터 세그먼트(ES)를 0으로 초기화했으므로, 실제 저장 위치는 0x0000:0x8000이 됩니다.
Since the data segment (ES) was previously initialized to 0, the actual storage location becomes 0x0000:0x8000.
mov bx, 0x8000
7)(인터럽트 실행)위에서 설정한 모든 레지스터 정보를 가지고 BIOS 디스크 인터럽트를 최종 호출(실행)합니다.
(Interrupt Execution) The BIOS disk interrupt is finally called (executed) using all the register information configured above.
디스크 하드웨어가 이 신호를 받아 지정된 데이터를 메모리에 복사해 넣습니다.
The disk hardware receives this signal and copies the specified data into memory.
int 0x13
✅ 부트로더가 1번인것을 어떻게 알 수 있나?
How can I tell that the bootloader is number 1?
컴퓨터 하드웨어(CPU와 BIOS)를 만든 제조사들이 전 세계적인 규칙(표준 디바이스 규격)으로 그렇게 정해놓았기 때문입니다.
This is because the manufacturers of computer hardware (CPU and BIOS) established it that way as a global rule (standard device specification).
하드디스크, 플로피디스크, USB 등 모든 부팅 매체의 맨 첫 번째 칸(1번 섹터, 512바이트)을 MBR(마스터 부트 레코드) 또는 부트 섹터라고 부릅니다.
The very first segment (Sector 1, 512 bytes) of any boot medium—such as a hard disk, floppy disk, or USB drive—is called the MBR (Master Boot Record) or boot sector.
시스템이 켜질 때 메인보드의 BIOS가 무조건 디스크의 첫 번째 섹터(실린더 0, 헤드 0, 섹터 1)를 가장 먼저 읽도록 설계되어 있기 때문입니다.(디스크 섹터 번호는 1부터 시작)
This is because the motherboard's BIOS is designed to always read the disk's first sector (cylinder 0, head 0, sector 1) first when the system powers on (disk sector numbering starts at 1).
— 오류 확인 / Check for errors
1)js = Jump if Carry
2)BIOS 디스크 읽기 인터럽트(int 0x13)는 작업을 마친 후 성공 여부를 CPU의 Carry Flag(CF)라는 신호등에 표시해 주도록 약속되어 있습니다.
The BIOS disk read interrupt (int 0x13) is designed to indicate the success or failure of an operation after completion using a signal known as the CPU’s Carry Flag (CF).
3)Carry Flag = 0은 디스크 읽기 성공이고 Carry Flag = 1은 디스크 읽기 실패입니다.
Carry Flag = 0 indicates a successful disk read, while Carry Flag = 1 indicates a failed disk read.
4)Carry Flag(캐리 플래그)가 1로 세팅되어 있다면 disk_error 라벨로 점프합니다.
If the Carry Flag is set to 1, the program jumps to the disk_error label.
jc disk_error
— 로드된 2번 섹터(sector2.asm)로 점프합니다.
Jump to the loaded sector 2 (sector2.asm).
jmp 0x8000
— 오류를 출력합니다.
Outputs an error.
disk_error:
mov si, msg_error
call print_string
jmp hang
1) 출력할 에러메세지를 지정합니다.
Specifies the error message to be output.
mov si, msg_error
2) 에러메세지를 화면에 출력합니다.
Displays the error message on the screen.
call print_string
3) 대기모드 진입(hang라벨로 점프)
Enter standby mode (jump to ‘hang’ label)
jmp hang
— 대기모드입니다.(자기 자신의 위치로 점프합니다.)
Standby mode. (Jumps to its own position.)
hang:
jmp $
— 메모리에 저장된 문자열을 한 글자씩 읽어서, 화면에 연속으로 출력하는 함수입니다.
This function reads a string stored in memory character by character and outputs it to the screen continuously.
1)SI 레지스터가 가리키는 메모리 주소에서 딱 1바이트(문자 1글자)를 읽어와 AL 레지스터에 저장합니다.
It reads exactly one byte (a single character) from the memory address pointed to by the SI register and stores it in the AL register.
1-1)동시에, 다음 글자를 읽을 수 있도록 SI 레지스터의 주소 값을 자동으로 1 증가시킵니다.
At the same time, the address value of the SI register is automatically incremented by 1 to allow the next character to be read.
1-2)이 명령어 한 줄로 “글자 읽기 + 다음 글자로 화살표 이동”이 동시에 일어납니다.
With this single command, “reading the character” and “moving the arrow to the next character” happen simultaneously.
lodsb
2)문자열의 끝인지 확인합니다.(문자열 끝에 0이 있는지 확인)
Checks if it is the end of the string (checks for a null terminator at the end of the string).
cmp al, 0
3)끝났으면 함수 종료 위치(.done)로 합니다.
If finished, proceed to the function termination point (.done).
4) je (Jump if Equal) , AL이 0이라면 .done 라벨 위치로 점프하라는 뜻입니다.je (Jump if Equal) means to jump to the .done label if AL is 0.
je .done
— 텔레타이프 형식으로 화면 출력을 설정합니다.(한 글자씩 출력)
Configures the screen output to teletype format (printing one character at a time).
mov ah, 0x0e
— BIOS 비디오 인터럽트를 실행합니다. CPU는 AL 레지스터에 들어있는 문자를 화면 커서 위치에 바로 찍어줍니다.
It executes the BIOS video interrupt. The CPU directly displays the character contained in the AL register at the screen cursor’s position.
int 0x10
— 함수 종료 및 복귀
Function Termination and Return
.done:
ret
1)je 명령어가 탈출하는 목적지 라벨입니다. (이름 앞에 마침표.가 붙은 것은 print_string 함수 내부에서만 쓰는 로컬 라벨이라는 뜻입니다.)
This is the destination label that the je instruction jumps to. (A period at the beginning of the name indicates a local label used only within the print_string function.)
.done
2)ret (Return): 이 함수를 호출했던 원래 자리(call print_string 바로 다음 줄)로 CPU 실행 위치를 돌려보내는 명령입니다.
ret (Return): An instruction that returns the CPU’s execution point to the original location where the function was called (the line immediately following call print_string).
ret
— 변수를 선언부분입니다.
This is the section where variables are declared.
1)각 변수에 문자를 1바이트씩 저장합니다.
Store one byte of character data in each variable.
2)13은 캐리지 리턴(\r)으로 커서를 현재 줄의 맨 왼쪽(첫 번째 칸)으로 이동시킵니다.
13 is the carriage return (\r), which moves the cursor to the far left (the first column) of the current line.
3)10 라인 피드(\n)로 커서를 현재 위치에서 정확히 한 줄 아래로 내립니다.
10 Moves the cursor exactly one line down from its current position using a line feed (\n).
msg_boot db '1. Booting MBR...', 13, 10, 0
msg_success db '2. Disk read success!', 13, 10, 0
msg_error db 'Disk read failed!', 13, 10, 0
— 계산된 횟수만큼 바이트(db) 공간을 전부 0으로 채우라는 명령어입니다.
This command fills the specified number of bytes (db) with zeros.
1) 512바이트에서 510바이트는 부트로더 및 데이터를 저장하는 공간입니다.
Of the 512 bytes, 510 bytes are used to store the bootloader and data.
2) 나머지 마지막 2바이트는 부팅가능 여부를 판별하기 위한 시그니처로 사용되며 보통 16진수 0xAA55가 들어갑니다.
The final two bytes serve as a signature to determine bootability and typically contain the hexadecimal value 0xAA55.
times 510 - ($ - $$) db 0
— 부팅 확인코드 기록 / Record Boot Verification Code
1) dw는 “define word”의 약자로, 2바이트(16비트) 데이터를 선언하거나 저장할 때 사용합니다.dw stands for “define word” and is used to declare or store 2-byte (16-bit) data.
2) 부트 섹터의 0xaa55값을 마지막에 기록합니다.
Write the value 0xaa55 to the end of the boot sector.
3) 이 값이 있어야 BIOS가 해당 섹터를 부팅 가능한 것으로 인식합니다.
The BIOS requires this value to recognize the sector as bootable.
dw 0xaa55