DOS实模式下读写内存遇到的问题,请指教

时间:2022-09-02 19:29:01
为了读到1M以上的内存,参考梁肇新的"在DOS实模式下直接存取4G内存",基本上是其源代码,也没看出有什么问题,但是执行到LIDT或者某些指令时,就会自动重启,不知道是怎么回事,大家帮忙看看,有可能是什么原因导致的?

#include "dos.h"
#include "stdio.h"

unsigned long GDT_Table[]=
{
0,0, //NULL - 00H
0x0000FFFF,0x00CF9A00, //Code32 - 08h Base=0 Limit=4G-1 Size=4G
0x0000FFFF,0x00CF9200 //Data32 - 10h Base=0 Limit=4G-1 Size=4G
};
unsigned char OldIDT [6]={0}; //Save The IDTR before Enter Protect Mode.
unsigned char pdescr_tmp [6]={0}; //NULL The IDTR s Limit=0 CPU will disable all Interrupts, include NMI.
#define KeyWait() { while(inportb(0x64) &2); }
void A20Enable(void)
{
KeyWait();
outportb(0x64,0xD1);
KeyWait();
outportb(0x60,0xDF); //Enable A20 with 8042.
KeyWait();
outportb(0x64,0xFF);
KeyWait ();
}

void LoadFSLimit4G(void)
{
A20Enable (); //Enable A20
//***Disable ints & Null IDT //***
asm{
cli //Disable inerrupts
SIDT OldIDT //Save OLD IDTR
LIDT pdescr_tmp //Set up empty IDT.Disable any interrupts,执行到这句就会重启
} // Include NMI.

//*** Lodd GDTR //***
asm{ // The right Code is Real, But BC++ s Linker NOT Work with 32bits Code.
db 0x66 //32 bit Operation Prefix in 16 Bit DOS.
MOV CX,DS //MOV ECX,DS
db 0x66 //Get Data segment physical Address
SHL CX,4 //SHL ECX,4
MOV word ptr pdescr_tmp [0],(3*8-1) //MOV word ptr pdescr-tmp [0], (3*8-1)
db 0x66
XOR AX,AX //XOR EAX,EAX
MOV AX,offset GDT_Table // MOV AX,offset GDT-Table
db 0x66
ADD AX,CX //ADD EAX,ECX
MOV word ptr pdescr_tmp [2], AX //GDTR Base low16 bits
db 0x66
SHR AX,16 //SHR EAX,16
MOV word ptr pdescr_tmp [4],AX //GDTR Base high16 bits
LGDT pdescr_tmp //Load GDTR
}


//**** Enter 32 bit Flat Protected Mode //****
asm{
mov DX,0x10 // The Data32 Selector
db 0x66,0x0F,0x20,0xC0 // MOV EAX,CR0
db 0x66
MOV BX,AX // MOV EBX,EAX
OR AX,1
db 0x66,0x0F,0x22,0xC0//MOV CRO,EAX // Set Protection enable bit//执行这句也会重启
//JMP flush//
    } //Clear machine perform cache.
// Now In Flat Mode, But The CS is Real Mode value.
//flush:
asm{ //And it s attrib is 16Bit Code Segment.
db 0x66
MOV AX,BX //MOV EAX,EBX
db 0x8E,0xE2 //MOV FS,DX
//Load FS Base=0 Size=4G now
db 0x66,0x0F,0x22,0xC0 //MOV CRO,EAX
//Return Real Mode.
LIDT OldIDT //LIDT OldIDT //Restore IDTR //这句也重启
STI // STI //Enable INTR
}
}

unsigned char ReadByte (unsigned long Address)
{
asm db 0x66
asm mov di,word ptr Address // MOV EDI, Address
asm db 0x67 //32 bit Address Prefix
asm db 0x64 //FS:
asm mov ax,word ptr [BX] // =MOV AL, FS: [EDI]
return _AX;
}


void WriteByte(unsigned long Address, unsigned short data)
{
asm db 0x66
asm mov di,word ptr Address //MOV EDI, Address
asm db 0x67 //32 bit Address Prefix
asm db 0x64 //FS
asm mov ax,data
asm mov word ptr [BX],ax
}


void main(void)
{
LoadFSLimit4G();

unsigned long addr;
unsigned short rtn0,rtn1;
unsigned short data;
data = 100;
addr = 0x00100000;
rtn0 = ReadByte(addr);
addr = 0x00110000;
WriteByte(addr,data);
rtn1 = ReadByte(addr);
addr = 0x00100000;
rtn0 = ReadByte(addr);
}

25 个解决方案

#1


知道的帮个忙~~~
在线等~~~

#2


