搜档网
当前位置:搜档网 › 保护模式编程

保护模式编程

保护模式编程
保护模式编程

第1章保护模式编程一

如果想更深、更亲近的了解电脑软件。那么学习cpu是你的必选!! 386是CPU史的一大转折点,那386做基础课是最好不过了。那么我们将开始进行学习之旅!!!大家跟我一块学习吧,呵呵!!!

1.1 准备工作

l1、NASM 编译环境(当然Masm 也可以但是用它来写COM程序比较

麻烦)

l2、虚拟机

Virtual PC(Windows平台,执行比较快,即模拟又虚拟硬件)、

WMWarve(WIndows平台虚拟硬件,)、

Bochs(支持Windows平台、也支持在Linux平台上运行有RPM版本的)

我们这些生长在Windows这棵大树下的朋友们,还是用Virtual PC吧.。

l3、写虚拟启动镜像文件的程序

:不知道我观察的对不对?用Nasm 编译一个bin 然后将它转换为img 镜像文件的时候。只要文件大小符合软驱的标准就能启动。那么就代表a.bin 与a.img 文件的内容一模样就是文件大小不一样!我是不太了解镜像文件格式.我用的是Virtual PC。

1.2 开始接触引导程序

1.2.1 Com文件

Com文件是纯二进制的文件,也是直接与Cpu交换的顺序指令文件。Com文件的大小是有限制的,不能超过64KB.因为8086时代的CPU地址线是20位的,20位能表达的数值也就是fffffh(1MB )。而寄存器最高也只是16位,无法用5个F的形式来表达地址,所以用CS(段基地址)*16:IP(偏移地址)来寻址!80386后通用寄存器都得到了32位扩展! 而Cpu地址线也得到了32位的扩展。引导程序前期是需要进入实模式的,因为这是硬件上的限制是IA32的限

制。386cpu只有两种模式: 实模式与保护模式!!!!,,

1.2.2 引导程序

引导程序也是有限制的,这个限制是靠Bios处理的,开机后Bios经过自检后,会从软驱或者硬盘的0面0磁道1扇区搜寻一个程序文件。该文件的数据必需是等于512Byte,并且以aa55h结尾的(高高低低)。那么bios会认为它是引导程序,这个时候就会把该512byte 装载到内存7c00开始处。然后将主控权交给程序的第一行代码。那么这个时候程序脱离Bios的控制。Cpu将执行程序的代码.

1.2.3 写一个引导程序

引导程序可以说是非常简单:

1、boot.asm(nasm 的源文件如下)

;-----------------------欲编译,这里改成100h就是com程序-------------------------------------------

%define _BOOT_DEBUG_ ;做调试的时候用100h

%ifdef _BOOT_DEBUG_

org 0100h

%else

org 07c00h ; 告诉编译器以下代码段将从07c00h内存地址处开始

%endif

mov ax,cs ;让数据段与附加段寄存器跟代码段一样,因为COM代码数据是混合.

mov ds,ax

mov es,ax

call _HelloWorld ;让程序显示一个HelloWorld

jmp $ ;$表示当前地址无限循环

_HelloWorld:

mov ax,strHello ;取得字符串的地址

mov bp,ax ;给堆栈基寄存器

mov cx,strLen

mov ax,1301H ;ah代表功能号

mov bx, 000ch ; 页号为0(BH = 0) 黑底红字(BL = 0Ch,高亮

mov dx,0001h ;显示的行与列

int 10h ;bios 10h显示中断

ret

strHello: db "Hello World"

strLen equ $ - strHello

times 510-($-$$) db 0 ; times重复定义510-($-$$)个$$ 表示段的起始地址

dw 0xAA55

那么引导程序完成了,用

nasm boot.asm -o https://www.sodocs.net/doc/336347631.html,

就可以运行看效果.,如改成引导程序只需把%define _DEBUG_BOOT_注释然后nasm boot.asm -o a.bin然后用工具将a.bin 转换成软驱大小的镜像文件载入虚拟机启动就可以.

第2章保护模式编程二

8086到80386的跳转,80386与8086在硬件上的区别在这就不说了!!那么80386与8086在软件逻辑上面的区别就是:8086是实模式,而80386 不仅包括实模式,而且还可以进入保护模式!!!

保护模式不仅不受64KB内存寻址的限制,而且还拥有4GB的寻址空间。这是因为386扩展了20地址线,将它扩展成32位了(32位能表达的字节数就是4GB).此时的段寄存器不再是段基地了,而被叫做是选择子,存放的是一个段描述符的索引值.而我们的通用寄存器与EIP 也是32位的,可以表达4GB地址!不过计算机开机后,CPU默认是实模式。这就需要我们编程手动转换到386.那么我们该怎么去做呢:

2.1 【准备GDT (全局描述符表)】

首先我们需要准备GDT结构体,它是386保护模式必须的东西。全局描述符寄存器GDTR 指向的是所有段描述符表的信息.前面提到得段选择子索引,指得就是指向段描述符的索引.段描述符是8个字节的结构体、里面存放着段的段界限、段基址、段属性等信息.LDTR寄存器是指向局部某一个段的描述符表。

段描述符表结构体用一个宏来表示(注意段1 2表示同一个段描述内容被分开来放的): 【段基址】32位、表示物理地址

【段界限】20位、表示段的总长度这里并不是地址,而是段的字节长度。

【段属性】12位. 系统、门、数据等属性

%macro Descriptor 3 ; 有三个参数:【段基址】、【段界限】、【段属性】

dw %2 & 0FFFFh ; 段界限1 (2 字节)

dw %1 & 0FFFFh ; 段基址1 (2 字节)

db (%1 >> 16) & 0FFh ; 段基址1 (1 字节)

dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性1 + 段界限2 + 属性2 (2 字节)

db (%1 >> 24) & 0FFh ; 段基址2 (1 字节)

%endmacro ; 共8 字节

看似很简单的结构体理解起来可不是那么简单!

【Descriptor结构体】有8个字节。

1、【第1、2字节】组合(word) 表示该段的[段界限①], dw %2 & 0FFFFh ;引用第二个参数去掉高16位

2、【第

3、

4、5字节】组合表示该段的[段基址①],dw %1 & 0FFFFh ;先得到第一个参数(段基址)低WORD。

3、接着把第5个字节赋值,db (%1 >> 16) & 0FFh 去掉第3第4个字节的内容.再把剩下的字节赋值

4、【第6个字节】是与【第7个字节】组合的内容可就更复杂了:

【第6个字节】的内容:

【7(p) 6(DPL) 5(DPL) 4(S) 3(Type) 2(Type) 1(Type) 0(Type)】

0-3位表示:[段属性]、说明存储段描述符所描述的存储段的具体属性。

4位表示:说明描述符的类型, 对于存储段描述符而言,S=1表示是系统段描述符。

5-6位表示:DPL 该段的特权级别也就是Ring 0-3;

7位表示:P: 存在(Present)位。

; P=1 表示描述符对地址转换是有效的,即描述的段在内存当中.

; P=0 表示描述符对地址转换无效,即该段不存在。使用该描述符进行内存访问时会引起异常

【第7个字节】的内容:

【7(G) 6(D) 5(0 ) 4(AVL) 3(段界限) 2(段界限) 1(段界限) 0(段界限)】

0-3位表示:[段界限②]

4位表示:软件可利用位。80386对该位的使用未做规定,Intel公司也保证今后开发生产的处理器只要与80386兼容,就不会对该位的使用做任何定义或规定。

5位表示:0 ;Intel资料也没表示

6位表示:是一个很特殊的位,在描述可执行段、向下扩展数据段或由SS寄存器寻址的段(通常是堆栈段)的三种描述符中的意义各不相同,通常置1

7位表示:段界限粒度(Granularity)位。

G=0 表示界限粒度为字节;

G=1 表示界限粒度为4K 字节。

注意,界限粒度只对段界限有效,对段基地址无效,段基地址总是以字节为单位。

那么这段宏dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)表示:

取[段界限]参数除去低16位取高4位,得到【段界限②】

取[段属性]参数的低8位12-15位(AVL属性等)

属性1 + 段界限2 + 属性2

【第8个字节】的内容:

[段基址②] 、db (%1 >> 24) & 0FFh 取基地址参数的最高8位

那么一个Descriptor 结构体就这样成形了.

2.2 【编写程序跳转到保护模式】

%include "386.inc" ;是Descriptor结构体宏

;%define _DEBUG_BOOT_

%ifdef _DEBUG_BOOT_

org 0100h

%else

org 07c00h

%endif

jmp LABEL_BEGIN

[SECTION .gdt] ;全局描述符数据段

; 段基址, 段界限, 属性

LABEL_GDT: Descriptor 0, 0, 0 ;空描述符

LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_CR | DA_32 ;代码段描述符

LABEL_DESC_DATA: Descriptor 0,SegDataLen - 1,DA_DRW ;数据段

LABEL_DESC_VIDEO: Descriptor 0B8000h,0FFFFh,DA_DRW ;显示器内存段由于DOS中断不能随意使用了,,只能输出到显示缓冲区

; GDT 结束

GdtLen equ $ - 1

GdtPtr dw GdtLen - 1 ;GDT 的段界限,

dd 0 ;GDT基地址

; GDT 选择子

SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT ;代码相对全局描述符起始地址的EA值SelectorData equ LABEL_DESC_DATA - LABEL_GDT ;数据段

SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT ;显示数据段

[SECTION .s16] ;16位代码段

[BITS 16] ;BITS指出处理器的模式是16位

LABEL_BEGIN:

mov ax, cs

mov ds, ax

mov es, ax

mov ss, ax ;初始化段寄存器

;初始化数据

mov eax,strHello

mov word[LABEL_DESC_DATA + 2],ax

mov byte[LABEL_DESC_DATA + 4],al

shr eax,16

mov byte[LABEL_DESC_DATA + 7],ah

; 初始化并把32位段代码的基地址分配给段描述符

mov eax, LABEL_CODE32 ;

mov word [LABEL_DESC_CODE32 + 2], ax ;ax

shr eax, 16

mov byte [LABEL_DESC_CODE32 + 4], al

mov byte [LABEL_DESC_CODE32 + 7], ah

; 为加载GDTR 作准备

mov eax, LABEL_GDT

mov dword [GdtPtr + 2], eax ;得到GDT基地址

; 加载全局描述符的信息结构体到GDTR

lgdt [GdtPtr]

CA20 ;利用键盘端口打开A20地址线

; 将CRO的PE位也就是0位置1 那么就进入386模式了

mov eax, cr0

or eax, 1

mov cr0, eax

jmp dword SelectorCode32:0 ;

;执行这一句会把SelectorCode32 装入cs, 并跳转到Code32Selector:0 处

;这个描述符集合是以一个空描述符开始得,现在LABEL_DESC_CODE32描述符的索引值因该是8,

;所以SelectorCode32的值应该就是LABEL_DESC_CODE32的索引值,Code32Selector:0当中的0是指LABEL_DESC_CODE32 的段基址+ 0

;那么在打开cr0的PE位后,这个JMP指令不再是直接跳到段地址去了;

;而是去GDTR全局描述符寄存器当中去找这个当前CS的索引,当前段基址+偏移的内存地址了。[SECTION .s32]; 32 位代码段. 由实模式跳入.

[BITS 32]

LABEL_CODE32:

;保护模式的死循环

mov ax, SelectorVideo

mov gs, ax ; 视频段选择子(目的)

mov edi, (80 * 10 + 9) * 2 ; 屏幕第10 行, 第0 列。

mov ah, 1Ch ; 0000: 黑底1100: 红字

mov esi,0

mov ds,SelectData

mov ecx,11

vi:

lodsb

mov [gs:edi], ax

inc edi

LOOPNZ vi

; 到此停止

jmp $

SegCode32Len equ $ - LABEL_CODE32

[SECTION .data] ;数据段

strHello: db "Hello World"

SegDataLen equ $- strHello

2.3 【总结】

编写一个386 程序主要用的步骤:

1、准备GDT描述符集合结构体

2、用lgdt [gdtPtr] 载入gdtPtr 这6个字节的结构体,,低字是描述符集合的界限也就是集合总长度,高双字是描述符集合的基地址.

3、打开A20地址线。有一种方法是向键盘端口IO,

4、置CR0的PE位即0位为1

5、JMP [段索引]:[段基址偏移]

呵呵接下来继续学习啊!!!

第3章保护模式编程三

在一、到二、我们了解386基本寻址机制,没错就是这么简单!!!接下来我们谈谈对上一个386进行扩展:

大家在第二节已经知道了进入386的基本步骤了,那么我们来具体设计吧.

编程首先当然是【声明】与【定义】:

3.1 【声明】:

在386.inc 头文件里定义好需要的宏信息(好东西直接拿来用了呵呵)

;---------------------------------386.inc-----------------------------------------------------

DA_32 EQU 4000h ;32位段

DA_DPL0 EQU 00h ; DPL = 0

DA_DPL1 EQU 20h ; DPL = 1 (表示描述符特权级Ring0-Ring3级)

DA_DPL2 EQU 40h ; DPL = 2

DA_DPL3 EQU 60h ; DPL = 3

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

; 存储段描述符类型值说明

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

DA_DR EQU 90h ; 存在的只读数据段类型值

DA_DRW EQU 92h ; 存在的可读写数据段属性值

DA_DRWA EQU 93h ; 存在的已访问可读写数据段类型值

DA_C EQU 98h ; 存在的只执行代码段属性值

DA_CR EQU 9Ah ; 存在的可执行可读代码段属性值

DA_CCO EQU 9Ch ; 存在的只执行一致代码段属性值

DA_CCOR EQU 9Eh ; 存在的可执行可读一致代码段属性值

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

%macro Descriptor 3 ;3表示宏的参数有3个%1表示是第一个参数的标识>>右移位

dw %2 & 0FFFFh ; 段界限1 (2 字节)

dw %1 & 0FFFFh ; 段基址1 (2 字节)

db (%1 >> 16) & 0FFh ; 段基址2 (1 字节)

dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性1 + 段界限2 + 属性2 (2 字节)

db (%1 >> 24) & 0FFh ; 段基址3 (1 字节)

%endmacro ; 共8 字节

%macro CA20 0 ; 打开地址线A20

in al, 92h

or al, 00000010b

out 92h, al

%endmacro

%macro DA20 0 ;关地址线

in al,92h

and al,11111101b

out 92h,al

%endmacro

3.2 【定义】:

;在定义模块之前我们首先要有个概念,那就是整体的雏形。可以简单划分出来也就是2个主要步骤:

1、定义GDT数据段:

{

1、定义Descriptor即段描述符:(通常是以一个全为零的Descriptor开始)。

2、定义GdtPtr 信息结构体(再加载gdtr时候要用到)。

3、定义每个段描述符对应的索引位置(即定义选择子)。

}

2、定义如上描述的具体段:

{

1、实模式入口段(这是个入口段:用于跳转到386的段,并不属于386段所以没有描述符)

2、剩下的段就全是386模式的段。

}

3.3 【具体编码】

还是要拿实实在在的能运行的代码来讲:

先说一下代码的主要功能:

1、从8086跳到386模式(Protect Mode)

2、在386模式对大地址的寻址测试(超过1MB)

3、测试完毕后回到8086模式(Real Mode)

具体细节上,就看代码吧!!!在386.asm 文件里实行具体模块的编写(代码比较多,刚开始阅读有点复杂,因为是汇编可读性不是很好不过这是照上面的方法定义的,可以先从宏观入手!!!):

;========================386.asm===============================

%include "386.inc" ; 常量, 宏, 以及一些说明

%define _DEBUG_B0OT_

%ifdef _DEBUG_B0OT_

org 0100h

%else

org 07c00h

%endif

jmp LABEL_BEGIN

;===========================;GDT全局描述符数据段============================== [SECTION .gdt]

LABEL_GDT Descriptor 0,0,0 ;以空开头

;这个段描述符描述的段有点特殊,因为在下面并没有实际的定义它。它的作用是从保护模式跳转到8086时要用到的,是用来初始化段寄存器的。本人估计是(保护模式与实模式段界限与段偏移是不同的, 而这个段描述符的段基址是0、段界限是0FFFFH ,段属性是读加写,与8086 的标准是一样,所以在回到8086模式之前,CPU在对所有段寄存器进行实模式的转换就能正确安排界限与属性了,!!! ),

LABEL_DESC_NORMAL Descriptor 0,0ffffh,DA_DRW

LABEL_DESC_DATA Descriptor 0,SegDataLen - 1 ,DA_DRW | DA_32 ;段属性是非一致的32位读写

数据段

LABEL_DESC_STACK Descriptor 0,TopOfStack - 1,DA_DRWA | DA_32 ;存在的已访问可读写的32位stack段在保护模式下的Call命令需要堆栈

LABEL_DESC_CODE32 Descriptor 0,SegCode32Len -1 ,DA_C | DA_32 ;Protect mode的32位代码区

;这个段是保护模式的段,但是它是以16位形式存放的,它是用来从保护模式跳回到8086模式。,。。。。因为直接用32位代码段跳转到8086模式是不行,必需从16位保护模式段跳转到16位8086模式。就像先前说的Normal 描述符,它也是一个具有8086属性的描述符。,所以CS段寄存器的状态也需要先转换成与8086模式相同段界限与段属性。这样才能正确的转换到8086模式,由此可见:全部的段寄存器都需要对应8086模式的描述符状态。才能正常的进入8086模式。

LABEL_DESC_CODE16 Descriptor 0,0ffffh ,DA_C ;

;以下两个段描述符是内存中的段不需要自己定义

LABEL_DESC_TEST Descriptor 0500000h,0ffffh,DA_DRW ;用于测试线性空间

LABEL_DESC_VIDEO Descriptor 0B8000h,0ffffh,DA_DRW ;显示器内存

GdtLen equ $ - LABEL_GDT

GdtPtr dw GdtLen - 1 ;段界限与段基地址

dd 0

;----------------选择子---------------------

SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT

SelectorData equ LABEL_DESC_DATA - LABEL_GDT

SelectorStack equ LABEL_DESC_STACK - LABEL_GDT

SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT

SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT

SelectorTest equ LABEL_DESC_TEST - LABEL_GDT

SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT

;为了便于区分实模式代码与保护模式代码,

