0%

L2-揭开钢琴的盖子

  • 计算机由五大部件组成:控制器、运算器(二者组成CPU)、存储器、输入设备、输出设备

L2 揭开钢琴的盖子

  • 计算机由五大部件组成:控制器、运算器(二者组成CPU)、存储器、输入设备、输出设备

  • 计算机如何工作:重复取指、执行的操作(见计算机组成原理)

  • 由前所述,计算机是通过不断地取指、执行操作来工作的,那么在计算机刚启动时,就应该有一个初始的IP,指向最初的工作内容,下图以x86为例进行介绍。

    由CS和IP找到ROM BIOS(Basic Input Output System)映射区,然后找到引导扇区(磁盘0磁道0扇区),从引导扇区开始运行第一个程序。

bootsect.s

  • 下面是引导扇区的代码,bootsect.s

    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
    .globl begtext, begdata, begbss, endtext, enddata, endbss
    .text //文本段
    begtext:
    .data //数据段
    begdata:
    .bss //未初始化数据段
    begbss:
    .text

    SETUPLEN = 4 ! setup程序代码占用扇区数
    BOOTSEG = 0x07c0 ! bootsect.s程序代码所在内存原始地址
    INITSEG = 0x9000 ! 将bootsect.s移动到0x9000处
    SETUPSEG = 0x9020 ! setup程序开始的地址

    entry _start //关键字entry告诉链接器“程序入口”

    _start:
    ! 下面这段代码将bootsect.s的程序代码从0x07c0处挪动到0x9000处
    mov ax,#BOOTSEG
    mov ds,ax //给了bootsect.s代码原地址
    mov ax,#INITSEG
    mov es,ax //给了bottsect.s代码目标地址
    mov cx,#256 //bootsect.s程序代码的字数
    sub si,si
    sub di,di
    rep movw //将0x07c0:0x0000处的256个字挪动到0x9000:0x0000处

    ! 挪动完成后从0x9000的go标号处开始执行
    jmpi go,INITSEG //将代码挪动完成后间接跳转到go标号处执行挪动的代码

    go: mov ax,cs
    mov ds,ax !设置ds=es=cs
    mov es,ax

    ! 加载setup.s程序,将setup程序读到0x9020:0x0000处
    load_setup:
    mov dx,#0x0000 ! drive 0, head 0
    mov cx,#0x0002 ! sector 2, track 0;从第2个扇区开始读setup的代码
    mov bx,#0x0200 ! address = 512, in INITSEG
    mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors;从第2个扇区开始读4个扇区
    int 0x13 // BIOS中断
    jnc ok_load_setup ! ok - continue
    !加载错误
    mov dx,#0x0000
    mov ax,#0x0000 //复位
    int 0x13
    j load_setup //重读

    ! 成功将setup.s程序读到0x9020:0x0000处后,输出一些信息,这里输出显示"FOS is Loading..."
    ! 接着读入system模块到0x1000:0x0000处;
    ! 最后间接跳转到0x9020:0x0000处,执行setup.s代码
    ok_load_setup:
    mov ah,#0x03 ! read cursor pos
    xor bh,bh
    int 0x10 //读光标
    mov cx,#23 //字符串长度为17,加上三个换行加回车的6个字符
    mov bx,#0x000c ! page 0, attribute c
    mov bp,#msg1 ! es:bp 指向待显示字符串
    mov ax,#0x1301 ! write string, move cursor
    int 0x10 //显示字符
    mov ax,#SYSSEG //SYSSEG = 0x1000
    mov es,ax
    call read_it //读入system模块
    jmpi 0,SETUPSEG //间接跳转到0x9020:0x0000处,执行setup.s

    msg1:
    .byte 13,10
    .ascii "FOS is Loading..."
    .byte 13,10,13,10

    .org 510
    boot_flag:
    .word 0xAA55
    .text
    endtext:
    .data
    enddata:
    .bss
    endbss:

    总结bootsect.s的功能:将bootsect.s的程序代码从0x07c0处挪动到0x9000处,然后跳转到go标号处开始执行;再将setup.s程序读到0x9020:0x0000处(bootsect代码是256个字,512个字节,0x90000+0x200=0x90200),并读入system模块;最后跳转到0x9020:0x0000处执行setup.s代码。之所以要将bootsect.s的程序代码从0x07c0处挪动到0x9000处是因为,如果不挪动bootsect.s程序代码,那么在读入system模块的时候,system模块的代码会将bootsect.s的程序代码覆盖掉。

---------------The End---------------