既然是实模式,执行LIDT干什么?切入保护模式装入描述符后就切回实模式。

#3


好像是切入保护模式前保存IDTR值,退出保护模式后还要恢复。
我如果不加LIDT这一句,曾经成功运行过,但是读回来的值并不对,而且往一个地址写值后,读其他地址的值竟然都变成这个值,很奇怪啊

其实对保护模式并不是很了解,只是现在需要用到DOS下读取高于1M的内存,才找了一些相关资料参考,很多东西都不是很了解,大家多多指教。

#4


“在DOS实模式下直接存取4G内存”不需要IDT,只要一个GDT,其中含有一个有效的描述符,程序开A20、关中断、切保护模式、给ds、es等装入描述符、返回实模式、开中断,这样就行了。

#5


cnzdgs,我已经按你说的步骤重新改了个程序,可是还有两个问题:
1.写内存的时候如果直接运行就会自动跳出程序,只能重启,单步的话就没有问题
2.读内存的值好像还是不对,而且往一个地址写值后,读取其他地址的值就都变成一样的,麻烦你帮我看看吧。。。


代码如下:
//打开A20
void openA20()

while(inp(0x64) & 2); outp(0x64,0xd1);
while(inp(0x64) & 2); outp(0x60,0xdf);
while(inp(0x64) & 2); outp(0x64,0xff);
}

unsigned long GDT_def[]={0,0,0x0000FFFF,0x008F9200}; //全局描述符表GDT
unsigned char GDT_Addr[6]={0}; //存放GDT的基地址和长度
void set4gb() 
{ asm{
cli//disable interrupt
push ds ; push es
mov word ptr GDT_Addr[0], (2*8-1) //GDT的长度存入GDT_Addr中
db 0x66 
mov ax,ds//mov eax,ds //计算GDT描述符表的线性基地址31-0
db 0x66
shl ax,4//shl eax,4 //段地址eax=ds×16
db 0x66
xor bx,bx//xor ebx,ebx //ebx清零
mov bx,offset GDT_def //bx=GDT的偏移地址
db 0x66
add ax,bx//add eax,ebx //GDT的线性基地址=eax+ebx
mov word ptr GDT_Addr[2],ax
db 0x66
shr ax,16
mov word ptr GDT_Addr[4],ax
lgdt GDT_Addr
//mov dword ptr GDT_Addr[2],eax //GDT的线性基地址存入GDT_Addr中
//lgdt fword ptr GDT_Addr 
mov bx,8 //设置数据段描述符的选择字
db 0x66,0x0f,0x20,0xc0//mov eax,cr0
or al,1//????
db 0x66,0x0f,0x22,0xc0//mov cr0,eax
jmp flush1
} //进入保护方式
flush1: asm{
mov ds,bx //DS装载具有4GB界限的数据段描述符
mov es,bx //ES装载具有4GB界限的数据段描述符
and al,0feh
db 0x66,0x0f,0x22,0xc0//mov cr0,eax
jmp flush2
} //返回实方式
flush2: asm{
pop es ; pop ds
sti
}
}


unsigned short read_ram(unsigned long addr)
{ asm push ds
asm mov ax,0
asm mov ds,ax
asm db 0x66
asm mov si,word ptr addr//asm mov esi,addr
asm db 0x67
asm db 0x64
asm mov ax,word ptr [bx]//
//asm mov al,[esi]
asm pop ds
return _AX;
}


void write_ram(unsigned long addr,unsigned short chr)
{ asm push ds
asm mov ax,0
asm mov ds,ax
asm db 0x66
asm mov si,word ptr addr//asm mov esi,addr
asm db 0x67
asm db 0x64
asm mov ax,chr
asm mov word ptr [bx],ax//asm mov [esi],al
asm pop ds
}


void main(void)
{
openA20();
set4gb();

unsigned long addr = 0xD0100000;
unsigned short data = 0x12;
unsigned short rtn0,rtn1;
addr = 0x00100000;
rtn0 = read_ram(addr);
addr = 0xD0100000;
rtn0 = read_ram(addr);
write_ram(addr,data);
rtn1 = read_ram(addr);
addr = 0x00100000;
rtn0 = read_ram(addr);
return;
}

#6


GDT_def要包含两个描述符,第1个全0,第2个才是要用的。

#7


不好意思,我没懂,那要如何定义?

#8


原来的代码好像没有什么问题,
你是在windows下运行BC++然后调试你的程序么?还是在纯DOS下
如果是windows的话,重启是正常的.因为windows下是保护模式.windows下那个cmd是32位的,16位command.com之类的老DOS程序是运行在虚拟8086模式.
我曾在windows下调试类似的代码每次都不行,最后在纯dos下(我用的是dos7.1)或者虚拟机里面的纯dos,都可以通过.