我就就把16位的实模式段先写前面:(一般的编程规范下数据段是写前面的)

;=============================-8086的16位实模式起始段============================ [SECTION .s16] ;16位代码段

[BITS 16] ;BITS指出处理器的模式是16位

LABEL_BEGIN: ;从100h处跳进来的

mov ax,cs

mov ds,ax

mov es,ax

mov ss,ax

mov sp,100h ;在实模式下并没有用到sp,这个100h 只是形象表示需要保存实模式的SP值.

mov [LABEL_GO_BACK_TO_REAL+3], ax ; 请看[LABEL_GO...]+3标号处的注释。是一个指令参数mov [SPValueInRealMode],sp ;在这里保存实模式的sp值

;-----------全局数据段描述符初始化-----------

xor eax,eax

mov ax,ds

shl eax,4 ;ds * 16 代表这DS原来的基地址

add eax,LABEL_DATA1 ;得到物理地址

mov WORD [LABEL_DESC_DATA + 2],ax

shr eax,16

mov BYTE [LABEL_DESC_DATA + 4],al

mov BYTE [LABEL_DESC_DATA + 7],ah ;此时数据段描述符已经有基址了也就是可以访问了

;-----------全局堆栈段描述符初始化-----------

xor eax,eax

mov ax,ds

shl eax,4

add eax,LABEL_STACK

mov WORD [LABEL_DESC_STACK + 2],ax

mov BYTE [LABEL_DESC_STACK + 4],al

mov BYTE [LABEL_DESC_STACK + 7],ah;此时堆栈段描述符已经有基址了也就是可以访问了

;-----------32位代码段描述符初始化-----------

xor eax,eax

mov ax,cs

shl eax,4

add eax,LABEL_SEG_CODE32

mov WORD [LABEL_DESC_CODE32 + 2],ax

shr eax,16

mov BYTE [LABEL_DESC_CODE32 + 4],al

mov BYTE [LABEL_DESC_CODE32 + 7],ah ;进入保护模式后开始执行的代码段

;-----------16位代码段描述符初始化-----------

xor eax,eax

mov ax,cs

shl eax,4

add eax,LABEL_SEG_CODE16

mov WORD [LABEL_DESC_CODE16 + 2],ax

shr eax,16

mov BYTE [LABEL_DESC_CODE16 + 4],al

mov BYTE [LABEL_DESC_CODE16 + 7],ah ;用来跳转到实模式的386代码段

;----------GDTR Ready -----------

xor eax,eax

mov ax,ds

shl eax,4

add eax,LABEL_GDT

mov [GdtPtr + 2],eax

lgdt [GdtPtr] ;loader gdtr

;----------打开A20--------

CA20

cli

;--------置CR0 PE位-----

mov eax,cr0

or eax,1

mov cr0,eax

;---------------跳到386-----------

jmp dword SelectorCode32:0 ;以上3个步骤无需多讲了

;这个是迎接保护模式跳回来的时候,执行的代码,,欢迎386回来啊!!!

LABEL_REAL_ENTRY:

mov ax,cs

mov ds,ax

mov es,ax

mov sp,[SPValueInRealMode] ;恢复到Real原来的堆栈注意这里要用[标号]

DA20 ;关闭20地址线

sti

mov ax,4c00h

int 21h

;===================================386保护模式段================================== ;-------------------数据段--------------------------

[SECTION .data1]

align 32

[BITS 32]

LABEL_DATA1:

SPValueInRealMode dw 0 ; 这个变量用来保存实模式跳入到保护模式前的SP值

;---------字符串-------------

PMMessage: db "welcome to Protect Mode ", 0 ; 进入保护模式后显示此字符串

OffsetPMMessage equ PMMessage - $$ ;保护模式寻址方式是按段偏移

StrTest: db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0

OffsetStrTest equ StrTest - $$ ;测试5MB空间所用的字符串

SegDataLen equ $ - LABEL_DATA1 ;数据段长

;-----------------------------------386全局堆栈段------------------------------------

[SECTION .stack32]

align 32

[BITS 32]

LABEL_STACK:

times 512 db 0 ;堆栈大小是512byte

TopOfStack equ $ - LABEL_STACK - 1 ;栈顶的值

;-----------------------------------进入保护模式后的起始代码段------------------------------------

[SECTION .s32]; 32 位代码段. 由实模式跳入.

[BITS 32]

LABEL_SEG_CODE32:

mov ax,SelectorData

mov ds,ax

mov ax,SelectorTest

mov es,ax

mov ax,SelectorVideo

mov gs,ax ;以上3个也不用多说了吧,段的选择子也就是GDT的索引

;---堆栈--

mov ax,SelectorStack

mov ss,ax

mov esp,TopOfStack ;当然堆栈段也是段的选择子咯

;-----------显示缓冲--------

mov ah, 0Ch ; 0000: 黑底1100: 红字

mov esi,OffsetPMMessage ;这个是字符串相对于它的段偏移值

mov edi,(80*10+0)*2 ;第10行

cld

.1:

lodsb

test al,al

jz .2

mov [gs:edi],ax ;要用ax做为参数传进缓冲区

add edi,2

jmp .1

.2: ;OffsetPMMessage 字符串显示完成:

;------------------------测试5MB空间的读------------------

call DispReturn ;显示回车,也就是改变edi的位置(edi 是显缓冲区段的偏移值)

call ReadTest

call WriteTest

call ReadTest ;当在执行Call命令的时候会将eip + 1压栈,然后跳转

jmp SelectorCode16:0 ;跳到最后那个16位代码段去了前面有16位段描述符的讲解。。;--------------显示回车---------------

DispReturn:

push ebx ;临时用ebx eax 所以先保存一下

push eax

mov bl,160

mov eax,edi

div bl

inc eax ;得到回车后的行数

mov bl,160

mul bl

mov edi,eax ;取得当前的位置

pop eax

pop ebx

ret ;ret 指令会恢复eip + 1

;--------------读取我们定义的大地址段---------------

ReadTest:

xor esi, esi

mov ecx, 8

. loop:

mov al, [es:esi]

call DispAL

inc esi

loop .loop

ret

;--------------写入我们定义的大地址段---------------

WriteTest:

push esi ;借这两个寄存器来传字符串

push edi

xor esi, esi

xor edi, edi

mov esi, OffsetStrTest ; 数据段的字符串

cld

.1:

lodsb

test al, al

jz .2

mov [es:edi], al ;把数据段的字符串传给测试段

inc edi

jmp .1

.2:

pop edi

pop esi

ret

;--------------显示AL的内容---------------

DispAL: ;对传来的Al字符进行处理

test al,al

jnz .next

mov al,'0'

.next:

mov ah, 0Ch ; 0000: 黑底1100: 红字

mov [gs:edi],ax

add edi,2

ret

SegCode32Len equ $ - LABEL_SEG_CODE32

;-----------------------------------准备8086模式的16位段------------------------------------ [SECTION .code16]

align 32

[BITS 16]

LABEL_SEG_CODE16:

mov ax,SelectorNormal ; 保护8086标准段属性的描述符选择子

mov ds, ax

mov es, ax

mov fs, ax

mov ss, ax ;使所有的段选择子都达到8086标准

mov eax,cr0

and al,0feh

mov cr0,eax ;CR0 PE位为0 回到Real mode

LABEL_GO_BACK_TO_REAL:

jmp 0:LABEL_REAL_ENTRY ;还记得上面的[LABEL_GO..]+3吗,指的就是0这个段值

Code16Len equ $ - LABEL_SEG_CODE16

呵呵、那么就完工了代码虽然比较繁杂,但是还不是特别复杂。有几个要注意的地方:

1、堆栈(新加了堆栈这个段,其寻址方式也是选择子。)在入口的时候是实模式,应该把当前的sp值保存下来,因为在返回系统时我们需要基本恢复原来的样子。就在保护模式的全局数据段定义一个变量用来保存SP。在返回DOS时候要记得恢复。

2、保护模式到实模式。实模式到保护模式比较简单就几个固定步骤不需要考虑太多,而从保护模式到实模式就比较复杂:首先需要定义两个8086标准属性的段描述符.它们分别是Normal数据段与16位代码段描述符。在16位代码段中cs的属性跟8086模式的CS段界限与段属性一致,并且把Normal的段属性分别付给全部的段寄存器.这样就让所有保护模式的段寄存器全部跟8086段界限与段属性一致!!!在cr0 的PE位为0后就可以用实模式寻址了!!!接下来继续学习:

第4章保护模式编程四--LDT描述符&&特权级&&门

4.1 LDT(局部描述符)

GDT是全局描述符,是整个系统的描述符,描述符着所有的段!!!在前几章我们已经熟悉了GDT的一些基本功能,与运作机制。对GDT描述符的定义与使用也就那么几项固定的步骤,接下来再了解LDT。

LDT是局部描述符。看字面LDT与GDT很相似.它们都是描述符。只不过GDT是全局描述符、而LDT是局部描述符。那么LDT该如何【定义】与【使用】呢?(与GDT非常类似,不过LDT是归属于GDT的):

4.1.1 【定义】:

1、在GDT的描述集合中插入一条LABEL_DESC_LDT Descriptor 0,LdtLen - 1,DA_LDT (这个段描述符描述的LDT数据段:是一个局部描述符段的集合,结构类似GDT.)

