I don't know whether this is a silly question, but I couldn't find any answer.

With the evolution of CPU architecture, register size was extended, from 8, to 16, 32, and eventually 64-bit. I was wondering whether there was any way to access the higher part of the registers.

Here's an example regarding the rax 64-bit register and its subsequent divisions:

6                              3               1 
4                              2               6       8       1
|                             rax                              |
            (???)              |              eax              |
                                     (???)     |       ax      |
                                               |  ah   |   al  |

One can easily access the higher and lower part of ax by referencing to ah and al respectively. But I couldn't find any reference for the rax and eax higher parts. (Denoted with (???))

  1. Is it possible to access the higher part of the 32-bit and 64-bit registers? If so, which ones?
  2. Assuming it is not possible, what are the reasons behind this?

Note: I am talking about direct access to these bytes, I am not asking for a sequence of instructions that could retrieve them.

  • 5
  • 4
    This is blatantly off topic, since it's a fundamentally question about an architecture, rather than something having anything do do with reverse engineering. – Chris Stratton Jul 9 '18 at 23:52
  • 1
    @ChrisStratton : Thank you for your input; I understand your point. Feel free to raise the matter on meta as it is a valid concern on your part. Personally, I believe Reverse Engineering (RE) encompasses (and applies to) several fields. No doubt this question could have had more ground on another SE site, in the same way Assembly-related questions could be redirected to StackOverflow, software-related questions could be redirected to SuperUser, etc. Understanding how softwares, languages, and architectures work is arguably part of the RE learning process. – Yuriko Jul 10 '18 at 8:14
  • 3
    This question could (should) be migrated to SO and closed as a duplicate. There's lots of stuff you have to learn as a foundation for learning reverse engineering, but that doesn't mean all of it is reverse engineering or belongs on this site. I don't think it's a good idea to split up the asm / cpu-architecture questions across multiple sites except for ones that specifically are only about reverse engineering; it makes it harder to find related questions, not easier, when generic ISA-design questions are split between here and SO. – Peter Cordes Jul 10 '18 at 10:42

Is it possible to access the higher part of the 32-bit and 64-bit registers? If so, which ones?

It is impossible to access the higher parts of the EAX and RAX registers, or of any other 32 and 64-bit registers, directly. You'll have to use indirect instruction sequences if you're interested in doing that. This is because there are no encodings to access those parts in any instruction.

Assuming it is not possible, what are the reasons behind this?

As noted by @nordwald in the comments below, it is simply defined so in the manuals. To get a more detailed official answer, we would need to ask members of the specification definition. We can safely assume the core reason is that the cost of providing access to all available register fractions exceeds the benefit.

