Remix.run Logo
Aardwolf 3 days ago

Many things in computing are elegant and beautiful, but this is not one if them imho (the overlapping segments, the multiple pointer types, the usage of 32 bits to only access 1MB, 'medium' having less data than 'compact', ...)

akira2501 3 days ago | parent | next [-]

> but this is not one

It really is though. Memory and thus data _and_ instruction encoding were incredibly important. Physical wires on the circuit board were at a premium then as well. It was an incredibly popular platform because it was highly capable while being stupidly cheap compared to other setups.

Engineering is all about tradeoffs. "Purity" almost never makes it on the whiteboard.

tonyedgecombe 2 days ago | parent | next [-]

The 68000 was from the same era yet it had a 24 bit address bus, enough for 16 MB.

actionfromafar 2 days ago | parent | next [-]

And the 68008¹ was developed to overcome this problem of requiring too many data and address lines.

1: https://en.wikipedia.org/wiki/Motorola_68008

gpderetta 2 days ago | parent [-]

sure, but that limitation didn't show up architecturally, other than requiring more cycles to perform a load or store.

elzbardico 2 days ago | parent | prev | next [-]

The 68000 was a high-end product, the 8088 was a lot cheaper, in a big part because of those design decisions, like having a 16 bit memory bus.

This design allowed for a smaller chip, and keeping backwards compatibility with the 8080.

jhallenworld 2 days ago | parent [-]

But there is more: IBM basically stole the entire CP/M software ecosystem by using the 8088: assembly language CP/M programs could be more or less just recompiled for MS-DOS.

Yet, it extended CP/M by allowing you to use more than 64 KB vs. 8080/Z80.

2 days ago | parent | prev [-]
[deleted]
Aardwolf 3 days ago | parent | prev [-]

But wouldn't allowing plain addition of 1-byte pointer offsets and 2-byte pointer offsets to a current address (just integer addition, no involvement of segments) have been simpler to design and for CPU usage? Rather than this non-linear system with overlapping segments. This would still allow memory-saving tiny pointers when things are nearby

rep_lodsb 2 days ago | parent [-]

The problem is that you can't hold a pointer to more than 64K of address space inside a 16-bit register.

x86 could have easily had an IP-relative addressing mode for data from the beginning (jumps and calls already had it), but to get a pointer you can pass around to use someplace else than the current instruction, it has to be either absolute, or relative to some other "base" register which stays constant. Like the segment registers.

gpderetta 2 days ago | parent [-]

Just combining two 16 bit registers for a logical 32 bit address would have been better than the weird partially overlapping addressspace.

rep_lodsb 2 days ago | parent | next [-]

How would you have redesigned the 8086 to do this? And why, other than because of some aesthetic objection to overlapping segments?

The 286 and 386 in protected mode did allow segments with any base address (24 or 32 bits), so your argument about extending the address space doesn't make sense.

gpderetta 2 days ago | parent [-]

you explained elsewhere how the overlap is used for relocatability, which is a reasonable justification. But if that were not a concern, non overlapping segments would have provided for a larger address space. I will readily admit that I'm not aware of all the constraints that lead to the 8086 design.

386 (not sure how 286 works) did extend segments to a larger address space, by converting them to segment selectors, but it requires a significantly more complex MMU as it is a form of virtual memory.

Narishma 2 days ago | parent [-]

> 386 (not sure how 286 works) did extend segments to a larger address space, by converting them to segment selectors

The 286 did that, though they only extended the address space to 24 bits. The 386 extended it again to 32 bits.

wvenable 2 days ago | parent | prev [-]

But then you'd end up wasting memory because the address space it would be divided into 64K blocks. The first PC had only 16KB of RAM but 128KB was probably more common. With the segments setup the way you describe a 128KB machine could use only 2 segment addresses out of 65,536 -- not very efficient or useful for relocating code and data.

Joker_vD 3 days ago | parent | prev [-]

Yeah, good thing that e.g. RV64 has RIP-relative addressing mode that can address anywhere in the whole 56-bits of available space with no problems, unlike the silly 8086 that resorted to using a base register to overcome the short size of its immediate fields.

akira2501 3 days ago | parent [-]

...and then x86_64 went ahead and added RIP relative addressing back in, and you get the full 64 bits of address space.

Joker_vD 3 days ago | parent [-]

...you know that that's not true, neither for x64 nor RV64, and my comment was sarcastic, right? Both can only straightforwardly address ±2 GiB from the instruction pointer; beyond that, it's "large code model" all over again, with the same inelegant workarounds that's been rediscovered since the late sixties or so. GOT and PLT versus pools of absolute 64-bit addresses, pick the least worst one.

akira2501 3 days ago | parent [-]

> and my comment was sarcastic, right?

Pardon me for not realizing and treating it appropriately.

> with the same inelegant workarounds that's been rediscovered since the late sixties or so

Short of creating instructions that take 64bit immediate operands you're always going to pay the same price. An indirection. This will look different because it will be implemented most efficiently differently on different architectures.

> GOT and PLT versus pools of absolute 64-bit addresses, pick the least worst one.

Or statically define all those addresses within your binary. That seems more "elegant" to you? You'll have the same problem but your loader will now be inside out or you'll have none of the features the loader can provide for you.

At that point just statically link all your dependencies and call it an early day.

Joker_vD 3 days ago | parent [-]

> You're always going to pay the same price. An indirection.

There is a difference between indirecting through a register, or through a memory (which in the end also requires a register, in addition to a memory load). On the other hand, I$ is more precious, and the most popular parts of GOT are likely to be in the voluminous D$ anyhow, so it's hard to tell which is more efficient.

> Or statically define all those addresses within your binary. That seems more "elegant" to you?

Of course not. I personally think a directly specifiable 64-bit offset from the base register that holds the start of the data section is more elegant. But dynamic libraries don't mesh too well with this approach although IIRC it has been tried.

> you'll have none of the features the loader can provide for you. At that point just statically link all your dependencies and call it an early day.

This works surprisingly well in practice, actually. Data relocations are still an issue though.