另外,如果你用的是BC++,BC++的内联汇编好像只支持286的,不支持32位的.
但BC++3.0自带TASM的,可以使用fs,eax,cr0等等.可以在IDE里面编调试汇编.
建一个工程,把带有main函数的C文件加进去,在把asm文件加进去,build一下就可以了,
也可以不用C,纯用汇编来写,要自己定义一个_main proc.最好还是加入到工程里面build,单个asm编译有时候会出问题.

#9


我是在纯DOS下,BC31调试程序的,在advanced code generation 里面已经选择了386模式
而且为了让DOS实模式下的16位程序可用32位寄存器和地址,在代码中加入操作码前缀0x66和地址前缀0x67,所以应该没有问题了。
但是结果就是不对,很郁闷啊


#10


cs!=0(gdt)
cs=0(是null不是gdt0)

#11


哦,我看了一下面的代码,



unsigned long GDT_def[]={0,0,0x0000FFFF,0x008F9200}; //全局描述符表GDT 
unsigned char GDT_Addr[6]={0}; //存放GDT的基地址和长度 
void set4gb() 
{ asm{ 
cli//disable interrupt 
push ds ; push es 
mov word ptr GDT_Addr[0], (2*8-1) //GDT的长度存入GDT_Addr中 
db 0x66 
mov ax,ds//mov eax,ds //计算GDT描述符表的线性基地址31-0 
db 0x66 
shl ax,4//shl eax,4 //段地址eax=ds×16 
db 0x66 
xor bx,bx//xor ebx,ebx //ebx清零 
mov bx,offset GDT_def //bx=GDT的偏移地址 
db 0x66 
add ax,bx//add eax,ebx //GDT的线性基地址=eax+ebx 
mov word ptr GDT_Addr[2],ax 
db 0x66 
shr ax,16 
mov word ptr GDT_Addr[4],ax 
lgdt GDT_Addr 
//mov dword ptr GDT_Addr[2],eax //GDT的线性基地址存入GDT_Addr中 
//lgdt fword ptr GDT_Addr 
mov bx,8 //设置数据段描述符的选择字  //设置了,怎么没有加载?
db   0x8E,0xE2   //MOV   FS,DX 跟前面一样,把段地址放在FS寄存器里
db 0x66,0x0f,0x20,0xc0//mov eax,cr0 
or al,1//???? 
db 0x66,0x0f,0x22,0xc0//mov cr0,eax 
jmp flush1 
} //进入保护方式 
flush1: asm{ 
mov ds,bx //DS装载具有4GB界限的数据段描述符 
mov es,bx //ES装载具有4GB界限的数据段描述符 
and al,0feh 
db 0x66,0x0f,0x22,0xc0//mov cr0,eax 
jmp flush2 
} //返回实方式 
flush2: asm{ 
pop es ; pop ds 
sti 



unsigned short read_ram(unsigned long addr) 
{ asm push ds 
//asm mov ax,0 
asm mov ds,ax  //这样不行的吧.实模式16位的赋值,还是用FS好
asm db 0x66 
asm mov si,word ptr addr//asm mov esi,addr 
asm db 0x67 
asm db 0x64 
asm mov ax,word ptr [bx]// 
//asm mov al,[esi] 
asm pop ds 
return _AX; 

#12


加载了选择字后,往一个地址写值后,别的地址不会被改写了,但是好像有时候写成功了,有时候失败了,读回来仍然是0
直接运行write_ram还是会自动跳出程序死机,单步就没问题,搞不懂为什么

Crazii,我按你的建议改城如下程序:
unsigned   char   read_ram  (unsigned   long   addr) 

asm   db   0x66 
asm   mov   di,word   ptr   addr   //   MOV   EDI,   addr 
asm   db   0x67   //32   bit   Address   Prefix 
asm   db   0x64   //FS: 
asm   mov   ax,word   ptr   [BX]   //   =MOV   AL,   FS:   [EDI] 
return   _AX; 

也死机了。。。

#13


单步的话倒是不会死机,但也是写了值,所有的地址读回来又都是这个值

#14


asm  mov  ax,word  ptr  [BX]  //  =MOV  AL,  FS:  [EDI] 
前面是把32位线性地址给edi了吧,这里应该用edi
asm  db  0x67  //32  bit  Address  Prefix 
asm  db  0x64  //FS: 
asm  mov  ax,word  ptr  [di]

#15


还是不对啊。。。不知道是哪里出了问题。。。。

#16


引用 15 楼 tingry 的回复:
还是不对啊。。。不知道是哪里出了问题。。。。 

哦?无语..还有问题啊.目前是什么情况.更严重了..?..

#17


呵呵,我也无语了

现在情况跟之前差不多,直接执行读写就死机,单步可以通过
如果对一个地址写了值,读回来是对的,但是读其他地址也都是这个值了。。。
(现在还没有管到底写对地方没有)

#18


你退出IDE,在DOS下运行,试试~~
我再看看代码..

#19


天 啊 

#20


退出IDE,直接运行,就没反应了

#21


我用第一楼的代码,去掉LDT之后,该了一个错误(见红色部分),运行通过.
退出IDE之后,最好先cls清屏,防止滚屏,然后运行程序,屏幕最上方会显示一个A,说明32位地址已经有效了.程序正常结束
不过我真的受不了那个0x66,0x67.等等的那些前缀......

#include   "dos.h " 
#include   "stdio.h " 

unsigned   long   GDT_Table[]= 

0,0,   //NULL   -   00H 
0x0000FFFF,0x00CF9A00,   //Code32   -   08h   Base=0   Limit=4G-1   Size=4G 
0x0000FFFF,0x00CF9200   //Data32   -   10h   Base=0   Limit=4G-1   Size=4G 
}; 
//unsigned   char   OldIDT   [6]={0};   //Save   The   IDTR   before   Enter   Protect   Mode.
unsigned   char   pdescr_tmp   [6]={0};   //NULL   The   IDTR   s   Limit=0   CPU   will   disable   all   Interrupts,   include   NMI. 
#define   KeyWait()   {   while(inportb(0x64)   &2);   } 
void   A20Enable(void) 

KeyWait(); 
outportb(0x64,0xD1); 
KeyWait(); 
outportb(0x60,0xDF);   //Enable   A20   with   8042. 
KeyWait(); 
outportb(0x64,0xFF); 
KeyWait   (); 


void   LoadFSLimit4G(void) 

A20Enable   ();   //Enable   A20 
//***Disable   ints   &   Null   IDT   //*** 
asm{ 
cli   //Disable   inerrupts 
//SIDT   OldIDT   //Save   OLD   IDTR
//LIDT   pdescr_tmp   //Set   up   empty   IDT.Disable   any   interrupts,执行到这句就会重启
}   //   Include   NMI. 

//***   Lodd   GDTR   //*** 
asm{   //   The   right   Code   is   Real,   But   BC++   s   Linker   NOT   Work   with   32bits   Code. 
db   0x66   //32   bit   Operation   Prefix   in   16   Bit   DOS. 
MOV   CX,DS   //MOV   ECX,DS 
db   0x66   //Get   Data   segment   physical   Address 
SHL   CX,4   //SHL   ECX,4 
MOV   word   ptr   pdescr_tmp   [0],(3*8-1) //MOV   word   ptr   pdescr-tmp   [0],   (3*8-1) 
db   0x66 
XOR   AX,AX   //XOR   EAX,EAX 
MOV   AX,offset   GDT_Table //   MOV   AX,offset   GDT-Table 
db   0x66 
ADD   AX,CX   //ADD   EAX,ECX 
MOV   word   ptr   pdescr_tmp   [2],   AX //GDTR   Base   low16   bits 
db   0x66 
SHR   AX,16   //SHR   EAX,16 
MOV   word   ptr   pdescr_tmp   [4],AX //GDTR   Base   high16   bits 
LGDT   pdescr_tmp   //Load   GDTR
}


//****   Enter   32   bit   Flat   Protected   Mode   //**** 
asm{ 
mov   DX,0x10   //   The   Data32   Selector 
db   0x66,0x0F,0x20,0xC0   //   MOV   EAX,CR0 
db   0x66 
MOV   BX,AX   //   MOV   EBX,EAX 
OR   AX,1 
db   0x66,0x0F,0x22,0xC0//MOV   CRO,EAX   //   Set   Protection   enable   bit//执行这句也会重启 
//JMP   flush// 
      }   //Clear   machine   perform   cache. 
//   Now   In   Flat   Mode,   But   The   CS   is   Real   Mode   value. 
//flush: 
asm{   //And   it   s   attrib   is   16Bit   Code   Segment. 
db   0x66 
MOV   AX,BX   //MOV   EAX,EBX 
db   0x8E,0xE2   //MOV   FS,DX 
//Load   FS   Base=0   Size=4G   now 
db   0x66,0x0F,0x22,0xC0   //MOV   CRO,EAX
//Return   Real   Mode.
//LIDT   OldIDT   //LIDT   OldIDT   //Restore   IDTR   //这句也重启
STI   //   STI   //Enable   INTR
}
}

unsigned   char   ReadByte   (unsigned   long   Address)
{
asm   db   0x66
asm   mov   di,word   ptr   Address   //   MOV   EDI,   Address
asm   db   0x67   //32   bit   Address   Prefix
asm   db   0x64   //FS:
asm   mov   ax,word   ptr   [bx]   //   =MOV   AL,   FS:   [EDI]  //这一句我不懂,bx寄存器,加了前面两个字节的指令前缀,就变成edi了....但貌似是正确的.
return   _AX;
}


void   WriteByte(unsigned   long   Address,   unsigned   short   data)
{
asm   db   0x66
asm   mov   di,word   ptr   Address   //MOV   EDI,   Address
asm   mov   ax,data  //把这一句放在前面,因为下面的指令前缀是针对最后一句写内存指令的,而不是本句
//另外,这里要访问data,默认是ds:data,所以在这个函数里面不要修改ds的值,否则访问不到正确的数据

asm   db   0x67   //32   bit   Address   Prefix
asm   db   0x64   //FS
asm   mov   word   ptr   [bx],ax
}


void   main(void) 


unsigned   long   addr;
unsigned   short   rtn0,rtn1;
unsigned   short   data;
LoadFSLimit4G();
data   =   0x4141;//第一个41是颜色属性,第二个0x41是'A'
addr   =   0x00100000;
rtn0   =   ReadByte(addr);
addr   =   0x000b8000; //写显存b800:0000 (16位分段的地址)= b8000(32位线性地址)
WriteByte(addr,data); 
rtn1   =   ReadByte(addr); 
addr   =   0x00100000; 
rtn0   =   ReadByte(addr); 
}

