当前位置: 首页 > news >正文

《汇编语言:基于X86处理器》第12章 浮点数处理与指令编码(2)

Intel X86架构数据的运算主要由通用寄存器处理,但浮点数例外,浮点数的运算由专门的FPU寄存器处理。二进制浮点数由三部分组成:符号,有效数字和阶码。这些格式都出自由IEEE组织制定的标准754-1985:以下是三种浮点数的格式:
●32位单精度数值包含1位符号、8位阶码,以及23位有效数字的小数部分。
●64位双精度数值包含1位符号、11位阶码,以及52位有效数字的小数部分。
●80 位扩展双精度数值包含1位符号、16 位阶码,以及 63 位有效数字的小数部分。
本篇介绍浮点数处理与指令编码。

12.3 x86指令编码

若要完全理解汇编语言操作码和操作数,就需要花些时间了解汇编指令翻译成机器语言的方法。由于Intel 指令集使用了丰富多样的指令和寻址模式,因此这个问题相当复杂。首先以实地址模式的8086/8088为例来说明,之后,再展示32 位处理器带来的变化。

Intel 8086处理器是第一个使用复杂指令集计算机(Complex Instruction Set Computer CISC)设计的处理器。这种指令集中包含了各种各样的内存寻址、移位、算术运算、数据传送和逻辑操作。与RISC(精简指令集计算机,Reduced Instruction Set Computer)指令相比,Intel 指令在编码和解码方面有些复杂。指令编码(encode)是指将汇编语言指令及其操作数转换为机器码。指令解码(decode)是指将机器指令转换为汇编语言。对 Intel 指令编码和解码的逐步解释至少将有助于唤起对MASM 作者们辛苦工作的理解和欣赏。

12.3.1 指令格式

一般的x86机器指令格式(图12-6)包含了一个指令前缀字节、操作码、Mod R/M 字节、伸缩索引字节(SIB)、地址位移和立即数。指令按小端顺序存放,因此前缀字节位于指令的起始地址。每条指令都有一个操作码,而其他字段则是可选的。少数指令包含了全部字段,平均来看,绝大多数指令都有2个或3个字节。下面是对指令字段的简介:

●指令前缀 覆盖默认操作数大小。

●操作码 (操作代码)指定指令的特定变体。比如,按照使用的参数类型,指令 ADD有 9种不同的操作码。

●Mod R/M 字段指定寻址模式和操作数。符号“R/M”代表的是寄存器和模式。表12-18列出了Mod字段,表12-19给出了当Mod=10b时16 位应用程序的R/M 字段。

●伸缩索引字节 (scale indexbyte,SIB)用于计算数组索引偏移量。

●地址位移 字段保存了操作数的偏移量,在基址-偏移量或基址-变址-偏移量寻址模式中,该字段还可以与基址或变址寄存器相加。

●立即数 字段保存了常量操作数。

表12-18 Mod字段取值

Mod

位移

00

DISP=0,位移低半部分和高半部分都无定义(除非r/m=110)

01

DISP=位移低半部分符号扩展到16位,位移高半部分无定义

10

DISP=位移高半部分和低半部分都有效

11

R/M 字段包含的是寄存器编号

表12-19 16位R/M字段取值(Mod=10)

R/M

有效地址

R/M

有效地址

000

[BX+SI]+D16

100

[SI]+D16

001

[BX+DI]+D16

101

[DI]+D16

010

[BP+SI]+D16

110

[BP]+D16

011

[BP+DI]+D16

111

[BX]+D16

D16表示偏移量是16位的。

12.3.2 单字节指令

没有操作数或只有一个隐含操作数的指令是最简单的指令。这种指令只需要操作码字段,字段值由处理器的指令集预先确定。表12-20列出了几个常见的单字节指令。在这些指令中,INCDX指令好像是不应该出现的,它出现的原因是:指令集的设计者决定为某些常用指令提供独特的操作码。其结果是,为了代码量和执行速度要对寄存器增量操作进行优化。

表12-20 单字节指令

指令

操作码

指令

操作码

AAA

37

LODSB

AC

AAS

3F

XLAT

D7

CBW

98

INC DX

42

12.3.3 立即数送寄存器

