美文网首页
x86 从实模式到保护模式 学习笔记(1)

x86 从实模式到保护模式 学习笔记(1)

作者: 嗒小木 | 来源:发表于2017-09-14 17:07 被阅读0次

    第五章 编写主引导扇区代码

    本章介绍了一个简短的主引导扇区代码,其主要功能是在屏幕上显示“label offset”,并显示一个标号的物理地址。

    如何在屏幕上显示

    在主引导扇区阶段, 0xB8000-0xBFFFF这段物理地址对应的内存是留给显卡的,每屏2000个字符,按顺序存放在这段空间里。所以我们想要在屏幕上输出,就要改写这段内存。

        mov ax,0xb800                 ;指向文本模式的显示缓冲区
        mov es,ax                     ;以下显示字符串"Label offset:"
        mov byte [es:0x00],'L'
        mov byte [es:0x01],0x07
    

    这段代码主要用于改写0xB8000位置的第一个字符表示区域,先把0xb800放入寄存器es中,然后以es作为基地址载入L的ASC码,最后输入8bit颜色信息。总共使用16bit显示一个字符。

    将标号地址输出到屏幕上

    下一步想要保存一个标号的物理地址,首先我们获得这个标号的地址:

        mov ax,number                 ;取得标号number的偏移地址
        mov bx,10
    

    然后分别求每一位的地址:

        ;设置数据段的基地址
        mov cx,cs
        mov ds,cx
    
        ;求个位上的数字
        mov dx,0
        div bx
        mov [0x7c00+number+0x00],dl   ;保存个位上的数字
    

    需要注意的是,在做16位除法时,被除数的高8位需要保存在寄存器dx中,低8位需要保存在ax中,做完除法后,商保存在ax中,余数保存在dx中。
    接下来,我们把存好的十进制数输出到显示屏上:

        ;以下用十进制显示标号的偏移地址
        mov al,[0x7c00+number+0x04]
        add al,0x30
        mov [es:0x1a],al
        mov byte [es:0x1b],0x04
    

    最后,使程序进入无限循环,并且补充主引导扇区到512字节,并补充结尾标志:0x55AA

        infi: jmp near infi                 ;无限循环
          
        number db 0,0,0,0,0
      
        times 203 db 0
                  db 0x55,0xaa
    

    本章的代码解读就到这里,后面有一些调试相关的内容,不在此赘述

    一个小问题

    代码中设置了数据段基地址,但是却没有用到,这是因为什么?跟全局描述符表有关系么?

    第六章 相同的功能,不同的代码

    本章主要介绍了显示上一章讲述内容的不同实现方法

    打印label offset

        jmp near start
         
    mytext db 'L',0x07,'a',0x07,'b',0x07,'e',0x07,'l',0x07,' ',0x07,'o',0x07,\
            'f',0x07,'f',0x07,'s',0x07,'e',0x07,'t',0x07,':',0x07
    number db 0,0,0,0, 0
    
    start:
         mov ax,0x7c0                  ;设置数据段基地址 
         mov ds,ax
         
         mov ax,0xb800                 ;设置附加段基地址 
         mov es,ax
         
         cld
         mov si,mytext                 
         mov di,0
         mov cx,(number-mytext)/2      ;实际上等于 13
         rep movsw
    

    本章节使用的方法是把要打印的内容直接存在内存当中,然后把数据段直接搬运到显示数据段。
    内容较好理解,需要注意的是movsw可以从[ds:si]把数据搬运到[es:di]中去,cld则是方向标志位清零指令,代表由低地址向高地址的方向传送数据,若方向标志位为1,则相反。同时rep movsw则表示“repeat”执行movsw,直到CX寄存器为0。每次执行movsw,CX寄存器自动减一。

    把标号地址存储为十进制

        ;得到标号所代表的偏移地址
        mov ax,number
        
        ;计算各个数位
        mov bx,ax
        mov cx,5                      ;循环次数 
        mov si,10                     ;除数 
    digit: 
        xor dx,dx
        div si
        mov [bx],dl                   ;保存数位
        inc bx 
        loop digit
    

    本章使用了循环来把标号地址转换为十进制。代码也十分简单,不赘述。
    小tips:把number地址保存到ax中时,number地址存在哪里呢?需要占用内存空间么?事实上,mov ax, number这条指令翻译成机器码后,number的地址是直接写入到这条指令中的,事实上这条指令是一条载入立即数的指令,而不是一眼看上去的寄存器之间的指令。

    把标号地址输出到显示器并无限循环

        ;显示各个数位
        mov bx,number 
        mov si,4                      
    show:
        mov al,[bx+si]
        add al,0x30
        mov ah,0x04
        mov [es:di],ax
        add di,2
        dec si
        jns show
        
        mov word [es:di],0x0744
    
        jmp near $
    

    同样的,运用循环的方法把数据搬运到显示区域,那么为什么不仍然使用movsw指令把它们传输过去呢?由于我们的高地址位存放的是十进制中的高位,而去显示的时候则需要地址位存放十进制的高位,所以无论是正向还是反向传输数据都不能满足我们的需求,这时候就需要我们自己来实现这个功能了。

    代码中出现了[bx+si]基址+变址的寻址方式,支持这种寻址方式的挚友一下四种:

    [bx+si]
    [bx+di]
    [bp+si]
    [bp+di]
    

    其他的方式都不支持。最后,我们通过jns show这条指令来保证循环的正确执行。jns这条指令在符号标志位(SF)为0时跳转,为1时不跳转。而dec指令会改变符号标志位,所以在si减为0后再次执行时,si减为0xffff,改变了符号标志位,结束了循环。

    最后,输出了字符D并开始无限循环。

    最终的结束区域使用$符号计算出需要填写0的数量,并写入,然后写入结束标志0x55AA

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

    相关文章

      网友评论

          本文标题:x86 从实模式到保护模式 学习笔记(1)

          本文链接:https://www.haomeiwen.com/subject/nsidsxtx.html