#22


太感动了,非常非常的感谢~~~~
我去试试看

#23


可以了,太谢谢了,困扰了我N久的问题。。。。。都快烦死了
谢谢~~~~

不过有点不明白,怎么它全速运行和单步运行得到的结果会不一样呢???
如果用F8执行就压根过不去,这是为什么啊?

#24


我也不知道啊,直接Ctrl+F9可以运行的,但是单步跟踪就死了.
估计是因为bc的turbo debugger是实模式下的调试器吧,保护模式下可能会出错,跟踪不了...不清楚

#25


又碰到了一个问题。。。。
发现每次读取内存的时候,只有最开始一两次读的是对的,后面的就都错了,搞不明白为什么?

#1


知道的帮个忙~~~
在线等~~~

#2


既然是实模式,执行LIDT干什么?切入保护模式装入描述符后就切回实模式。

#3


好像是切入保护模式前保存IDTR值,退出保护模式后还要恢复。
我如果不加LIDT这一句,曾经成功运行过,但是读回来的值并不对,而且往一个地址写值后,读其他地址的值竟然都变成这个值,很奇怪啊

其实对保护模式并不是很了解,只是现在需要用到DOS下读取高于1M的内存,才找了一些相关资料参考,很多东西都不是很了解,大家多多指教。

