text: 80386  I/O
  从以下几个方面介绍了80386的I/O特性：80386允许以两种方式执行输入/输出:80386提供了一个独立的I/O地址空间，与物理内存不同，它可以用来给外部设备使用的输入/输出端口寻址。I/O地址空间由2^16 (64K)个可单独寻址的8位端口组成；任意两个连续的8位端口都可以视为16位端口；并且四个连续的8位端口可以被视为一个32位端口。因此，I/O地址空间可以容纳最多64K个8位端口，最多32K个16位端口，或最多16K个32位端口。程序可以用两种方式指定端口的地址。使用直接字节常量，程序可以指定：使用DX中的值，程序可以指定:80386可以一次向位于I/O空间的设备传输32位、16位或8位。像内存中的双字一样，32位端口应该在可被4整除的地址上对齐，这样32位就可以在一次总线访问中传输。像内存中的字一样，16位端口应该在偶数地址上对齐，以便16位可以在单个总线访问中传输。一个8位端口可以位于偶数或奇数地址。指令IN和OUT在I/O地址空间中的寄存器和端口之间移动数据。INS和out指令在内存地址空间和I/O地址空间中的端口之间移动数据串。I/O设备也可以放在80386内存地址空间中。只要设备像内存组件一样响应，处理器就无法区分它们。内存映射I/O提供了额外的编程灵活性。任何引用存储器的指令都可以用来访问位于存储器空间中的I/O端口。例如，MOV指令可以在任何寄存器和端口之间传输数据；and、OR和TEST指令可用于操作设备内部寄存器中的位(见图1-1)。通过完整指令集执行的内存映射I/O维护了选择所需I/O设备的全部寻址模式(例如，直接地址、间接地址、基寄存器、索引寄存器、缩放)。与任何其他内存引用一样，内存映射I/O在受保护模式下执行时受到访问保护和控制。80386的I/O指令提供了对处理器I/O端口的访问，用于与外设之间的数据传输。这些指令将I/O地址空间中的端口地址作为一个操作数。I/O指令有两类：I/O指令IN和OUT用于在I/O端口和EAX(32位I/O)、AX(16位I/O)或AL(8位I/O)通用寄存器之间移动数据。IN和OUT指令直接地址I/O端口，在指令中编码最多256个端口地址之一的地址，或间接通过DX寄存器到最多64K端口地址之一。IN (Input from Port)将字节、字或双字从输入端口传输到AL、AX或EAX。如果程序用IN指令指定AL，则处理器从所选端口向AL传输8位。如果程序用IN指令指定AX，则处理器从所选端口向AX传输16位。如果一个程序用IN指令指定EAX，处理器将32位从端口传输到EAX。OUT(输出到端口)将字节、字或双字从AL、AX或EAX传输到输出端口。程序可以使用与IN指令相同的方法指定端口的编号。块(或字符串)I/O指令INS和OUTS在I/O端口和内存空间之间移动数据块。块I/O指令使用DX寄存器在I/O地址空间中指定端口的地址。INS和OUTS使用DX来指定：块I/O指令使用SI或DI来指定源或目标内存地址。对于每次传输，SI或DI根据标志寄存器中的方向位的指定自动递增或递减。当与repeat前缀一起使用时，INS和OUTS对应块输入或输出操作。REP，重复前缀，修改输入输出和输出，以提供一种在I/O端口和存储器之间传输数据块的方法。这些块I/O指令是字符串原语。它们无需使用单独的LOOP指令或中间寄存器来保存数据，从而简化了编程并提高了数据传输的速度。字符串I/O原语可以对字节字符串、字字符串或双字字符串进行操作。每次传输之后，ESI或EDI中的内存地址对于字节操作数更新1，对于字操作数更新2，对于双字操作数更新4。方向标志(DF)中的值决定处理器是否自动增加ESI或EDI (DF=0)，或者是否自动减少这些寄存器(DF=1)。INS(从端口输入字符串)将字节或字串元素从输入端口传输到存储器。助记符INSB、INSW和INSD是显式指定操作数大小的变体。如果程序指定了INSB，处理器将从所选端口传输8位到由ES:EDI指示的存储器位置。如果程序指定了INSW，处理器将从端口传输16位到由ES:EDI指示的存储器位置。如果程序指定了INSD，处理器将32位从端口传输到由ES:EDI指示的存储器位置。目标段寄存器选择(ES)不能为INS指令改变。与REP前缀相结合，INS将信息块从输入端口移动到一系列连续的存储器位置。OUTS(输出字符串到端口)将字节、字或双字字符串元素从内存传输到输出端口。助记符OUTSB、OUTSW和OUTSD是显式指定操作数大小的变体。如果程序指定了OUTSB，处理器将从ES:EDI指示的存储器位置传输8位到所选端口。如果程序指定了OUTSW，处理器将从ES:EDI指示的存储器位置传输16位到所选端口。如果程序指定了OUTSD，处理器将从ES:EDI指示的内存位置传输32位到所选端口。与REP前缀相结合，OUTS将一块信息从DS:ESI指示的一系列连续内存位置移动到输出端口。有两种机制为I/O函数提供保护：这些机制只能在保护模式下运行，包括虚拟8086模式；它们不在真实模式下运行。在实模式下，没有I/O空间保护;任何过程都可以执行I/O指令，并且任何I/O端口都可以由I/O指令寻址。处理I/O的指令需要受到限制，但也需要由在非零特权级别执行的过程执行。出于这个原因，处理器使用标志寄存器的两位来存储I/O特权级别(IOPL)。IOPL定义了执行 I/O 相关指令所需的特权级别。只有当CPL≤IOPL时，才能执行以下指令：这些指令被称为“敏感”指令，因为它们对IOPL很敏感。要使用敏感指令，过程必须在至少与IOPL指定的特权级别(CPL≤IOPL)相同的特权级别上执行。特权较低的程序使用敏感指令的任何尝试都会导致一般保护异常。因为每个任务都有自己唯一的标志寄存器副本，所以每个任务可以有不同的IOPL。一个主要功能是执行I/O的任务(设备驱动程序)可以从拥有3的IOPL中获益，从而允许该任务的所有过程执行I/O。其他任务通常将IOPL设置为0或1，为最有特权的过程保留执行I/O指令的权利。任务只能通过POPF指令改变IOPL;然而，这样的改变是有特权的。除非在特权级别0执行，否则任何过程都不能改变IOPL(标志寄存器中的I/O特权级别)。特权较低的程序试图改变IOPL不会导致异常;IOPL只是保持不变。除了CLI和STI之外，还可以使用POPF指令来改变中断启用标志(IF);然而，POPF对IF的改变是ipl敏感的。一个过程只有在至少与IOPL具有同等特权的级别上执行时，才能用POPF指令改变IF。如果一个不太特权的过程试图以这种方式改变IF，则不会导致异常;它只是保持不变。直接引用处理器I/O空间中的地址的I/O指令有in、INS、OUT、OUTS。80386具有选择性地捕获对特定I/O地址的引用的能力。启用选择性捕获的结构是TSS段中的I/O权限位图(如图1-2所示)。I/O权限映射是位向量。地图的大小及其在TSS段中的位置是可变的。处理器通过在TSS的固定部分中的I/O映射基字段来定位I/O权限映射。I/O映射基域是16位宽，包含I/O权限映射开始的偏移量。I/O权限映射的上限与TSS网段的上限相同。在保护模式下，当处理器遇到I/O指令(In、INS、OUT或OUTS)时，首先检查CPL是否≤IOPL。如果此条件为真，则I/O操作可以继续。如果不为真，处理器检查I/O权限映射。映射中的每个位对应于一个I/O端口字节地址;例如，端口41的位位于I/O映射基数+ 5，位偏移量1。处理器测试与I/O操作所跨越的I/O地址相对应的所有位;例如，双字操作测试对应于四个相邻字节地址的四个位。如果设置了任何测试位，处理器发出一般保护异常信号。如果所有测试位都为零，则I/O操作可以继续。I/O权限映射不必表示所有的I/O地址。未被映射跨越的I/O地址被视为在映射中有一个位。例如，如果TSS limit等于I/O map base + 31，则映射前256个I/O端口;对任何大于255的端口进行I/O操作都会导致异常。如果I/O map base大于等于TSS limit，则TSS段没有I/O权限映射，80386程序中所有I/O指令在CPL &gt; IOPL时都会导致异常。因为I/O权限映射在TSS段，所以不同的任务可以有不同的权限映射。因此，操作系统可以通过更改任务TSS中的I/O权限映射来为任务分配端口。