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

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

问题描述

我开始学习汇编,这在我看来不合逻辑.

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

我了解了rax->eax->ax的历史原因,所以让我们专注于new64 位寄存器.例如,我可以使用r8r8d,但为什么不使用r8dlr8dh?r8wr8b 也是如此.

我最初的想法是我可以同时使用 8 个 r8b 寄存器(就像我可以使用 alah 在同时).但我不能.并使用 r8b 使完整的 r8 寄存器忙".

这就提出了一个问题——为什么?如果不能同时使用其他部分,为什么只需要使用寄存器的一部分?为什么不只保留 r8 而忘记较低的部分?

解决方案

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

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

指令编码备注---------------------------------------------------------mov ax,value b8 01 00 <-- 整个寄存器mov al,value b4 01 <-- 低字节mov ah,value b0 01 <-- 高字节

因为 8086 是 16 位处理器,所以三个不同的版本涵盖了所有选项.
在 80386 中添加了 32 位支持.设计人员有一个选择,要么添加对 3 个额外寄存器集的支持(x 8 个寄存器 = 24 个新寄存器),并以某种方式为这些找到编码,要么保留大部分内容与以前一样.

以下是设计师的选择:

指令编码备注---------------------------------------------------------mov eax,value b8 01 00 00 00(与 mov ax,value 相同的编码!)mov ax,value 66 b8 01 00(前缀 66 + mov eax,value 编码)mov al,value(与之前相同)mov ah,value(和之前一样)

他们只是添加了一个 0x66 前缀来将寄存器大小从(现在)默认的 32 位更改为 16 位,加上一个 0x67 前缀来更改内存操作数的大小.就这样吧.

否则意味着将指令编码的数量加倍或为每个新"部分寄存器添加三个六个新前缀.
到 80386 出现时,所有 指令字节已经被占用,因此没有新前缀的空间.这个操作码空间已经被诸如 AAAAADAAM, AAS, DAADAS代码> SALC.(这些已在 X64 模式下被禁用以释放急需的编码空间).

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

movzx eax,cl//mov 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 个额外编码位.这添加了 spldilsilbpl,但不包括任何 xh reg.(当不使用 rex 前缀时,您仍然可以获得四个 xh regs).

<块引用>

并且使用 r8b 会使完整的 r8 忙"

是的,这称为部分寄存器写入".因为编写 r8b 改变了部分,但不是 r8 的全部,r8 现在被分成两半.一半变了,一半没变.CPU 需要加入两半.它可以通过使用额外的 CPU 周期来执行工作,或者通过向任务添加更多电路以便能够在单个周期内完成它来实现此目的.
后者在硅方面很昂贵,在设计方面很复杂,它还增加了额外的热量,因为要做额外的工作(每个周期做更多的工作 = 产生更多的热量).请参阅 为什么 GCC 不使用部分寄存器? 了解有关不同的 x86 CPU 如何处理部分寄存器写入(以及随后读取完整寄存器).

<块引用>

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

不,它们不是无法访问.

mov rax,bignumber//eax中的随机值mov al,0//清除al异或 r8d,r8d//r8=0mov r8b,16//设置r8b或 r8,rax//改变 r8 upper 而不改变 r8b

您使用掩码加上 andorxornot and 来更改寄存器的一部分,而无需影响其余部分.

ah 确实从来没有需要,但它确实导致了 8086 上更紧凑的代码(以及更有效的可用寄存器).有时写EAX或RAX然后分别读取AL和AH(例如movzx ecx, al/movzx edx, ah)作为解包字节的一部分仍然很有用.>

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.

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

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