#4


“在DOS实模式下直接存取4G内存”不需要IDT,只要一个GDT,其中含有一个有效的描述符,程序开A20、关中断、切保护模式、给ds、es等装入描述符、返回实模式、开中断,这样就行了。

#5


cnzdgs,我已经按你说的步骤重新改了个程序,可是还有两个问题:
1.写内存的时候如果直接运行就会自动跳出程序,只能重启,单步的话就没有问题
2.读内存的值好像还是不对,而且往一个地址写值后,读取其他地址的值就都变成一样的,麻烦你帮我看看吧。。。


代码如下:
//打开A20
void openA20()

while(inp(0x64) & 2); outp(0x64,0xd1);
while(inp(0x64) & 2); outp(0x60,0xdf);
while(inp(0x64) & 2); outp(0x64,0xff);
}

unsigned long GDT_def[]={0,0,0x0000FFFF,0x008F9200}; //全局描述符表GDT
unsigned char GDT_Addr[6]={0}; //存放GDT的基地址和长度
void set4gb() 
{ asm{
cli//disable interrupt
push ds ; push es
mov word ptr GDT_Addr[0], (2*8-1) //GDT的长度存入GDT_Addr中
db 0x66 
mov ax,ds//mov eax,ds //计算GDT描述符表的线性基地址31-0
db 0x66
shl ax,4//shl eax,4 //段地址eax=ds×16
db 0x66
xor bx,bx//xor ebx,ebx //ebx清零
mov bx,offset GDT_def //bx=GDT的偏移地址
db 0x66
add ax,bx//add eax,ebx //GDT的线性基地址=eax+ebx
mov word ptr GDT_Addr[2],ax
db 0x66
shr ax,16
mov word ptr GDT_Addr[4],ax
lgdt GDT_Addr
//mov dword ptr GDT_Addr[2],eax //GDT的线性基地址存入GDT_Addr中
//lgdt fword ptr GDT_Addr 
mov bx,8 //设置数据段描述符的选择字
db 0x66,0x0f,0x20,0xc0//mov eax,cr0
or al,1//????
db 0x66,0x0f,0x22,0xc0//mov cr0,eax
jmp flush1
} //进入保护方式
flush1: asm{
mov ds,bx //DS装载具有4GB界限的数据段描述符
mov es,bx //ES装载具有4GB界限的数据段描述符
and al,0feh
db 0x66,0x0f,0x22,0xc0//mov cr0,eax
jmp flush2
} //返回实方式
flush2: asm{
pop es ; pop ds
sti
}
}


