保护模式下编程

;1.

;===========================================================

;在保护模式下32位CPU仍然可以用20位地址来实现32位地址线寻址

;16位CPU: 16位段寄存器+16位偏移地址 (经地址加法器) -> 20位物理内存地址

;32位CPU: 32位地址的内存段信息存入在一张内存表中,只需将表的索引存入16寄存器当中即可

;保存表中索引的段寄存器称为:段选择子

;表中每个表示32位内存段信息称为:段描述符(保存了段的地址和段的长度)。    

;整张表称为:段描述符表

;段选择子16位,其中高13位用来表示描述符表中的索引,其低3位用表示段描述符中所指向的段描述符的属性

;

 

;启动程序在屏幕中央打印一行字符串

[BITS 16]

org 07c00h    ;指明程序开始地址是07c00h,而不是原来 的00000

;int 汇编指令    int 10h

jmp main

 

gdt_table_start:    ;告诉编译器段描述符开始

    ;Intel规定描述符表的第一个描述符必须是空描述符

    gdt_null:

        dd 0h

        dd 0h    ;Intel规定段描述符表的第一个表项必须为0

    gdt_data_addr    equ    $-gdt_table_start    ;数据段的开始位置

    gdt_data:    ;数据段描述符

        dw 07ffh ;段界限

        dw 0h    ;段基地址18位

        db 10010010b    ;段描述符的第六个字节属性(数据段)

        db 1100000b    ;段描述符的第七个字节属性

        db 0    ;段描述符的最后一个字节也就是段基地址

    

    gdt_video_addr equ $-gdt_table_start

    gdt_video:    ;用来描述显存地址空间的段描述符

        dw    0FFH    ;显存段界限就是1M

        dw    8000H

        db    0BH

        db    10010010b

        db    11000000b

        db    0

    

    gdt_code_addr    equ    $-gdt_table_start    ;代码段的开始位置

    gdt_code:

        dw 07ff    ;段界限

        dw 1h    ;段基地址0~18位

        db 80h    ;段基地址19~23位

        db 10011010b    ;段描述符的第六个字节(代码段)

        db 11000000b    ;段描述符的第七个字节

        db 0            ;段基地址的第二部分

gdt_table_end:

        

        

;通过lgdt指令可以把GDTR描述表的大小和起始地址存入gdtr寄存器中

gdtr_addr:

    dw gdt_table_end-gdt_table_start-1    ;段描述表长度

    dd gdt_table_start    ;段描述表基地址

;lgdt [gdtr_addr]    ;让CPU读取gdtr_addr所指向内存内容保存到gdtr寄存器当中

;A20地址线,切换到保护模式时,A20地址线必须开启。地址回绕作用,放弃32位CPU地址线的高12位。?

;端口的读写操作:

    ;in     accume port    ;将端口的内容读到寄存器AL或AX当中,其中accume只能是AL或AX。

    ;out port accume    ;将accume中的内容写到端口中,这里accume可以是其它寄存器

    

;开启A20地址线

main:

    ;修改数据段描述跟段基地址有关的字节,初始化数据段描述符的基地址

    xor eax,eax    ;清空eax

    add eax,data_32    ;将32位地址信息拷贝到eax中

    mov word [gdt_data+2],ax    ;把ax中的内容拷贝到段描述符的第3、4两个字节当中,因是word类型的拷贝?

    shr eax,16    ;右移16位

    mov byte [gdt_data+4],al    ;将先前eax中的第5个字节移到段描述符当中

    mov byte [gdt_data+7],ah    ;将先前eax中的第8个字节移到段描述符当中

    

    ;修改代码段描述跟段基地址有关的字节,初始化数据段描述符的基地址

    xor eax,eax

    add eax,code_32

    mov word [gdt_code+2]

    shr eax,16

    mov byte [gdt_code+4],al

    mov byte [gdt_code+7],ah

    

    ;在转放保护模式之前,必须废除原来的中断向量表,用cli指令可以废除实模式下的中断向量表

    cli

    lgdt [gdtr_addr]    ;让CPU读取gdtr_addr所指向内存内容保存到gdtr寄存器当中

 

    enable_a20:

        in al,92h    ;只要往0x92号端口中写入信息就可以开启A20地址线

        or al,00000010b    ;00000010表示开启A20地址线的数据

        out 92h,al        ;把设置好的数据写进0x92号端口当中

    

