zoukankan      html  css  js  c++  java
  • 保护模式下编程

    ;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<stdio.h>

    #include<fcnt.h>

    #include<sys/types.h>

    #include<sys/stat.h>

    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 可以进行反编译

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

  • 相关阅读:
    阶段一-01.万丈高楼,地基首要-第2章 单体架构设计与准备工作-2-17 MyBatis 数据库逆向生成工具
    阶段一-01.万丈高楼,地基首要-第2章 单体架构设计与准备工作-2-16 数据源连接数详解
    阶段一-01.万丈高楼,地基首要-第2章 单体架构设计与准备工作-2-14 数据层HikariCP与MyBatis整合
    阶段一-01.万丈高楼,地基首要-第2章 单体架构设计与准备工作-2-13 HikariCP数据源简述
    阶段一-01.万丈高楼,地基首要-第2章 单体架构设计与准备工作-2-12 SpringBoot自动装配简述
    阶段一-01.万丈高楼,地基首要-第2章 单体架构设计与准备工作-2-10 聚合工程整合SpringBoot
    阶段一-01.万丈高楼,地基首要-第2章 单体架构设计与准备工作-2-9 数据库物理外键移除原因讲解
    阶段一-01.万丈高楼,地基首要-第2章 单体架构设计与准备工作-2-8 生产环境增量与全量脚本迭代讲解
    阶段一-01.万丈高楼,地基首要-第2章 单体架构设计与准备工作-2-7 PDMan数据库建模工具使用
    阶段一-01.万丈高楼,地基首要-第2章 单体架构设计与准备工作-2-6 构建聚合工程-2
  • 原文地址:https://www.cnblogs.com/luowei010101/p/1982817.html
Copyright © 2011-2022 走看看