I will try listing a few possible reasons for why the cost exceeds the benefit now but didn't in the past:

  1. Benefit of more available register space

    During the time of 16 bit registers registers were scarce (due to chip manufacturing costs) and chips were boasting the number of registers made available to the user. There was a demand for more registers. Exposing half-registers allowed better utilization of the available (precious and expensive) register real-estate. As time passed more registers were made available, the need of better usage of the exact register size decreased. Nowadays, you can use 64 bit registers for only their lower byte or word without "worrying" about not having enough registers.

  2. Cost of increasing instruction set

    Since the available instruction set was relatively small and there were only a few registers, enough of bits were available to encode the different half of registers without increasing the size of the instruction set. In the original 16-bit register days most of the available instruction set space was used. Moving to 32 (and then again to 64-bit), instead of re-implementing the same instructions with different register sizes prefixes were introduced (0x66 for 32 bit registers, for example), keeping the rest of the instruction in tact. This made it trivial to support 32-bit registers on top of the 16-bit original instruction set, but accessing the higher parts of those registers required a more complex design. You can notice there were only 8 fully sized registers (AX, CX, DX, BX, SP, BP, SI and DI) as well as only 8 half sized registers (AL, CL, DL, BL, AH, CH and DH), so that only 3 bits were required to specify the required register.

    Nowadays, encoding all portions of the registers would increase the instruction set and overall CPU complexity.

  3. Supporting legacy code

    When Intel made the first 16-bit processors (the 8086 processor family) they wanted to keep what they called source compatibility. This meant 8-bit processor (Z80/8080) code could be assembled to a 16-bit processor with no code/source changes, although the underlying binary representation of instructions was allowed to change. Thus, the 8-bit registers were bound to stay from the early 8-bit processors even though the instruction binary representation was completely redesigned and 8-bit mode was not supported by the new CPUs (unlike 32 and 64-bit modes which are binary backwards compatible). The Z80 processor family had six 8-bit registers that could have been accessed together to form a 16-bit word.

    Additionally, in transition from 16 to 32-bit processors and then from 32 to 64, maintaining backward compatibility and supporting legacy execution modes was desired. For that reason, maintaining the same binary encoding was achieved by keeping the same instruction set as well as the same binary encoding for multiple instructions, "forcing" the inclusion of half-registers as part of the available instruction set.

  4. Cost of register access synchronization

    As pipelining optimizations became more and more popular the burden of synchronization register access (among other things) increased, making it harder for the CPU to keep track of register access.

    In modern CPUs the half-registers are not actually overlapping with the full registers and instead, the CPU sets a special invalidation flag on all others when one is changed, so it'll know to read the updated register when if others are accessed. This allows advanced CPU level optimizaions (such as register renaming, for example) but makes execution actually slower when half-sized registers are used interchangeably (which is no longer that frequent).

  5. Accessing half-sized register is only partially available to begin with

    Although you might assume otherwise, accessing or using half-sized registers is not always possible. For example, you cannot push half a register (there's no "Push AL"). This was done to only support the minimum needed to make the half-registers useful, but they are not treated as full-blow registers.

Obviously, some reasons can carry more weight than others, some may be unrelated to the original decision, some original reasons may be missing here, etc. Those are merely educated guesses and YMMV

  • 2
    As for the first part, it is simply specified in the Intel Instruction Manual, section 3.4.1 "General Purpose Registers". I think points 1 and 2 sum up the problem quiet well. – Nordwald Jul 9 '18 at 10:42
  • Thank you. I believe your answer, along with Nordwald's comment, hits the nail on the head. I will wait one or two days before accepting an answer, in case anyone has another explanation. – Yuriko Jul 9 '18 at 11:55
  • Even given the OP's goals, it's still somewhat misleading to claim that it is impossible "access" these. Of course it is possible; just use a bit shift instruction. What there isn't is a pseudo-register which specifically codes for the higher part as a unique operand. – Chris Stratton Jul 9 '18 at 23:48
  • 2
    Not all x86 CPUs rename partial registers separately from the full registers. Only Intel P6-family and Sandybridge-family do so, not AMD, not Intel P4, not Silvermont / Knight's Landing. See Why doesn't GCC use partial registers? for a rundown on what different uarches do. (Haswell and later don't even rename AL or AX separately from RAX, only AH/BH/CH/DH). When a reg isn't renamed separately, writing it is a read-modify-write of the full register. (Potentially creating false dependencies rather than later merging costs.) – Peter Cordes Jul 10 '18 at 10:49
  • 1
    I accepted this answer as it seemed to make more sense to me, even though MSalters' answer is interesting and have more upvotes at the moment. – Yuriko Jul 11 '18 at 14:39

It's the wrong question, really. AH is the exception.

Now the real question is, why is AH such an exception? It's an old register, from the 8086 era. It exists to facilitate moving over code from the 8080.

The 8080 has different registers from the 8086, so you can't move over code directly. In particular, it didn't have an AL,AH or AX register. It did have an 8 bits A accumulator and an 8 bits F flag register, which combined to form a 16 bits AF register.

The 8086 kept a 16 bits accumulator, but moved the flags to its own register. However, there is still a vestigial remnant. LAHF loads AH from the lower 8 bits of F, leaving the lower 8 bits in AL untouched. This instruction simplifies the porting of 8080 code to the 8086. And yes, that support for the 8080 is still present in a Core i9 in 2018.

  • 2
    Thank you for your answer. I figured out the answer was probably no, and that ah was the exception for historical reason. Your answer states it clearly, and gives sense to what seemed to have been an odd decision. – Yuriko Jul 9 '18 at 13:24

Update: Thanks to Nirlzr, I see I missed the emphasized point at the bottom of the post. This answer, though not what OP is looking for, may serve useful for anyone down the road who is looking for a way to access those bits. Apologies for missing OPs full intention!

I actually wrote an in-depth article on this topic a couple of years ago: Accessing and Modifying Upper Bits in x86 and x64 Registers

So while there aren't any direct instructions to specifically access the upper half of a 32- or 64-bit register, you can get to that data by using shift and rotate instructions (of which your choice of use mostly depends on if you care about maintaining the integrity of the bits in the lower-half of the registers).

I would piecemeal the post here for a more fleshed-out-looking answer, but it's best to just read the post as is, which has a very intentional flow that includes copious examples.

  • 2
    A nice and thorough article, thanks! but note OP specifically mentioned that is not what he's looking for at the bottom of the question. – NirIzr Jul 9 '18 at 14:12
  • Oops. You're right. I totally glossed over that for some reason, lol. Well, for anyone who might later find this thread looking for how to access those bits, perhaps this will find purpose. =) – dsasmblr Jul 9 '18 at 14:14
  • 1
    Yep, no harm done if you ask me :) – NirIzr Jul 9 '18 at 14:16
  • 1
    @dsasmblr: indeed, I wanted a focus on direct accessing. However, your answer still complements others. I will read your article later. Thank you for your input! – Yuriko Jul 9 '18 at 14:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.