;转入保护模式,只要将CR0寄存器的第1位(PE位)置为1即可

;80386提供了4个32位的控制寄存器CR0~CR3,其中CR0中的某些位是用来标志是否要进入保护模式

;CR1寄存器保留没有被使用

;CR2和CR3用于分页机制

;CR0的PE位控制分段管理机制,PE=0,CPU运行于实模式;PE=1,CPU运行于保护模式

;CR0的PG位控制分段管理机,PG=0,禁止分页管理机制;PG=1,启用分页管理机制。

    mov eax,cr0

    or eax,1    ;用于把CR0寄存器的第1置为1

    mov cr0,eax    ;把CR0寄存器的第1置为1

;跳转到保护模式中

    jmp gdt_code_addr:0

    

;在保护模式下编程(在屏幕中央打印hello world)

[BITS 32]

    data_32:

        db    "hello world"

    code_32:

        MOV ax,gdt_data_addr

        mov ds,ax

        mov ax,gdt_video_addr

        mov gs,ax

        mov cx,11

        mov edi,(80*10+12)*2    ;在屏幕中央显示

        mov bx,0

        mov ah,0ch

        s:mov al,[ds:bx]

        mov [gs:edi]

        mov [gs:edi+1],ah

        inc bx

        add edi,2

        loop s

        jmp $    ;死循环

        times 510-($-$$)    db 0

        dw 0aa55h

      

      

    

    

;ok ! ^_^

;1.启动虚拟机,用nasm boot.asm -o boot.bin 编译    

;2.把程序写到软盘镜像里去,用编译好的写入文件程序写入: ./write_image boot.bin boot.img

;3.将boot.img复制到自己在Bochs-2.4.6目录下建的文件夹下,并修改run.bat

;-----------------------------------------------

;其中write_image.c,可以在rad hat的vi编辑器这样写:?

#include

#include

#include

#include

 

int main(int argc,char *argv[])

{

    int fd_source;

    int fd_dest;

    int read_count=0;

    char buffer[512]={0};

    fd_source=open("boot.bin",O_RDONLY);

    IF(fd_source<0)

    {

        perror("open boot.bin error");

        return 0;

    }

    fd_dest=open("virtual_floppy.vfd",O_WRONLY);

    while ((read_count=read(fd_source,buffer,512))>0)

    {

    write(fd_dest,buffer,read_count);

    memset(buffer,0,512);

    }

    printf("wrinte image OK !");

    return 0;

}

;保存成write_image.c,然后编译:gcc write_image.c -o write_image

;用虚拟软盘制作工具,制作一个虚拟软盘(取名boot.img),最后再用它将引导程序写入boot.img

; ^_^ ok!

;启动程序有问题的话,可以用bocsh虚拟机对操作系统进行调试。

;===================================================

;bocsh的调试功能bocshdbg

;    continue(c) 程序继续运行直到遇到断点为止

;    step(s)    音步跟踪

;    vbreak(vb)    在虚拟地址上设置一个断点

;    pbreak(b)    在物理地址上设置一个断点

;    lbreak(lb)    在线性地址上设置一个断点

;    disassemble    反汇编指令

;================================================================

;================================================================

 

;2.    一个在屏幕中央显示一行字符串的引导程(实模式下写)

;启动程序在屏幕中央打印一行字符串

 

org 07c00h    ;指明程序开始地址是07c00h,而不是原来 的00000

;int 汇编指令    int 10h

    mov ax,cs

    mov es,ax

    mov bp,msgstr    ;es:bp指向的内容就是我们要显示的字符串地址?

    

    mov cx,12    ;字符串长度

    mov dh,12    ;显示起始行号

    mov dl,36    ;显示的列号

    mov bh,0    ;显示的页数,在第0页显示

    mov al,1    ;串结构

    mov bl,0c    ;黑底红字

    

    msgstr: db "hello my os"

    int 10h        ;BIOS中断

    times 510-($-$$) db 0 ;重复N次每次填充值为0

    ;因为BIOS的第一个扇区是512字节,当最后两字节是55AA时,它就是引导程序?

    dw 55aaH

    jmp $    ;为了不让程序结束,设置一个死循环,不断跳转到当前位置?

    

;在Linux操作系统下,用nasm 进行编译,命令:# nasm boot.asm -o boot.bin

;用 ndisasm boot.bin 可以进行反编译

注:引自大灰狼之视频教程《零基础汇编汇编视频课程》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗哥分享

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值