首页 > 其他分享 >初略了解常用寄存器

初略了解常用寄存器

时间:2023-01-06 12:36:30浏览次数:54  
标签:常用 累加器 初略 索引 指令 寄存器 堆栈 指针

常用寄存器对应表

寄存器 名字 含义
EAX Accumulator Register 累加器寄存器
EBX Base Register 基址寄存器
ECX Counter Register 计数器寄存器
EDX Data Register 数据寄存器
ESI Source Index 来源索引
EDI Destination Index 目的地索引
EBP Base Pointer 基指针
ESP Stack Pointer 堆栈指针

寄存器名字的来历

[原作者:William Swanson]
[翻译:nicere]

挑选英特尔寄存器的艺术

我为一家名为Scene Zine的在线杂志写了这篇文章。Scene Zine迎合了Demo Scene,这是一个数字艺术社区,致力于通过音乐、艺术和计算机编程的混合来挑战计算机的极限。演示场景制作的一个特殊类别,即4K介绍,侧重于最终产品的原始文件大小。其目标是将尽可能多的高质量音乐、图形和动画放入仅有的4096字节中。要做到这一点需要高度专业化的尺寸优化技术,因为4096字节的空间比两页打字的文本或一个真彩色的Windows XP图标还要小。这篇文章讨论了其中的一些技术。

有些人评论说,他们希望在Scene Zine中看到更多的专家编程文章。为了纠正这种情况,这篇文章是为所有汇编语言程序员准备的。它讨论了在你的代码中选择使用哪些寄存器的精细艺术。这些信息应该可以简化你的编码,帮助你写出更小的程序。

当英特尔的工程师设计最初的8086处理器时,他们对每个寄存器都有一个特殊的目的。当他们设计指令集时,他们根据预期每个寄存器的功能创建了许多优化和特殊指令。根据英特尔最初的计划使用寄存器可以使代码充分利用这些优化。不幸的是,这似乎是一门失传的艺术。很少有编码员意识到英特尔的整体设计,而且大多数编译器都过于简单,或者专注于执行速度,无法正确使用寄存器。然而,了解寄存器和指令集是如何结合在一起的,是通向毫不费力地进行大小编码的重要一步。

除了优化大小之外,持续使用寄存器还有其他好处。就像使用好的变量名一样,使用一致的寄存器可以使代码更有可读性。当它们被正确使用时,寄存器的含义几乎和高级语言中的循环计数器i一样清晰。事实上,我偶尔会用x86寄存器来命名我的C语言变量,因为寄存器的名字非常有描述性。有了正确的寄存器使用,x86汇编几乎可以像高级语言一样自我记录。

一致的寄存器使用带来的另一个好处是更好的压缩。在使用压缩器来打包最终构建的产品中,例如4K介绍,创造更多的冗余代码会导致更小的打包尺寸。当代码一致地使用寄存器时,相同的指令序列就会重复出现。这反过来又会提高压缩率。

作为回顾,所有x86-家族的CPU都有8个通用的寄存器。寄存器的宽度为32位,尽管16位的版本也可以通过一个特殊的一字节指令前缀来访问。在16位模式下,情况是相反的。默认情况下,低16位可以访问,而完整的寄存器只能通过前缀字节访问。

每个寄存器的名称实际上是一个缩写。即使是 "字母 "寄存器EAX、EBX、ECX和EDX也是如此。下面的列表显示了寄存器的名称和它们的含义。

EAX - 累加器寄存器
EBX - 基址寄存器
ECX - 计数器寄存器
EDX - 数据寄存器
ESI - 来源索引
EDI - 目的地索引
EBP - 基指针
ESP - 堆栈指针
除了全尺寸的通用寄存器外,x86处理器还有8个字节大小的寄存器。由于这些寄存器直接映射到EAX、EBX、ECX和EDX,大多数人把它们看作是大寄存器的一部分。然而,从指令集的角度来看,这些8位寄存器是独立的实体。例如,CL和CH寄存器没有分享ECX寄存器的任何有用属性。除了AL和AH,其他的8位寄存器在指令集中没有任何特殊的意义,所以本文没有提到它们。

EAX: The Accumulator

有三种主要的处理器架构:寄存器、堆栈和累加器(register, stack, and accumulator)。在寄存器结构中,加法或减法等操作可以发生在任何两个任意的寄存器之间。在堆栈结构中,操作发生在堆栈的顶部和堆栈上的其他项目之间。在累加器结构中,处理器有一个称为累加器的计算寄存器。所有的计算都发生在累加器中,而其他寄存器作为简单的数据存储位置。

很明显,X86处理器没有累加器结构。然而,它确实有一个类似累加器的寄存器。EAX / AL。尽管大多数计算可以发生在任何两个寄存器之间,但指令集给予累加器作为计算寄存器的特殊优先权。例如,所有九个基本操作(ADD, ADC, AND, CMP, OR, SBB, SUB, TEST, 和 XOR)都有特殊的一字节操作码,用于在累加器和常数之间进行操作。特殊的操作,如乘法、除法、符号扩展和BCD校正,只能在累加器中进行。

