My partner Elaine Gord was on VisiOn's C compiler team in 1982-1984 with two others. They experimented with having two instruction sets: the native 8088 code for best performance, and a C virtual machine bytecode for code density. The two modes were mixed at the function level, and shared the same call/return stack mechanism. This was terrible for speed, but was thought necessary because the target machines did not have enough ram for the total VisiOn functionality. I don't know if the bytecode scheme got into "production".
All of Microsoft's applications used to do that! The 16-bit versions of Word, Excel, PowerPoint, etc all were implemented using bytecode. That technology was only integrated in Microsoft C and made public in 1992 [1], before that the Microsoft applications group used their own private C toolchain. A copy of it can be found in the Word 1.1 source release [2] ("csl").
[1] https://sandsprite.com/vb-reversing/files/Microsoft%20P-Code... [2] https://github.com/danielcosta/MSWORD
I think Multiplan for 8-bit systems was implemented in a similar fashion, which enabled it to be widely ported.
A fount of knowledge about Microsoft's productivity application group history is Steve Sinofsky's blog, https://hardcoresoftware.learningbyshipping.com/
For p-code references, the relevant blog post is https://hardcoresoftware.learningbyshipping.com/p/003-klunde....
There was a proprietary programming language called CSL, also named after CharlesS. This language, based on C, had a virtual machine, which made it easier to run on other operating systems (in theory) and also had a good debugger—attributes that were lacking in the relatively immature C product from Microsoft.
CharlesS is Charles Simonyi, ex-Xerox PARC employee, hired away by Microsoft and worked on MS Word as well as creating the Hungarian naming system (the Apps version is the definitive version, not the bastard watered-down Systems version used in the Windows header files) - see https://en.wikipedia.org/wiki/Hungarian_notation.The blog post included excerpts from internal MS docs for apps developers. An OCR version of one such page in the blog post follows:
====
One of the most important decisions made in the development of Multiplan was the decision to use C compiled to pseudo-code (Pcode). This decision was largely forced by technological constraints. In early 1981, the microcomputer world was mainly composed of Apple II's and CP/M-80 machines; they had 8-bit processors, and 64K of memory was a lot; 128K was about the maximum. In addition, each of the CP/M-80 machines was a little different; programs that ran on one would not automatically run on another. Pcode made the development of ambitious applications possible; compiling to machine code would have resulted in programs too big to fit on the machines (even with Pcode it was necessary to do a goodly amount of swapping). It also allowed us to isolate machine dependencies in one place, the interpreter, making for very portable code (all that was necessary to port from one machine to another was a new interpreter). For Multiplan, this was an extremely successful strategy; it probably runs on more different kinds of machines than any other application ever written, ranging from the TI/99 to the AT&T 3B series.
Of course, Pcode has its disadvantages as well, and we've certainly run into our share. One disadvantage is that it's slow; many of our products have a reputation for slowness for exactly that reason. There are of course ways to speed up the code, but to get a great deal of speed requires coding a goodly amount in tight hand-crafted assembly language. Another disadvantage is our Pcode's memory model. Since it was originally designed when most machines had very little memory, the original Pcode specification supported only 64K of data; it was not until Multiplan 1.1 was developed in early 1983 that Pcode was extended to support larger data spaces. A final disadvantage of Pcode is that we need our own special tools in order to develop with it; most obviously these include a compiler, linker, and debugger. In order to support these needs, there has been a Tools group within the Applications Development group almost from the beginning, and we have so far been largely unable to take advantage of development effort in other parts of the company in producing better compilers and debuggers. (It should be noted that the Tools group is responsible for considerably more than just Pcode support these days.)
Although portability was one of the goals of using Pcode, it became apparent fairly early on that simply changing the interpreter was not sufficient for porting to all machines. The major problem lay in the different I/O environments available; for example, a screen-based program designed for use on a 24 by 80 does not adapt well to different screen arrangements. To support radically different environments requires radically rewriting the code; we decided the effort was worth it for two special cases: the TRS-80 Model 100 (first laptop computer) and the Macintosh. In retrospect, the Model 100 was probably not worth the effort we put into it, but the Macintosh proved to be an extremely important market.
====
Jon DeVaan's comment to the blog post mentions, https://hardcoresoftware.learningbyshipping.com/p/003-klunde...: P-Code was a very important technology for Microsoft's early apps. Cross platform was one reason, as Steven writes. It was also very important for reducing the memory size of code. When I started, we were writing apps for 128k (K, not m or g) RAM Macs. There were not hard drives, only 400k floppy disks. (Did I mention we all had to live in a lake?)
P-Code was much smaller than native code so it saved RAM and disk space. Most Macs had only one floppy disk drive. A market risk for shipping Excel was requiring 512k Macs with two disk drives which allowed for the OS and Excel code to live on the first drive and user's data on the second. Mac OS did not have code swapping functions, each app had to roll its own from memory manager routines, so the P-Code interpreter provided that function as well.
On early Windows versions of Excel the memory savings aspect was extremely important. The size of programs grew as fast as typical RAM and hard disk sizes for many years so saving code size was a primary concern. Eventually Moore's Law won and compilers improved to where the execution trade-off was no longer worth it. When Windows 95 introduced 32 bit code these code size dynamics returned for a different reason – IO bandwidth. 16 bit Excel with P-Code outperformed 32 bit Excel in native code in any scenario where code swapping was needed. Waiting for the hard drive took longer than the ~7x execution overhead of the P-Code interpreter.
Another Jon DeVaan comment, https://hardcoresoftware.learningbyshipping.com/p/008-compet... : I am surprised to hear Steven say that the app teams and Excel in particular were looking in any serious way at the Borland tools. The reality was the CSL compiler had a raft of special features and our only hope of moving to a commercial tool was getting the Microsoft C team to add the features we needed. This was the first set of requirements that came from being the earliest GUI app developers. Because of the early performance constraints a lot of "tricks" were used that became barriers to moving to commercial tools. Eventually this was all ironed out, but it was thought to be quite a barrier at the time. About this time the application code size was starting to press the limits of the CSL P-Code system and we really needed commercial tools.
And Steve Sinofsky's reply: Technically it was the linker not the compiler. The Excel project was getting big and the apps Tools team was under resource pressure to stop investing in proprietary tools while at the same time the C Tools group was under pressure to win over the internal teams. It was *very* busy with the Systems team, particularly the NT team, on keeping them happy. We’re still 5 years away from Excel and Word getting rid of PCode. Crazy to think about. But the specter of Borland was definitely used by management to torment the Languages team who was given a mission to get Microsoft internally using its tools.C was very much the Javascript of its day. Hand rolling toolchains and compilers to the bytecode they knew and loved (or despised).
This is awesome history. Formal history only remembers the what’s, the when’s, rarely does it catalog the why’s, or how’s. The decision making process of those early programmers helped shape a whole industry.
Wow, just what I wanted for Christmas. Back in the day I found VisiOn's approach fascinating since almost everything at the time was either more tightly integrated or completely unintegrated.
Maybe someone could do the the old Reason software bus based system next? As detailed in Jan 1984 Byte magazine. Lord only knows if there are surviving copies anywhere in the world.
Yep Mitch Kapor and the eff.
[dead]
[flagged]
It's interesting how this article deconstructs the buzzwords used in the marketing material to get from "VisiOn is a multitasking object-oriented OS using a VM to be portable" to "VisiOn is a DOS shell that uses C structs in a documented API and was never meaningfully portable"
Seriously, the "VM" thing is stupid:
> The term "virtual machine" used by VisiOn developers means something different from what we mean by the words "virtual machine" today. The closest word we use today would be "API". That's right, Visi On applications use a cross-platform API. Just like almost any other operating system today. I bet it was a really cool idea back in 1983, though.
As if VM isn't overloaded enough already (quick: does it mean "Virtual Memory", "Virtual Machine" as in the JVM, or "Virtual Machine" as in Xen?) using it to mean "API" is just dishonest.
Processes on modern operating systems are a type of virtual machine. On Linux or Windows, processes are provided with the illusion of infinite memory. And there is the illusion of infinite parallel threads of execution, bounded only by storage. That is a kind of virtualization, just not full machine virtualization.
At the machine level the boundary between machine instructions and system calls and API becomes blurry. If you don't have a floating point unit, the instructions trap and a routine in the kernel can perform the same thing. Instruction sets are APIs. Many operating systems present their system calls as if they were special machine instructions.
This kind of phrasing is less common today, where "virtual machine" is usually equivalent to "full machine virtualization", but you'll encounter this broader sense in earlier literature.
Also in this OS textbook
Yeah I remember an ad for an early x86 OS saying, "Each program here is running on a virtual 8086" and I was like "No... wait yes?".
Virtual machines are such a cool idea that they just keep getting reinvented. You can solve any problem with more abstraction!
> On Linux or Windows, processes are provided with the illusion of infinite memory.
OK, how does this apply to what MS-DOS was doing? Remember that VisiOn is an MS-DOS shell.
The Visi-On API provides a handle-based memory manager. Handles are a kind of managed pointer that allow memory blocks to be moved around even though the hardware does not have virtual memory. So long as the program always follows the rules on acquiring and releasing the handle before using the underlying pointer, it enables a kind of poor man's virtual memory. The first version of Mac OS and Windows 3.x worked in a similar way. I'm guessing that the designers of Visi-On thought of this, and the other abstractions the API provides, as a kind of virtualization.
The latter 2 usages are pretty much the same. They just have different virtual instruction sets.
> The latter 2 usages are pretty much the same.
Only if you ignore most of reality, sure.
> Only if you ignore most of reality, sure.
No, not really.
Hypervisor VM: emulates a virtual computer with virtual, emulated hardware, but a simulated version of the same CPU as the host, allowing 1 OS to run under another.
E.g. Xen, VMware, KVM, bhyve
Bytecode VM: emulates a partial virtual environment, with an emulated CPU and some form of conversion or translation from virtual environment to the underlying real API and real OS, allowing programs to execute on radically different OSes on different CPUs.
E.g. JVM, MoarVM, Parrot VM, Dis in Inferno
Emulator VM: emulates a virtual computer with virtual, emulated hardware, including a virtual CPU.
E.g. MESS, RetroVM, ZesaruX
Container: emulates an OS from userland down, but shares the same OS kernel across instances.
E.g. Docker, LXC, LXD, Incus, FreeBSD jails, Solaris Zones