2、在GDT选择子集合中照样也插入一条SelectorLdt equ ;LABEL_DESC_LDT - LABEL_GDT(看似跟普通的段描述符没啥两样哦,不过接下来就有点小区别了!)

3、建一个LDT数据段,这个数据段里的结构与GDT描述符段类似!

{

LABEL_LDT: ;这里与GDT不同,LDT的段描述符一开始就是实际的段

LABEL_DESC_LDT_CODEA: Descriptor 0,LdtCodeALen - 1,DA_C | DA_32

LABEL_DESC_LDT_DATA: Descriptor 0,LdtDataLen - 1,DA_DRW

LdtLen equ $ - LABEL_LDT;

;在加载LDT的时候并不是靠一个绝对结构体,而是通过GDT这个桥梁索引加载的。

;LDT内部段的选择子

SelectorLdtCodeA equ LABEL_DESC_LDT_CODEA - LABEL_LDT | SA_TIL

SelectorLdtData equ LABEL_DESC_LDT_DATA - LABEL_LDT | SA_TIL

;LDT内部的选择子多了一个SA_TIL属性,他是段选择的TI位也就是第2位如果是1代表当前选择子代表的段是LDT的内部段。因为选择子的0-2位并不参与索引。所以选择子的索引都是8的倍数或0,}

4、接着完成LDT里面段描述符描述的段。这里只定义了一个代码段:

LABEL_DESC_LDT_CODEA(这个跟普通的代码段一样,有执行、32位等属性. 为了测试LDT 的效果那么我们还是加一个Data段吧)那么LDT的基本定义已经完成了!!!

4.1.2 使用

1、LDT描述符与其内部的局部段描述符信息、,我们在进入保护模式前初始化!!!那么我们的进入保护模式之前的段在哪还知道吗?当然就是最最开始了!在16位段实模式入口那里!!!我们在这里做完GDT描述符的初始化工作后:

;--------------初始化LDT描述符---------

xor eax,eax

mov eax,ds

shl eax,4

add eax,LABEL_LDT

mov WORD [LABEL_DESC_LDT + 2],ax

shr eax,16

mov BYTE [LABEL_DESC_LDT + 4],al

mov BYTE [LABEL_DESC_LDT + 7],ah

;---------------初始化LDT内部的数据段描述符----------

xor eax,eax

mov eax,ds

shl eax,4

add eax,LABEL_LDT_DATA

mov WORD [LABEL_DESC_LDT_DATA + 2],ax

shr eax,16

mov BYTE [LABEL_DESC_LDT_DATA + 4],al

mov BYTE [LABEL_DESC_LDT_DATA + 7],ah

;---------------初始化LDT内部的代码段描述符----------

xor eax,eax

mov eax,ds

shl eax,4

add eax,LABEL_LDT_CODEA

mov WORD [LABEL_DESC_LDT_CODEA + 2],ax

shr eax,16

mov BYTE [LABEL_DESC_LDT_CODEA + 4],al

mov BYTE [LABEL_DESC_LDT_CODEA + 7],ah

上面几个初始化段基地址的代码相信大家已经很熟悉了!!!!!!!!

2、先前就说过了LDT跟GDT类似都是描述符集合,那么他们在使用起来也有点类似!!!

第4章指令系统层习题参考解答-汇编语言与计算机组成原理 答案

1.什么是“程序可见”的寄存器? 程序可见寄存器是指在用户程序中用到的寄存器,它们由指令来指定。 2. 80x86微处理器的基本结构寄存器组包括那些寄存器?各有何用途? 基本结构寄存器组按用途分为通用寄存器、专用寄存器和段寄存器3类。 通用寄存器存放操作数或用作地址指针;专用寄存器有EIP和EFLAGS,分别存放将要执行的下一条指令的偏移地址和条件码标志、控制标志和系统标志;段寄存器存放段基址或段选择子。 3.80x86微处理器标志寄存器中各标志位有什么意义? 常用的7位: CF进位标志: 在进行算术运算时,如最高位(对字操作是第15位,对字节操作是第7位)产生进位或借位时,则CF置1;否则置0。在移位类指令中,CF用来存放移出的代码(0或1)。 PF奇偶标志: 为机器中传送信息时可能产生的代码出错情况提供检验条件。 当操作结果的最低位字节中1的个数为偶数时置1,否则置0。 AF辅助进位标志: 在进行算术运算时,如低字节中低4位(第3位向第4位)产生进位或借位时,则AF置1;否则AF置0。 ZF零标志:如指令执行结果各位全为0时,则ZF置1;否则ZF置0。 SF符号标志:其值等于运算结果的最高位。 如果把指令执行结果看作带符号数,就是结果为负,SF置1;结果为正,SF置0。 OF溢出标志: 将参加算术运算的数看作带符号数,如运算结果超出补码表示数的范围N,即溢出时,则OF置1;否则OF置0。 DF方向标志: 用于串处理指令中控制处理信息的方向。 当DF位为1时,每次操作后使变址寄存器SI和DI减小;当DF位为0时,则使SI和DI增大,使串处理从低地址向高地址方向处理。 4.画出示意图,简述实模式下存储器寻址的过程。 20位物理地址如下计算(CPU中自动完成):10H×段基址+偏移地址=物理地址 5. 画出示意图,简述保护模式下(无分页机制)存储器寻址的过程。 采用对用户程序透明的机制由选择子从描述子表中选择相应的描述子,得到欲访问段的段基址、段限等有关信息,再根据偏移地址访问目标存储单元。

保护模式与实模式

什么是实模式、保护模式和虚拟8086方式 1:实模式:寻址采用和8086相同的16位段和偏移量,最大寻址空间1MB,最大分段64KB。可以使用32位指令。32位的x86 CPU用做高速的8086。 2:保护模式:寻址采用32位段和偏移量,最大寻址空间4GB,最大分段4GB (Pentium Pre及以后为64GB)。在保护模式下CPU可以进入虚拟8086方式,这是在保护模式下的实模式程序运行环境。 第一:实模式下程序的运行回顾. 程序运行的实质是什么?其实很简单,就是指令的执行,显然CPU 是指令得以执行的硬件保障,那么CPU如何知道指令在什么地方呢? 对了,80x86系列是使用CS寄存器配合IP寄存器来通知CPU指令在内存 中的位置. 程序指令在执行过程中一般还需要有各种数据,80x86系列有DS、 ES、FS、GS、SS等用于指示不同用途的数据段在内存中的位置。 程序可能需要调用系统的服务子程序,80x86系列使用中断机制 来实现系统服务。 总的来说,这些就是实模式下一个程序运行所需的主要内容 (其它如跳转、返回、端口操作等相对来说比较次要。) 第二:保护模式---从程序运行说起 无论实模式还是保护模式,根本的问题还是程序如何在其中运行。 因此我们在学习保护模式时应该时刻围绕这个问题来思考。 和实模式下一样,保护模式下程序运行的实质仍是“CPU执行指令, 操作相关数据”,因此实模式下的各种代码段、数据段、堆栈段、中 断服务程序仍然存在,且功能、作用不变。 那么保护模式下最大的变化是什么呢?答案可能因人而异,我的 答案是“地址转换方式”变化最大。 第三:地址转换方式比较 先看一下实模式下的地址转换方式,假设我们在ES中存入0x1000, DI中存入0xFFFF,那么ES:DI=0x1000*0x10+0xFFFF=0x1FFFF,这就是众 所周知的“左移4位加偏移”。 那么如果在保护模式下呢?假设上面的数据不变ES=0x1000, DI=0xFFFF,现在ES:DI等于什么呢? 公式如下:(注:0x1000=1000000000000b= 10 0000 0000 0 00) ES:DI=全局描述符表中第0x200项描述符给出的段基址+0xFFFF 现在比较一下,好象是不一样。再仔细看看,又好象没什么区别! 为什么说没什么区别,因为我的想法是,既然ES中的内容都不是 真正的段地址,凭什么实模式下称ES为“段寄存器”,而到了保护模式 就说是“选择子”? 其实它们都是一种映射,只是映射规则不同而已:在实模式下这

实模式和保护模式的区别

