本文是以下几篇文章对应的主引导扇区代码汇编代码:

         ;代码清单17-1
         ;文件名:c17_mbr.asm
         ;文件说明:硬盘主引导扇区代码 
         ;创建日期:2012-07-13 11:20        ;设置堆栈段和栈指针 
         
         core_base_address equ 0x00040000   ;常数,内核加载的起始内存地址 
         core_start_sector equ 0x00000001   ;常数,内核的起始逻辑扇区号 

;===============================================================================
SECTION  mbr  vstart=0x00007c00         

         mov ax,cs      
         mov ss,ax
         mov sp,0x7c00
      
         ;计算GDT所在的逻辑段地址
         mov eax,[cs:pgdt+0x02]             ;GDT的32位物理地址 
         xor edx,edx
         mov ebx,16
         div ebx                            ;分解成16位逻辑地址 

         mov ds,eax                         ;令DS指向该段以进行操作
         mov ebx,edx                        ;段内起始偏移地址 

         ;跳过0#号描述符的槽位 
         ;创建1#描述符,保护模式下的代码段描述符
         mov dword [ebx+0x08],0x0000ffff    ;基地址为0,界限0xFFFFF,DPL=00 
         mov dword [ebx+0x0c],0x00cf9800    ;4KB粒度,代码段描述符,向上扩展 

         ;创建2#描述符,保护模式下的数据段和堆栈段描述符 
         mov dword [ebx+0x10],0x0000ffff    ;基地址为0,界限0xFFFFF,DPL=00
         mov dword [ebx+0x14],0x00cf9200    ;4KB粒度,数据段描述符,向上扩展 

         ;初始化描述符表寄存器GDTR
         mov word [cs: pgdt],23             ;描述符表的界限   
 
         lgdt [cs: pgdt]
      
         in al,0x92                         ;南桥芯片内的端口 
         or al,0000_0010B
         out 0x92,al                        ;打开A20

         cli                                ;中断机制尚未工作

         mov eax,cr0                  
         or eax,1
         mov cr0,eax                        ;设置PE位
      
         ;以下进入保护模式... ...
         jmp dword 0x0008:flush             ;16位的描述符选择子:32位偏移
                                            ;清流水线并串行化处理器
         [bits 32]               
  flush:                                  
         mov eax,0x00010                    ;加载数据段(4GB)选择子
         mov ds,eax
         mov es,eax
         mov fs,eax
         mov gs,eax
         mov ss,eax                         ;加载堆栈段(4GB)选择子
         mov esp,0x7000                     ;堆栈指针
         
         ;以下加载系统核心程序
         mov edi,core_base_address

         mov eax,core_start_sector
         mov ebx,edi                        ;起始地址
         call read_hard_disk_0              ;以下读取程序的起始部分(一个扇区)

         ;以下判断整个程序有多大
         mov eax,[edi]                      ;核心程序尺寸
         xor edx,edx
         mov ecx,512                        ;512字节每扇区
         div ecx

         or edx,edx
         jnz @1                             ;未除尽,因此结果比实际扇区数少1
         dec eax                            ;已经读了一个扇区,扇区总数减1
   @1:
         or eax,eax                         ;考虑实际长度≤512个字节的情况
         jz pge                             ;EAX=0 ?

         ;读取剩余的扇区
         mov ecx,eax                        ;32位模式下的LOOP使用ECX
         mov eax,core_start_sector
         inc eax                            ;从下一个逻辑扇区接着读
   @2:
         call read_hard_disk_0
         inc eax
         loop @2                            ;循环读,直到读完整个内核

   pge:
         ;准备打开分页机制。从此,再也不用在段之间转来转去,实在晕乎~ 
         
         ;创建系统内核的页目录表PDT
         mov ebx,0x00020000                 ;页目录表PDT的物理地址
         
         ;在页目录内创建指向页目录表自己的目录项
         mov dword [ebx+4092],0x00020003 

         mov edx,0x00021003                 ;MBR空间有限,后面尽量不使用立即数
         ;在页目录内创建与线性地址0x00000000对应的目录项
         mov [ebx+0x000],edx                ;写入目录项(页表的物理地址和属性)      
                                            ;此目录项仅用于过渡。
         ;在页目录内创建与线性地址0x80000000对应的目录项
         mov [ebx+0x800],edx                ;写入目录项(页表的物理地址和属性)

         ;创建与上面那个目录项相对应的页表,初始化页表项 
         mov ebx,0x00021000                 ;页表的物理地址
         xor eax,eax                        ;起始页的物理地址 
         xor esi,esi
  .b1:       
         mov edx,eax
         or edx,0x00000003                                                      
         mov [ebx+esi*4],edx                ;登记页的物理地址
         add eax,0x1000                     ;下一个相邻页的物理地址 
         inc esi
         cmp esi,256                        ;仅低端1MB内存对应的页才是有效的 
         jl .b1
         
         ;令CR3寄存器指向页目录,并正式开启页功能 
         mov eax,0x00020000                 ;PCD=PWT=0
         mov cr3,eax

         ;将GDT的线性地址映射到从0x80000000开始的相同位置 
         sgdt [pgdt]
         mov ebx,[pgdt+2]
         add dword [pgdt+2],0x80000000      ;GDTR也用的是线性地址
         lgdt [pgdt]

         mov eax,cr0
         or eax,0x80000000
         mov cr0,eax                        ;开启分页机制
   
         ;将堆栈映射到高端,这是非常容易被忽略的一件事。应当把内核的所有东西
         ;都移到高端,否则,一定会和正在加载的用户任务局部空间里的内容冲突,
         ;而且很难想到问题会出在这里。 
         add esp,0x80000000                 
                                             
         jmp [0x80040004]  
       
;-------------------------------------------------------------------------------
read_hard_disk_0:                           ;从硬盘读取一个逻辑扇区
                                            ;EAX=逻辑扇区号
                                            ;DS:EBX=目标缓冲区地址
                                            ;返回:EBX=EBX+512 
         push eax 
         push ecx
         push edx
      
         push eax
         
         mov dx,0x1f2
         mov al,1
         out dx,al                          ;读取的扇区数

         inc dx                             ;0x1f3
         pop eax
         out dx,al                          ;LBA地址7~0

         inc dx                             ;0x1f4
         mov cl,8
         shr eax,cl
         out dx,al                          ;LBA地址15~8

         inc dx                             ;0x1f5
         shr eax,cl
         out dx,al                          ;LBA地址23~16

         inc dx                             ;0x1f6
         shr eax,cl
         or al,0xe0                         ;第一硬盘  LBA地址27~24
         out dx,al

         inc dx                             ;0x1f7
         mov al,0x20                        ;读命令
         out dx,al

  .waits:
         in al,dx
         and al,0x88
         cmp al,0x08
         jnz .waits                         ;不忙,且硬盘已准备好数据传输 

         mov ecx,256                        ;总共要读取的字数
         mov dx,0x1f0
  .readw:
         in ax,dx
         mov [ebx],ax
         add ebx,2
         loop .readw

         pop edx
         pop ecx
         pop eax
      
         ret

;-------------------------------------------------------------------------------
         pgdt             dw 0
                          dd 0x00008000     ;GDT的物理/线性地址
;-------------------------------------------------------------------------------                             
         times 510-($-$$) db 0
                          db 0x55,0xaa