首页 > 代码库 > 实模式_0

实模式_0

实模式是一种简单的16位模式,存在于所有x86处理器上。 Real Mode是第一个x86模式设计,在保护模式诞生之前被许多早期的操作系统使用。 出于兼容性目的,所有x86处理器都以实模式开始执行。 

 

所有现代操作系统(Windows,Linux,...)都运行在保护模式,由于Real模式提供的许多限制和问题(请参见下面的实模式操作系统警告页面)。较旧的操作系统(如DOS)和程序以实模式运行,因为它是当时唯一可用的模式。有关如何从实模式切换到保护模式的信息,请参阅相应的文章。
注意:有一种称为虚拟8086模式的模式,允许运行在保护模式下的操作系统模拟单个应用程序的实模式分段模型。这可以用于允许保护模式操作系统仍然能够访问例如。 BIOS功能。
下面你会找到一个缺点和专业人员的列表。这些主要是“与保护模式相比”。
缺点
少于1 MB的RAM可供使用。
没有基于硬件的内存保护(GDT),也没有虚拟内存。
没有内置的安全机制来防止错误或恶意应用程序。
默认CPU操作数长度只有16位。
所提供的存储器寻址模式比其他CPU模式更具限制性。
访问超过64k需要使用难以处理的段寄存器。
优点
BIOS安装设备驱动程序以控制设备和处理中断。
BIOS功能为操作系统提供了低级API函数的高级集合。
由于缺少要检查的描述符表和较小的寄存器,存储器访问速度更快。
常见的误解
程序员经常认为,因为实模式默认为16位,所以32位寄存器不可访问。这不是真的。
所有32位寄存器(EAX,...)仍然可以使用,只需将“Operand Size Override Prefix”(0x66)添加到任何指令的开头。如果你只是尝试使用一个32位寄存器,你的汇编器可能会为你做这件事。
内存寻址
在实模式中,有一个超过1 MB的“可寻址”存储器(包括高存储区)。请参阅检测内存(x86)和内存映射(x86)以确定实际可用的内存量。可用量将远小于1 MB。内存访问通过段:偏移系统使用分段完成。
有六个16位段寄存器:CS,DS,ES,FS,GS和SS。当使用段寄存器时,地址用以下符号表示(其中“段”是段寄存器中的值,“偏移”是地址寄存器中的值):

 

 12F3  :  4B27
   ^       ^
Segment   Offset

和偏移量通过以下等式与物理地址相关: 

 PhysicalAddress = Segment * 16 + Offset

 

因此,12F3:4B27对应于物理地址0x17A57。任何物理地址可以以多种方式表示,具有不同的段和偏移。例如,物理地址0x210可以是0020:0010,00000:0210或0021:0000。

 

堆栈
SS和SP是16位段:指定20位物理地址(如上所述)的偏移寄存器,它是堆栈的当前“顶部”。堆栈存储16位字,向下生长,并且必须在字(16位)边界对齐。它在每次程序执行PUSH,POP,CALL,INT或RET操作码时使用,并且在BIOS处理任何硬件中断时使用。
高内存区
如果将DS(或任何段寄存器)设置为值0xFFFF,则它指向低于1 MB的16字节的地址。如果随后使用该段寄存器作为基址,偏移量为0x10至0xFFFF,则可以访问从0x100000到0x10FFEF的物理内存地址。在1 MB以上的这个(几乎64 kB)面积在实模式下被称为“高存储区”。注意,您必须激活A20地址线才能工作。
寻址模式
默认情况下,实模式使用16位寻址模式。组装程序员通常熟悉更常见的32位寻址模式,并且可能需要进行调整 - 因为在16位寻址模式下可用作“指针”的寄存器受到的限制更多。以实模式运行的典型程序通常受限于可用字节数,并且在每个操作码中需要一个额外的字节来使用32位寻址。
请注意,您仍然可以在实模式下使用32位寻址模式,只需在任何指令的开头添加“地址大小覆盖前缀”(0x67)即可。如果你只是尝试使用32位寻址模式,你的汇编器可能会为你做这件事。但是你仍然受到在每个存储器访问中使用的段的当前“限制”的限制(在实模式下,永远为64K(虚幻模式可以更大)。

 pasting

  • [BX + val]
  • [SI + val]
  • [DI + val]
  • [BP + val]
  • [BX + SI + val]
  • [BX + DI + val]
  • [BP + SI + val]
  • [BP + DI + val]
  • [address]

 

 从保护模式切换到实模式

 

如上所述,保护模式操作系统可以使用虚拟8086模式模式来访问BIOS功能。然而,VM86模式有其自身的复杂性和困难。一些操作系统设计人员认为,在需要访问BIOS功能的情况下,暂时返回到实模式是更简单和更清洁的。这需要创建一个特殊的Ring 0程序,并将其放置在可以在Real模式下访问的物理内存地址中。
OS通常需要传递关于要执行哪个BIOS功能的信息包。
程序需要执行以下步骤:

禁用中断:

使用CLI关闭可屏蔽中断。

 

 

 

 

 

 

禁用NMI(可选)。
关闭分页:
将控制转移到1:1页。
确保GDT和IDT在1:1页面中。
清除第零控制寄存器中的PG标志。
将第三个控制寄存器设置为0。
将GDT与16位表一起使用(如果已有可用的话,请跳过此步骤):
创建具有16位数据和代码段的新GDT:
限制:0xFFFFF
基址:0x0
16位
特权级别:0
粒度:0
读写:1
加载新的GDT,确保当前使用的选择器保持不变(cs / ds / ss中的索引将是新GDT中原始段的副本)
远程跳转到16位保护模式:
远程跳转到具有16位段索引的16位保护模式。
加载具有16位索引的数据段选择器:
使用16位数据段加载ds,es,fs,gs,ss。
负载实模式IDT:
限制:0x3FF
基本0x0
使用lidt
禁用保护模式:
将CR0中的PE位设置为false。
远跳到实模式:
使用实模式段选择器(通常为0)远跳到实模式。
使用实模式值重载数据段寄存器:
用适当的实模式值(通常为0)加载ds,es,fs,gs,ss。
将堆栈指针设置为适当的值:
将sp设置为不会干扰实模式程序的堆栈值。
启用中断:
使能STI屏蔽中断。
在实际模式下继续所有bios中断。
x86组装示例
[bits 16]
 
idt_real:
	dw 0x3ff		; 256 entries, 4b each = 1K
	dd 0			; 实模式IVT @ 0x0000
savcr0: dd 0 ; Storage location for pmode CR0.   Entry16: ; We are already in 16-bit mode here!   cli ; Disable interrupts.   ; Need 16-bit Protected Mode GDT entries! mov eax, DATASEL16 ; 16-bit Protected Mode data selector. mov ds, eax mov es, eax mov fs, eax mov gs, eax mov ss, eax     ; Disable paging (we need everything to be 1:1 mapped). mov eax, cr0 mov [savcr0], eax ; save pmode CR0 and eax, 0x7FFFFFFe ; Disable paging bit & enable 16-bit pmode. mov cr0, eax   jmp 0:GoRMode ; 执行远跳以设置CS。  
GoRMode: mov sp, 0x8000 ; pick a stack pointer. mov ax, 0 ; mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax lidt [idt_real] sti ; 恢复中断 - 小心,未处理的int将杀死它。
pasting

 

实模式_0