哈工大操作系统lab:实验2 操作系统的引导

19 阅读8分钟

** 我自己写了份更详细的6.7的注释代码,我觉得应该已经够详细了罢()**

欢迎来到我的博客:TWind的博客

我的CSDN::Thanwind-CSDN博客

我的掘金:Thanwinde 的个人主页


花了好久终于弄完了第一个操作系统的实验(不知道延时了几次)

激动的我赶紧来写点东西来进食后人()


6.1 开始实验之前

        首先,它的linux-0.11是个tar文件,​编辑你得先 

 tar -zxvf ****.tar.gz

解压之后才会出来实验文件


6.2 完成 bootsect.s 的屏幕输出功能

        这里其实建议先学一学汇编再来看,不然你会      非        常        坐        牢!

注释虽然写得很多,但还是不是很详细,建议搭配ai食用

我就浅写一下我的理解:

entry _start
_start:

没什么好说的,是汇编文件的开始执行的标志

     mov ah,#0x03
    xor bh,bh
    int 0x10

ah对于int 0x10中断来说相当于一个标记,int 0x10会根据ah的值来执行对应的操作

具体可以查表(懒得贴)这里把0x03给了ah,相当于设定了读取光标的任务

xor bh,bh        bh代表页码,给设成0,在默认屏幕输出

int 0x10        执行中断,读取光标位置,默认存储在DH,DL中

    mov cx,#36
    mov bx,#0x0007
    mov bp,#msg1

这里cx是输出字符的字节数(固定的!汇编每一种操作都有固定的寄存器,相当反人类)

bx一般表示输出的属性,颜色这类的

bp就是输出的字符的地址

    mov es,ax
    mov ax,#0x1301
    int 0x10

这里是把ax设成0x1301,这决定了显示的属性:前两位决定了是要输出字符,后两位决定输出的字符跟着光标

接下来调动0x10中断开始输出

inf_loop:
    jmp inf_loop
msg1:
    .byte   13,10
    .ascii  "Hello OS world, my name is LZJ"
    .byte   13,10,13,10
.org 510
boot_flag:
    .word   0xAA55

前两句无限循环,避免终止程序,OS不能终止

接下来msg1是输出的详细信息,.byte是各种回车换行用的

.org可以理解为占用到510字节,毕竟要求bootsect必须有512字节,我们这几行显然不够,最后的

.word 正好占用了两字节,0xAA55相当于标识符,在512字节的最后面,表示你是正统的操作系统文件()


6.3编译和运行

        没啥讲的,照着来即可


6.4 bootsect.s 读入 setup.s

        74如果你懂了上面的这些,这篇代码也不是很难,同样的建议搭配GPT食用

我的bootsect.s

SETUPLEN=2
SETUPSEG=0x07e0
entry _start
_start:
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#32
    mov bx,#0x0007
    mov bp,#msg1
    mov ax,#0x07c0
    mov es,ax
    mov ax,#0x1301
    int 0x10
load_setup:
    mov dx,#0x0000
    mov cx,#0x0002
    mov bx,#0x0200
    mov ax,#0x0200+SETUPLEN
    int 0x13
    jnc ok_load_setup
    mov dx,#0x0000
    mov ax,#0x0000
    int 0x13
    jmp load_setup
ok_load_setup:
    jmpi    0,SETUPSEG
msg1:
    .byte   13,10
    .ascii  "This is my first bootsect~"
    .byte   13,10,13,10
.org 510
boot_flag:
    .word   0xAA55

setup.s:

entry _start
_start:
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#17
    mov bx,#0x0007
    mov bp,#msg2
    mov ax,cs
    mov es,ax
    mov ax,#0x1301
    int 0x10
inf_loop:
    jmp inf_loop
msg2:
    .byte   13,10
    .ascii  "SETUPING..."
    .byte   13,10,13,10
.org 510
boot_flag:
    .word   0xAA55


6.5 再次编译 6.6 修改 build.c

同样没啥讲的


6.7 setup.s 获取基本硬件参数

这更是重量级

极其南蚌

朴实无华的寄存器大乱斗

具体代码不难理解,就是各种操作的各种不同的寄存器很让人头大

~~  建议抄一遍就行  ~~

没有执念的话,转化函数那里可以认真看一下,其他的就算了罢()

        我自己尽了毕生所学写了份详细的注释:

(没放上去跑过!可能编译不过,就当看翻译罢)

INITSEG  = 0x9000   !定义常量,放数据的地址
entry _start    !汇编开始的标志
_start:
! Print "NOW we are in SETUP"
    mov ah,#0x03    !ah功能号,03表示读取光标位置
    xor bh,bh       !把页码bh设成0
    int 0x10        !执行十号中断,读取光标位置并存放在dh dl中
    mov cx,#25      !cx先存输出的长度
    mov bx,#0x0007  !同样是类似功能符 设置输出的属性
    mov bp,#msg2    !bp是存储输出内容地址的地方
    mov ax,cs       !和下一句都是设置正确的段地址来让字符串正常加载
    mov es,ax       
    mov ax,#0x1301  !调整功能号,输出模式
    int 0x10        !十号中断输出

    mov ax,cs       !我觉得是多余了,重置了段寄存器
    mov es,ax
