《操作系统真象还原》之BIOS--软件接力的第一棒

  

  1. 启动计算机,寄存器被初始化,BIOS开始执行
  2. FFFF0—> jmp F000:005C
    CS:F000 IP:005C;内存中最顶端的ROM(Read-Only Memory,只读存储器)中固化有我们的BIOS,上电后CPU就会执行地址为0xFFFF0处的代码,但是我们可以这里距离顶端只有16个字节,可执行的代码有限。所以,这个地址处存放的是一条跳转指令jmp far f000:e05b.
  3. 0xfe05b—>依次执行BIOS中的指令,做一些硬件的检测工作.
  4. BIOS所做的最后一件事:将主引导扇区中(0面0道1扇区,最后两个字节为硬盘主引导扇区的有效标志,必须为0x55,0xaa)的内容加载到7C00的位置处。
  5. BIOS的最后一条指令:JMP 0000:7C00
    CS:0000 IP:7C00

  接力棒即将交出给MBR。本节的代码在MBR控制之后。

  MBR:将硬盘中的loader.S代码读入内存,跳转到代码内存位置,执行loader.S,将接力棒交给loader.S

 

  1. MBR中的代码:将硬盘中的loader.S代码读入内存,跳转到代码内存位置,执行loader.S。(核心)以下是三个实例程序。
  2. ;------------- loader和kernel ----------
    LOADER_BASE_ADDR equ 0x900
    LOADER_START_SECTOR equ 0x2

  3. ;主引导程序
    ;------------------------------------------------------------
    %include "boot.inc"
    SECTION MBR vstart=0x7c00
    mov ax,cs
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov fs,ax
    mov sp,0x7c00
    mov ax,0xb800
    mov gs,ax

    ; 清屏
    ;利用0x06号功能,上卷全部行,则可清屏。
    ; -----------------------------------------------------------
    ;INT 0x10 功能号:0x06 功能描述:上卷窗口
    ;------------------------------------------------------
    ;输入:
    ;AH 功能号= 0x06
    ;AL = 上卷的行数(如果为0,表示全部)
    ;BH = 上卷行属性
    ;(CL,CH) = 窗口左上角的(X,Y)位置
    ;(DL,DH) = 窗口右下角的(X,Y)位置
    ;无返回值:
    mov ax, 0600h
    mov bx, 0700h
    mov cx, 0 ; 左上角: (0, 0)
    mov dx, 184fh ; 右下角: (80,25),
    ; 因为VGA文本模式中,一行只能容纳80个字符,共25行。
    ; 下标从0开始,所以0x18=24,0x4f=79
    int 10h ; int 10h

    ; 输出字符串:MBR
    mov byte [gs:0x00],'1'
    mov byte [gs:0x01],0xA4

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

    mov byte [gs:0x04],'M'
    mov byte [gs:0x05],0xA4 ;A表示绿色背景闪烁,4表示前景色为红色

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

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

    mov eax,LOADER_START_SECTOR ; 起始扇区lba地址
    mov bx,LOADER_BASE_ADDR ; 写入的地址
    mov cx,1 ; 待读入的扇区数
    call rd_disk_m_16 ; 以下读取程序的起始部分(一个扇区)

    jmp LOADER_BASE_ADDR

    ;-------------------------------------------------------------------------------
    ;功能:读取硬盘n个扇区
    rd_disk_m_16:
    ;-------------------------------------------------------------------------------
    ; eax=LBA扇区号
    ; ebx=将数据写入的内存地址
    ; ecx=读入的扇区数
    mov esi,eax ;备份eax
    mov di,cx ;备份cx
    ;读写硬盘:
    ;第1步:设置要读取的扇区数
    mov dx,0x1f2
    mov al,cl
    out dx,al ;读取的扇区数

    mov eax,esi ;恢复ax

    ;第2步:将LBA地址存入0x1f3 ~ 0x1f6

    ;LBA地址7~0位写入端口0x1f3
    mov dx,0x1f3
    out dx,al

    ;LBA地址15~8位写入端口0x1f4
    mov cl,8
    shr eax,cl
    mov dx,0x1f4
    out dx,al

    ;LBA地址23~16位写入端口0x1f5
    shr eax,cl
    mov dx,0x1f5
    out dx,al

    shr eax,cl
    and al,0x0f ;lba第24~27位
    or al,0xe0 ; 设置7~4位为1110,表示lba模式
    mov dx,0x1f6
    out dx,al

    ;第3步:向0x1f7端口写入读命令,0x20
    mov dx,0x1f7
    mov al,0x20
    out dx,al

    ;第4步:检测硬盘状态
    .not_ready:
    ;同一端口,写时表示写入命令字,读时表示读入硬盘状态
    nop
    in al,dx
    and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙
    cmp al,0x08
    jnz .not_ready ;若未准备好,继续等。

    ;第5步:从0x1f0端口读数据
    mov ax, di
    mov dx, 256
    mul dx
    mov cx, ax ; di为要读取的扇区数,一个扇区有512字节,每次读入一个字,
    ; 共需di*512/2次,所以di*256
    mov dx, 0x1f0
    .go_on_read:
    in ax,dx
    mov [bx],ax
    add bx,2
    loop .go_on_read
    ret

  4.  

    %include "boot.inc"
    section loader vstart=LOADER_BASE_ADDR

    ; 输出背景色绿色,前景色红色,并且跳动的字符串"1 MBR"
    mov byte [gs:0x00],'2'
    mov byte [gs:0x01],0xA4 ; A表示绿色背景闪烁,4表示前景色为红色

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

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

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

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

    mov byte [gs:0x0a],'D'
    mov byte [gs:0x0b],0xA4

    mov byte [gs:0x0c],'E'
    mov byte [gs:0x0d],0xA4

    mov byte [gs:0x0e],'R'
    mov byte [gs:0x0f],0xA4

    jmp $ ; 通过死循环使程序悬停在此

     

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

     

posted @ 2022-11-29 22:40  stu--wy  阅读(163)  评论(0)    收藏  举报