19

I know that Virtual 8086 mode was added on the 80386 to make it possible to run old applications in a Protected Mode environment.

The 80286 had a lot of shortcomings, such as not being able to switch back to Real Mode from Protected Mode (except by reset), but its PM environment wasn't that bad by itself. What made it unusable was its incompatibility with the old applications, which expected to be able to do segment arithmetic and other tricks that can be used in Real Mode.

But why wasn't this issue addressed in software at the time? People chose to ditch PM completely until the 80386 came along and instead use RM with dirty hacks to access extended memory (Unreal Mode or switching to PM to access memory and then reset).

Here's a way that I think they could have done it (but I'm not sure it would work in practice). The OS (or DOS extender or whatever) sets up a GDT (and possibly LDTs) which contain the descriptors required by each application (on the 286 you cannot have flat descriptors that cover all memory, because registers are 16-bit). However, all of these descriptors are marked as Not Present in the GDT/LDT except for the time slices in which the applications actually run. Of course, there's no need to create all these descriptors in a single-tasking system like MS-DOS. The only descriptors that are marked Present are the Ring 0 ones (kernel stuff, basically).

When an old 8086 application is run, allocate segments for it as required. If the segments are in extended memory, use LOADALL to set the segment registers to them, since there is no entry in the GDT that can be used. If the application starts resetting segment registers (either by using segment register instructions, far calls or whatever), the CPU will trap and generate a fault, since regardless of what value the segment register takes, it won't be a valid GDT entry. The virtual monitor can then use LOADALL to set the descriptor caches to the new segment (possibly in extended memory), while keeping the value in the visible segment register itself as it was set by the 8086 application.

TLDR: If the program ever reads the segment register, it will get what it expects. If it tries to write to the segment register, the virtual monitor will handle the case by using LOADALL to load segments above 1MB in the descriptor caches while keeping the unrelated values set by the software inside the visible part of the segment registers.

Was this too slow to be practical? Did DOS programs perform so much segment reloading that the LOADALLs would slow the system down to a crawl?

If DOS was extended to run in PM when the 80286 came out, while keeping compatibility with 8086 programs, programmers could have started making a lot of 16-bit PM applications instead of RM ones, which would reduce the overhead of accessing extended memory for all applications, not just applications which used DOS extenders.

user3840170
  • 23,072
  • 4
  • 91
  • 150
DarkAtom
  • 2,337
  • 8
  • 31
  • 3
    "Premature optimization is the root of all evil (or at least most of it) in programming" – Brian H Feb 19 '22 at 20:35
  • 6
    This isn't an optimization attempt. It's a system design change. The reason why someone would want to do this is to stay away from the ugliness of accessing extended memory in Real Mode. As history tells us, staying inside this chaos allowed for more chaos to come, because more RM got written instead of PM applications. Optimization would mean that you have a working system and you make various tweaks to increase performance. – DarkAtom Feb 19 '22 at 20:39
  • System design? --- "But why wasn't this issue addressed in software at the time?" – Brian H Feb 19 '22 at 21:10
  • 4
    Yes. System software. DOS and Real Mode itself were a mess ever since 1MB became a barrier, until the 386 came with V86 mode. I think everybody agrees on that. – DarkAtom Feb 19 '22 at 21:11
  • 7
    First off- It's a good question. Second, don't shoot the messenger. Third, millions of Microsoft customers of the time didn't care about this nearly as much as they cared about "perfect" backwards compatibility for their apps. – Brian H Feb 19 '22 at 21:15
  • "Second, don't shoot the messenger." Sorry, I didn't intend to sound like that. It seems I have been exposed to too much toxic Internet today. I guess users didn't care, but most people were very annoyed by the 640KB limit. In fact, the 640KB code limit would disappear with my proposed setup :) It's not only about extended-memory efficiency and ugliness. – DarkAtom Feb 19 '22 at 21:20
  • 7
    "People chose to ditch PM completely until the 80386 came along" - No, they didn't. Windows 3.0 / Windows 3.1 in standard mode make good use of the 286 PM features. Actually, Windows 3.0 abstracted from protected mode in a way, that Windows applications could run unchanged in real mode or protected mode. This feature was removed from Windows 3.1, though, as 8088-class computers were obsolete at the time of Windows 3.1 – Michael Karcher Feb 19 '22 at 23:38
  • LOADALL being undocumented and with no promise of future support would probably not be a smart thing to design your new OS around as soon as the 80286 is the new thing. – Tommy Feb 20 '22 at 11:37
  • 1
    HIMEM.SYS, for example, depended on the use of LOADALL (because resetting the CPU was too slow), and had to be completely rewritten for the 386. 286 computers were widespread even long after the 386 came along. The change from this setup to a normal 386 V86 environment would only require changes inside the OS - and they would mostly be trivial, because only segment handling would need to be changed from the ugly 286 way to the regular V86 way. The rest of the virtual monitor could stay the same. Not to mention, the OS would probably need to be changed anyway, to make it 32-bit :) – DarkAtom Feb 20 '22 at 11:42
  • @MichaelKarcher The memory winapi I believe had abstracted out the real memory mess already in windows 2. It’s funny how C source code from that era can run mostly unchanged today, using those old calls. – Kuba hasn't forgotten Monica Feb 20 '22 at 16:29
  • @MichaelKarcher Even better, there was Xenix 286. – Leo B. Feb 20 '22 at 18:02
  • Ram was costly enough that most 286 systems only had 640k. – Ian Ringrose Feb 20 '22 at 21:26
  • 1
    @MarkWilliams I don’t believe the alternative approach described in the question was superfluous and irrelevant; on the contrary, I think this is the core of the stated question. Moreover, your edit invalidated RETRAC’s answer, which directly referred to the approach described (and in fact, shows it’s not actually ahistorical at all). – user3840170 Feb 20 '22 at 22:33
  • @DarkAtom even knowing that LOADALL would be present on all 286s requires hindsight; himem.sys first appeared in 1988, three years after the appearance of the 386. Himem.sys explicitly doesn’t satisfy the “software at the time” part of your question, first appearing a solid four years after the AT. (EDIT: and, given the timing, I’d be amazed if it ever were “completely rewritten” for the 386, rather than just using a different implementation there from first launch as it appeared two years after the DeskPro 386) – Tommy Feb 21 '22 at 00:48
  • @user3840170 I felt I'd left enough of the general approach in the question for RETRAC's answer to make sense. It had been flagged to close as requiring opinions, so I was trying to preserve it. – Mark Williams Feb 21 '22 at 09:33
  • 2
    @Kuba see this answer for details of the mode-agnostic memory handling used in Windows 3 for Windows 2 programs (and in OS/2). – Stephen Kitt Feb 21 '22 at 15:11