实模式和保护模式的区别 实模式和保护模式的区别 2009-08-31 20:19 551人阅读评论(1) 收藏举报 从80386开始,cpu有三种工作方式:实模式,保护模式和虚拟8086模式。只有在刚刚启动的时候是real-mode,等到linux操作系统运行起来以后就运行在保护模式(所以存在一个启动时的模式转换问题)。 实模式只能访问地址在1M以下的内存称为常规内存,我们把地址在1M 以上的内存称为扩展内存。 在保护模式下,全部32条地址线有效,可寻址高达4G字节的物理地址空间; 扩充的存储器分段管理机制和可选的存储器分页管理机制,不仅为存储器共享和保护提供了硬件支持,而且为实现虚拟存储器提供了硬件支持; 支持多任务,能够快速地进行任务切换和保护任务环境; 4个特权级和完善的特权检查机制,既能实现资源共享又能保证代码和数据的安全和保密及任务的隔离; 支持虚拟8086方式,便于执行8086程序。 1.虚拟8086模式是运行在保护模式中的实模式,为了在32位保护模式下执行纯16位程序。它不是一个真正的CPU模式,还属于保护模式。 2.保护模式同实模式的根本区别是进程内存受保护与否。可寻址空间的区别只是这一原因的果。 实模式将整个物理内存看成分段的区域,程序代码和数据位于不同区域,系统程序和用户程序没有区别对待,而且每一个指针都是指向"实在"的物理地址。这样一来,用户程序的一个指针如果指向了系统程序区域或其他用户程序区域,并改变了值,那么对于这个被修改的系统程序或用户程序,其后果就很可能是灾难性的。为了克服这种低劣的内存管理方式,处理器厂商开发出保护模式。这样,物理内存地址不能直接被程序访问,程序内部的地址(虚拟地址)要由操作系统转化为物理地址去访问,程序对此一无所知。至此,进程(这时我们可以称程序为进程了)有了严格的边界,任何其他进程根本没有办法访问不属于自己的物理内存区域,甚至在自己的虚拟地址范围内也不是可以任意访问的,因为有一些虚拟区域已经被放进一些公共系统运行库。这些区域也不能随便修改,若修改就会有: SIGSEGV (linux 段错误);非法内存访问对话框(windows 对话框)。 CPU启动环境为16位实模式,之后可以切换到保护模式。但从保护模式无法切换回实模式

保护模式下寻址(易懂)

保护模式下寻址(易懂) 保护模式下寻址(易懂):网上看到的一强帖,不转不行了,牛人啊,把这段代码拿捏的相当到位括号中是我的加注段机制轻松体验[内存寻址]实模式下的内存寻址:让我们首先来回顾实模式下的寻址方式段首地址×16+偏 移量=物理地址为什么要×16?因为在8086CPU中,地址线是20位,但寄存器是16位的,最高寻址64KB,它无法寻址到1M内存。于是,Intel设计了这种寻址方式,先缩小4位成16位放入到段寄存器,用到时候,再将其扩大到20位,这也造成了段的首地址必须是16的倍数的限制。保护模式下分段机制的内存寻址:保护模式下分段机制是利用一个称作段选择符的偏移量,从而到描述符表找到需要的段描述符,而这个段描述符中就存放着真正的段的物理首地址,再加上偏移量一段话,出现了三个新名词:1、段选择子2、描述符表3、段描述符我们现在可以这样来理解这段话:有一个结构体类型,它有三个成员变量:段物理首地址段界限段属性内存中,维护一个该结构体类型的是一个数组。而分段机制就是利用一个索引,找到该数组对应的结构体,从而得到段的物理首地址,然后加上偏移量,得到真正的物理地址。公式:xxxx:yyyyyyyy其中,xxxx也就是索引,yyyyyyyy是偏移量(因为32位寄存器,所以8个

16进制)xxxx存放在段寄存器中。现在,我们来到过来分析一下那三个新名词。段描述符,一个结构体,它有三个成员变量:1、段物理首地址2、段界限3、段属性我们再来重温一遍描述符表,也就是一个数组,什么样的数组呢?是一个段描述符组成的数组。接下来看看段选择子:段选择子,也就是数组的索引,但这时候的索引不在是高级语言中数组的下标,而是我们将要找的那个段描述符相对于数组首地址(也就是全局描述表的首地址)偏移位置。就这么简单,如图:图中,通过Selector(段选择子)找到存储在Descriptor Table(描述符表)中某个Descriptor(段描述符),该段描述符中存放有该段的物理首地址,所以就可以找到内存中真正的物理段首地址SegmentOffset(偏移量):就是相对该段的偏移量物理首地址+偏移量就得到了物理地 址本图就是DATA但这时,心细的朋友就发现了一个GDTR这个家伙还没有提到!我们来看一下什么是GDTR ?Global Descriptor Table Register(全局描述符表寄存器)但是这个寄存器有什么用呢?大家想一下,段描述符表现在是存放在内存中,那CPU是如何知道它在哪里呢?所以,Intel 公司设计了一个全局描述符表寄存器,专门用来存放段描述符表的首地址,以便找到内存中段描述符表。这时,段描述符表地址被存到GDTR寄存器中了。好了,分析就到这,我们来看一下正式的定义:当x86 CPU 工作在保护模式时,可

Gate A20与保护模式

Gate A20与保护模式 大家都知道,8088/8086只有20位地址线,按理它的寻址空间是2^20,应该是1024KB,但PC机的寻址结构是segment:offset,segment和offset都是16 位的寄存器,最大值是0ffffh,换算成物理地址的计算方法是把segment左移4位,再加上offset,所以segment:offset所能表达的寻址空间最大应为0ffff0h + 0ffffh = 10ffefh(前面的0ffffh是segment=0ffffh并向左移动4位的结果,后面的0ffffh是可能的最大offset),这个计算出的10ffefh是多大呢?大约是1088KB,就是说,segment:offset的地址表达能力,超过了20位地址线的物理寻址能力,你说这是不是有点麻烦。在早先,由于所有的机器都没有那么大的内存,加上地址线只有20位,所以当你用segment:offset的方式企图寻址100000h这个地址时,由于没有实际的第21位地址线,你实际寻址的内存是00000h的位置,如果你企图寻址100001h这个地址时,你实际得到的内容是地址00001h上的内容,所以这个事对实际使用几乎没有任何影响,但是后来就不行了,出现了80286,地址线达到了24位,使segment:offset寻址100000h--10ffefh这将近64K的存储器成为可能,为了保持向下兼容,于是出现了A20 Gate,这是后话,我们后面再细说。 我们可能经常听到一些只有在PC机上才有的一些关于存储器的专有名词,包括:常规内存(Conventional Memory)、上位内存区(Upper Memory Area)、高端内存区(High Memory Area)和扩展内存(Extended Memory),我尽量把这几个东东说明白,这需要下面这张著名的图。 这张图很清楚地说明了问题,大家都知道,DOS下的“常规内存”只有640K,这640K就是从0--A0000H这段地址空间;所谓“上位内存区”,指的就是20位地址线所能寻址到的1M地址空间的上面384K空间,就是从A0001H--100000H 这段地址空间,也就是我们说的用于ROM和系统设备的地址区域,这384K空间和常规内存的640K空间加起来就是20位地址线所能寻址的完整空间 1024KB;由于80286和80386的出现使PC机的地址线从20位变成24位又变成32位,寻址能力极大地增加,1M以上的内存寻址空间,我们统称为“扩展内存”;这里面绝大部分内存区域只能在保护模式下才能寻址到,但有一部分既可以在保护模式下,也可以在实模式下寻址,这就是我们前面提到过的地址100000h--10ffefh之间的这块内存,为了表明其特殊性,我们把这块有趣的内存区叫做“高端内存”。 前面我们提过由于IBM的愚蠢设计给PC机的内存结构埋下了麻烦的伏笔,现在我们来说说这个麻烦。我们都见过PC机上的内存条,但是由于上位内存区

第3章作业

第3章从8086到Pentium系列微处理器的技术发展 教材习题解答 1. 简述80286的特点和保护模式的保护功能。 【解】80286的特点: ①CPU内部分为四个处理部件:EU(执行部件)、AU(地址部件)、IU(指令部件)和BU(总线部件)。这四个处理部件可以并行的进行操作,提高了处理速度。 ②数据线和地址线完全分离。在一个总线周期中,当有效数据出现在数据总线上的时候,下一个总线周期的地址已经送到地址总线,形成总线周期的流水作业。 ③具有“实地址模式”(Real Address Mode,简称为“实模式”)和“保护虚地址模式”(Protected V irtual Address Mode,简称为“保护模式”)”两种工作模式。 ④能运行实时多任务操作系统,支持存储管理和保护功能。 ⑤实现了虚拟存储管理。 ⑥与80286 配合使用的数学协处理器是80287,它基本与8087相同,但适应80286 的两种工作模式。 保护模式体现了80286的特色,主要是对存储器管理、虚拟存储和对地址空间的保护。在保护模式下,可为每个任务提供多达1GB的虚拟存储空间和保护机制,有力地支持了多用户、多任务的操作。那些内存装不下的逻辑段,将以文件形式存在外存储器中,当处理器需要对它们进行存取操作时就会产生中断,通过中断服务程序把有关的程序或数据从外存储器调入到内存,从而满足程序运行的需要。 保护模式为不同程序设置了四个特权级别,可让不同程序在不同的特权级别上运行。依靠这一机制,可支持系统程序和用户程序的分离,并可进一步分离不同级别的系统程序,大大提高了系统运行的可靠性。 2. 简述80386 的特点、80386引脚与8086的区别。 【解】80386 的特点: 80386是全32位结构,它的外部数据总线和内部数据通道,包括寄存器、ALU和内部总线都是32位的。 80386 有3 种工作模式:实模式、虚拟86模式、386的保护模式。 80386的硬件结构可分成6个逻辑单元,它们以流水线方式工作,运行速度可达4MIPS。其硬件设计有支持段页式存储管理部件,易于实现虚拟存储系统。在保护模式下的分段寻址体系,与操作系统相配合可以组成虚拟存储器系统,一个任务的最大虚拟空间可达246=64 TB。 80386硬件支持多任务处理,用一条指令就可以实现任务切换。 80386设置了4级特权级,按优先顺序依次为0级、1级、2级、3级,前3级用于操作系统程序,后1级用于用户程序。 80386引脚与8086的区别见表3-1。 表3-1 80386引脚与8086的区别 8086CPU 80386CPU 共有40个引脚共有132个引脚 16条地址/数据复用线4条地址线34条地址线 32条数据线 在总线宽度控制信号16 BS的控制下,可实现16位或32位数据传送。字节控制信号0 BE~3 BE 协处理器接口信号 (1) PEREQ:协处理器向80386发出的请求信号,有效时表示协处理器请求与存储器之间传送数据。80386响应该请求后,将按照指令的要求控制对存储器的读写。 (2) BUSY:协处理器向80386发出的状态信号,有效时表示协处理器正在执行指令,处于忙状态,暂时不能接受新的指令。 (3)ERROR:协处理器向80386发出的状态信号,有效时表示协处理器出错。80386在检测到ERROR信号后,将转到错误处理子程序来处

