为什么我可以在寄存器中访问较低的双字/字/字节,而不能访问较高的双字/字/字节? [英] Why can I access lower dword/word/byte in a register but not higher?

查看:294
本文介绍了为什么我可以在寄存器中访问较低的双字/字/字节,而不能访问较高的双字/字/字节?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我开始学习汇编程序,对我来说这似乎不合逻辑。



为什么我不能在寄存器中使用多个更高的字节?



我了解 rax -> eax -> ax ,所以让我们关注 new 64位寄存器。例如,我可以使用 r8 r8d ,但是为什么不能使用 r8dl r8dh r8w r8b 也是如此。



我的最初的想法是我可以同时使用8个 r8b 寄存器(就像我可以使用 al ah )。但是我不能。使用 r8b 会使完整的 r8 寄存器忙。



哪个提出了问题-为什么?如果您不能同时使用其他部分,为什么只需要使用寄存器的一部分?为什么不只保留 r8 而忘了下部呢?

解决方案


为什么不能在寄存器中使用多个更高的字节


每个指令的排列都需要在指令中编码。原始的8086处理器支持以下选项:

 指令编码说明
---------- -----------------------------------------------
mov ax,值b8 01 00 <-整个寄存器
mov al,值b4 01 <-低位字节
mov ah,值b0 01 <-高位字节

由于8086是16位处理器,三个不同的版本涵盖了所有选项。

在80386中添加了32位支持。设计人员可以选择,要么增加对3个额外寄存器集的支持(x 8个寄存器= 24个新寄存器),要么以某种方式找到它们的编码,或者保留大部分内容。



这是设计师选择的:

 指令编码说明
----------------------------------------------- ----------
mov eax,值b8 01 00 00 00(与mov ax,值相同!)
mov ax,值66 b8 01 00(前缀66 +编码对于mov eax,value)
mov al,value(与之前相同)
mov ah,value(与之前相同)

他们只是添加了 0x66 前缀即可将寄存器大小从(现在)默认的32位更改为16位,再加上一个 0x67 前缀可以更改内存操作数的大小。

否则将意味着加倍指令编码数量或添加 3 每个新部分寄存器都有六个新前缀。

到80386推出时,所有指令字节已被占用,因此没有空间可容纳新的前缀。此操作码空间已被 AAA AAD AAM AAS ,< a href = http://www.felixcloutier.com/x86/DAA.html rel = nofollow noreferrer> DAA DAS SALC 。 (这些已在X64模式下禁用,以释放大量需要的编码空间)。



如果只想更改寄存器的高字节,只需执行以下操作:

  movzx eax,cl //移动al,cl,但是速度更快
shl eax,24 //将al移动到高字节。




为什么不选择两个(比如r8dl和r8dh)


在最初的8086中,有8个字节大小的寄存器:

  al,cl,dl,bl,ah,ch,dh,bh<-按此顺序。 

索引寄存器,基址指针和堆栈寄存器没有字节寄存器。



在x64中已更改。如果有 REX 前缀(表示x64寄存器),则 al..bh (8个regs)编码 al .. r15l 。含16个法规来自rex前缀的1个额外的编码位。这会添加 spl dil sil bpl ,但不包括任何 xh 规则。 (当不使用 rex 前缀时,您仍然可以获得四个 xh 注册表)。


然后使用r8b使整个r8忙


是,这称为部分寄存器写入。因为写 r8b 会改变一部分,但不是全部 r8 r8 现在分为两半。一半改变了,另一半没有改变。 CPU需要将两个部分合并。它可以通过使用额外的CPU周期来执行工作,或者通过在任务中添加更多电路以使其能够在一个周期内完成任务来实现。

就硅而言,后者是昂贵的而且设计复杂,还会增加额外的热量,因为需要做更多的工作(每个周期更多的工作=产生的热量更多)。请参阅为什么GCC不使用部分寄存器?不同的x86 CPU如何处理部分寄存器的写操作(以及后来读取的完整寄存器)。


如果我使用r8b,则无法访问同时存在高56位,但是它们不可访问


不,它们不是不可访问的

  mov rax,bignumber // eax中的随机值
mov al,0 //清除al
xor r8d,r8d // r8 = 0
mov r8b,16 //设置r8b
或r8,rax //更改r8的上限而不更改r8b

您使用掩码加上 xor not和可以更改部分寄存器,而不会影响其余部分。



