『第 7 楼』:
 
 
使用 LLM 解释/回答一下
  
实方式下XMS中的图象数据直接访问方法 
  直接访问4GB内存 
  无论CPU在实方式下或保护方式下,其物理地址的形成都将使用段描述符寄存器。对于CPU在形成物理地址时,实方式与0特权级不分页的保护方式是相同的。只是对段的基地址和段的界限值设置不同。 
  当CPU复位后,CPU处于实方式下,段描述符寄存器的界限值被自动设置为64KB,段描述符寄存器的基地址可通过对段寄存器的赋值最大只能设置为FFFFH×16。因此,段内的寻址空间只能为64KB,CPU访问内存的空间只能为FFFFH×16+64KB。 
  基于CPU物理地址形成的统一性,,在实方式下直接访问4GB内存的关键是扩大段描述符寄存器的界限值。使诸如“MOV AX,”指令的32位寄存器间接寻址操作实现4GB内存的访问。然而,CPU在实方式下并没有提供改变段描述符寄存器的界限值的操作指令。改变段描述符寄存器的内容只能在保护方式下进行。当设置控制寄存器CR0的PE位=1时,CPU进入保护方式;当设置控制寄存器CR0的PE位=0时,CPU返回实方式。通过设置CR0改变工作方式时,段描述符寄存器的内容不发生变化。因此,在DOS实方式下直接访问4GB内存之前,让CPU进入保护方式下,通过装载具有4GB界限的段描述符到段描述符寄存器DS、ES、FS和GS中去。然后返回到实方式下。就可使诸如“MOV AX,”、“MOV AX,FS:”指令的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={0}; //存放GDT的基地址和长度 
void set4gb( )  
{ asm{ 
cli //关中断 
mov word ptr GDT_Addr, (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, 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:, ah //源图象数据与阈值进行比较 
asm mov al, 0 //如果源图象数据<阈值,al=0 
asm jc TN1 
asm mov al, 255 //如果源图象数据≥阈值,al=255 
TN1: asm mov fs:, al //存运算结果到二值化数据块中 
asm inc esi //源图象数据块的32位线性地址增一 
asm inc edi //二值化图象数据块的32位线性地址增一 
asm loopd TN0 
} 
④程序结构 
void main( ) 
{ openA20( ); 
set4gb( ); 
… … //用户程序 
} 
 
  距你发贴的时间已有两三个月了,也不晓得有没有用^_^ 
 
    
 
  
 |