DOS下内存分页程序的编写

DOS下内存分页程序的编写

 

一.        前言

 

首先,我们先要弄清楚80386编程中涉及到的三个地址概念:物理地址、逻辑地址和线性地址。

 

1.物理地址:本质上,一个物理地址是CPU插脚上可测量的电压。CPU通过就是物理地址来访问内存的。

 

2.逻辑地址:这是基于CUP内存分段来表示内存的一种方式。它通常表示为:XXXX:YYYYYYYY,这里XXXX称为段选择子,而YYYYYYYY是针对段选择子所选择的段的偏移量。程序员在编程过程中接触的地址基本都是这个地址。

 

3.线性地址:将逻辑地址中的段基地址加上偏移量便形成了线性地址。在CPU内存不分页的情况下,线性地址就是物理地址。若进行了分页管理,线性地址则需要进行页表地址转换来得出相应的物理地址。

 

这三个地址的映射关系图如下:

DOS下内存分页程序的编写_第1张图片

 

二.        分页管理

 

80386的分段机制是不能关闭的。但是分页机制却可以通过控制寄存器CR0中的最高位PG位来控制。如果PG=1,分页机制生效,把线性地址转换为物理地址。如果PG=0,分页机制无效,线性地址就直接作为物理地址。

    

分页机制把线性地址空间和物理地址空间分别划分为大小相同的块。这样的块称之为页(80386中,页的大小为4K字节)。80386通过页目录/页表将线性地址和物理地址对照起来。下图就是80386的这种转换关系:

 

80386有一个页目录和最多1024个页表,页目录有CR3寄存器来指定。页表/页目录由4字节的项组成,每项的格式如下:

 

 

写好80386的分页程序的关键就是需要先在内存中建立起页目录表/页表关系,将页目录表的物理地址赋给CR3寄存器。然后打开CR0PG位(PG=1)就进入了内存分页了。

 

三.        实例

 

简略介绍了一下内存以及分页机制,下面我们来写一个内存分页程序。我们把环境设在DOS下面。比照上面的页目录表/页表图,我们将要在内存中建立的页目录表/页表图绘制如下:

DOS下内存分页程序的编写_第2张图片

 

源代码如下:

 

.386P

DESC STRUC

     LIMIT  DW  0

     BASEL  DW  0

     BASEM  DB  0

     ATTR   DW  0

     BASEH  DB  0

DESC ENDS

 

REG STRUC

     LIMIT  DW  0

     BASE   DD  0

REG ENDS

 

;**************************

PDIR SEGMENT USE16

     DD 00204001H       ;dir 0

     DD 1023 DUP (0)

PDIR ENDS

 

PAGE1 SEGMENT USE16

     DD 200001H                ;page 0

     DD 201001H                ;page 1

     DD 202001H                ;page 2

     DD 0B8001H                ;page 3

     DD 201H DUP (0)           ;page 4 ~ 204h

     DD 205001H                ;page 305h

     DD 400H - 205H DUP (0)    ;page 206h~400h

PAGE1 ENDS

 

PDATA SEGMENT USE16

      PMSG  DB 'CAN YOU SEE ME? :)', 0

PDATA ENDS

 

PSTACK SEGMENT USE16

     DB 256 DUP (0)

     PSTACKLEN = 256

PSTACK ENDS

 

PCODE SEGMENT USE16

     MOV AX, GDTDATA4SEL

     MOV DS, AX

     MOV AX, GDTDISP4SEL

     MOV ES, AX

     MOV SI, OFFSET PMSG

     MOV DI, 80 * 10

     CLD

 

PNEXT:

     LODSB

     OR AL, AL

     JZ POVER

     MOV AH, 7

     STOSW

     JMP PNEXT

 

POVER:

     RETF

 

PCODE ENDS

 

PTEMP SEGMENT USE16

      MOV EAX, 00203000H

      MOV CR3, EAX

 

      MOV EAX, CR0

      OR  EAX, 80000000H

      MOV CR0, EAX

 

      JMP TEMP1

TEMP1:

 

      MOV AX, GDTSTACK4SEL   ;LINEAR ADDRESS: 2000H

      MOV SS, AX

      MOV SP, PSTACKLEN

 

      DB 9AH

      DW 0

      DW GDTCODE4SEL        ;LINEAR ADDRESS: 1000H

 

      ;;;;;;;;;;;;;

      MOV EAX, CR0

      AND EAX, 07FFFFFFFH

      MOV CR0, EAX

 

      JMP TEMP2