立即操作数(常数)按照小端顺序(起始地址为最低字节)添加到指令。首先关注的是立即数送寄存器指令,暂不考虑内存寻址的复杂性。将一个立即字送寄存器的MOV指令的编码格式为:B8+rw dw,其中操作码字节的值为B8+rw,表示将一个寄存器编号(0~7)与B8相加;dw为立即字操作数,低字节在低地址。(表12-21列出了操作码使用的寄存器编号。)下面例子中出现的所有数值都为十六进制。

表12-21 寄存器编号(8/16位)

寄存器

编号

寄存器

编号

AX/AL

0

SP/AH

4

CX/CL

1

BP/CH

5

DX/DL

2

SI/DH

6

BX/BL

3

DI/BH

7

示例: PUSH CX 机器指令为51。编码步骤如下:

1)带一个16位寄存器操作数的PUSH指令编码为50。

2)CX的寄存器编码为1,因此1+50得到操作码为51。

示例: MOV AX,1 机器指令为B8 01 00(十六进制)。编码过程如下:

1)立即数送16位寄存器的操作码为 B8。

2)AX的寄存器编号为0,将0加上B8(参见表12-21)。

3)立即操作数(0001)按小端顺序添加到指令(01,00)。

示例: MOV BX,1234h 机器指令为BB 34 12。编码过程如下:

1)立即数送16位寄存器的操作码为B8。

2)BX 的寄存器编号为3,将3加上 B8 得到操作码BB。

3)立即操作数字节为34 12。

从实践的角度出发,建议手动汇编一些MOV立即数指令来提高能力,然后通过MASM的源列表文件中的生成代码来检查汇编结果。

完整代码测试笔记

;12.3.3.asm         12.3.3 立即数送寄存器
;将一个立即字送寄存器的MOV指令的编码格式为:B8+rw dw,其中操作码字节的值为B8+rw,
;表示将一个寄存器编号(0~7)与B8相加;dw为立即字操作数,低字节在低地址。INCLUDE Irvine32.inc.code
main PROC;push指令编码为50, DX的寄存器编码为2,因此2+50得到操作码52push dx;立即数送16位寄存器的操作码为B8,DX的寄存器编号为2,相加得到操作码BA;立即操作数22h按小端顺序添加到指令(22 00),所以机器码为 BA 22 00mov dx, 22h;立即数送16位寄存器的操作码为B8,CX的寄存器编号为1,相加得到操作码B9;立即操作数1234h按小端顺序添加到指令(34 12),所以机器码为 B9 34 12mov cx, 1234hINVOKE ExitProcess, 0
main ENDP
END main

查看反汇编:

12.3.4 寄存器模式指令

在使用寄存器操作数的指令中,ModR/M字节用一个3位的标识符来表示寄存器操作数。表12-22列出了寄存器的位编码。操作码字段的位0用于选择8位或16位寄存器1表示16位寄存器,0表示8位存器。

表12-22 Mod R/M字段标识寄存器

R/M

寄存器

R/M

寄存器

000

AX or Al

100

SP or AH

001

CX or CL

101

BP or CH

010

DX or DL

110

SI or DH

011

BX or BL

111

DI or BH

比如,MovAx,Bx的机器码为89D8。寄存器送其他操作数的16位MOV指令的Intel编码为89r,其中/r表示操作码后面带一个Mod R/M字节。Mod R/M字节有三个字段(mod、reg和r/m)。例如,若 Mod R/M 的值为D8,则它包含如下字段:

mod

reg

r/m

11

011

000

●6~7是mod字段,指定寻址模式。mod字段为11表示r/m字段包含的是一个寄存器编号。

●位3~5是reg字段,指定源操作数。在本例中,BX就是编号为011的寄存器。

●位0~2是r/m字段,指定目的操作数。本例中,AX是编号为000的寄存器。

表12-23 列出了更多使用8位和16位寄存器操作数的例子,

表 12-23 MOV 指令编码和寄存器操作数的示例

指令

操作码

mod

reg

r/m

mov ax, dx

8B

11

000

010

mov al,dl

8A

11

000

010

mov cx,dx

8B

11

001

010

mov cl,dl

8A

11

001

010

12.3.5 处理器操作数大小前缀

