凌晨一点
初级用户
积分 255
发帖 54
注册 2003-10-24
状态 离线
|
『第
7 楼』:
实方式下XMS中的图象数据直接访问方法
直接访问4GB内存
无论CPU在实方式下或保护方式下,其物理地址的形成都将使用段描述符寄存器。对于CPU在形成物理地址时,实方式与0特权级不分页的保护方式是相同的。只是对段的基地址和段的界限值设置不同。
当CPU复位后,CPU处于实方式下,段描述符寄存器的界限值被自动设置为64KB,段描述符寄存器的基地址可通过对段寄存器的赋值最大只能设置为FFFFH×16。因此,段内的寻址空间只能为64KB,CPU访问内存的空间只能为FFFFH×16+64KB。
基于CPU物理地址形成的统一性[1],[2],在实方式下直接访问4GB内存的关键是扩大段描述符寄存器的界限值。使诸如“MOV AX,[EBX]”指令的32位寄存器间接寻址操作实现4GB内存的访问。然而,CPU在实方式下并没有提供改变段描述符寄存器的界限值的操作指令。改变段描述符寄存器的内容只能在保护方式下进行。当设置控制寄存器CR0的PE位=1时,CPU进入保护方式;当设置控制寄存器CR0的PE位=0时,CPU返回实方式。通过设置CR0改变工作方式时,段描述符寄存器的内容不发生变化。因此,在DOS实方式下直接访问4GB内存之前,让CPU进入保护方式下,通过装载具有4GB界限的段描述符到段描述符寄存器DS、ES、FS和GS中去。然后返回到实方式下。就可使诸如“MOV AX,[EBX]”、“MOV AX,FS:[EBX]”指令的32位寄存器间接寻址操作实现4GB内存的访问。
由于这种编程方法产生的是基于实方式下的执行程序。因此,它不能在保护方式下和虚拟8086方式下运行,即,不能在Windows中运行,也不能在DOS系统中装载扩充内存EMS驱动程序(如EMM386.EXE)。
编程方法
(1)编程环境
本文采用Borland C++ 3.1程序设计环境,在程序中使用内嵌汇编方法实现特定的操作,在Options的“Compile”-“Advanced Code generation”中选择386指令集。由于集成开发环境下的内部编译器不能识别内嵌的386汇编指令,要实现32位寄存器和32位地址操作汇编指令,可让集成开发环境调用TASM.EXE进行编译,即设置Options中的“Compile”-“Code generation”-“Compile via assemler”为ON。这样便可完整地运用386汇编指令,在以下编程示例中采用了这种编译方法。
(2)基本操作函数
①打开A20地址线
要访问4GB内存,必须打开A20地址线。
void openA20()
{ while(inp(0x64) & 2); outp(0x64,0xd1);
while(inp(0x64) & 2); outp(0x60,0xdf);
while(inp(0x64) & 2); outp(0x64,0xff);
}
②设置数据段的4GB界限函数
首先,建立一个全局描述符表GDT,即GDT_def,它含有二个描述符,第一个为空描述符(保护方式下系统要求的),第二个是具有4GB段界限的数据段描述符。它的选择字为8。再计算出GDT的基地址和长度存入GDT_Addr中。然后,装载GDT,进入保护方式,把选择字8赋给FS和GS,此时第二个数据段描述符被装载到FS和GS的描述符寄存器中。最后返回实方式。通理,也可设置DS和ES的4GB界限。
unsigned long GDT_def[ ]={0,0,0x0000FFFF,0x008F9200}; //全局描述符表GDT
unsigned char GDT_Addr[6]={0}; //存放GDT的基地址和长度
void set4gb( )
{ asm{
cli //关中断
mov word ptr GDT_Addr[0], (2*8-1) //GDT的长度存入GDT_Addr中
mov eax, ds //计算GDT描述符表的线性基地址31~0
shl eax, 4 //段地址eax=ds×16
xor ebx, ebx //ebx清零
mov bx, offset GDT_def //bx=GDT的偏移地址
add eax,ebx //GDT的线性基地址=eax+ebx
mov dword ptr GDT_Addr[2], eax //GDT的线性基地址存入GDT_Addr中
lgdt fword ptr GDT_Addr //将GDT_Addr装载到GDTR寄存器中
mov bx, 8 //设置数据段描述符的选择字
mov eax, cr0
or al,1
mov cr0,eax //设置CR0的PE位=1
jmp flush1 //进入保护方式
}
flush1: asm{
mov fs,bx //FS装载具有4GB界限的数据段描述符
mov gs,bx //GS装载具有4GB界限的数据段描述符
and al,0feh
mov cr0,eax //设置CR0的PE位=0
jmp flush2 //返回实方式
}
flush2: asm{
mov ax, 0
mov fs,ax //设置FS描述符的基地址为0
mov gs,ax //设置GS描述符的基地址为0
sti //开中断
}
}
③直接访问4GB内存的编程示例
在FS和GS具有4GB的访问界限后,通过32位寄存器间接寻址的指令就可实现4GB内存的访问。例如图象二值化的运算函数如下:
void two_mem(unsigned long addrd, unsigned long addrs, unsigned long leng,
unsigned char yuzi)
{ asm mov ecx, leng //leng为图象数据块的字节数
asm mov esi, addrs //addrs为源图象数据块的32位线性基地址
asm mov edi, addrd //addrd为二值化图象数据块的32位线性基地址
asm mov ah, yuzi //yuzi为阈值
TN0: asm cmp fs:[esi], ah //源图象数据与阈值进行比较
asm mov al, 0 //如果源图象数据<阈值,al=0
asm jc TN1
asm mov al, 255 //如果源图象数据≥阈值,al=255
TN1: asm mov fs:[edi], al //存运算结果到二值化数据块中
asm inc esi //源图象数据块的32位线性地址增一
asm inc edi //二值化图象数据块的32位线性地址增一
asm loopd TN0
}
④程序结构
void main( )
{ openA20( );
set4gb( );
… … //用户程序
}
距你发贴的时间已有两三个月了,也不晓得有没有用^_^
|
|