对于 ah 确实没有需求,但确实在8086上导致了更紧凑的代码(并且有效更多可用的寄存器)。有时编写EAX或RAX然后分别阅读AL和AH仍然有用(例如 movzx ecx,al / movzx edx,啊)作为拆包字节的一部分。


I started to learn assembler, and this does not looks logical to me.

Why can't I use multiple higher bytes in a register?

I understand the historical reason of rax->eax->ax, so let's focus on new 64-bit registers. For example, I can use r8 and r8d, but why not r8dl and r8dh? The same goes with r8w and r8b.

My initial thinking was that I can use 8 r8b registers at the same time (like I can do with al and ah at the same time). But I can't. And using r8b makes the complete r8 register "busy".

Which raises the question - why? Why would you need to use only a part of a register if you can't use other parts at the same time? Why not just keep only r8 and forget about the lower parts?

解决方案

why can't I use multiple higher bytes in a register

Every permutation of an instruction needs to be encoded in the instruction. The original 8086 processor supports the following options:

instruction     encoding    remarks
---------------------------------------------------------
mov ax,value    b8 01 00    <-- whole register
mov al,value    b4 01       <-- lower byte
mov ah,value    b0 01       <-- upper byte

Because the 8086 is a 16 bit processor three different versions cover all options.
In the 80386 32-bit support was added. The designers had a choice, either add support for 3 additional sets of registers (x 8 registers = 24 new registers) and somehow find encodings for these, or leave things mostly as they were before.

Here's what the designers opted for:

instruction     encoding           remarks
---------------------------------------------------------
mov eax,value    b8 01 00 00 00    (same encoding as mov ax,value!)
mov ax,value     66 b8 01 00       (prefix 66 + encoding for mov eax,value)
mov al,value     (same as before)
mov ah,value     (same as before)

They simply added a 0x66 prefix to change the register size from the (now) default 32 to 16 bit plus a 0x67 prefix to change the memory operand size. And left it at that.

To do otherwise would have meant doubling the number of instruction encodings or add three six new prefixes for each of your 'new' partial registers.
By the time the 80386 came out all instruction bytes were already taken, so there was no space for new prefixes. This opcode space had been eaten up by useless instructions like AAA, AAD, AAM, AAS, DAA, DAS SALC. (These have been disabled in X64 mode to free up much needed encoding space).

If you want to change only the higher bytes of a register, simply do:

movzx eax,cl     //mov al,cl, but faster   
shl eax,24       //mov al to high byte.

But why not two (say r8dl and r8dh)

In the original 8086 there were 8 byte sized registers:

al,cl,dl,bl,ah,ch,dh,bh  <-- in this order.

The index registers, base pointer and stack reg do not have byte registers.

In the x64 this was changed. If there is a REX prefix (denoting x64 registers) then al..bh (8 regs) encode al..r15l. 16 regs incl. 1 extra encoding bit from the rex prefix. This adds spl, dil, sil, bpl, but excludes any xh reg. (you can still get the four xh regs when not using a rex prefix).

And using r8b makes the complete r8 "busy"

Yes, this is called a 'partial register write'. Because writing r8b changes part, but not all of r8, r8 is now split into two halves. One half has changed and one half has not. The CPU needs to join the two halves. It can either do this by using an extra CPU cycle to perform the work, or by adding more circuitry to the task to be able to do it in a single cycle.
The latter is expensive in terms of silicon and complex in terms of design, it also adds extra heat because of the extra work being done (more work per cycle = more heat produced). See Why doesn't GCC use partial registers? for a run-down on how different x86 CPUs handle partial-register writes (and later reads of the full register).

if I use r8b I can't access upper 56 bits at the same time, they exist, but unaccessible

No they are not unaccessible.

mov  rax,bignumber         //random value in eax
mov  al,0                  //clear al
xor  r8d,r8d               //r8=0
mov  r8b,16                //set r8b
or   r8,rax                //change r8 upper without changing r8b  

You use masks plus and, or, xor and not and to change parts of a register without affecting the rest of it.

There really was never a need for ah, but it did lead to more compact code on 8086 (and effectively more usable registers). It's still sometimes useful to write EAX or RAX and then read AL and AH separately (e.g. movzx ecx, al / movzx edx, ah) as part of unpacking bytes.

这篇关于为什么我可以在寄存器中访问较低的双字/字/字节,而不能访问较高的双字/字/字节?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