操作系统的引导

来源:互联网 发布:js 数组包含对象 编辑:程序博客网 时间:2024/05/22 00:19

一、源代码

!bootsect.s !当 PC 的电源打开后,80x86 结构的 CPU 将自动进入实模式,并从地址 0xFFFF0 开始自动执行程序代码,这!个地址通常是 ROM-BIOS 中的地址。PC 机的 BIOS 将执行某些系统的检测,在物理地址 0 处开始初始化中!断向量。此后,它将可启动设备的第一个扇区读入内存地址 0x7C00 处,并跳转到这个地方。此代码即为引导扇!区的代码,它将完成自身的复制迁移,加载setup.s设置程序,最后在终端显示启动字符串,并跳到setup程序执!行。.globl begtext,begdata,begbss,endtext,enddata,endbss.textbegtext:.databegdata:.bssbegbss:.textSETUPLEN=4BOOTSEG=0x07c0INITSEG=0x9000SETUPSEG=0x9020entry startstart:!将自身从内存0x7c00处复制到0x90000处!ds:si=>es:di 执行cx次movw    mov ax,#BOOTSEG    mov ds,ax    mov ax,#INITSEG    mov es,ax    sub si,si    sub di,di    mov cx,#256    rep     movw    jmpi go,INITSEG !段间跳转,设置cs为INITSEGgo:    mov ax,cs    mov ds,ax!cs=>ds    mov es,ax!cs=>es!从磁盘加载setup程序到内存0x90200处!调用0x13号中断!输入:ah为功能号,0x02为从磁盘读到内存,al为读取扇区个数!dh为磁头号,dl为驱动器号,ch为柱面(磁道)号,cl为开始扇区,es:bx指向数据缓冲区!如果出错,CF标志置位,ah返回出错码load_setup:    xor dx,dx    mov cx,#0x0002    mov ax,#0x0200+SETUPLEN    mov bx,#0x0200    int #0x13    jnc ok_load_setup    !复位磁盘控制器,重试    !调用0x13号中断    !ah为功能号,0x0为复位,dl为驱动器号    xor dl,dl    xor ah,ah    int #0x13    j load_setup!显示字符串!调用0x10号中断!输入:ah功能号为0x13表示显示字符串,al为0x01表示光标停在字符串结尾处!es:bp指向显示字符串的起始位置,cx为字符串长度,dh为显示位置行号,dl为列号,bh为显示页号,bl为字符属性ok_load_setup:!读取光标位置!调用0x10号中断!ah功能号为0x03,bh为页号!输出dh为光标位置行号,dl为列号    mov ah,#0x03    xor bh,bh    int 0x10    mov cx,#25    mov bx,#0x000A    mov bp,#msg1    mov ax,#0x1301    int 0x10    jmpi 0,SETUPSEG!跳转到setup程序段执行msg1:    .byte 13,10    .ascii "Funix is booting..."    .byte 13,10,13,10!ASCII码:13为回车,10为换行.org    510 !定位当前偏移地址为510    .word 0xAA55!有效引导扇区的标志.textendtext:.dataenddata:.bssendbss:
!setup.s!被bootsect.s加载到内存0x90200处,首先显示一个字符串,然后打印光标位置、内存尺寸以及磁盘参数信息.globl begtext,begdata,begbss,endtext,enddata,endbss.textbegtext:.databegdata:.bssbegbss:.textBOOTSEG=0x07c0INITSEG=0x9000SETUPSEG=0x9020entry startstart:    mov ax,cs    mov ds,ax    mov es,ax    mov ah,#0x03    xor bh,bh    int 0x10    mov cx,#23    mov bx,#0x0007    mov bp,#msg1    mov ax,#0x1301    int 0x10    mov ax,#INITSEG    mov ds,ax!设置硬件信息保存的段地址为INITSEG    mov ah,0x03    xor bh,bh    int 0x10!读取光标位置    mov [0],dx!保存到ds:0x0处    !取扩展内存的大小值    !调用0x15号中断,ah功能号为0x88取系统所含扩展内存大小    !返回ax=从0x100000(1M)处开始的扩展内存大小(KB),若出错则CF置位,ax=出错码    mov ah,#0x88    int 0x15    mov [2],ax!保存到ds:0x2处    !取第一个硬盘的信息    !第一个硬盘参数表的首地址是中断向量0x41的向量值,第二个硬盘参数表紧接其后,为0x46。    !表长为16个字节    !ds:si=>es:di    mov ax,#0x0000    mov ds,ax    lds si,[4*0x41]!取中断向量0x41的值,即hd0参数表的地址->ds:si    mov ax,#INITSEG    mov es,ax    mov di,#0x0080    mov cx,#0x10    rep    movsb    mov ax,#INITSEG    mov ds,ax    mov ax,#SETUPSEG    mov es,ax    mov ah,#0x03    xor bh,bh    int 0x10    mov cx,#11    mov bx,#0x0007    mov bp,#cur    mov ax,#0x1301    int 0x10!打印提示字符串    mov ax,[0x0]!取出光标位置    call print_hex!打印数值    call print_nl!打印回车换行    mov ah,#0x03    xor bh,bh    int 0x10    mov cx,#12    mov bx,#0x0007    mov bp,#mem    mov ax,#0x1301    int 0x10    mov ax,[0x2]    call print_hex!打印内存大小    mov ah,#0x03    xor bh,bh    int 0x10    mov cx,#25    mov bx,#0x0007    mov bp,#cyl    mov ax,#0x1301    int 0x10    mov ax,[0x80]    call print_hex!打印柱面个数    call print_nl    mov ah,#0x03    xor bh,bh    int 0x10    mov cx,#8    mov bx,#0x0007    mov bp,#head    mov ax,#0x1301    int 0x10    mov ax,[0x80+0x02]    call print_hex!打印磁头个数    call print_nl    mov ah,#0x03    xor bh,bh    int 0x10    mov cx,#8    mov bx,#0x0007    mov bp,#sect    mov ax,#0x1301    int 0x10    mov ax,[0x80+0x0e]    call print_hex!打印每磁道扇区数    call print_nlok: j ok !停止!将ax中16位数字打印成按十六进制显示字符输出print_hex:    mov cx,#4!打印4个字符    mov dx,ax!参数ax传递给dxprint_digit:    rol dx,#4!循环左移4位,最高位变为最低位    mov ah,#0xe!中断功能号,显示al中的字符    mov al,dl    and al,#0xf!高4位置零,设置al为显示数字    add al,#0x30!转换ascii码    cmp al,#0x39    jbe good_digit!不超过9直接中断打印出来    add al,#0x41-0x30-0xa!超过9加上'A'-'0'-10设置为大写字母显示good_digit:    int 0x10!调用中断0x10    loop print_digit!循环打印4个字符    ret !中断打印回车换行print_nl:    mov ax,#0xe0d    int 0x10    mov al,#0xa    int 0x10    retmsg1:    .ascii "Now we are in SETUP"    .byte 13,10,13,10cur:    .ascii "Cursor POS:"mem:    .ascii "Memory SIZE:"cyl:    .ascii "KB"    .byte 13,10,13,10    .ascii "HD Info"    .byte 13,10    .ascii "Cylinders:"head:    .ascii "Headers:"sect:    .ascii "Sectors:".textendtext:.dataenddata:.bssendbss: 