3 Answers3

28

I believe what you are describing was in fact done. Concurrent DOS for the 286 could multitask DOS programs in protected mode. See the Wikipedia article on Multiuser DOS as well as DOS VMs.

The 80286 had a lot of shortcomings, such as not being able to switch back to Real Mode from Protected Mode (except by reset), but its PM environment wasn't that bad by itself. What made it unusable was its incompatibility with the old applications

They weren't old applications. That's the perspective of hindsight. When Intel started to design the 80286, the IBM PC didn't exist yet! The 286 was brought to market in February 1982, only ~6 months after the IBM PC. Intel seems to have intended the 286 to be designed into new systems, using new operating systems, that would run exclusively in protected mode.

Speaking of new operating systems, I'd note that DOS was always a stop-gap measure, quick and ugly. It was to be replaced with something better, and soon. By 1985, IBM and Microsoft had signed the contract to develop OS/2 and DOS went through a long period of stagnation in the mid-late 80s. This is speculation, but it seems the thinking at the time was that everyone was going to switch to OS/2 or UNIX shortly, so why invest a great deal of effort retrofitting multitasking into a dead-end OS ill-suited to it? (Of course, OS/2 got terribly delayed, and Microsoft ended up doing just that anyway, with Windows in 386 mode.)

RETRAC
  • 13,656
  • 3
  • 42
  • 65
  • 3
    This is exactly how Concurrent DOS worked, and I believe it's also how Xenix 286 ran DOS apps. It only worked on later 286s as the early 286 proceessors were a bugfest. – Alan Cox Feb 22 '22 at 21:17
  • Even if compatibility with existing software weren't an issue, the inability of the 80286 to have more than two general-purpose segment descriptors loaded at a time meant that protected mode apps couldn't be as efficient as real-mode apps. If instead of having a segment descriptor dedicated to each segment register, the upper bits of a segment selector would choose a segment descriptor, which could then select a scaling factor for the remainder of the segment registers, the 80286 could have supported 16MB or more just as efficiently as the 8086 supported 1MB. – supercat Feb 13 '24 at 00:16
12

As RETRAC writes, this was indeed done.

Companies other than Digital Research also considered similar approaches, but ultimately discarded them. One documented case is IBM with OS/2’s DOS support before version 2.0; this is briefly described in The Design of OS/2. One approach considered to run DOS applications in protected mode was to allocate segment descriptors as needed, using documented interfaces; however, even assuming bug-free silicon, this doesn’t leave room for segments descriptors required by the protected-mode operating system itself. The second approach was similar to yours, using LOADALL, but IBM didn’t pursue this because of the 80286 implementation bugs they ran into. Presumably these were the same as those that delayed the release of Concurrent DOS; the bug described in The Design of OS/2 is that CX is destroyed on general protection faults, meaning that faulting code can’t be restarted correctly.

Stephen Kitt
  • 121,835
  • 17
  • 505
  • 462
1

I don't think the engineers at Intel really understood what made real-mode 8086 segmentation so useful: it allowed memory to be allocated in 16-byte chunks, and allowed any group of up to 4096 such chunks to be consolidated and treated as a 64K segment, without needing to have separate descriptors for each one. The fact that the segment register was scaled by a constant factor of 16 wasn't nearly as important as the fact that there was no need to manage memory in a size chunk between an application's individual allocations and the 64K maximum for a segment.

supercat
  • 35,993
  • 3
  • 63
  • 159
  • 3
    "without needing to have separate descriptors for each one". You need to reload segment registers whenever you want to get out of a 64KB segment. That doesn't change in 16-bit PM. Nothing stopped an OS to implement an API such as "allocate X KB of memory, without regards to segment bounds" and then "give me a descriptor at offset Y in this memory chunk". In real mode you would do the same, but reload the segment registers yourself instead of asking the OS to do it. – DarkAtom Feb 20 '22 at 11:14
  • 2
    Any contiguous group of 4096 such chunks. Unlike with paging later. – ilkkachu Feb 20 '22 at 14:16
  • @ilkkachu well, to be fair, you can't divide memory into chunks of less than 4KB using paging =)) – DarkAtom Feb 20 '22 at 16:33
  • 5
    @DarkAtom: In real mode, MOV ES,CX would take the same amount of time to execute as MOV BX,AX. In protected mode, it would require fetching a segment descriptor, which took IIRC four times as long. As for what changes, if code requests multiple small allocations and gets given allocations which get divided amount two different segments, and then releases most of the small allocations those and wants to allocate bigger chunks, there won't be any way for it to consolidate space at the end of one segment and the start of the next. – supercat Feb 20 '22 at 19:59