现在将注意力转回到x86处理器(IA-32)的指令编码。有些指令以操作数大小前缀开始,覆盖了其修改指令的默认段属性。问题是,为什么有指令前缀?在编写8088/8086 指令集时,几乎所有 256 个可能的操作码都用于处理带有8位和 16 位操作数的指令。当 Intel 开发32位处理器时,就需要想办法发明新的操作码来处理32位操作数,而同时还要保持与之前处理器的兼容性。对于面向 16 位处理器的程序,所有使用 32位操作数的指令都添加一个前缀字节。对于面向 32 位处理器的程序,默认为 32 位操作数,因此所有使用 16 位操作数的指令添加一个前缀字节。8 位操作数不需要前缀。

示例:16位操作数 现在对表 12-23 中的 MOV 指令进行汇编,以此为例来看看在16位模式下前缀字节是如何起作用的。.286 伪指令指明编译代码的目标处理器,确保不使用32位寄存器。下面的每条MOV指令都给出了其指令编码:

.model small
.286
.stack 100h
.code
main PROCmov ax, dx				;8B C2mov al, dl				;8A C2

现在对32位处理器汇编相同的指令,使用.386 伪指令,默认操作数为 32 位。指令将包括16 位和 32 位操作数。第一条MOV指令(EAX、EDX)使用的是32 位操作数,因此不需要前缀。第二条MOV(AX、DX)指令由于使用的是16位操作数,因此需要操作数大小前缀(66):

.model small
.386
.stack 100h
.code
main PROCmov eax, edx			;8B C2mov ax, dx				;66 8B C2mov al, dl				;8A C2

12.3.6 内存模式指令

