[BillPg] has been designing a fantasy 1980s-era home computer. As part of the exercise, he’s reevaluating all the assumptions that have grown organically over time in the small computer landscape. Hindsight is, so they say, 20/20, but sometimes hindsight can also be colored by modern thinking. Sometimes an idea that seems stupid today made sense in the context of its time. In particular, [Bill] has thoughts on the much-maligned 8086 memory segments.
If you haven’t run into it before, the 8086/8088 had a problem. It wanted to be more or less conceptually software compatible with the 8080 and Z80 computers, which had 16-bit addresses, leading to a limit of 64K of memory. When Intel was designing the next generation of chips, it knew that 64K had to go, but telling developers that code would require huge reengineering was a non-starter. So the idea was to provide multiple 64K spaces broken up into segments.
As with most things, there is theory, and there is practice. In theory, a 16-bit segment provided four extra address bits to add to the existing 16-bit address, producing a 32-bit address, even though the CPU only had 20 bits of address bus. Code that fit in 64K could pretend like that was the whole world, and a tricked-out system could have 16 worlds. Future systems could, in theory, have had more.
In practice, Intel made the segment the top 16 bits of a 32-bit address and then added it to the ordinary 16-bit address. So address 0000:0010 (segment=0, address=10 hex) is the same memory location as 0001:0000. Address 0010:0010 is the same as address 0000:0110 and 0001:0100. This wasn’t really the intent, just a byproduct of how the chip worked.
Eventually, the segments would become indices into a table (like the title graphic), but by then, bad practices wiped out a good idea. It is doubtful that the original designers thought anyone would take advantage of the overlapping address, but, of course, they did.
By the time the 80286 and beyond produced segments that were really keys which defined a block of memory, everyone was already in the mode of using the segment and offset as a large pointer. C compilers even had “modes” that let you treat the segment as just more address bits. Because of that, even on newer processors, people had a tendency to build a “flat” segment and use it. That is, make a segment that starts at 0, ends at the end of memory, and then forget about segments.
In fact, many people independently discovered that you could define a flat segment in protected mode, return to real mode, and then enjoy a flat address space. This was later christened unreal mode, and a topic we’ve covered a few times before.
We agree with [Bill]. Segments were a good idea at the time and might have been more important if people had used them the “right” way. Of course, there would have been ups and downs. Proper segments might have allowed for easy virtual memory, for example. But at the price of possibly swapping in and out huge segments instead of relatively small pages. Today, most of what segments were supposed to do is part of the memory management unit and is mostly hidden from the application developer. Still, interesting to reflect on why Intel made that choice and how we got to where we are today.

The Intel segmented memory architecture was an abomination. The only good thing that came out of it was the understanding that it was so bad and the ‘386 needed to include a proper memory management system rather than the kludge that was the ‘286.
I think that people’s memories have been softened with the passage of time.
This wired architecture belongs in the same bin as non-binary word sizes and octal.
I agree…
Said in hindsight, but I always liked the flat memory model of the Motorola CPUs more (6×00 series, 68k series). Just think how diffirent compute could have been if we never had the Intel segmentation model (which was pointless, as the 8080 died almost immediately after the 8086 was released, and Z80 code never made it to the IBM PC).
I do like backward compatibility, but again in hindsight…that early compatibility should have been dropped, as it was never used.
“In theory, a 16-bit segment provided four extra address bits”
huh???
I don’t think you really got the point of 8086-style segmented addressing. x86-16 code used absolute addresses. That isn’t a problem as long as you want to have only one program in memory at a time.
Now comes 8086, you have more memory, bigger address space… room for multiple programs/pieces of code at once in memory like TSRs. Maybe you want to split your software into modules that invoke each other through software interrupts (like network drivers do) etc.
If you’d divided 20bits into 16 segments with 64k each you run into a problem. For each piece of code that runs individually you’d have to waste 64k of memory, even if the TSR only takes up 100 bytes.
The segmentation scheme intel chose allows you/DOS to provide as many segments with a usable start address of 0 as you like, wasting no more than 16bytes of memory for each.
To give an example: If you wanted to handle an interrupt for a sound card or your kezboard, you’d have to install an Interrupt Service Routine by putting the appropriate segment:offset address into the Interrupt Vector Table.
You could happily ask DOS for a few hundred bytes anywhere in memory, put your code with 16byte alignment into it, and compute the segment:offset address so your code runs with offset=0 when invoked as interrupt.