保护模式编程

第1章保护模式编程一 如果想更深、更亲近的了解电脑软件。那么学习cpu是你的必选!! 386是CPU史的一大转折点,那386做基础课是最好不过了。那么我们将开始进行学习之旅!!!大家跟我一块学习吧,呵呵!!! 1.1 准备工作 l1、NASM 编译环境(当然Masm 也可以但是用它来写COM程序比较 麻烦) l2、虚拟机 Virtual PC(Windows平台,执行比较快,即模拟又虚拟硬件)、 WMWarve(WIndows平台虚拟硬件,)、 Bochs(支持Windows平台、也支持在Linux平台上运行有RPM版本的) 我们这些生长在Windows这棵大树下的朋友们,还是用Virtual PC吧.。 l3、写虚拟启动镜像文件的程序 :不知道我观察的对不对?用Nasm 编译一个bin 然后将它转换为img 镜像文件的时候。只要文件大小符合软驱的标准就能启动。那么就代表a.bin 与a.img 文件的内容一模样就是文件大小不一样!我是不太了解镜像文件格式.我用的是Virtual PC。 1.2 开始接触引导程序 1.2.1 Com文件 Com文件是纯二进制的文件,也是直接与Cpu交换的顺序指令文件。Com文件的大小是有限制的,不能超过64KB.因为8086时代的CPU地址线是20位的,20位能表达的数值也就是fffffh(1MB )。而寄存器最高也只是16位,无法用5个F的形式来表达地址,所以用CS(段基地址)*16:IP(偏移地址)来寻址!80386后通用寄存器都得到了32位扩展! 而Cpu地址线也得到了32位的扩展。引导程序前期是需要进入实模式的,因为这是硬件上的限制是IA32的限

保护模式下寻址

保护模式下寻址 网上看到的一强帖,不转不行了,牛人啊,把这段代码拿捏的相当到位括号中是我的加注 段机制轻松体验 [内存寻址] 实模式下的内存寻址: 让我们首先来回顾实模式下的寻址方式 段首地址×16+偏移量=物理地址 为什么要×16?因为在8086CPU中,地址线是20位,但寄存器是16位的,最高寻址64KB,它无法寻址到1M 内存。于是,Intel设计了这种寻址方式,先缩小4位成16位放入到段寄存器,用到时候,再将其扩大到20位,这也造成了段的首地址必须是16的倍数的限制。 保护模式下分段机制的内存寻址: 保护模式下分段机制是利用一个称作段选择符的偏移量,从而到描述符表找到需要的段描述符,而这个段描述符中就存放着真正的段的物理首地址,再加上偏移量 一段话,出现了三个新名词: 1、段选择子 2、描述符表 3、段描述符 我们现在可以这样来理解这段话:有一个结构体类型,它有三个成员变量:段物理首地址段界限段属性内存中,维护一个该结构体类型的是一个数组。而分段机制就是利用一个索引,找到该数组对应的结构体,从而得到段的物理首地址,然后加上偏移量,得到真正的物理地址。 公式:xxxx:yyyyyyyy 其中,xxxx也就是索引,yyyyyyyy是偏移量(因为32位寄存器,所以8个16进制)xxxx存放在段寄存器中。 现在,我们来到过来分析一下那三个新名词。段描述符,一个结构体,它有三个成员变量:1、段物理首地址2、段界限3、段属性 我们再来重温一遍描述符表,也就是一个数组,什么样的数组呢?是一个段描述符组成的数组。 接下来看看段选择子:段选择子,也就是数组的索引,但这时候的索引不在是高级语言中数组的下标,而是我们将要找的那个段描述符相对于数组首地址(也就是全局描述表的首地址)偏移位置。 就这么简单,如图:

80386 和 保护模式

80386 和保护模式 ___William Liu Intel CPU 一般可以运行在两种模式之下,即实模式和保护模式。早期的 Intel CPU ( 8086 , 8088 )只能工作在实模式之下,系统中只能运行单个任务,而且只能使用实地址模式。对于 Intel 80386 以上的芯片则还可以运行在 32 位的保护模式之下。在保护模式之下的 CPU 可以支持多任务;支持 4GB 的物理内存;支持 64TB 的虚拟内存;支持内存的页式管理和段式管理以及支持特权级。 本文档将首先介绍 Intel 80386 CPU 的几个内部寄存器,然后再由浅入深的分别介绍保护模式下的段式管理,页式管理,虚拟内存,多任务以及特权级管理等几个方面。 Intel 80386 CPU 的内部寄存器 这一部分先大致介绍一下 386 的内部寄存器,具体细节在后面的几节中再详细说明。一般来说, CPU 设计用来系统编程的系统寄存器包括如下几类: ?标志寄存器 (EFLAGS) ?内存管理寄存器 (GDTR , LDTR , IDTR , TR) ?控制寄存器 (CR0 , CR1 , CR2 , CR3 , CR4) ?兼容 8086 通用寄存器( EAX , EBX , ECX , EDX ) ?兼容 8086 段寄存器( CS , DS , ES , SS , FS , GS ) ?兼容 8086 数据寄存器( ESI , EDI , EIP , ESP ) 下面分别加以介绍:

1) 标志寄存器 EFLAGS : 跟 8086/8088 的 FLAGS 大致差不多。只不过位宽由 16bit 变成了 32bit ,负责的状态标志也多了一些。见图一所示: 图一: EFLAGS 的结构 其中系统标志: VM -虚拟 8086 模式; RF -恢复标志; NT -任务嵌套标志;IOPL - I/O 特权级标志; IF -中断允许标志。 2) 内存管理寄存器: 一共有 4 个,用于分段内存管理,都是用于存放指针的,只是所指的再内存单元中的内容有所不同。 GDTR 全局描述符表寄存器( Global Descriptor Table Register ),存放的是一个指向内存单元列表的指针,用于指向全局段描述表( GDT ),如图二所示。共 48bit ,高 32bit 是 GDT 的基址,低 16bit 描述 GDT 的长度。由于每项 8Byte ,所以共可以有 2^(16)/8=2^13 项。 IDTR 中断描述符表寄存器( Interrupt Descriptor Table Register ),存放的是也一个指向内存单元列表的指针,用于指向全局中断描述符表( IDT ),如图二所示。跟 GDTR 一样,共 48bit 。

X86体系中保护模式下的内存访问机制_湛辉来

文章编号:1000-582X(2002)06-0067-04 X86体系中保护模式下的内存访问机制Ξ 湛辉来,曾 一 (重庆大学计算机学院,重庆400044) 摘 要:保护模式是X86体系结构的一种重要的工作方式,为在X86平台上实现的多任务、多进程操作系统提供了相应的硬件支持。在计算机的底层开发过程中,了解计算机体系结构中的主要部件-内存的工作方式是及其重要的。文章介绍了当前较流行的微机体系结构-X86系统中的内存的组织、管理、和访问机制,分析了X86在保护模式下分段机制、分页机制中的一些主要的数据结构和相应的硬件支持,并详细描述了分段机制中逻辑地址到线性地址的转换,分页机制中虚拟地址到物理地址的转换过程。通过分页和分段机制,可以实现多进程操作系统中的进程独立地址空间,以及对操作系统中内存的保护措施,从而及时而有效地防止了因内存的非法访问而导致的系统崩溃。 关键词:保护模式;描述符;描述符表;页表;页目录 中图分类号:TP31611文献标识码:A Intel的80X86CPU系列芯片可在2种模式下工作:实模式、保护模式[1]。实模式是MS-DOS的运行环境。当内存寻址模式为实模式时,采用80286的20位地址线的存储器分段线性管理技术,存储器容量最大为1M B,线性地址与物理地址一致,对物理内存单元读写直接、灵活。但因受限于640K基本内存,使大量的扩展内存的使用受到制约,同时,分段的64K范围限制影响了内存访问的连续性,从而使系统的内存资源难以得到充分利用。在保护模式下,系统采用分段机制和分页机制,提供了灵活的寻址方式,使得应用程序可以访问多达4G B的存储空间,并为操作系统和应用程序实现虚拟存储器提供了相应的硬件支持,从而打破了实模式下内存寻址所受到的制约,提高了内存资源的使用效率。 1 X86的分段机制 X86的分段机制就是将X86的线性地址空间分成若干空间段(Segment),利用这些段来存储(记录)代码和数据,通过对段的保护来提供一种对数据或代码的保护[2]。这种寻址模式允许CPU访问装在1M B以上的数据和程序代码,就象在实模式中访问1M B以内的内存一样。1.1 分段机制中的数据结构 在分段机制,X86使用了如下几种主要数据结构: 1)段描述符(Segment Descriptor):用来描述一个段的位置、长度以及访问权限。一个描述符由8个字节组成,具体的格式如下 : 其中:基地址描述了该段的开始位置共32位,使得该段可以从4G空间里的任意一个位置开始。段的长度是由20位的界限和一个标志位G决定。标志位G位于0字节的最高一个比特,它的含义如下: 0:段的长度=界限 范围为1B~1M B 1:段的长度=界限34K 范围为4K B-4G B 2)全局描述符表(G DT,G lobal Describe T able):存放系统用的段描述符和各项任务共用的段描述符; 3)局部描述符表(LDT,Local Describe T able):存放某个任务专用的各段的段描述符; 每个描述符表中最大可以放8,192个段描述符,因此,每个描述符表的最大长度为64K B。 1.2 分段机制的硬件支持 X86提供了如下几个用于支持分段机制的寄存器:  2002年6月重庆大学学报 (自然科学版)V ol.25 N o.6第25卷第6期Journal of Chongqing University(Natural Science Edition)Jun.2002 Ξ收稿日期:2002-01-21 作者简介:湛辉来(1972-),男,重庆人,重庆大学硕士研究生。主要研究方向:软件工程及计算机体系结构。