由于大多数计算发生在累加器中,x86架构包含许多优化指令,用于将数据移入和移出该寄存器。首先,处理器有16个字节大小的XCHG操作码,用于在累加器和任何其他寄存器之间交换数据。这些指令并不十分有用,但它们显示了英特尔工程师对累加器比对其他寄存器有多么强烈的偏好。对他们来说,把数据换到累加器中,比在原地工作要好。其他将数据移入和移出累加器的指令有LODS, STOS, IN, OUT, INS, OUTS, SCAS, 和XLAT. 最后,MOV指令有一个特殊的单字节操作码,用于将数据从一个恒定的内存位置移入累加器。

在你的代码中,尽量在累加器中执行更多的工作。正如你所看到的,其余七个通用寄存器的存在主要是为了支持在累加器中进行的计算。

EDX: The Data Register

在剩下的七个通用寄存器中,数据寄存器EDX与累加器的关系最为密切。处理过大数据项的指令,如乘法、除法、CWD和CDQ,将最有意义的位存储在数据寄存器中,将最没有意义的位存储在累加器中。在某种意义上,数据寄存器是累加器的64位扩展。数据寄存器在IO指令中也起作用。在这种情况下,累加器持有要从端口读取或写入的数据,而数据寄存器持有端口地址。

在你的代码中,数据寄存器对存储与累加器的计算有关的数据最有用。根据我的经验,如果书写得当,大多数计算只需要这两个寄存器来存储。

ECX: The Count Register

计数寄存器ECX是x86中无处不在的循环变量i的等价物,x86中每个与计数有关的指令都使用ECX。最明显的计数指令是LOOP、LOOPZ和LOOPNZ。另一条基于计数器的指令是JCXZ,顾名思义,当计数器为0时就会跳转。 计数寄存器也出现在一些移位操作中,它持有要执行的移位次数。最后,计数寄存器通过REP、REPE和REPNE的前缀来控制字符串指令。在这种情况下,计数寄存器决定了操作的最大重复次数。

特别是在演示中,大多数计算都是在一个循环中发生的。在这种情况下,ECX是循环计数器的合理选择,因为没有其他寄存器围绕它有这么多的分支操作。唯一的问题是,这个寄存器是向下计数的,而不是像高级语言中那样向上计数。然而,设计一个向下计数并不难,所以这只是一个小困难。

EDI: The Destination Index

每个产生数据的循环都必须在内存中存储结果,这样做需要一个移动指针。目的索引,EDI,就是这个指针。目标索引持有所有字符串操作的隐含写入地址。值得注意的是,最有用的字符串指令是很少使用的STOS。STOS将数据从累加器复制到内存,并增加目标索引。这条单字节指令非常完美,因为任何计算的最终结果都应该在累加器中,而将结果存储在一个移动的内存地址中是一项常见的任务。

许多编码员把目标索引看作不过是额外的存储空间。这是个错误。所有的例程都必须存储数据,而一些寄存器必须作为存储指针。由于目标索引是为这项工作设计的,用它来做额外的存储空间是一种浪费。使用堆栈或其他的寄存器进行存储,并使用EDI作为你的全局写指针。

ESI: The Source Index

源索引,ESI,具有与目的索引相同的属性。唯一的区别是,源索引是用于读而不是写。尽管所有的数据处理程序都是写的,但并不是所有的都是读的,所以源索引并不是那么普遍的有用。然而,当需要使用它的时候,源索引和目的索引一样强大,并且有相同类型的指令。

当然,在你的代码不读取任何种类的数据的情况下,使用源索引以方便存储空间是可以接受的。

ESP and EBP: The Stack Pointer and the Base Pointer

在八个通用寄存器中,只有堆栈指针ESP和基数指针EBP被广泛用于其最初的用途。这两个寄存器是x86函数调用机制的核心。当一个代码块调用一个函数时,它把参数和返回地址推到堆栈上。一旦进入,函数将基础指针设置为等于堆栈指针,然后将自己的内部变量放在堆栈上。从那时起,函数就相对于基指针而不是堆栈指针来引用其参数和变量。为什么不是堆栈指针?由于某些原因,堆栈指针的寻址模式很糟糕。在16位模式下,它根本不可能是一个方括号的内存偏移。在32位模式下,它只能通过在操作码中添加一个昂贵的SIB字节来出现在方括号中。

在你的代码中,从来没有理由将堆栈指针用于堆栈以外的任何地方。然而,基点指针是可以使用的。如果你的例程通过寄存器而不是通过堆栈传递参数(他们应该这样做),就没有理由把堆栈指针复制到基本指针中。基准指针成为一个自由的寄存器,可以用于你需要的任何东西。

EBX: The Base Register

在16位模式下,基础寄存器EBX作为一个通用的指针。除了专门的ESI、EDI和EBP寄存器外,它是唯一可以出现在方括号内存访问中的通用寄存器(例如,MOV [BX], AX)。然而,在32位的世界中,任何寄存器都可以作为内存偏移,所以基数寄存器不再特殊。

