实模式下的“Hello World”

boot.asm
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[org 0x7C00]
[bits 16]

start:
    ; 设置80x25文本模式
    mov ah, 0x00
    mov al, 0x03
    int 0x10

    ; 在屏幕上输出字符
    mov ah, 0x0E
    mov al, 'X'
    int 0x10

    ;  低功耗循环
    sti           ; 允许中断
idle_loop:
    hlt           ; 暂停 CPU
    jmp idle_loop ; 中断后恢复
    
;-----------------------------
; MBR签名填充
;-----------------------------
times 510-($-$$) db 0  ; 填充剩余空间(510 - 已用空间)
dw 0xAA55	          ; MBR有效签名(55h AAh)
nasm -f bin boot.asm -o boot.bin
qemu-system-i386 -drive format=raw,file=boot.bin

mov ah, 0x00
mov al, 0x03
int 0x10

具有清屏效果,不加的话 qemu运行会多出如下图部分

org 0x7C00 指令解析

  • 汇编器默认假设程序从地址 0x0000 开始汇编
  • org 0x7C00 用于显式指定代码的实际加载地址是内存中的 0x7C00
  • 设置此指令后,所有标签、偏移量和地址引用都会以 0x7C00 为基准计算,从而确保程序在内存中能正常运行

简而言之:这是告诉汇编器“代码虽然写在文件开头,但将来运行时会被放在 0x7C00,因此请按这个地址来排布和计算”

$$$

NASM 汇编中,$$ 表示当前段(section)的起始地址(不是 CPU 的段寄存器概念)

对于没有显式分段的简单引导程序,$$ 默认就是整个程序的起始地址

$ 表示当前行的地址(即当前指令/数据的汇编地址)

$-$$ 的实际意义

  • 计算的是:从程序开始到当前位置的字节数 因为 $$ 是程序起点,$ 是当前位置,所以 $-$$ 就是已生成的二进制代码的字节数

  • 示例: 假设代码从 org 0x7C00 开始,前 3 条指令共占用 5 字节:

    0x7C00: mov ah, 0x00  (2字节)
    0x7C02: mov al, 0x03  (2字节)
    0x7C04: int 0x10      (1字节)

    当汇编到 int 0x10 时,$ = 0x7C05$$ = 0x7C00,所以 $-$$ = 5(已用 5 字节)

times 510-($-$$) db 0 的作用

  • 填充至 510 字节

    引导扇区必须是 512 字节,最后 2 字节是签名 0xAA55,因此前 510 字节需要填满代码和数据

  • 动态计算填充量

    510-($-$$) 会根据已生成的代码长度,自动计算需要填充的 0 的个数

    例如:若已用 30 字节,则填充 510-30=4800

x86 引导扇区开发注意事项

基本限制

  1. 512字节大小

    传统MBR必须严格限制在512字节内

  2. 签名要求

    最后两个字节必须是0x550xAA

  3. 执行位置

    BIOS会将引导扇区加载到0x7C00内存地址

开发注意事项

  1. CPU模式

    启动时CPU处于16位实模式

  2. 寄存器初始值

    BIOS执行后DL寄存器包含启动驱动器号

  3. 堆栈设置

    需要自行设置堆栈(通常SS:SP = 0x0000:0x7C00)

  4. 内存布局

    0x0000-0x03FF是中断向量表,0x0040-0x005F是BIOS数据区

代码组织

  1. ORG指令

    需要使用ORG 0x7C00告诉汇编器代码加载位置

  2. 引导标志

    分区表中通常需要设置活动分区标志(0x80)

  3. 分区表

    如果需要分区表,它占用MBR的最后64字节(偏移0x1BE-0x1FD)

BIOS 中断

中断调用规范

通用调用步骤

  1. 设置功能号:AH 寄存器
  2. 设置参数:其他寄存器
  3. 调用中断:INT xxh
  4. 检查返回:通常 CF=0 成功,CF=1 失败

寄存器使用约定

寄存器 典型用途
AH 功能号
AL 数据/子功能
BX 缓冲区偏移/通用
CX 计数/通用
DX 端口/通用
ES:BX 缓冲区指针
CF 进位标志(错误指示)

中断技术

中断向量表修改

cli                ; 禁用中断
mov ax, 0
mov es, ax         ; ES=0
mov word [es:10h*4], new_handler  ; 设置IP
mov word [es:10h*4+2], cs         ; 设置CS
sti                ; 启用中断

中断链式调用