! init ss:sp
    mov ax,#INITSEG
    mov ss,ax       !把栈段地址设成0x9000
    mov sp,#0xFF00  !这是栈指针地址,设置得在本段中比较高的位置,因为栈是先进后出 给call用

! Get Params
    mov ax,#INITSEG
    mov ds,ax       !段地址设成0x9000
    mov ah,#0x03    !同上,获取光标地址
    xor bh,bh
    int 0x10
    mov [0],dx      ![0]代表ds:0x0000,[2]同理
    mov ah,#0x88    !15号中断获取扩展内存大小的功能号
    int 0x15        !15号中断执行
    mov [2],ax      !返回值在ax,存储到[2]
    mov ax,#0x0000  
    mov ds,ax       !把栈段地址设成0x0000 开始读硬盘的信息
    lds si,[4*0x41] !从内存中 0x0041 * 4 处读取一个双字(32 位)的值,将其加载到 ds:si 就是设置开始地址
    mov ax,#INITSEG
    mov es,ax       
    mov di,#0x0004  !从[4]继续写数据
    mov cx,#0x10    !执行0x10次 也就是 16次
    rep
    movsb           !重复读16次 从ds:si到es:di

! Be Ready to Print
    mov ax,cs
    mov es,ax
    mov ax,#INITSEG
    mov ds,ax       !重置段地址 准备输出

! Cursor Position
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#18
    mov bx,#0x0007
    mov bp,#msg_cursor
    mov ax,#0x1301
    int 0x10
    mov dx,[0]      !输出前缀
    call    print_hex   !调用自定义的输出函数
! Memory Size
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#14
    mov bx,#0x0007
    mov bp,#msg_memory
    mov ax,#0x1301
    int 0x10
    mov dx,[2]
    call    print_hex   !不再赘述
! Add KB
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#2
    mov bx,#0x0007
    mov bp,#msg_kb
    mov ax,#0x1301
    int 0x10            !不再赘述
! Cyles
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#7
    mov bx,#0x0007
    mov bp,#msg_cyles
    mov ax,#0x1301
    int 0x10
    mov dx,[4]
    call    print_hex   !不再赘述
! Heads
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#8
    mov bx,#0x0007
    mov bp,#msg_heads
    mov ax,#0x1301
    int 0x10
    mov dx,[6]
    call    print_hex   !不再赘述
! Secotrs
    mov ah,#0x03
    xor bh,bh
    int 0x10
    mov cx,#10
    mov bx,#0x0007
    mov bp,#msg_sectors
    mov ax,#0x1301
    int 0x10
    mov dx,[12]
    call    print_hex   !不再赘述

inf_loop:
    jmp inf_loop    !执行完了,不断无限循环

print_hex:
    mov    cx,#4    !重复4遍(一个地址刚好是四个16进制数)
print_digit:
    rol    dx,#4    !把dx向左移4位(bit),相当于把最高位放到最右边
    mov    ax,#0xe0f
    and    al,dl    !和0xe0f与操作 会只剩下最右边的一位 赋给al
    add    al,#0x30 !把al加上0x30 相当于变成对应的数字的ascii
!SF 符号标志 Sign Flag 
!表示减法结果的符号位。
!如果结果为负 最高位为 1 则 SF = 1。
!如果结果为正 最高位为 0 则 SF = 0。
!OF 溢出标志 Overflow Flag
表示有符号数计算是否发生溢出。
如果正数减正数变成负数,或者负数减负数变成正数,就会设置为 1。
否则为 0。
    cmp    al,#0x3a !根据以上信息 如果al比0x3a小 结果为负 SF = 1 OF = 0 代表这是个数字字符
    jl     outp     !如SF != OF 就会执行跳转 到outp输出
    add    al,#0x07 !能执行到这里证明al比0x3a大 为字母字符 再额外加上0x07变成ascii
outp:
    int    0x10     !输出字符
    loop   print_digit  !没有处理4遍的话 回到头再处理(检测cx) 随后cx--
    ret    !返回到调用处(return;)
print_nl:       !这里是一个输出换行符的功能 但是没用到
    mov    ax,#0xe0d     ! CR
    int    0x10
    mov    al,#0xa     ! LF
    int    0x10
    ret

msg2:
    .byte 13,10
    .ascii "NOW we are in SETUP"
    .byte 13,10,13,10
msg_cursor:
    .byte 13,10
    .ascii "Cursor position:"
msg_memory:
    .byte 13,10
    .ascii "Memory Size:"
msg_cyles:
    .byte 13,10
    .ascii "Cyls:"
msg_heads:
    .byte 13,10
    .ascii "Heads:"
msg_sectors:
    .byte 13,10
    .ascii "Sectors:"
msg_kb:
    .ascii "KB"

.org 510
boot_flag:
    .word 0xAA55