基准寄存器的名字来自XLAT指令。XLAT使用AL作为索引,EBX作为基数,在表中查找一个值。XLAT相当于MOV AL, [BX+AL],如果你需要用表中的一个8位值替换另一个8位值,有时是很有用的(想想颜色查询)。

所以,在所有的通用寄存器中,EBX是唯一一个没有重要专用用途的寄存器。它是一个存储额外指针或计算步骤的好地方,但没有更多的用途。

总结

x86处理器系列中的八个通用寄存器都有一个独特的用途。每个寄存器都有特殊的指令和操作码,使实现这一目的更加方便或有效。下面简要介绍一下这些寄存器及其用途。

EAX - 所有主要的计算都在EAX中进行,因此它类似于一个专门的累加器寄存器。
EDX - 数据寄存器是对累加器的扩展。它对于存储与累加器当前计算有关的数据最为有用。
ECX - 和高级语言中的变量i一样,计数寄存器是通用的循环计数器。
EDI - 每个循环都必须将其结果存储在某个地方,目的索引指向那个地方。通过单字节的STOS指令将数据从累加器中写出来,这个寄存器使数据操作的大小更加有效。
ESI - 在处理数据的循环中,源索引持有输入数据流的位置。和目的索引一样,EDI也有一个方便的单字节指令,用于将数据从内存加载到累加器中。
ESP - ESP是神圣的堆栈指针。由于重要的PUSH、POP、CALL和RET指令都需要它的值,所以从来没有一个好的理由将堆栈指针用于其他方面。
EBP--在将参数或变量存储在堆栈上的函数中,基础指针持有当前堆栈帧的位置。然而,在其他情况下,EBP是一个自由的数据存储寄存器。
EBX - 在16位模式下,基数寄存器作为一个指针很有用。现在它是完全自由的额外存储空间。
下面是一个典型例程的概要,作为这些寄存器如何相互配合的例子。

点击查看代码
                mov     esi, source_address
                mov     edi, destination_address
                mov     ecx, loop_count
my_loop:        lodsd

                ;Do some calculations with eax here.

                stosd
                loop    my_loop
在这个例子中,ECX是循环计数器,ESI指向输入数据,EDI指向输出数据。一些计算,如模糊、过滤,或者可能是颜色查询,都是在循环中使用EAX作为变量进行的。这个例子有点简单,但希望它能说明大致的思路。真正的程序可能会处理比DWORD更复杂的数据,而且可能还会涉及到一堆浮点运算。

总而言之,按照英特尔的意图使用寄存器有几个好处。在第一种情况下,它可以使你的代码利用许多优化和特殊指令。它还可以使代码更易读,因为寄存器执行的是可预测的功能。最后,通过促进更多的重复性指令序列,持续使用寄存器会导致更好的压缩。

标签:常用,累加器,初略,索引,指令,寄存器,堆栈,指针
From: https://www.cnblogs.com/nicere/p/17030075.html

相关文章

  • linux 常用工具
    hexdump-Ctcpdump-ieth0host192.168.0.116-w1.cap-c2000 测速工具:iPerfiperf是一个网络性能测试工具,它可以测试TCP和UDP带宽质量,可以测量最大TCP带......
  • git常用操作
    gitgitinit#gitinit命令用于在目录中创建新的Git仓库.#例如我们在当前目录下创建一个名为runoob的项目.$mkdirrunoob$cdrunoob/$gitinitInitialize......
  • Android画布(二)ShapeDrawable常用函数
    ShapeDrawable常用函数setBounds()用来指定当前ShapeDrawable在当前控件中的显示位置setBounds(intleft,inttop,intright,intbottom)setBounds(Rectbounds)getPaint......
  • 有效提升软件产品质量,测试人员必备软件测试常用方法
    软件测试是软件产品生产周期中必不可少的一项重要活动,是确保软件质量的关键性环节。软件测试不仅仅是一种技术活动,它更是保证软件质量、提升软件产品的可靠性和功能性的......
  • python-函数-常用内建函数
    1.函数-常用内建函数内建函数python解释器内置了很多函数,你可用直接使用它们在前面学习过的内建函数有:print()、len()、open()、range()内建函数描述map()......
  • JAVA常用的工具类
    1集合工具类1.1java.util.Collections使用的基本list示意List<Integer>list=newArrayList<>();list.add(2);list.add(1);list.add(3);1.1.1基本操作Collections.so......
  • springMVC常用注解介绍
    @Controller注解一个类表示控制器,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model......
  • Docker常用命令
    Docker的常用命令帮助命令dockerversion #显示docker的版本信息dockerinfo   #显示docker的系统信息,包括镜像和容器的数量docker --help #万能命令帮助......
  • 对一些常用RDD算子的总结
    虽然目前逐渐sql化,但是掌握RDD常用算子是做好Spark应用开发的基础,而数据转换类算子则是基础中的基础,因此学习这些算子还是很有必要的。这篇博客主要参考Spark官方文档......
  • git常用命令
    git常用命令1.初始化一个本地仓库gitinit2.查看状态gitstatusgitlog3.加到暂缓区gitadd.(当前文件夹所有改动会被添加)4.提交gitcommit-m"xxxxx"......