如果 Mod R/M 字节只用于标识寄存器操作数,那么 Intel 指令编码就会相对简单。实际上,Intel 汇编语言有着各种各样的内存寻址模式,这就使得Mod R/M 字节编码相当复杂。(指令集的复杂性是RISC设计支持者常见的批评理由。

Mod R/M 字节正好可以指定256 个不同组合的操作数。表 12-24 列出了Mod 00 时的Mod R/M字节(十六进制)。(完整的表格参见《Intel 64and IA-32 Architectures SoftwareDeveloper's Manual》,卷2A。)Mod R/M 字节编码的作用如下:Mod 列中的两位指定寻址模式的集合。比如,Mod 00 有 8 种可能的 R/M 数值(000b~111b),有效地址列给出了这些数值标识的操作数类型。

假设想要编码MOV AX,[SI],Mod位为00b,R/M位为100b。从表12-19可知 AX的寄存器编号为000b,因此完整的Mod R/M 字节为00 000 100b 或04h:

mod

reg

r/m

00

000

100

十六进制字节 04 在表12-24 的AX列第5 行。

MOV [SI],AL 的Mod R/M 字节还是一样的(04h),因为寄存器AL 的编号也是000。现在对指令MOV [SI],AL进行编码。8 位寄存器的传送操作码为88。Mod R/M 字节为04h,则机器码为88 04。

MOV 指令示例

表12-25列出了8位和16位MOV指令所有的指令格式和操作码。表 12-26 和表 12-27给出了表 12-25 中缩写符号的补充信息。手动汇编 MOV指令时可以用这些表作为参考。(更多细节请参阅Intel 手册。)

表12-28列出了更多的MOV指令,这些指令能手动汇编,且可以与表中的机器代码比较。假设myWord的起始地址偏移量为0102h

12.3.7 本节回顾

1.写出下列 MOV 指令的操作码:

.data
myByte BYTE ?
myWord WORD ?
.codemov ax, @datamov ds, ax					;a. 8Emov ax, bx					;b. 8Bmov bl, al					;c. 8Amov al, [si]				;d. 8Amov myByte, al			    ;e. A2mov myWord, ax			    ;f. A3

2.写出下列 MOV 指令的 Mod R/M 字节:

.data
array WORD 5 DUP(?)
.codemov ax, @datamov ds, ax					;a. D8mov dl, bl					;b. D3mov bl, [di]				;c. 1Dmov ax, [si+2]				;d. 44mov ax, array[si]			;e. 84mov array[di], ax			;f. 85

12.4 本章小结

二进制浮点数由三部分组成:符号、有效数字和阶码。Intel处理器使用了三种浮点数一进制存储格式,这些格式都出自由IEEE组织制定的标准754-1985:二进制浮点数运算:

●32位单精度数值包含1位符号、8位阶码,以及23位有效数字的小数部分。

●64位双精度数值包含1位符号、11位阶码,以及52位有效数字的小数部分。

●80 位扩展双精度数值包含1位符号、16 位阶码,以及 63 位有效数字的小数部分。

若符号位为 1,则数值为负数;若该位为 0,则数值为正数。

浮点数的有效数字由小数点左右两边的十进制数字构成。

并非所有处于0到1之间的实数都可以在计算机内表示为浮点数,其原因是有效位的个数是有限的。

规格化有限数是指,能够编码为0到无穷之间的规格化实数的所有非零有限数值。正无穷(+∞)代表最大正实数,负无穷(-∞)代表最大负实数。NaN 为位模式,不表示有效浮点数。

Intel 8086 处理器被设计为只处理整数运算,因此 Intel 提供了独立的 8087 浮点数协处理器(floating-pointcoprocessor)芯片与8086一起置于计算机的主板上。随着Intel486的出现,浮点操作被整合到主 CPU 内,称为浮点单元(FPU)。

FPU 有 8 个相互独立的可寻址的 80 位寄存器,分别命名为RO~R7,它们构成一个寄存器堆栈。在计算时,浮点操作数以扩展实数的形式保存在FPU 堆栈中。内存操作数也可以用于计算。FPU 在把算术运算操作的结果保存到内存时,会把结果转换为下述格式之一:整数、长整数、单精度数、双精度数或者 BCD 码。

Intel 浮点指令助记符用字母F开始,以区别于 CPU 指令。指令的第二个字母(通常为B或I)表示如何解释内存操作数:B表示为操作数为二进制编码的十进制数(BCD 码),I表示操作数为二进制整数。如果没有指定,那么内存操作数就假设为实数格式。

Intel 8086 是第一个使用复杂指令集计算机(CISC)设计的处理器。其庞大的指令集包含了各种各样的内存寻址、移位、算术运算、数据传送和逻辑操作。

指令编码是指把汇编语言指令及其操作数转换为机器码。指令解码是指将机器码指令转换为汇编语言指令及操作数。

x86机器指令格式包含一个可选的前缀字节、一个操作码字段、一个可选的Mod R/M字节、若干可选的立即数字节,以及若干可选的内存偏移量字节。具备全部这些字段的指令很少。前缀字节覆盖了目标处理器默认的操作数大小。操作码字段是指令独一无二的操作编码。Mod R/M 字段指定了寻址模式和操作数。在使用寄存器操作数的指令中,Mod R/M 字节用一个3位标识符来表示每个寄存器操作数。

12.5 关键术语

http://www.lryc.cn/news/614158.html

相关文章:

  • 准确----SFTP新增用户
  • 三步完成,A100本地vLLM部署gpt-oss,并启动远程可访问api
  • 串口转以太网实战:S7-200驱动大棚温湿度监控、HMI与通风设备
  • Vue框架进阶
  • FPGA实现Aurora 64B66B数据回环传输,基于GTX高速收发器,提供2套工程源码和技术支持
  • 机器学习支持向量机(SVM)
  • 论文精读(二)| 开源软件漏洞感知技术综述
  • 深度学习·MAFT
  • Linux中的内核同步源码相关总结
  • 2025华数杯数学建模A题【 多孔膜光反射性能的优化与控制】原创论文分享
  • 提升LLM服务效率的秘密武器——vLLM!
  • 【MongoDB学习笔记2】MongoDB的索引介绍
  • [GESP202309 五级] 2023年9月GESP C++五级上机题题解,附带讲解视频!
  • 【具身智能】具身智能的革命——人形机器人如何重塑人类日常生活
  • VIOO IQOO7手机 解锁BL ROOT教程
  • Effective C++ 条款30:透彻了解inlining的里里外外
  • 安装CST时,报错问题处理
  • Suno AI 完全上手教程:从文字到音乐,打造自己专属音乐
  • Qwen Agent 入门介绍与简单使用示例
  • 用不均匀硬币实现公平决策
  • 【Bellman负环】Cycle Finding
  • 遥测自跟踪天线系统组成、特点、功能、工作流程
  • 降低程序运行时CPU和GPU峰值占用的技术方案
  • ADB 命令执行模块开发:双模式(普通模式Shell交互模式)实现、线程安全与资源管理优化
  • 机器学习——支持向量机(SVM)实战案例
  • Android 中解决 Button 按钮背景色设置无效的问题
  • BGP笔记及综合实验
  • 如何在simulink中双击一个模块弹出一个exe?
  • 三防平板+天通卫星电话,打通无人之境的通信经脉
  • 前端开发:JavaScript(7)—— Web API