12第十二章模式转换——从实模式进入保护模式

第十二章模式转换——从实模式进入保护模式 12.1 NASM编译实践 12.1.1 实验20 ——NASM编译 ●情景描述 使用NASM编译一段程序,并可以在裸机上运行。 ●实现功能 在开机后显示"Hello, OS world!" ●流程 1)初始化寄存器 2)调用BIOS int 10h,显示字符串 3)进入死循环 ●代码及注释 %define _BOOT_DEBUG_ %ifdef _BOOT_DEBUG_ org 0100h %else org 07c00h %endif ;告诉编译器加载到0100处生成,这样方便生成com文件。 ;或者告诉编译器加载到7c00处生成,这样方便 mov ax,cs;将当前段地址放入ax,可以不要 mov ds,ax;将当前段地址放入数据寄存器,可以不要 mov es,ax;将当前段地址放入extra寄存器,可以不要 call DispStr;调用显示字符串程序 jmp $;无限循环(跳转到当前地址) DispStr: mov ax,BootMessage ;BootMessage的首地址送ax mov bp,ax; 段内地址送到bp,至此地址完成——CS:BP = 串地址 mov cx,16; CX = 串长度 mov ax,01301h; AH = 13,AL = 01h mov bx,000ch; BH = 0(页号为0),BL = 0ch(黑底红字高亮) mov dl,0 ; 0送dx寄存器的低字节 int 10h; 10h号中断(视频中断,对应的中断向量为40-43H) ret ; 从显示子程序中返回到主程序 BootMessage: db "Hello, OS world!" times 510-($-$$) db 0; 用“0”填充0面0磁道1扇区剩下的空间,使生成的二进制代码恰好为512字节; ;$是当前行的段内偏移地址,$$是本小节代码的第一行代码,在本程序中就是“org 07c00h”的段内偏移地址。 dw 0xaa55;结束标志.使得0面0磁道1扇区510字节存55,511字节存aa,0扇区从0-511号单元,共512个字节。(引导扇区的结束标志)

段地址轻松体验(实模式和保护模式)

段机制轻松体验(实模式和保护模式) [内存寻址] 实模式下的内存寻址: 让我们首先来回顾实模式下的寻址方式 段首地址×16+偏移量=物理地址 为什么要×16?因为在8086CPU中,地址线是20位,但寄存器是16位的,最高寻址64KB,它无法寻址到1M内存。于是,Intel设计了这种寻址方式,先缩小4位成16位放入到段寄存器,用到时候,再将其扩大到20位,这也造成了段的首地址必须是16的倍数的限制。 保护模式下分段机制的内存寻址: 保护模式下分段机制是利用一个称作段选择符的偏移量,从而到描述符表找到需要的段描述符,而这个段描述符中就存放着真正的段的物理首地址,再加上偏移量 一段话,出现了三个新名词: 1、段选择子 2、描述符表 3、段描述符 我们现在可以这样来理解这段话:有一个结构体类型,它有三个成员变量:段物理首地址段界限段属性 内存中,维护一个该结构体类型的是一个数组。而分段机制就是利用一个索引,找到该数组对应的结构体,从而得到段的物理首地址,然后加上偏移量,得到真正的物理地址。 公式:xxxx:yyyyyyyy 其中,xxxx也就是索引,yyyyyyyy是偏移量(因为32位寄存器,所以8个16进制)xxxx 存放在段寄存器中。 现在,我们来到过来分析一下那三个新名词。段描述符,一个结构体,它有三个成员变量:1、段物理首地址2、段界限3、段属性 我们再来重温一遍描述符表,也就是一个数组,什么样的数组呢?是一个段描述符组成的数组。 接下来看看段选择子:段选择子,也就是数组的索引,但这时候的索引不在是高级语言中数组的下标,而是我们将要找的那个段描述符相对于数组首地址(也就是全局描述表的首地址)偏移位置。

计算机组成原理B及答

《计算机组成原理(含汇编)》 站点:专业年级:姓名:学号: 一、名词解释题(每空4分,共20分) 1.冯-诺依曼计算机 2.CISC和RISC 3.定点数和浮点数 4.微命令、微指令,微程序 5.指令流程 二、填空题(每空1分,共20分) 1、某定点小数字长16位,含一位符号位,补码表示,其能表示数的范围为,分辨率为。 2、CPU从主存取出一条指令并执行该指令的时间叫,它通常包含若干 个,而后者又包含若干个组成多级时序系统。 3、在组合逻辑(硬连逻辑)控制器中,是由组合逻辑电路产生的。电路固 定下来之后修改和扩展。 4、保护模式存储器寻址方式下,逻辑地址由段选择器和两部分组成。段选择 器存放在中,但它不能直接确定段基址,而由CPU通过一定的方法取得段基址,再和相加,从而求得所选存储单元的线性地址,线性地址再通过转换成物理地址。 5、已知BX=8830H,CF=1,执行指令:ADC BX,87CFH之后,BX=____________,标志 位的状态分别为CF=_______,ZF=_______,OF=________,SF=__________。 6、在80X86汇编语言程序中,逻辑地址可表示为: 7、伪指令语句是用来指示的语句,伪指令语句除 了其外,其他项均没有对应的目标代码 8、设字长8位,-128的补码是 三、问答题(共20分) 1、在实模式下如何实现存储器寻址?(5分)。

2、何谓组合逻辑控制器?何谓微程序控制器?试比较它们的优缺点。(6分) 3、简要说明80x86的三组基本结构寄存器组包含哪些寄存器?简要说明各寄存器组的用途。(9分) 四、计算题(共20 分) 1、分别写出下列各二进制数的原码、补码,设字长(含一位数符)为8位。(5分) (1)0;(2)0.1101;(3)- 0.1101;(4)1101; (5) –1101 2、用变形补码计算,并指出是否有溢出。(5分) (1) 补=11,110011 ; 补 = 00,10110,计算 补 - 补 = ?[X-Y]的真值=? (2) 补=00,101010; 补 = 11,111001,算 补 - 补 = ?[X+Y]的真值=? 3、设两个浮点数,X=×0.110111,Y=×(- 0.101001),其浮点格式为:阶码四位 位数8位,且均用双符号位。按浮点加减运算规则计算 补+ 补 = ?(10分) 五、阅读程序(共20分) 1、假设(BX)=12FFH,有以下程序段,阅读程序后在“;”后填上指令的功能及执行该指令后相应寄存器的内:(6分) MOV CL,8 ;功能(CL)= ROL BX,CL ;功能(BX)= AND BX,0FFH ;功能(BX)= CMP BX,0FFH ;功能 (BX)= ,ZF= ,CF= 2、在实模式下,已知堆栈寄存器SS的内容是0100H,堆栈指针SP的内容00FEH, 写出计算段基址(物理地址)的过程,并画出执行以下程序段后,堆栈区和SP的内容变化过程示意图(标出存储单元的物理地址)(6分) MOV AX,1234H MOV BX,5678H PUSH AX PUSH BX POP CX 3、程序如下: MOV CX, 9 MOV AL ,01H

CPU实模式保护模式虚拟8086方式分析比较

