48

On x86 the first four general-purpose registers are named AX, CX, DX, BX. It would be quite intuitive if their indices (those used in instruction encoding) were in alphabetical order, but instead of ABCD we actually have ACDB. E.g. mov bl, 1 is encoded as B3 01 while mov cl, 1 is B1 01.

Is there any reason why they weren't enumerated in alphabetical order?

Thorbjørn Ravn Andersen
  • 2,262
  • 1
  • 14
  • 25
Ruslan
  • 1,444
  • 14
  • 18
  • 27
    You bring as Exhibit A the x86 architecture, and you're asking why it lacks in consistency in mapping between register mnenomics and opcode binary values? :-) Remember, at that time, a new CPU was allowed to be much more of a clean break with the past than what we might expect today. Just look at the interaction between the 80286's real and protected modes (which Intel did fix in the 386 especially by introducing V86 mode). – user Dec 01 '17 at 15:19
  • 11
    The AX/CX/DX/BX order also makes an appearance in PUSHA, which suggests it might correspond to the internal register file implementation... – Stephen Kitt Dec 01 '17 at 15:31
  • 42
    i always learned these registers as accumulate, count, data, and base. They weren't ordered alphabetically so much as ordered by usage, ax for most arithmetic operations, cx for loop counters, dx for either left over arithmetic (think of the remainder or carry for div/mul) or i/o data, and bx for a base pointer to memory. Roughly, the ACDB is the order of importance for your average use case – Steve Cox Dec 01 '17 at 18:54
  • 2
    @SteveCox this ordering doesn't seem natural in any sense. Well, accumulator is first, that's OK. But why would then be counter, not base pointer? Memory accesses seem to be a more frequent operation than looping, or at least the same in frequency. – Ruslan Dec 01 '17 at 20:29
  • 2
    @Ruslan well yeah this is going to be fundamentally subjective. Although it doesn't surprise me that the guys writing the instruction set (a bunch of cpu nerds) would prioritize looping and arithmetic over memory access – Steve Cox Dec 01 '17 at 20:49
  • 1
    @Ruslan: Arbitrary memory accesses are actually very uncommon in computer code. Most programs access memory in structured patterns, for example by counting (CX) over some source/dest indices (SI, DI). I'd conjecture that BX was the "afterthought" — like, we have an eighth register to name, what should we call it? And since three of the registers were AX,CX,DX, "BX" was the "obvious" choice! (The two registers I haven't mentioned yet in this comment are SP, the stack pointer, and BP, the base/frame pointer. I would have called BX a "byte" pointer to distinguish it from BP "base pointer".) – Quuxplusone Dec 01 '17 at 22:20
  • 6
    @SteveCox actually in the time we're talking about guys writing the instruction set would prioritize minimal number of gates and then minimal gate depth/delay over just about everything else, I would think. So I wonder if there's an explanation in considering the (non-microcoded) instruction decode logic w.r.t. the special purposes of each "general" register. – davidbak Dec 01 '17 at 23:36
  • 3
    @davidbak: That's an interesting point. BX is the only one of those 4 that can be used in a 16-bit addressing mode. ([ (BX|BP) + (DI|SI) + 0 / disp8/disp16], where any of the 3 components are optional.) BX is also the only 16-bit-addressing-mode register that has a low/high half. So maybe in 8086 it was physically on the boundary between the split low/high registers and the address-capable registers that the AGU had to read. – Peter Cordes Dec 02 '17 at 03:18
  • 13
    Consider looking at 8080 and 8086 opcodes since those are the ancestors of x86. – Thorbjørn Ravn Andersen Dec 02 '17 at 23:53
  • 2
    They're not named in an unintuitive order. Strictly you mean their ordering in the opcode field that encodes register. – smci Dec 03 '17 at 03:58

4 Answers4

57

There are no technical reasons, as any order would work and result in the same amount of gates. More likely it originated in the process by which the 8086 was developed. A main goal was to allow easy conversion of 8080 programs, so the development of the 8086 structure started out from a 8080 programming model. 8080 registers were ordered as 16 bit pairs in the sequence of PSW/A, B/C, D/E, H/L (SPand PC) with HL being the general base or memory pointer and DE being a backup (*1). So assigning them in the same order with similar functionality will result in

  • PSW/A becomes AX, and AL is also the general purpose 8 bit accumulator,
  • B/C becomes CX, as these were the general purpose, usually counter registers
  • D/E becomes DX, as general purpose 16 bit pair, and finally
  • H/L becomes BX, as the primary pointer register.

I wouldn't be much surprised if early documents reveal 8080-like names used during development.