TEMP2:

      MOV EAX, 0

      MOV CR3,EAX

     

      DB 0EAH

      DW OFFSET FROMTEMP

      DW GDTCODESEL

PTEMP ENDS

 

;***************************

 

DATA SEGMENT USE16

     GDTNULL DESC <0, 0, 0, 0, 0>

     GDTNULLSEL = 0

 

     GDTDATA DESC <0FFFFH, 0, 0, 92H, 0>

     GDTDATASEL = OFFSET GDTDATA - OFFSET GDTNULL

 

     GDTCODE DESC <0FFFFH, 0, 0, 98H, 0>

     GDTCODESEL = OFFSET GDTCODE - OFFSET GDTNULL

 

     GDTSTACK DESC <0FFFFH, 0, 0, 92H, 0>

     GDTSTACKSEL = OFFSET GDTSTACK - OFFSET GDTNULL

 

     GDTDISP DESC <0FFFFH, 8000H, 0BH, 92H, 0>

     GDTDISPSEL = OFFSET GDTDISP - OFFSET GDTNULL

 

     ;FROM

     GDTDATA0 DESC <1000H, 0, 0, 92H, 0>

     GDTDATA0SEL = OFFSET GDTDATA0 - OFFSET GDTNULL

 

     GDTCODE0 DESC <1000H, 0, 0, 92H, 0>

     GDTCODE0SEL = OFFSET GDTCODE0 - OFFSET GDTNULL

     GDTCODE1 DESC <1000H, 0, 0, 98H, 0>

     GDTCODE1SEL = OFFSET GDTCODE1 - OFFSET GDTNULL

 

     GDTTEMP0 DESC <1000H, 0, 0, 92H, 0>

     GDTTEMP0SEL = OFFSET GDTTEMP0 - OFFSET GDTNULL

     GDTTEMP1 DESC <1000H, 0, 0, 98H, 0>

     GDTTEMP1SEL = OFFSET GDTTEMP1 - OFFSET GDTNULL

 

     GDTSTACK0 DESC <1000H, 0, 0, 92H, 0>

     GDTSTACK0SEL = OFFSET GDTSTACK0 - OFFSET GDTNULL

 

     GDTPDIR0 DESC <1000H, 0, 0, 92H, 0>

     GDTPDIR0SEL = OFFSET GDTPDIR0 - OFFSET GDTNULL

 

     GDTPAGE0 DESC <1000H, 0, 0, 92H, 0>

     GDTPAGE0SEL = OFFSET GDTPAGE0 - OFFSET GDTNULL

 

     ;TO

     GDTDATA2  DESC <1000H, 0, 30H, 92H, 0>

     GDTDATA2SEL = OFFSET GDTDATA2 - OFFSET GDTNULL

 

     GDTCODE2  DESC <1000H, 1000H, 20H, 92H, 0>

     GDTCODE2SEL = OFFSET GDTCODE2 - OFFSET GDTNULL

     GDTCODE3 DESC <1000H, 1000H, 20H, 98H, 0>

     GDTCODE3SEL = OFFSET GDTCODE3 - OFFSET GDTNULL

 

     GDTTEMP2 DESC <1000H, 5000H, 20H, 92H, 0>

     GDTTEMP2SEL = OFFSET GDTTEMP2 - OFFSET GDTNULL

     GDTTEMP3 DESC <1000H, 5000H, 20H, 98H, 0>

     GDTTEMP3SEL = OFFSET GDTTEMP3 - OFFSET GDTNULL

 

     GDTSTACK2 DESC <1000H, 2000H, 20H, 92H, 0>

     GDTSTACK2SEL = OFFSET GDTSTACK2 - OFFSET GDTNULL

 

     GDTPDIR2 DESC <1000H, 3000H, 20H, 92H, 0>

     GDTPDIR2SEL = OFFSET GDTPDIR2 - OFFSET GDTNULL

 

     GDTPAGE2 DESC <1000H, 4000H, 20H, 92H, 0>

     GDTPAGE2SEL = OFFSET GDTPAGE2 - OFFSET GDTNULL

 

     ;VIR

     GDTDATA4 DESC <1000H, 0, 0, 92H, 0>

     GDTDATA4SEL = OFFSET GDTDATA4 - OFFSET GDTNULL

 

     GDTCODE4 DESC <1000H, 1000H, 0, 98H, 0>

     GDTCODE4SEL = OFFSET GDTCODE4 - OFFSET GDTNULL

 

     GDTSTACK4 DESC <1000H, 2000H, 0, 92H, 0>

     GDTSTACK4SEL = OFFSET GDTSTACK4 - OFFSET GDTNULL

 

     GDTDISP4 DESC <1000H, 3000H, 0, 92H, 0>

     GDTDISP4SEL = OFFSET GDTDISP4 - OFFSET GDTNULL

 

     GDTR REG <OFFSET GDTR - OFFSET GDTNULL, 0>