1:实模式:寻址采用和8086相同的16位段和偏移量,最大寻址空间1MB,最大分段64KB。可以使用32位指令。32位的x86 CPU用做高速的8086。 2:保护模式:寻址采用32位段和偏移量,最大寻址空间4GB,最大分段4GB (Pentium Pre及以后为64GB)。在保护模式下CPU可以进入虚拟8086方式,这是在保护模式下的实模式程序运行环境。 第一:实模式下程序的运行回顾. 程序运行的实质是什么?其实很简单,就是指令的执行,显然CPU 是指令得以执行的硬件保障,那么CPU如何知道指令在什么地方呢? 对了,80x86系列是使用CS寄存器配合IP寄存器来通知CPU指令在内存 中的位置. 程序指令在执行过程中一般还需要有各种数据,80x86系列有DS、 ES、FS、GS、SS等用于指示不同用途的数据段在内存中的位置。 程序可能需要调用系统的服务子程序,80x86系列使用中断机制 来实现系统服务。 总的来说,这些就是实模式下一个程序运行所需的主要内容 (其它如跳转、返回、端口操作等相对来说比较次要。) 第二:保护模式---从程序运行说起 无论实模式还是保护模式,根本的问题还是程序如何在其中运行。 因此我们在学习保护模式时应该时刻围绕这个问题来思考。 和实模式下一样,保护模式下程序运行的实质仍是“CPU执行指令, 操作相关数据”,因此实模式下的各种代码段、数据段、堆栈段、中 断服务程序仍然存在,且功能、作用不变。 那么保护模式下最大的变化是什么呢?答案可能因人而异,我的 答案是“地址转换方式”变化最大。 第三:地址转换方式比较 先看一下实模式下的地址转换方式,假设我们在ES中存入0x1000, DI中存入0xFFFF,那么ES:DI=0x1000*0x10+0xFFFF=0x1FFFF,这就是众 所周知的“左移4位加偏移”。 那么如果在保护模式下呢?假设上面的数据不变ES=0x1000, DI=0xFFFF,现在ES:DI等于什么呢? 公式如下:(注:0x1000=1000000000000b= 10 0000 0000 0 00) ES:DI=全局描述符表中第0x200项描述符给出的段基址+0xFFFF 现在比较一下,好象是不一样。再仔细看看,又好象没什么区别! 为什么说没什么区别,因为我的想法是,既然ES中的内容都不是 真正的段地址,凭什么实模式下称ES为“段寄存器”,而到了保护模式 就说是“选择子”? 其实它们都是一种映射,只是映射规则不同而已:在实模式下这 个“地址转换方式”是“左移4位”;在保护模式下是“查全局/局部描述表” 。前者是系统定义的映射方式,后者是用户自定义的转换方式。而它 影响的都是“shadow register” 从函数的观点来看,前者是表达式函数,后者是列举式函数: 实模式:F(es-->segment)={segment |segment=es*0x10}

CPU的实模式和保护模式简介

16位微处理器 8086寄存器组成 8086/8088包括:4个16位的数据寄存器,2个16位的指针寄存器,2个16位变址寄存器,分成四组。 存算术逻辑运算的操作和运算结果。 段寄存器能在8086 中实现1M物理空间寻址,并可与8080 CPU进行兼容。段寄存器都是16位的,分别称为代码段(Code Segment)寄存器CS、数据段(Data Segment)寄存器DS、堆栈段(Stack Segment)寄存器SS,附加段寄存器。 标志寄存器在8086中有一个16位用于反映处理器的状态和运算结果的某些特征。(其中只有9位有定义) 这些标志位分为两类: 其一是运算结果标志,主要用于反映处理器的状态和运算结果特征。(例如:进位标志、零标志、符号标志、溢出标志等) 其二是状态控制标志,它控制着处理器的操作。要通过专门的指令才能使状态控制标志发生变化。(例如:方向标志、中断允许标志、追踪标志)

内存的寻址(实模式) 8086 CPU有20根地址线,可直接寻址的物理地址空间为1M。尽管8086/8088内部的ALU 每次最多进行16位运算,但存放存储单元地址偏移的指针寄存器都是16位的,所以8080/8086通过内存分段和使用段寄存器的方法来有效地实现寻址1M的空间。 存储单元的逻辑地址由段值和偏移两部分组成,用如下形式表示: 段值:偏移 所以根据逻辑地址可以方便地得到存储单元的物理地址,计算公式如下: 物理地址(20位) = 段值*16+偏移 段值通过段寄存器的值来取得,偏移可由指令指针的IP或其他可作为内存指针使用的寄存器给出。偏移还可以直接用16位数给出。 指令中不使用物理地址,而使用逻辑地址,由总线接口单元BIU按需要根据段值和偏移自动形成20位物理地址。 32位微处理器 1985年,真正的32位微处理器80386DX诞生,为32位软件的开发提供了广阔的舞台。80386寄存器的宽度大多是32位,可分为如下几组: 通用寄存器、段寄存器、指令指针 及标志寄存器、系统地址寄存器、调试寄存器、控制寄存器和测试寄存器。 应用程序主要使用前面三组寄存器,只有系统才会使用其他寄存器。

实模式、保护模式和虚拟8086模式

实模式、保护模式和虚拟8086模式 一直以来,都搞不清楚这几个概念。在网上搜了一下,把它晒上,不会再忘记!有其它的内容,请看到的朋友请补充。 实模式与保护模式 1. 实模式,又叫实地址模式,CPU完全按照8086的实际寻址方法访问从00000h--FFFFFh (1MB大小)的地址范围的内存,在这种模式下,CPU只能做单任务运行;寻址公式为:物理地址=左移4位的段地址+偏移地址,即:物理地址是由16位的段地址和16位的段内偏移地址组成的。 2.保护模式,又叫内存保护模式,寻址采用32位段和偏移量,最大寻址空间4GB,在这种模式下,系统运行于多任务,设计这种模式的原因和好处是:保护模式增加了寻址空间,增加了对多任务的支持,增加了段页式寻址机制的内存管理(分段机制使得段具有访问权限和特权级,各应用程序和操作系统的代码和核心是被保护的,这也是多任务支持的实现关键和保护这个名字的由来)。寻址过程为:物理地址=由段地址查询全局描述符表中给出的段基址+偏移地址,即:物理地址由影像寄存器中的基址加上16位或者32位的偏移组成。 1:实模式 是CPU启动的时候的模式 这时候就相当于一个速度超快的8086 不能使用多线程 不能实现权限分级 还不能访问20位以上地址线,也就是说只能访问1M内存(!!!) 2:保护模式 操作系统接管CPU后. 会使CPU进入保护模式. 这时候可以发挥80x86的所有威力.. 包括权限分级.内存分页.等等等等各种功能 从80386开始,cpu有三种工作方式:实模式,保护模式和虚拟8086模式。只有在刚刚启动的时候是real-mode,等到linux操作系统运行起来以后

什么叫实模式,什么叫保护模式

什么叫实模式,什么叫保护模式? 概念……没背过 特点……DOS就是实模式的,现在的Winows、Unix之类东西都是保护模式的。实模式下只能访问那可怜的1M内存,要想突破这个限制,麻烦得很,你还记得DOS下那一堆什么XMS (扩充内存)、EMS(扩展内存)吧?本来是连续的64M内存,在DOS下访问,还得调用什么中断,好麻烦。Watcom C++之所以当时有些名气就是占了这个便宜,说集成开发环境,那和BC是没法比,但就因为它提供了DOS4GW支持,可以很方便的是你的程序在DOS访问4G 内存,写32位的程序。 至于保护模式,特性挺多,如线性地址、虚拟内存、权限保护之类的东西都是CPU提供的功能,为开发现代操作系统提供了很多便利。 x86CPU在初始化的时候都是先进入实模式,然后操作系统再切换到保护模式。 详情你找本书看看吧。有一本《386保护模式编程》你可以看看,写得挺多。 dos下内存是很简单的,指针指向的几乎就是实际的物理地址。实模式的指针是16bit的,就是直接访问的内存是640k,640k~1M之间的内存是通过中断把页切换到640k里读写完之后又Move到640k以外的。Dos4GW又提供了别的中断来访问1M~8M以外的内存。你理解在操作系统限制的时候做这些 突破是多么“技巧”的一个年代吧。 其实386已经支持32位指针了,这时又引入了GDT,LDT等概念。怎么说呢....靠,这样吧,比如你有一大衣柜(GDT),有无数的小抽屉(LDT)。这个抽屉是你的,那个是你MM的,你MM翻你的抽屉就会发现套套,你翻你MM的抽屉会发现情书...所以要保护! 《386保护模式编程》书 我看问题的追溯到8086/8088 在80866/88时代,地址线只有20条,只能寻址到1M 8086采用段式存储管理,每个段最多寻址64k ,原因:指针是16bit的 到80286是地址线达到32条,可以寻址到4G的虚拟空间 此时提出了保护模式可采用分页式存储管理,以寻址到4G的空间 现在的操作系统都是基于80286的保护模式区别与8086提供的实地址方式 简介(转) 这篇文章将会着重介绍内存寻址的不同方法。因为我没能全部亲自参与工作--,所以我将用这篇文章作为对不同技术的摼 閿。首先,我将从范围为00000000-000FFFFF 的1M 内存的地址使用分配方式的描述开始。然后我将试图探讨1MB-4GB 范围内存寻址的问题。1M 内存 当我们说到1M 内存时,我们将谈到实模式(real-mode),一种分段式内存模式,不具有32 位寻址和内存分页的功能。 低端内存的特定区域都保留做特殊用途并必须进行正确处理。为了说明这一点,我将列出 1M 内存的映射的实例。 00000 003FF IVT Interrupt Vector Table (中断向量表) 00400 004FF BDA BIOS Data Area (基本输入输出数据区) 00500 00501 PRTSCR 1st byte is Print Screen Status BYTE (第一字节是打印屏幕状态字节) 00501 9xxxx OS OS specific (操作系统专用区)

相关主题