original_int dd 0

; 保存原中断
mov ax, [es:10h*4]
mov [original_int], ax
mov ax, [es:10h*4+2]
mov [original_int+2], ax

; 在自定义处理程序中
my_handler:
    pushf
    call far [original_int]  ; 调用原中断
    ...

注意事项

  1. 实模式限制:BIOS 中断只能在实模式下使用
  2. 寄存器保存:中断处理程序必须保存和恢复所有使用的寄存器
  3. 速度问题:BIOS 调用比直接硬件访问慢
  4. 兼容性:不同 BIOS 实现可能有差异

INT 19h:启动加载中断

传统 BIOS 系统中,int 0x19 通常由 BIOS 在 POST 完成后自动调用,以启动操作系统的引导过程

int 0x19

是 BIOS 提供的 “Bootstrap Loader” 中断

INT 19h 的功能:

  • 按启动顺序尝试读取启动设备第一个扇区(512 字节)
  • 将其加载到内存地址 0x0000:0x7C00(即物理地址 0x07C00
  • 然后跳转执行 0x0000:0x7C00 的代码(即 MBR)

你写的 bootloader 就是放在这里运行的!

INT 10h:视频服务

功能设置方式:AH 指定功能,其他寄存器传递参数

AH 功能 参数 返回值
00h 设置显示模式 AL=模式号
01h 设置光标形状 CH=起始扫描线, CL=结束扫描线
02h 设置光标位置 BH=页号
DH=行, DL=列
03h 获取光标位置 BH=页号 CH/CL=光标形状
DH/DL=位置
0Eh 电传输出字符 AL=字符
BH=页号, BL=颜色(图形模式)
13h 写字符串 AL=模式
BH=页号, BL=颜色
CX=长度
DH/DL=行/列
ES:BP=字符串指针

示例

mov ah, 0Eh    ; 电传输出功能
mov al, 'A'    ; 要显示的字符
mov bh, 0      ; 显示页号
int 10h        ; 调用视频中断

◉ 设置文本显示模式

mov ah, 0x00    ; 功能号 0x00 = 设置显示模式
mov al, 0x03    ; 模式参数 0x03 = 80x25 16色文本模式
int 0x10        ; 调用BIOS视频中断

功能详解

  1. AH=0x00 BIOS 中断 INT 0x10 的功能号,表示"设置显示模式"
  2. AL=0x03 指定具体的显示模式:
    • 0x03:经典的 80列×25行 文本模式,支持 16色 (DOS时代的标准控制台模式)
  3. 实际效果
    • 清空屏幕
    • 重置光标到左上角(0,0)
    • 启用文本模式(非图形模式)
    • 恢复默认配色(黑底灰字)

常见应用场景

  1. 系统启动时初始化显示

    (许多Bootloader会首先调用此功能确保显示正常)

  2. 程序退出时恢复屏幕状态

    (避免程序运行后遗留乱码)

  3. 清除屏幕内容

    (比逐字符输出空格更高效)

其他常用显示模式(AL值)

模式 分辨率 类型 备注
0x03 80×25 文本 最常用
0x12 640×480 图形 16色 VGA
0x13 320×200 图形 256色 “Mode X”

注意事项

  1. 实模式下有效(现代操作系统已不再使用BIOS中断)
  2. 某些虚拟机/模拟器可能不支持非标准显示模式
  3. 图形模式下(如0x13)需要额外设置显存操作

◉ AH = 0x0E:Teletype Output

  1. mov ah, 0x0E
  • 功能:在屏幕上打印一个字符,并自动移动光标(类似老式打字机)
  • 特点:
    • 支持 ASCII 字符(AL 寄存器存放字符)
    • 自动处理 换行(\n)、退格(\b) 等控制字符
    • 光标会跟随移动(类似现代终端)
  1. mov al, ‘X’

    • AL 寄存器存放要打印的字符,这里传入 'X'(ASCII 码 0x58

    • 可以替换为任意 ASCII 字符,例如:

      mov al, 'A'    ; 打印 'A'
      mov al, 0x41   ; 同上(ASCII 'A' = 0x41)
      mov al, 10     ; 打印换行(\n)
      mov al, 13     ; 打印回车(\r,回到行首)

    关键细节

    1. 字符颜色和属性
    • 默认颜色:白字黑底(取决于 BIOS 设置)
    • 如果要修改颜色,可以用INT 0x10 的其他功能(如 AH=0x09 写字符和属性)
    1. 光标位置
    • 每次调用 AH=0x0E 后,光标自动右移

    • 可以用 AH=0x02 手动设置光标位置:

      mov ah, 0x02
      mov bh, 0     ; 页号(通常 0)
      mov dh, 12    ; 行(0~24)
      mov dl, 40    ; 列(0~79)
      int 0x10
    1. 换行处理
    • \n(ASCII 10)只会换行,不回车

    • 通常需要结合 \r(ASCII 13)实现完整换行:

      mov ah, 0x0E
      mov al, 13    ; 回车(回到行首)
      int 0x10
      mov al, 10    ; 换行(下移一行)
      int 0x10

打印字符串

[org 0x7C00]      ; 引导程序加载地址
[bits 16]

start:
    xor ax,ax
    mov ds,ax
       
    mov si, hello   ; DS:SI 指向字符串
    call print_string

    sti 
idle_loop:
    hlt  
    jmp idle_loop

print_string:
    mov ah, 0x0E      
.next_char:
    lodsb             ; 取 DS:SI 指向的字节放入 AL,并 SI++
    cmp al, 0
    je .done
    int 0x10          
    jmp .next_char
.done:
    ret

hello db "hello,world!", 0

times 510-($-$$) db 0
dw 0xAA55
[org 0x7C00]
[bits 16]

start:
  mov si,0

print:
   mov ah,0x0e
   mov al,[hello+si]
   int 0x10
   add si,1
   cmp byte [hello+si] ,0
   jne print

   sti 
idle_loop:
    hlt  
    jmp idle_loop
hello:
 db "hello,world!",0
 
times 510-($-$$) db 0
dw 0xAA55

区别在于 字符串遍历方式代码结构

  1. 字符串遍历方式

    第一段代码(lodsb + call 结构)

  • 使用 lodsb 指令

    • lodsb(Load String Byte)会自动从 DS:SI 读取一个字节到 AL,并递增 SI
    • 更简洁,不需要手动计算偏移量
  • 封装成 call 子程序

    • 可复用,适合多次打印不同字符串

    第二段代码(手动索引 [hello+si]

  • 手动计算字符串地址

    • 通过 [hello+si] 直接访问字符串的每个字符
    • 需要手动 add si, 1 递增索引
  • 直接内联代码

    • 没有封装成子程序,适合简单的一次性打印
  1. 代码结构

    第一段代码(结构化,可复用)

  • 使用 call print_string 调用子程序,适合更复杂的程序

  • 字符串 message 可以定义在任意位置,只要 DS:SI 指向它即可


    第二段代码(直接内联,简单)
  • 直接在 print 循环里完成所有操作,适合短小代码

  • 字符串 hello 必须紧邻代码,因为 [hello+si] 是硬编码偏移

INT 13h:磁盘服务

AH 功能 参数 返回值
00h 复位磁盘 DL=驱动器号(80h=主硬盘,81h=从硬盘) CF=0成功,CF=1失败;AH=状态码
01h 获取磁盘状态 DL=驱动器号 AH=上次操作的状态码(00h=无错误)
02h 读扇区(CHS) AL=扇区数 CH=柱面(低8位),CL=扇区(位0-5)|柱面高2位(位6-7) DH=磁头,DL=驱动器 ES:BX=数据缓冲区 CF=状态;AH=错误码,AL=实际读取扇区数
03h 写扇区(CHS) 同02h 同02h
04h 校验扇区(CHS) 同02h(无数据缓冲区) CF=状态;AH=错误码
05h 格式化磁道(CHS) AL=交错值(通常为0) CH/DH/CL/DL=同02h ES:BX=格式化参数表地址 CF=状态;AH=错误码
08h 获取驱动器参数 DL=驱动器号 CF=状态; CH=最大柱面低8位,CL=扇区数(位0-5)|柱面高2位(位6-7) DH=最大磁头数,DL=驱动器总数 ES:DI=磁盘参数表地址(部分BIOS)
09h 初始化驱动器参数 DL=驱动器号 CF=状态;AH=错误码
0Ch 寻道(CHS) CH/CL=柱面,DH=磁头,DL=驱动器 CF=状态;AH=错误码
0Dh 复位磁盘控制器 DL=驱动器号 CF=状态;AH=错误码
15h 检测LBA支持(EDD) DL=驱动器号 CF=状态; AH=扩展类型(0=不支持,1=EDD 1.0,2=EDD 3.0) CX:DX=总扇区数(若支持)
41h 检查EDD扩展是否安装 BX=55AAh,DL=驱动器号 CF=状态; BX=AA55h(若支持),AH=扩展版本(01h=1.x,20h=2.0+) C=1支持EDD
42h 读扇区(LBA) DL=驱动器号 DS:SI=磁盘地址包(DAP)指针 CF=状态;AH=错误码
43h 写扇区(LBA) 同42h 同42h
44h 校验扇区(LBA) 同42h(无数据缓冲区) CF=状态;AH=错误码
45h 锁定/解锁驱动器 DL=驱动器号 CF=状态;AH=错误码
46h 弹出可移动介质 DL=驱动器号 CF=状态;AH=错误码
47h 扩展寻道(LBA) DL=驱动器号 DS:SI=DAP(仅需LBA地址) CF=状态;AH=错误码
48h 获取扩展驱动器参数 DL=驱动器号 DS:SI=返回缓冲区地址 CF=状态; 缓冲区包含:LBA总扇区数、物理扇区大小等信息

示例

mov ah, 02h    ; 读磁盘功能
mov al, 1      ; 读取1个扇区
mov ch, 0      ; 柱面0
mov cl, 2      ; 扇区2
mov dh, 0      ; 磁头0
mov dl, 80h    ; 硬盘0
mov bx, 0x7E00 ; 读取到内存0x7E00处
int 13h        ; 调用磁盘中断
jc error       ; 如果出错(CF=1)跳转

◉ AH = 0x02:读扇区

📘 参数要求(使用 CHS 模式):

寄存器 含义
AH = 0x02 功能号:读取扇区
AL 要读的扇区数(最多 128)
CH 柱面号低 8 位
CL bits 0–5 = 扇区号(1–63)bits 6–7 + CH = 柱面高 2 位(合成 10 位柱面)
DH 磁头号
DL 驱动器号(0x80=第一个硬盘)
ES:BX 数据缓冲区地址(目标内存)

📗 返回:

  • CF 清除 → 成功
  • CF 设置 → 错误,AH 为错误代码

🚧 BIOS CHS 模式的局限

  • 扇区号最大只能是 63(6 位)
  • 磁头号最大只能是 255(8 位)
  • 柱面号最大只能是 1023(10 位)

所以最大容量 ≈ 1024×256×63×512B ≈ 8.4GB

⚠️ 这就是为什么老旧 BIOS 无法启动大于 8.4GB 的硬盘

📚 想读取多个扇区怎么办?

比如你想读取第 2~4 个扇区,需要计算对应的 CHS 坐标并多次调用 INT 13h 或调整 AL 为多扇区读取

注意:不能跨越柱面/磁头边界,否则需重新设置 CH/DH/CL

关键功能说明

  1. LBA扩展功能(AH=42h-48h):

    • 需通过 DAP(Disk Address Packet) 传递参数,结构如下:

      DAP STRUCT
        db 10h      ; 包大小(16字节)
        db 0         ; 保留
        dw 扇区数     ; 读取/写入的扇区数
        dd 缓冲区地址 ; 内存缓冲区(32位段:偏移)
        dq LBA地址    ; 64位LBA起始扇区号
      DAP ENDS
    • 突破CHS的 8GB限制,支持大容量磁盘。

  2. 驱动器号约定:

    • 00h-7Fh:软盘驱动器(如00h=第一软驱)
    • 80h-FFh:硬盘驱动器(如80h=主硬盘,81h=从硬盘)
  3. 状态码(AH返回值):

    • 00h:成功
    • 01h:无效命令
    • 02h:地址标记未找到
    • 03h:写保护
    • 04h:扇区未找到
    • 80h:驱动器未响应

INT 16h:键盘服务

AH 功能 参数 返回值
00h 读取键 AH=扫描码, AL=ASCII码
01h 检查键 ZF=1(无键), ZF=0(有键)
02h 获取键盘状态 AL=键盘状态字节

示例

mov ah, 00h    ; 等待按键
int 16h        ; 调用键盘中断
cmp al, 1Bh    ; 检查是否按了ESC
je exit        ; 如果是则退出

自定义中断向量

[org 0x7C00]      ; 引导程序加载地址
[bits 16]

start:
    ; 设置自定义中断向量
    cli                     ; 关闭中断
    mov ax, 0
    mov es, ax              ; ES=0(中断向量表段址)
    mov word [es:0x60*4], my_handler    ; 设置IP
    mov word [es:0x60*4+2], cs          ; 设置CS
    sti                     ; 重新开启中断

    ; 触发自定义中断测试
    int 0x60

    sti 
idle_loop:
    hlt  
    jmp idle_loop

; 自定义中断处理程序
my_handler:
    pusha
    push ds
    
    mov ax, cs
    mov ds, ax              ; 确保DS=CS(方便访问数据)
    
    ; 中断处理逻辑(示例:打印字符)
    mov ah, 0x0E            ; BIOS tele-type功能
    mov al, '!'
    int 0x10                ; 调用BIOS显示中断
    
    pop ds
    popa
    iret                    ; 中断返回

; 引导扇区填充
times 510-($-$$) db 0
dw 0xAA55

中断唤醒

键盘中断唤醒休眠中的CPU

[org 0x7C00]      ; 引导程序加载地址
[bits 16]

start:
    cli
    
    ; 初始化段寄存器
    xor ax, ax              ; AX = 0
    mov ds, ax              ; 数据段DS = 0
    mov es, ax              ; 附加段ES = 0
    
    ;===========================================
    ; 设置键盘中断向量(IRQ1对应INT 0x09)
    ; 中断向量表位于内存0x0000:0x0000处
    ; 每个中断向量占4字节(段:偏移)
    ;===========================================
    mov word [0x9*4], keyboard_isr  ; 设置中断处理程序的偏移地址
    mov word [0x9*4+2], cs          ; 设置中断处理程序的段地址(CS)
    
    ;===========================================
    ; 配置8259 PIC(可编程中断控制器)
    ; 允许键盘中断(IRQ1)
    ;===========================================
    in  al, 0x21            ; 读取主PIC的IMR(中断屏蔽寄存器)
    and al, 0b11111101      ; 清除第1位(允许IRQ1)
                            ; 0b11111101 = 0xFD
    out 0x21, al            ; 写回IMR
    
    sti                     ; 开中断(Enable Interrupts)
    
;-------------------------------------------------
; 主循环:等待键盘中断
;-------------------------------------------------
wait_for_key:
    hlt                     ; 暂停CPU,直到有中断发生
    jmp wait_for_key        ; 中断返回后继续等待
    
;=================================================
; 键盘中断服务例程(ISR)
; 触发条件:当有键盘按键被按下或释放时
; 中断号:INT 0x09(IRQ1)
;=================================================
keyboard_isr:
    push ax                 ; 保存AX寄存器
    
    in  al, 0x60            ; 从键盘端口0x60读取扫描码
    test al, 0x80           ; 检查扫描码最高位(1=按键释放,0=按键按下)
    jnz .skip_print         ; 如果是按键释放,跳过打印
    
    ; 如果是按键按下,打印字符'K'
    mov ah, 0x0E            ; BIOS显示功能号(Teletype输出)
    mov al, 'K'             ; 要显示的字符
    int 0x10                ; 调用BIOS视频服务
    
.skip_print:
    ; 发送EOI(End Of Interrupt)信号给PIC
    mov al, 0x20            ; EOI命令码
    out 0x20, al            ; 发送到主PIC的命令端口
    
    pop ax                  ; 恢复AX寄存器
    iret                    ; 中断返回

times 510-($-$$) db 0 
dw 0xAA55 

显存编程

显存版 “Hello,World”

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
mov ax,0xb800
mov ds,ax

mov byte [0x00],'a'
mov byte [0x02],'s'
mov byte [0x04],'m'
 
sti 
idle_loop:
  hlt  
  jmp idle_loop
    
times 510-($-$$) db 0
db 0x55,0xaa

分析:

  • 0xb800 是 VGA 彩色文本模式的显存段
  • 每个字符占两个字节(字符 + 属性),这里只写了字符,属性默认为0(通常是黑白)

运行效果:

增强版:为字符添加颜色属性

通过一次写入 word(两个字节),我们可以同时设置字符和其属性(如背景蓝、前景黄):

mov ax,0xb800
mov ds,ax

mov word [0x00], 0x1E61   ; 'a'
mov word [0x02], 0x2E73   ; 's'
mov word [0x04], 0x4E6D   ; 'm'
 
sti 
idle_loop:
  hlt  
  jmp idle_loop

times 510-($-$$) db 0
db 0x55,0xaa

属性字节结构:高 4 位为背景色,低 4 位为前景色

颜色代码 含义
0x1 蓝色背景
0xE 黄色前景

显示效果如下:

显示字符串

[org 0x7C00]             ; 设置代码起始偏移地址为0x7C00,这是BIOS加载引导扇区的默认地址

; 初始化段寄存器
mov ax, cs               ; 将代码段寄存器的值赋给AX
mov ds, ax               ; 设置数据段寄存器DS,保证可以正确访问msg字符串

mov ax, 0xB800           ; 文本模式下显存的段地址是0xB800
mov es, ax               ; 设置额外段寄存器ES用于写显存(段:偏移方式)

mov si, msg              ; SI指向字符串msg的起始地址
mov di, 0x0000           ; DI设置为0,表示显存中的起始偏移位置(左上角)

.next_char:
    lodsb                ; 从[DS:SI]加载一个字节到AL,并SI自动+1
    cmp al, 0            ; 检查是否是字符串结束符
    je .done             ; 如果是0(null terminator),跳转到.done
    mov ah, 0x1E         ; 设置字符属性:红底黄字(背景红 0100,前景黄色 1110)
    mov [es:di], ax      ; 将字符(AL)和属性(AH)组合写入[ES:DI]
    add di, 2            ; 显存每个字符占两个字节:字符+属性,故偏移加2
    jmp .next_char       ; 跳转回继续处理下一个字符

sti 
idle_loop:
  hlt  
  jmp idle_loop

; 存放要显示的字符串
msg db 'hello, asm world!', 0 ; 字符串内容,以0结尾

; 填充剩余字节,确保总大小为512字节(引导扇区标准)
times 510-($-$$) db 0    ; 用0填充,直到偏移510
db 0x55, 0xaa            ; 引导扇区签名,必须位于末尾两个字节

执行流程

  1. BIOS 加载引导扇区至 0x7C00
  2. 设置段寄存器(DS/ES)
  3. SI指向字符串,DI指向显存起始
  4. 每个字符配上颜色属性写入显存
  5. 显示完毕后停机

显存中每个字符占用 2 字节,因此 DI 每次递增 2

显示效果:

分析

段寄存器初始化

; 设置代码段和数据段
mov ax, cs
mov ds, ax          ; DS指向代码段,用于读取字符串

; 设置显存段
mov ax, 0xb800
mov es, ax          ; ES指向显存段
  • mov ax, cs:将代码段寄存器CS的值复制到AX
  • mov ds, ax:将DS设置为代码段,这样字符串数据可以被正确访问
  • mov ax, 0xb800:0xB800是彩色文本模式显存的段地址
  • mov es, ax:将ES设置为显存段,用于写入显示内容

指针初始化

; SI = 字符串指针
mov si, msg
; DI = 显存偏移地址,从 0x0000 开始
mov di, 0x0000
  • mov si, msg:SI寄存器指向字符串"hello, asm world!“的起始位置
  • mov di, 0x0000:DI寄存器指向显存的起始位置(屏幕左上角)

字符串显示循环

.next_char:
    lodsb               ; AL = [DS:SI], SI++
    cmp al, 0
    je .done            ; 若遇到字符串结尾(0),跳出

    ; 颜色:黄色前景(0xE),蓝色背景(0x1) → 0x1E
    mov ah, 0x1E        ; ah = 属性字节
    mov [es:di], ax     ; 写入显存:字符+属性,使用ES段
    add di, 2           ; 显存指针移到下一个字符位置
    jmp .next_char

lodsb:从内存 [DS:SI] 读取一个字节到AL,然后SI自动加1

cmp al, 0:比较AL是否为0(字符串结束符)

je .done:如果AL=0,跳转到.done标签结束循环

mov ah, 0x1E:设置字符属性

 0x1E = 0001 1110b
 低4位(1110b=0xE):黄色前景
 高4位(0001b=0x1):蓝色背景
 mov [es:di], ax:将字符(AL)和属性(AH)写入显存
 add di, 2:显存中每个字符占用2字节(1字节字符+1字节属性)
 jmp .next_char:继续处理下一个字符

数据定义

; 放置字符串(以0结尾)
msg db 'hello, asm world!', 0
msg:字符串标签
db:定义字节数据
0:字符串结束符(null terminator)

总结与应用场景

学到的内容

  • BIOS 引导机制和MBR 格式
  • 显存操作与字符显示
  • 实模式段寄存器管理
  • 字符属性控制
  • 中断向量表修改
  • PIC 编程与中断响应

应用场景

  • 嵌入式/低功耗系统:用 hlt 等待中断触发
  • 操作系统开发入门:掌握 BIOS 启动、实模式内存管理