DATA ENDS

 

STACK SEGMENT USE16

    DB 256 DUP (0)

    STACKLEN = $

STACK ENDS

 

CODE SEGMENT USE16

     ASSUME CS: CODE, DS:DATA, SS:STACK

 

START:

     MOV AX, DATA

     MOV DS, AX

     CLI

     MOV AX, STACK

     MOV SS, AX

     MOV SP, STACKLEN

     STI

 

     MOV AX, DATA

     MOV BX, 10H

     MUL BX

     MOV GDTDATA.BASEL, AX

     MOV GDTDATA.BASEM, DL

     MOV GDTDATA.BASEH, DH

 

     ADD AX, OFFSET GDTNULL

     ADC DX, 0

 

     MOV WORD PTR GDTR.BASE, AX

     MOV WORD PTR GDTR.BASE + 2, DX

 

     MOV AX, CODE

     MOV BX, 10H

     MUL BX

     MOV GDTCODE.BASEL, AX

     MOV GDTCODE.BASEM, DL

     MOV GDTCODE.BASEH, DH

 

     MOV AX, STACK

     MOV BX, 10H

     MUL  BX

     MOV GDTSTACK.BASEL, AX

     MOV GDTSTACK.BASEM, DL

     MOV GDTSTACK.BASEH, DH

 

     MOV AX, PDATA

     MOV BX, 10H

     MUL BX

     MOV GDTDATA0.BASEL, AX

     MOV GDTDATA0.BASEM, DL

     MOV GDTDATA0.BASEH, DH

 

     MOV AX, PCODE

     MOV BX, 10H

     MUL BX

     MOV GDTCODE0.BASEL, AX

     MOV GDTCODE0.BASEM, DL

     MOV GDTCODE0.BASEH, DH

     MOV GDTCODE1.BASEL, AX

     MOV GDTCODE1.BASEM, DL

     MOV GDTCODE1.BASEH, DH

 

     MOV AX, PTEMP

     MOV BX, 10H

     MUL BX

     MOV GDTTEMP0.BASEL, AX

     MOV GDTTEMP0.BASEM, DL

     MOV GDTTEMP0.BASEH, DH

     MOV GDTTEMP1.BASEL, AX

     MOV GDTTEMP1.BASEM, DL

     MOV GDTTEMP1.BASEH, DH

 

     MOV AX, PSTACK

     MOV BX, 10H

     MUL BX

     MOV GDTSTACK0.BASEL, AX

     MOV GDTSTACK0.BASEM, DL

     MOV GDTSTACK0.BASEH, DH

 

     MOV AX, PDIR

     MOV BX, 10H

     MUL BX

     MOV GDTPDIR0.BASEL, AX

     MOV GDTPDIR0.BASEM, DL

     MOV GDTPDIR0.BASEH, DH

 

     MOV AX, PAGE1

     MOV BX, 10H

     MUL BX

     MOV GDTPAGE0.BASEL, AX

     MOV GDTPAGE0.BASEM, DL

     MOV GDTPAGE0.BASEH, DH

 

     ;;;;;;;;;;;;;;;;;;;;

     LGDT FWORD PTR GDTR

 

     CLI

   

     MOV EAX, CR0

     OR  EAX, 1

     MOV CR0, EAX

 

     DB 0EAH

     DW OFFSET PROTECT

     DW GDTCODESEL

 