Remember, while 8086 registers are more adapted for general use than in the 8080, they had dedicated functions like BX in addressing, and/or optimized coding for certain applications - like AL/AX as primary accumulator. This provided a way for more compact coding when the registers were used in the specialized way, thus faster execution of programs acknowledging these differences.

More important it also gave programs rewritten by automated translation utilities (like Digital Research's XLT86) from 8080 assembler sources to 8086 assembler, an encoding comparably compact to 8080 code. An important argument toward early adopters, as memory requirements where still a major cost factor back then, and asking them to buy a CPU that needs bigger ((E)P)ROMs just for holding the (translated) existing one didn't come over well.


*1 - The physical 8080 model is different, as PSW and A were not part of the register file, which in addition featured a W/Z pair for internal operation (like buffering memory access, or addresses during 16 bit operations)

Raffzahn
  • 222,541
  • 22
  • 631
  • 918
  • 15
    There is also a slightly mnemonic quality to some of the register names: Accumulator, Branch control (B as used in the Z80 instruction DJNZ), Count as in the CX register used by the x86 LOOP instruction and REP modifiers, and a general-purpose Data register. I would assume the naming of H:L is simply based on "high" and "low". – njuffa Dec 02 '17 at 15:58
  • 2
    @njuffa: So BX / "base" may have been named just to fit into the alphabetic pattern. Other letters could have been chosen, like "P" for pointer or T for table, but weren't. Similar to how the new segment regs in 386 were named FS and GS (meaningless letters), mostly to follow 8086's SS (stack) / CS (code) / DS (data), ES (extra? used implicitly only for string / rep-string insns). – Peter Cordes Dec 03 '17 at 04:25
  • 3
    @PeterCordes x86 has SP (stack pointer) and BP (base pointer) registers, so the 'P' for pointers is there in some sense. I agree that BX as an indexing base, to be used in conjunction with the SI (source index) and DI (destination index) registers is also mnemonic. – njuffa Dec 03 '17 at 05:01
  • 2
    @njuffa: Funny to imagine if we'd had PX and PA / PH, and then later EPX / RPX. Then 386 setcc would have given us instructions like setp ah and seta ph. >.< And 8086 already had PF (as a bit in FLAGS). I'm glad they (or Stephen Morse specifically) chose B for base :) – Peter Cordes Dec 03 '17 at 05:22
  • 3
    Well, not all his wished came thru, as for example he called what later became Convert Byte to Word in his papers Signe EXtend – Raffzahn Dec 03 '17 at 13:18
  • 1
    @Raffzahn - well, "convert byte to word" is ambiguous between zero-extend and sign-extend operations, so isn't a great choice of name... – Jules Feb 09 '18 at 23:43
  • @Jules I never said it is. I'd also have prefered Stephen Morse' suggestion for a name :) – Raffzahn Feb 09 '18 at 23:52
  • 1
    The register names are rather unusual due to the history of the processor line. That history also explains why the instruction encoding uses the non-alphabetical-order eax, ecx, edx, ebx https://blogs.msdn.microsoft.com/oldnewthing/20190121-00/?p=100745 – phuclv Feb 03 '19 at 15:49
  • 1
    Side comment: I'd never heard of Morse, so I looked him up. I was amused to note he considered a PDP-11 to be 'a mainframe'. I guess it is, if you're designing the 8086. – dave May 12 '19 at 22:04
  • @Raffzahn The 13-year-old boy in me does find it very amusing to think that we might have had assembly code where, before/after doing something to a value, you SEX it up. – ssokolow Feb 01 '22 at 01:10
  • Actually Base makes a lot of sense. SI and DI are index registers. (Source Index and Destination Index) and BX + SI and BX + DI are valid, so BX is base register for array access. – Joshua Jul 22 '22 at 16:14
  • 1
    Ken Shirriff’s blog post tying the 8008/8080/8085/8086 to the Datapoint 2200 gives a similar explanation, the only difference being the history of AX. – Stephen Kitt Aug 13 '23 at 21:32
18

@davidbak suggests a possible physical implementation motivation for the design choice:

In the time we're talking guys writing the instruction set would prioritize minimal number of gates and then minimal gate depth/delay over just about everything else, I would think.

BX is the only one of ACDB that can be used in a 16-bit addressing mode:
[ (BX|BP) + (DI|SI) + (0 | disp8 | disp16) ], where any of the 3 components are optional.

BX is also the only 16-bit-addressing-mode register that has a low/high half. So maybe in 8086 it was physically on the boundary between the split low/high registers and the address-capable registers that the AGU had to read.

