AkiraZheng's Time.

手写操作系统2:编写MBR主引导扇区

Word count: 1kReading time: 4 min
2025/08/23

一、汇编编译器

安装nasm汇编编译器:sudo apt-get install nasm

二、MBR

  1. 编写mbr.s汇编文件,放在boot目录下

    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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    %include "boot.inc"
    SECTION MBR vstart=0x7c00 ;起始地址编译在0x7c00
    mov ax,cs
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov fs,ax
    mov sp,0x7c00
    mov ax,0xb800 ; ax为文本信号起始区
    mov gs,ax ; gs = ax 充当段基址的作用

    ; ah = 0x06 al = 0x00 想要调用int 0x06的BIOS提供的中断对应的函数
    ; 即向上移动即完成清屏功能
    ; cx dx 分别存储左上角与右下角的左边 详情看int 0x06函数调用
    mov ax,0600h
    mov bx,0700h
    mov cx,0
    mov dx,184fh

    ;调用BIOS中断
    int 0x10

    ;新增功能 直接操作显存部分
    ;低位字节储存ascii字符 小端储存内存顺序相反
    mov byte [gs:0x00],'L'

    ;背景储存在第二个字节 含字符与背景属性
    mov byte [gs:0x01],0xA4

    mov byte [gs:0x02],'O'
    mov byte [gs:0x03],0xA4

    mov byte [gs:0x04],'V'
    mov byte [gs:0x05],0xA4

    mov byte [gs:0x06],'E'
    mov byte [gs:0x07],0xA4

    mov byte [gs:0x08],'6'
    mov byte [gs:0x09],0xA4

    mov byte [gs:0x0A],' '
    mov byte [gs:0x0B],0xA4

    mov byte [gs:0x0C],'O'
    mov byte [gs:0x0D],0xA4

    mov byte [gs:0x0E],'S'
    mov byte [gs:0x0F],0xA4

    ; 思考一下 在输出完“Love 6OS后 按照书上的逻辑是要把磁盘读入
    ; 先把配置信息给送到寄存器中”

    ; 为什么要送到 eax中呢 因为IN OUT IO接口
    ; 规定的就是dx里面存放的是端口号 ax是需要或者输送的信息
    mov eax,LOADER_START_SECTOR

    mov bx,LOADER_BASE_ADDR ;把要目标内存位置放进去 bx常作地址储存

    mov cx,4;读取磁盘数 cx常作计数

    call rd_disk_m_16

    jmp LOADER_BASE_ADDR ; 直接当跳跳熊 开跳0x600
    ;------------------------------------------------------------------------
    ;读取第二块硬盘
    rd_disk_m_16:
    ;------------------------------------------------------------------------
    ;1 写入待操作磁盘数
    ;2 写入LBA 低24位寄存器 确认扇区
    ;3 device 寄存器 第4位主次盘 第6位LBA模式 改为1
    ;4 command 写指令
    ;5 读取status状态寄存器 判断是否完成工作
    ;6 完成工作 取出数据

    ;;;;;;;;;;;;;;;;;;;;;
    ;1 写入待操作磁盘数
    ;;;;;;;;;;;;;;;;;;;;;
    mov esi,eax ; !!! 备份eax
    mov di,cx ; !!! 备份cx

    mov dx,0x1F2 ; 0x1F2为Sector Count 端口号 送到dx寄存器中
    mov al,cl ; !!! 忘了只能由ax al传递数据
    out dx,al ; !!! 这里修改了 原out dx,cl

    mov eax,esi ; !!!袄无! 原来备份是这个用 前面需要ax来传递数据 麻了

    ;;;;;;;;;;;;;;;;;;;;;
    ;2 写入LBA 24位寄存器 确认扇区
    ;;;;;;;;;;;;;;;;;;;;;
    mov cl,0x8 ; shr 右移8位 把24位给送到 LBA low mid high 寄存器中

    mov dx,0x1F3 ; LBA low
    out dx,al

    mov dx,0x1F4 ; LBA mid
    shr eax,cl ; eax为32位 ax为16位 eax的低位字节 右移8位即8~15
    out dx,al

    mov dx,0x1F5
    shr eax,cl
    out dx,al

    ;;;;;;;;;;;;;;;;;;;;;
    ;3 device 寄存器 第4位主次盘 第6位LBA模式 改为1
    ;;;;;;;;;;;;;;;;;;;;;


    ; 24 25 26 27位 尽管我们知道ax只有2 但还是需要按规矩办事
    ; 把除了最后四位的其他位置设置成0
    shr eax,cl

    and al,0x0f
    or al,0xe0 ;!!! 把第四-七位设置成0111 转换为LBA模式
    mov dx,0x1F6 ; 参照硬盘控制器端口表 Device
    out dx,al

    ;;;;;;;;;;;;;;;;;;;;;
    ;4 向Command写操作 Status和Command一个寄存器
    ;;;;;;;;;;;;;;;;;;;;;

    mov dx,0x1F7 ; Status寄存器端口号
    mov ax,0x20 ; 0x20是读命令
    out dx,al

    ;;;;;;;;;;;;;;;;;;;;;
    ;5 向Status查看是否准备好惹
    ;;;;;;;;;;;;;;;;;;;;;

    ;设置不断读取重复 如果不为1则一直循环
    .not_ready:
    nop ; !!! 空跳转指令 在循环中达到延时目的
    in al,dx ; 把寄存器中的信息返还出来
    and al,0x88 ; !!! 0100 0100 0x88
    cmp al,0x08
    jne .not_ready ; !!! jump not equal == 0

    ;;;;;;;;;;;;;;;;;;;;;
    ;6 读取数据
    ;;;;;;;;;;;;;;;;;;;;;

    mov ax,di ;把 di 储存的cx 取出来
    mov dx,256
    mul dx ;与di 与 ax 做乘法 计算一共需要读多少次 方便作循环 低16位放ax 高16位放dx
    mov cx,ax ;loop 与 cx相匹配 cx-- 当cx == 0即跳出循环
    mov dx,0x1F0
    .go_read_loop:
    in ax,dx ;两字节dx 一次读两字
    mov [bx],ax
    add bx,2
    loop .go_read_loop

    ret ;与call 配对返回原来的位置 跳转到call下一条指令

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

  2. 将源文件编译成二进制bin文件

    1
    nasm -o mbr.bin mbr.S

CATALOG
  1. 一、汇编编译器
  2. 二、MBR