二、无心插柳

在和室友交流的过程中,发现他setup.s中调用print_hex函数传递参数使用的是栈,下面讨论一下这种情况。

!不管代码是否优雅,纯粹是用栈实现一下!此为调用push axcall print_hexpop ax!函数实现print_hex:    push bp!保存bp    mov bp,sp!设置bp为此时刻的sp,此时栈中有三个字——ax,ip和bp    push ds!保存ds    mov cx,ss    mov ds,cx!设置ds段地址为栈段地址    mov cx,#4    mov dx,(bp+4)!按照bp向上偏移4字节访问参数axprint_digit:    rol dx,#4    mov ah,#0xe    mov al,dl    and al,#0xf    add al,#0x30    cmp al,#0x39    jbe good_digit    add al,#0x41-0x30-0xagood_digit:    int 0x10    loop print_digit    pop ds!恢复ds    pop bp!恢复bp    ret

对setup.s而言,上面的实现可谓很不优雅,因为print_hex函数传递参数可以用寄存器直接传递(像一部分那样),也可以直接访问0x900xx地址(不具有普遍性,不可取),用栈本身也是用内存,只不过此处使用的是默认的栈分配而非手动指定分配的内存。但是突然对栈有了更明确的理解:之前写汇编函数上来就是push ebp;mov ebp,esp感觉是习惯就没怎么思考,这次才想明白ebp的作用只是保存那一时刻的esp,因为之后再push,pop可能会改变esp的值,但ebp基本不变就能得到恒定的参数表达或者访问形式。注意call本身调用的时候相当于先push eip,然后段内近跳转,所以用ebp访问参数的时候不能忘了还有eip的偏移,同理ret相当于pop eip。

三、运行结果

这里写图片描述

查看Bochs配置文件bochs/bochsrc.bxrc

megs: 16
mode=flat, cylinders=204, heads=16, spt=38

发现扩展内存大小刚好是15M,加上1M为16M,其他柱面数,磁头数和每磁道扇区数都和结果一致。Bingo!

四、参考资料

  • HIT-OSLAB-MANUAL操作系统的引导
  • LDS指令
  • INT 10中断功能
  • INT13中断详解
  • INT 15中断功能
0 0
原创粉丝点击