unsigned short read_ram(unsigned long addr)
{ asm push ds
asm mov ax,0
asm mov ds,ax
asm db 0x66
asm mov si,word ptr addr//asm mov esi,addr
asm db 0x67
asm db 0x64
asm mov ax,word ptr [bx]//
//asm mov al,[esi]
asm pop ds
return _AX;
}


void write_ram(unsigned long addr,unsigned short chr)
{ asm push ds
asm mov ax,0
asm mov ds,ax
asm db 0x66
asm mov si,word ptr addr//asm mov esi,addr
asm db 0x67
asm db 0x64
asm mov ax,chr
asm mov word ptr [bx],ax//asm mov [esi],al
asm pop ds
}


void main(void)
{
openA20();
set4gb();

unsigned long addr = 0xD0100000;
unsigned short data = 0x12;
unsigned short rtn0,rtn1;
addr = 0x00100000;
rtn0 = read_ram(addr);
addr = 0xD0100000;
rtn0 = read_ram(addr);
write_ram(addr,data);
rtn1 = read_ram(addr);
addr = 0x00100000;
rtn0 = read_ram(addr);
return;
}

#6


GDT_def要包含两个描述符,第1个全0,第2个才是要用的。

#7


不好意思,我没懂,那要如何定义?

#8


原来的代码好像没有什么问题,
你是在windows下运行BC++然后调试你的程序么?还是在纯DOS下
如果是windows的话,重启是正常的.因为windows下是保护模式.windows下那个cmd是32位的,16位command.com之类的老DOS程序是运行在虚拟8086模式.
我曾在windows下调试类似的代码每次都不行,最后在纯dos下(我用的是dos7.1)或者虚拟机里面的纯dos,都可以通过.

另外,如果你用的是BC++,BC++的内联汇编好像只支持286的,不支持32位的.
但BC++3.0自带TASM的,可以使用fs,eax,cr0等等.可以在IDE里面编调试汇编.
建一个工程,把带有main函数的C文件加进去,在把asm文件加进去,build一下就可以了,
也可以不用C,纯用汇编来写,要自己定义一个_main proc.最好还是加入到工程里面build,单个asm编译有时候会出问题.

#9


我是在纯DOS下,BC31调试程序的,在advanced code generation 里面已经选择了386模式
而且为了让DOS实模式下的16位程序可用32位寄存器和地址,在代码中加入操作码前缀0x66和地址前缀0x67,所以应该没有问题了。
但是结果就是不对,很郁闷啊


#10


cs!=0(gdt)
cs=0(是null不是gdt0)

#11


哦,我看了一下面的代码,