PROTECT:

     MOV AX, GDTDATASEL

     MOV DS, AX

     MOV ES, AX

 

     MOV AX, GDTSTACKSEL

     MOV SS, AX

     MOV SP, STACKLEN

 

     ;COPY PDATA to 200 000H

     MOV AX, GDTDATA0SEL

     MOV DS, AX

     MOV AX, GDTDATA2SEL

     MOV ES, AX

     MOV CX, 1000H

     MOV SI, 0

     MOV DI, 0

     CLD

     REP MOVSB

 

     ;COPY PCODE TO 201 000H

     MOV AX, GDTCODE0SEL

     MOV DS, AX

     MOV AX, GDTCODE2SEL

     MOV ES, AX

     MOV CX, 1000H

     MOV SI, 0

     MOV DI, 0

     CLD

     REP MOVSB

 

     ;COPY PTEMP TO 205 000H

     MOV AX, GDTTEMP0SEL

     MOV DS, AX

     MOV AX, GDTTEMP2SEL

     MOV ES, AX

     MOV CX, 1000H

     MOV SI, 0

     MOV DI, 0

     CLD

     REP MOVSB

 

     ;COPY PSTACK TO 202 000H

     MOV AX, GDTSTACK0SEL

     MOV DS, AX

     MOV AX, GDTSTACK2SEL

     MOV ES, AX

     MOV CX, 1000H

     MOV SI, 0

     MOV DI, 0

     CLD

     REP MOVSB

 

     ;COPY PDIR TO 203 000H

     MOV AX, GDTPDIR0SEL

     MOV DS, AX

     MOV AX, GDTPDIR2SEL

     MOV ES, AX

     MOV CX, 1000H

     MOV SI, 0

     MOV DI, 0

     CLD

     REP MOVSB

 

     ;COPY PAGE0 TO 204 000H

     MOV AX, GDTPAGE0SEL

     MOV DS, AX

     MOV AX, GDTPAGE2SEL

     MOV ES, AX

     MOV CX, 1000H

     MOV SI, 0

     MOV DI, 0

     CLD

     REP MOVSB

 

     ;;;;;;;COPY COMPLIETE;;;;;;;;;;;

     MOV AX, GDTPAGE2SEL

     MOV ES, AX

 

     XOR EAX, EAX

     MOV AX, DATA

     MOV CL, 4

     SHL EAX, CL

     AND EAX, 0FFFFF000H

 

     MOV EDI, EAX

     OR  EAX, 1

 

     MOV CL, 12

     SHR EDI, CL

     MOV CL, 2

     SHL EDI, CL

 

     MOV ES:[DI], EAX

 

     ;JMP TEMP

     DB 0EAH

     DW 0

     DW GDTTEMP3SEL

 

FROMTEMP:

     MOV AX, GDTDATASEL

     MOV DS, AX

     MOV ES, AX

     MOV AX, GDTSTACKSEL

     MOV SS, AX

     MOV SP, STACKLEN

 

     MOV EAX, CR0

     AND EAX, 0FFFFFFFEH

     MOV CR0, EAX

 

     DB 0EAH

     DW OFFSET REAL

     DW SEG REAL

REAL:

     MOV AX, DATA

     MOV DS, AX

     MOV ES, AX

     MOV FS, AX

     MOV GS, AX

 

     MOV AX, STACK

     MOV SS, AX

     MOV SP, STACKLEN

 

     STI

 

     MOV AH, 4CH

     INT 21H

CODE ENDS

     END START

 

 

下面来分析一下这个程序:

1.       使用下面的代码来启动分页管理:

 

     

MOV EAX, CR0

      OR  EAX, 80000000H

      MOV CR0, EAX

 

      JMP TEMP1

TEMP1:

 

 

在启用分页机制前,线性地址就是物理地址;在启用分页机制后,线性地址要通过分页机制的转换,才成为物理地址。尽管使用一条转移指令,可清除预取队列,但随后在取指令时使用的线性地址就要经过分页机制转换才成为物理地址。

 

为了顺利过渡,在启用分页机制之后的过渡代码段,仍要维持线性地址等同于物理地址。为了实现这一点:我们将启动分页这段代码放在205000H起的段中,并且将页表205H项指向地址205000H。其实仅仅做到这一点还是不够的,还有一个最重要的也是最容易忽略的:就是要将GDTR指向的地址等同于物理地址,上图中就是xxx000h所在的数据段。

 

2.当需要访问的物理地址(注意是物理地址)超过1M的偶数地址时,注意打开A20门。由于本文中200000H的第20位(从0开始)为0,所以不需要打开A20门;假如地址像300000H,则必须要打开A20门,否则所有的访问还是为200000H地址范围了。重申一遍:A20门仅仅和物理地址有关,与逻辑地址和线性地址没有任何关系。

 

 

四.        参考文献

1.80X86汇编语言程序设计教程》,杨季文 等。

你可能感兴趣的:(编程,汇编,dos,语言,2010,X86)