操作系统-从零开始编写
- 教程:https://github.com/ruiers/os-tutorial-cn
01-引导扇区
02-引导打印
mov ah, 0x0e ; 设置功能号为 0x0E,TTY 模式输出字符
mov al, 'H' ; 将字符 'H' 的 ASCII 码加载到 AL 寄存器
int 0x10 ; 调用 BIOS 中断 0x10,显示字符
mov al, 'e'
int 0x10
mov al, 'l'
int 0x10
int 0x10 ; 'l' is still on al, remember?
mov al, 'o'
int 0x10
jmp $ ; jump to current address = infinite loop
; padding and magic number
times 510 - ($-$$) db 0
dw 0xaa55
-
输出字符到屏幕
- AH = 0x0E:表示调用 BIOS 的 0x10 中断,用于显示字符(TTY 模式)。
- AL:要显示的字符存储在 AL 寄存器中。
- int 0x10:BIOS 提供的显示服务中断。0x10 是视频服务的功能号。
- 每调用一次 int 0x10,就会将 AL 中的字符显示在屏幕上。
-
显示 “Hello”
mov al, 'e' int 0x10 ; 显示 'e' mov al, 'l' int 0x10 ; 显示 'l' int 0x10 ; 再次显示 'l',因为 AL 仍然是 'l' mov al, 'o' int 0x10 ; 显示 'o'
- 这几行代码逐步将 “Hello” 的每个字符显示在屏幕上。
- 第一次显示 ‘l’ 后,未修改 AL,直接再次调用 int 0x10 会再次显示 ‘l’。
-
无限循环
jmp $ ; $ 表示当前地址,跳转到自身 = 无限循环
-
填充和魔术数字
times 510 - ($-$$) db 0 dw 0xaa55
- times 指令:用于填充字节到指定长度。$:表示当前地址。$$:表示文件的起始地址。$-$$:表示当前代码(已经写的)的大小。times 510 - ($-$$) db 0:将文件填充到 510 字节(一个扇区的大小为 512 字节,保留最后的 2 字节给魔术数字)。填充内容为 0。
- dw 0xaa55:定义双字(2 字节)的值 0xAA55。0xAA55 是启动扇区的魔术数字,BIOS 通过检查这个值来判断该扇区是否是一个有效的启动扇区。如果最后两字节不是 0xAA55,BIOS 会认为这个扇区不可引导。
代码执行流程
- BIOS 加载扇区:BIOS 在启动时,会读取磁盘的第一个扇区(512 字节)到内存的 0x7C00 地址。检查最后两字节是否为 0xAA55,如果是,则跳转到加载地址执行。
- 显示 “Hello”:程序通过调用 int 0x10 的功能号 0x0E,逐字输出 “Hello”。
- 进入无限循环:程序最后通过 jmp $ 进入无限循环,防止后续执行未知指令。
补充知识
times 指令,NASM 汇编器中的伪指令,用于重复生成指定数量的指令或数据。
times <count> <value>
-
$ 和 $$ 符号
- $:表示当前地址。
- $$:表示当前段的起始地址(通常是文件或代码段的开始位置)。
- $ - $$:计算从段的起始地址到当前地址的偏移量,表示当前代码的大小。
-
db 0
- db(Define Byte):定义一个字节。
- db 0:定义一个值为 0 的字节。
- 在这里,db 0 用作填充值,填充代码区域未使用的部分。