unsigned long GDT_def[]={0,0,0x0000FFFF,0x008F9200}; //全局描述符表GDT 
unsigned char GDT_Addr[6]={0}; //存放GDT的基地址和长度 
void set4gb() 
{ asm{ 
cli//disable interrupt 
push ds ; push es 
mov word ptr GDT_Addr[0], (2*8-1) //GDT的长度存入GDT_Addr中 
db 0x66 
mov ax,ds//mov eax,ds //计算GDT描述符表的线性基地址31-0 
db 0x66 
shl ax,4//shl eax,4 //段地址eax=ds×16 
db 0x66 
xor bx,bx//xor ebx,ebx //ebx清零 
mov bx,offset GDT_def //bx=GDT的偏移地址 
db 0x66 
add ax,bx//add eax,ebx //GDT的线性基地址=eax+ebx 
mov word ptr GDT_Addr[2],ax 
db 0x66 
shr ax,16 
mov word ptr GDT_Addr[4],ax 
lgdt GDT_Addr 
//mov dword ptr GDT_Addr[2],eax //GDT的线性基地址存入GDT_Addr中 
//lgdt fword ptr GDT_Addr 
mov bx,8 //设置数据段描述符的选择字  //设置了,怎么没有加载?
db   0x8E,0xE2   //MOV   FS,DX 跟前面一样,把段地址放在FS寄存器里
db 0x66,0x0f,0x20,0xc0//mov eax,cr0 
or al,1//???? 
db 0x66,0x0f,0x22,0xc0//mov cr0,eax 
jmp flush1 
} //进入保护方式 
flush1: asm{ 
mov ds,bx //DS装载具有4GB界限的数据段描述符 
mov es,bx //ES装载具有4GB界限的数据段描述符 
and al,0feh 
db 0x66,0x0f,0x22,0xc0//mov cr0,eax 
jmp flush2 
} //返回实方式 
flush2: asm{ 
pop es ; pop ds 
sti 



unsigned short read_ram(unsigned long addr) 
{ asm push ds 
//asm mov ax,0 
asm mov ds,ax  //这样不行的吧.实模式16位的赋值,还是用FS好
asm db 0x66 
asm mov si,word ptr addr//asm mov esi,addr 
asm db 0x67 
asm db 0x64 
asm mov ax,word ptr [bx]// 
//asm mov al,[esi] 
asm pop ds 
return _AX; 

#12


加载了选择字后,往一个地址写值后,别的地址不会被改写了,但是好像有时候写成功了,有时候失败了,读回来仍然是0
直接运行write_ram还是会自动跳出程序死机,单步就没问题,搞不懂为什么

Crazii,我按你的建议改城如下程序:
unsigned   char   read_ram  (unsigned   long   addr) 

asm   db   0x66 
asm   mov   di,word   ptr   addr   //   MOV   EDI,   addr 
asm   db   0x67   //32   bit   Address   Prefix 
asm   db   0x64   //FS: 
asm   mov   ax,word   ptr   [BX]   //   =MOV   AL,   FS:   [EDI] 
return   _AX; 

也死机了。。。

#13


单步的话倒是不会死机,但也是写了值,所有的地址读回来又都是这个值

#14


asm  mov  ax,word  ptr  [BX]  //  =MOV  AL,  FS:  [EDI] 
前面是把32位线性地址给edi了吧,这里应该用edi
asm  db  0x67  //32  bit  Address  Prefix 
asm  db  0x64  //FS: 
asm  mov  ax,word  ptr  [di]

#15


还是不对啊。。。不知道是哪里出了问题。。。。

#16


引用 15 楼 tingry 的回复:
还是不对啊。。。不知道是哪里出了问题。。。。 

哦?无语..还有问题啊.目前是什么情况.更严重了..?..

#17


呵呵,我也无语了

现在情况跟之前差不多,直接执行读写就死机,单步可以通过
如果对一个地址写了值,读回来是对的,但是读其他地址也都是这个值了。。。
(现在还没有管到底写对地方没有)

#18


你退出IDE,在DOS下运行,试试~~
我再看看代码..

#19


天 啊 

#20


退出IDE,直接运行,就没反应了

#21


我用第一楼的代码,去掉LDT之后,该了一个错误(见红色部分),运行通过.
退出IDE之后,最好先cls清屏,防止滚屏,然后运行程序,屏幕最上方会显示一个A,说明32位地址已经有效了.程序正常结束
不过我真的受不了那个0x66,0x67.等等的那些前缀......

#include   "dos.h " 
#include   "stdio.h " 

unsigned   long   GDT_Table[]= 

0,0,   //NULL   -   00H 
0x0000FFFF,0x00CF9A00,   //Code32   -   08h   Base=0   Limit=4G-1   Size=4G 
0x0000FFFF,0x00CF9200   //Data32   -   10h   Base=0   Limit=4G-1   Size=4G 
}; 
//unsigned   char   OldIDT   [6]={0};   //Save   The   IDTR   before   Enter   Protect   Mode.
unsigned   char   pdescr_tmp   [6]={0};   //NULL   The   IDTR   s   Limit=0   CPU   will   disable   all   Interrupts,   include   NMI. 
#define   KeyWait()   {   while(inportb(0x64)   &2);   } 
void   A20Enable(void) 

KeyWait(); 
outportb(0x64,0xD1); 
KeyWait(); 
outportb(0x60,0xDF);   //Enable   A20   with   8042. 
KeyWait(); 
outportb(0x64,0xFF); 
KeyWait   (); 


void   LoadFSLimit4G(void) 

A20Enable   ();   //Enable   A20 
//***Disable   ints   &   Null   IDT   //*** 
asm{ 
cli   //Disable   inerrupts 
//SIDT   OldIDT   //Save   OLD   IDTR
//LIDT   pdescr_tmp   //Set   up   empty   IDT.Disable   any   interrupts,执行到这句就会重启
}   //   Include   NMI. 

//***   Lodd   GDTR   //*** 
asm{   //   The   right   Code   is   Real,   But   BC++   s   Linker   NOT   Work   with   32bits   Code. 
db   0x66   //32   bit   Operation   Prefix   in   16   Bit   DOS. 
MOV   CX,DS   //MOV   ECX,DS 
db   0x66   //Get   Data   segment   physical   Address 
SHL   CX,4   //SHL   ECX,4 
MOV   word   ptr   pdescr_tmp   [0],(3*8-1) //MOV   word   ptr   pdescr-tmp   [0],   (3*8-1) 
db   0x66 
XOR   AX,AX   //XOR   EAX,EAX 
MOV   AX,offset   GDT_Table //   MOV   AX,offset   GDT-Table 
db   0x66 
ADD   AX,CX   //ADD   EAX,ECX 
MOV   word   ptr   pdescr_tmp   [2],   AX //GDTR   Base   low16   bits 
db   0x66 
SHR   AX,16   //SHR   EAX,16 
MOV   word   ptr   pdescr_tmp   [4],AX //GDTR   Base   high16   bits 
LGDT   pdescr_tmp   //Load   GDTR
}


//****   Enter   32   bit   Flat   Protected   Mode   //**** 
asm{ 
mov   DX,0x10   //   The   Data32   Selector 
db   0x66,0x0F,0x20,0xC0   //   MOV   EAX,CR0 
db   0x66 
MOV   BX,AX   //   MOV   EBX,EAX 
OR   AX,1 
db   0x66,0x0F,0x22,0xC0//MOV   CRO,EAX   //   Set   Protection   enable   bit//执行这句也会重启 
//JMP   flush// 
      }   //Clear   machine   perform   cache. 
//   Now   In   Flat   Mode,   But   The   CS   is   Real   Mode   value. 
//flush: 
asm{   //And   it   s   attrib   is   16Bit   Code   Segment. 
db   0x66 
MOV   AX,BX   //MOV   EAX,EBX 
db   0x8E,0xE2   //MOV   FS,DX 
//Load   FS   Base=0   Size=4G   now 
db   0x66,0x0F,0x22,0xC0   //MOV   CRO,EAX
//Return   Real   Mode.
//LIDT   OldIDT   //LIDT   OldIDT   //Restore   IDTR   //这句也重启
STI   //   STI   //Enable   INTR
}
}

unsigned   char   ReadByte   (unsigned   long   Address)
{
asm   db   0x66
asm   mov   di,word   ptr   Address   //   MOV   EDI,   Address
asm   db   0x67   //32   bit   Address   Prefix
asm   db   0x64   //FS:
asm   mov   ax,word   ptr   [bx]   //   =MOV   AL,   FS:   [EDI]  //这一句我不懂,bx寄存器,加了前面两个字节的指令前缀,就变成edi了....但貌似是正确的.
return   _AX;
}


void   WriteByte(unsigned   long   Address,   unsigned   short   data)
{
asm   db   0x66
asm   mov   di,word   ptr   Address   //MOV   EDI,   Address
asm   mov   ax,data  //把这一句放在前面,因为下面的指令前缀是针对最后一句写内存指令的,而不是本句
//另外,这里要访问data,默认是ds:data,所以在这个函数里面不要修改ds的值,否则访问不到正确的数据

asm   db   0x67   //32   bit   Address   Prefix
asm   db   0x64   //FS
asm   mov   word   ptr   [bx],ax
}


void   main(void) 


unsigned   long   addr;
unsigned   short   rtn0,rtn1;
unsigned   short   data;
LoadFSLimit4G();
data   =   0x4141;//第一个41是颜色属性,第二个0x41是'A'
addr   =   0x00100000;
rtn0   =   ReadByte(addr);
addr   =   0x000b8000; //写显存b800:0000 (16位分段的地址)= b8000(32位线性地址)
WriteByte(addr,data); 
rtn1   =   ReadByte(addr); 
addr   =   0x00100000; 
rtn0   =   ReadByte(addr); 
}

#22


太感动了,非常非常的感谢~~~~
我去试试看

#23


可以了,太谢谢了,困扰了我N久的问题。。。。。都快烦死了
谢谢~~~~

不过有点不明白,怎么它全速运行和单步运行得到的结果会不一样呢???
如果用F8执行就压根过不去,这是为什么啊?

#24


我也不知道啊,直接Ctrl+F9可以运行的,但是单步跟踪就死了.
估计是因为bc的turbo debugger是实模式下的调试器吧,保护模式下可能会出错,跟踪不了...不清楚

#25


又碰到了一个问题。。。。
发现每次读取内存的时候,只有最开始一两次读的是对的,后面的就都错了,搞不明白为什么?