Or maybe not: 8086 ModR/M and opcode encodings were designed before the hardware by Stephen Morse, primarily a software guy. We can't know whether he considered a HW layout benefit, or thought of this as a logical reason, or whether it just worked out well for the HW design, or maybe I'm way off base and it isn't even helpful for the HW implementation.

(off topic re: low-8 of other registers) In x86-64, a REX prefix changes the meaning from AH/CH/DH/BH to SPL/BPL/SIL/DIL, in that order (Intel manual vol.2, Appendix B.1.4.2, Table B-5). In 16/32 bit modes, 16-bit operand size was the smallest for SP/ESP and the other non-X registers. (Making registers more uniform helps compilers, except that compilers sometimes end up wasting a REX prefix by picking a register that needs one to access the low 8 component.)


The pusha/popa ordering matches too, and while that's interesting, the internal implementation probably uses a counter and goes through the same fetch-by-index logic as explicit register operands. So it doesn't add new information that pusha goes in order of the encodings.

It makes sense that the physical layout of the register file would match the register-number encodings, though, to keep the decoding logic simple. I had a look at the addressing-mode encodings, to see if there was a similar pattern there. (I haven't looked at 8086 gate diagrams, but maybe the AGU fetches directly from the last 4 registers in the register file without going through the full indexing that can select any of the 8.)

It's complicated by the fact that 16-bit doesn't have a SIB byte, so base and base+index modes share the same 3 bit R/M field in the ModR/M byte.

Intel x86 manual vol.2, 2.1.5 Addressing-Mode Encoding of ModR/M and SIB Bytes, Table 2-1. 16-Bit Addressing Forms with the ModR/M Byte

Effective Address R/M field (added): /r encoding
[BX+SI] 000
[BX+DI] 001
[BP+SI] 010
[BP+DI] 011
[SI] 100 SI=110
[DI] 101 DI=111
disp16 (no base) 110 (BP=101)
[BX] 111 BX=011

So BX alone is at the opposite end from BX+SI and BX+DI, but it's one wrap-around away from being adjacent to the other two codes that involve BX. When SI and DI are involved, bit0=0 means SI, bit0=1 means DI.

When there are both base and index registers (bit2=0), then bit1=0 means BX, bit1=1 means BP. So that's maybe consistent with BX being earlier in the physical layout of the register file (lower numbers to access it). But R/M fields clearly need significant decoding before they turn into register fetches.

Still, I think it's plausible that the AGU in 8086 has a "back door" into the register file that can only select from the last 4 registers (in /r field and pusha encoding order). Note that 8086 uses the adder in its regular ALU for address calculations, but the addressing-mode decoding hardware might use different paths to fetch inputs for the ALU's address calculations. (Totally guessing; certainly possible it just decodes those addressing modes to the usual 3-bit register codes and drives the normal ALU through regular register-fetch paths.)


In 32/64-bit addressing modes, the encoding matches the usual register encoding, so presumably (in CPUs without out-of-order / register renaming, like 80386) the AGU can access the register file with the same 3-bit address as the ALU.

(fun fact, 32/64-bit shares the same pattern of [e/rbp] being the escape code for disp32 with no base (or RIP relative), which is why EBP always needs at least a disp8 = 0. This is why disassembly looks like [ebp+0] vs. [edx].) Same for r13 in x86-64 mode, because it has the same code as rbp (except for the extra bit in the REX prefix).

user3840170
  • 23,072
  • 4
  • 91
  • 150
Peter Cordes
  • 3,207
  • 17
  • 26
  • 4
    well done finding patterns in the ModR/M byte! – davidbak Dec 02 '17 at 06:12
  • 6
    Nice work, just it begins with a little flaw: Stephen Morse, who was the lead architect and single handed wrote most specs, was a software guy. He did 'invent' the modr/m byte as well as seting the opcodes to be used, before any hardware at such a detail level needed for above reasoning was defined, even less designed. Reading his 1980 book '8086 Primer' gives some insight in the way he saw the CPU. It also describes part the reasoning for the reg field the OP asked about. Last but not least, when looking at other modr/m encodings, above theory would result in more gates. – Raffzahn Dec 02 '17 at 14:03
  • 7
    The 8086 Primer is BTW a very important book for anyone who wants to understand the 8086. The same way Stephen Morese development of the 8086 is special, as he is not a hardware guy, and wasn't involved in any CPU project before or after the 8086 (not even the 8088), the book is special, as the description of the 8086 he gives differ in structure and wording quite from all Intel material. The way it's written reveals a lot about his view when designing and what parts he emphased on. – Raffzahn Dec 02 '17 at 14:20
  • 1
    @Raffzahn: More gates for the AGU to have a back door into the register file? Yeah, I wondered about that; fetching through the regular indexing logic would presumably be fewer gates. Maybe more total gates on chip, but fewer gate-delays in some of the critical paths? Thanks for the info that the ModR/M encodings were set before HW design by a software guy. I'm a software guy, too, though, and I thought of this, so perhaps he did consider BX as on the boundary between data and pointer. – Peter Cordes Dec 02 '17 at 14:28
  • Incidentally the reason why [BP] was the encoding scavenged for something else is [BP] almost never comes up as push sp; mov sp, bp; results in [BP+0] reading the caller's BP which is almost pointless. – Joshua Dec 04 '17 at 15:35
  • 1
    @Joshua: Yes, obviously a good design choice for 16-bit mode. But in 32 and 64-bit addressing modes, [esp] and [rsp] are usable directly, so you can omit making a stack frame and use rbp for arbitrary values. (gcc for several years has enabled -fomit-frame-pointer as part of -O1 in 64-bit mode, and last few years for 32-bit mode.) Using [rsp+whatever] has the downside of always needing a SIB byte, because base=RSP is the escape encoding for SIB byte present. (Fun fact: RSP can't be the index because that's the encoding for no index, allowing [rsp] instead of [rsp+rsp*1]). – Peter Cordes Dec 04 '17 at 15:44
  • 1
    IMO it's too bad 32-bit mode didn't choose some other register as the always-SIB register, like possibly ecx. It's traditionally a counter, not a pointer, and if you do ever want to use it as a pointer it would still only cost 1 extra byte. (Or free if you were using an indexed addressing mode anyway). esp could still have been the can't-be-an-index register. But on CPUs with a stack engine, explicit [esp] addressing modes mixed with push / call / ret cost an extra stack-sync uop on Intel CPUs. (AMD's stack engine doesn't need extra sync uops). – Peter Cordes Dec 04 '17 at 15:49
  • In 32 bit mode, using a stack frame often results in smaller assembly code. mov reg, [rbp + x] is a shorter instruction than mov reg, [rsp + x]. – Joshua Dec 04 '17 at 18:23
  • 2
    I wonder how the cost of the 8x88's approach compared with the cost of something like the 6809's approach which could IIRC use some bits of an extension byte as part of an offset and others for register/segment selection. In many programs, the fraction of instructions that would need an ES: prefix is much higher than the fraction of instructions that would need a displacement of +/-32..127, or beyond +/-16384, so adding an extra byte to instructions needing a longer displacement would be a small price to pay for having some addressing modes imply ES: [the way BP implies SS:]. – supercat Dec 05 '17 at 21:30
10

A related question was asked on StackOverflow after this question was asked.

Background

In the other question, it was assumed that the register names are simply taken from the alphabet, so an extension to the x86 registers (as it has been done with the x86-64) would lead to the following register names: AX-BX-CX-DX-EX-FX-GX-...

However, this assumption is wrong. In nearly all early CPUs (8080, 6502, 6800 ...) CPU register names were named by their (intended) function, not by any systematic naming scheme (such as taking the letters from the alphabet or using numbers).

The x86 registers are also named by the function the registers were intended for:

R0 = AX = Accumulator
R1 = CX = Counter
R2 = DX = Data (maybe also Double)
R3 = BX = Base
R4 = SI = Source Index
R5 = DI = Destination Index
R6 = BP = Base Pointer
R7 = SP = Stack Pointer

The actual answer

It is pure incident that the words "Accumulator", "Base", "Counter" and "Data" start with the first four letters of the alphabet (A, B, C, D).

The developer could have decided to use the word "Pointer" instead of "Base"; in this case the register names would be AX-CX-DX-PX instead of AX-CX-DX-BX.

I doubt that the developers of Intel even noticed that the register names follow the first four letters of the alphabet so they could be sorted alphabetically.

Explanation of the register names

  • Accumulator: In early CPUs, there were only one register that could perform arithmetic operations: The accumulator. I'm not sure about x86-64, but in the 8086 the AX register still was the only one that could be used for multiplication and division.
  • Counter: The rep and loop instructions are using this register for counting.
  • Double: The DX register is used to hold the high 16 bits of a 32-bit ("double word") value.
  • Base: Before the 80386, BX was the only register not explicitly intended for storing addresses that could also be used for memory addressing. (Example[BX+5])
  • Source Index: Registers whose only purpose was to point to an address were often called "index registers". SI is the index register holding the address lods and movs are reading data from.
  • Destination Index: DI is the index register holding the address stos and movs are writing data to.
  • Base Pointer: This register points to the base address of the stack frame.

EDIT

I removed the term "GPR" from the description of BX.

This is because the word "GPR" implies that a register does not have any intended purpose. However, the names of the registers already imply a certain purpose of each register.

Martin Rosenau
  • 3,399
  • 3
  • 11
  • 23
  • 1
    "Before the 80386, BX was the only GPR that could be used for memory addressing" — wrong: SI, DI, BP can also be used in 16-bit mode to address memory with an offset. – Ruslan May 11 '19 at 13:05
  • @Ruslan I updated the description of the "Base". – Martin Rosenau May 11 '19 at 14:43
  • 3
    I disagree with your definition of General Purpose as excluding SI / DI / BP. As long as you can use them for general ALU instructions like add ax,si or mul si, they're GPRs. Would you disqualify CX because it's "intended" for counts (with instructions like loop and shl reg,cl to support it)? See Why are rbp and rsp called general purpose registers?. The argument for SP is better; you need to disable interrupts (or be in user-space so interrupts will use a different stack) to actually use it for anything but a stack pointer. – Peter Cordes Jul 23 '20 at 08:11
  • 2
    But in common computer-architecture terminology, even SP is a GPR, because it's part of the same "address" space encoded with 3-bit register numbers in machine code. OTOH, http://www.keil.com/support/man/docs/armasm/armasm_dom1359731128950.htm describes ARM as having 13 GPRs (r0..r12), considering PC (R15) as non-GRP which makes sense even though it's addressable like a GPR, SP (R13) as non-GPR which is debatable, and LR (R14) as non-GPR which seems totally weird to me. But then they go on to point out that LR can be used as a GPR, and (in ARM state) so can SP. – Peter Cordes Jul 23 '20 at 08:16
  • @PeterCordes The 8086 was intended to compete with the 68000, which used address and data registers. The registers of many 8-bit CPUs (680x, 650x) of that time can also be divided in address and data registers. I think that the Intel developers saw AX, CX, DX and BX as "data registers" and SI, DI, BP and SP as "address registers". So "BX" is the only "data register" that can be used for addressing memory. – Martin Rosenau Jul 23 '20 at 10:28
  • 1
    Sure, I'd agree with that address vs. data terminology, but address registers are also GPRs. Even moreso than 68000, when you don't happen to need that many addresses in regs, you can very much use them for other kinds of data. BTW, I wondered whether there was agreement that 68k's GPRs included the address regs, and found disagreement on it's wikipedia talk page: https://en.wikipedia.org/wiki/Talk%3AMotorola_68000#Counting_general_registers – Peter Cordes Jul 23 '20 at 10:36
  • @PeterCordes The point is that I think that the entire term "GPR" in my answer was wrong. As far as I understand the German Wikipedia correctly, the term "GPR" was (at least in the 1970s) not used for CPUs whose registers were intended for a certain purpose (e.g. CX = Counter). – Martin Rosenau Jul 23 '20 at 12:37
  • 3
    Gah, sometimes I hate loose ill-defined terminology like "GPR", or "16-bit CPU" or "word size". It just leads to a lot of wasted time discussing what label to stick on something that everyone involved understands the actual details of. English https://en.wikipedia.org/wiki/Processor_register#Types makes the distinction that a GPR is one that can hold an address or data, which would rule out AX, CX, and DX. So I don't like that definition either. Fortunately, Wikipedia is not an authoritative source on terminology so we're free to disregard it. – Peter Cordes Jul 23 '20 at 12:47
6

Agreeing with existing answers but being too long to shove into a comment, per a recent post on Ken Shirriff’s blog that traced the instruction set from the Datapoint 2200 through the 8008, 8080 and 8085 and into the 8086:

The 8086 extended its registers to 16 bits and added several new registers. An Intel patent (below) shows that the 8086's registers were originally called A, B, C, D, E, H, and L, matching the Datapoint 2200. The A register was extended to the 16-bit XA register, while the BC, DE, and HL registers were used unchanged. When the 8086 was released, these registers were renamed to AX, CX, DX, and BX respectively.18


  1. This renaming is why the instruction set has the registers in the order AX, CX, DX, BX, rather than in alphabetical order as you might expect. The other factor is that Intel decided that AX, BX, CX, and DX corresponded to Accumulator, Base, Count, and Data, so they couldn't assign the names arbitrarily.

So the alphabetical naming was likely a late-stage renaming, explaining why it had no effect upon encoding.

user3840170
  • 23,072
  • 4
  • 91
  • 150
Tommy
  • 36,843
  • 2
  • 124
  • 171