Fabulous Adventures In Coding
Eric Lippert is a principal developer on the C# compiler team. Learn more about Eric.
West of HouseYou are standing in an open field west of a white house, with a boarded front door.There is a small mailbox here.
>open the mailboxOpening the small mailbox reveals a leaflet.
>take the leafletTaken.
>read the leaflet"WELCOME TO ZORK!
ZORK is a game of adventure, danger, and low cunning. In it you will explore some of the most amazing territory ever seen by mortals. No computer should be without one!"
And thus began in 1984 my lifelong enjoyment of "interactive fiction". Somewhere in a filing cabinet in my office closet I have hundreds of hand-drawn boxes-and-lines maps on graph paper reminding me of the layout of each classic Infocom game, where each object can be found, and so on.
I was eleven years old in 1984 and naively thought of myself as a pretty darn sophisticated computer programmer. I'd written some simple games for the Commodore PET (the classic CBM 4032) at school, obtained my very own Commodore 64 -- thanks mom -- and had found and fixed a bug in professionally-written software. Woo! But I could not for the life of me figure out how the Implementors at Infocom had written such a huge, complex game that could understand English sentences, maintain consistent positions of hierarchical objects that interact with the environment (that is, the torch is in the coffin which is in the boat being swept downstream), and so on.
When a few years later I did learn how it all worked, I was blown away. It was one of those moments where you suddenly see clearly that there's a whole new way of looking at computers as problem-solving tools. As is well-known now, what the geniuses at Infocom did was designed and implemented their own virtual machine, the Z-Machine. They then wrote the games in the bytecode language of the virtual machine. This has two enormous advantages.
First, by abstracting over the real machine, you can write your huge, complex game once, write relatively small, easy Z-Machine implementations for as many different brands of computers as you like, and suddenly you have write-once-run-anywhere capabilities, massively increasing the number of platforms you can sell to. Most video games at the time were written in, say, Commodore 64 assembly language, and then re-implemented in Atari assembly language, and so on; the cost scaled linearly with the number of platforms. With the Infocom approach, the per-platform costs were a lot lower.
Second, the VM can implement what we now think of as paging. The (immutable) game code can be read in from disk a page at a time, executed, and then discarded. The only stuff that needs to stay in memory is the relatively small current game state and of course the Z-Machine implementation itself. But the code can be huge, much larger than available memory. Back when available memory was 16 to 32 kilobytes, that's a significant advantage.
Nowadays of course lots of people have written their own implementations of the Z-Machine for their own amusement. I never did, but I've always wanted to. Having worked on so many bytecode-interpreted languages over the last fifteen years, it would probably be pretty straightforward to do so. It would be a fair amount of work, but it would be fun to blog about, and I'm sure there would be a lot of great opportunities to illustrate how to make a real-world bytecode interpreter. But on the other hand, I have lots of stuff to keep me busy in my spare time already, and a tendency to bite off more than I can finish in one go.
It's therefore quite fortunate that I do not have to do so, because Mike Greger is doing exactly that. He's already written several Z-machine implementations in C# and is blogging about the process. I am very much looking forward to reading Mike's blog and learning about the obscure technical and historical details that make the Z-machine so interesting. Mike tells me that he's having a hard time finding people who are fascinated by both C# and the Z-Machine; well, count me in, obviously! I'm sure that a few of my readers are also interested in both.
Cool! Zork was a bit before my time, but I've played my share of infocom z-machine games anyway and even considered making an interpreter in C# too. Instead I ended up making a z-machine interpreter interpreter in C#... Subtle distinction perhaps, but as long as I don't get eaten by a grue I'm happy. :)
I've always liked the The Hitchhiker's Guide to the Galaxy infocom game... Definitely count me in for that :)
Isn't .NET itself a kind of virtual machine, with its own byte-code that could potentially run anywhere? I thought that, along with Mono ( http://mono-project.com/Main_Page ), it was an implementation of ECMA ( http://mono-project.com/ECMA ) initiative whose goal is just that: to create cross-platform, cross-CPU standards, so that the user is not bound to a certain platform simply by the set of business applications he/she has been using...
Also, there is COSMOS: and open-source operating system written completely in C# ( http://cosmos.codeplex.com/ )
As for the games, it takes a vivid imagination to see the terse lines on the console and compensate for the lack of graphics with your mind's eye; me, I get soooo tired from using my brain and being nice, cool, and reserved, during the business hour, that I just want some ACTION: DOOM, Unreal Tournament, etc.: I sometimes play with/against my 16-year-old son, and not without success, too! :-D
This seems like a fine place to point out my own "ZLR", a Z-machine JIT which uses dynamic methods to translate Z-code -> IL -> machine code.
@Denis, .NET is in no way, shape or form a "kind of virtual machine".
Of course it is in every way, shape and form a virtual machine; or, at least, the CLI portion of it is. The second sentence of the CLI spec states "The CLI specifies a virtual execution system that insulates CLI-compliant programs from the underlying operating system." Without the CLI, .NET is nothing; the whole of .NET is founded on the notion of specification of a virtual machine.
Do not confuse "virtual machine" with, say "bytecode interpreter". A "virtual machine" is a specification, it's an abstract idea codified by a standards document. Bytecode interpreters, jitters, and the like, are all techniques for the implementation of a spec-compliant virtual machine. -- Eric
IMHO - anyone who thinks it is should be banned from programming.
You might consider damping down the rhetoric if you want your points to be taken seriously by anyone in the future. I naturally tend to flip the bozo bit on people who tell me that I should be banned from programming, for whatever reason.
The fact that IL (specifically MSIL) is used as an intermediary before being compiled to 100% native code for the machine is completely immaterial.
No, it is 100% material. The whole system is predicated upon having a self-describing, versionable, platform-agnostic persisted format for code and metadata. The PE format is where it's at in .NET, and the IL specification is a big part of that. -- Eric
In fact for most modern processors, assembly manguage and machine code is really nothing more than an intermediate stage that never gets "executed" be the core of the processor.
In the old days, one could look at the bits in an instruction and determine (by looking a a schematic) what circuits would turn on, and off. I remember quite well [spring 1978] debugging (with a voltmeter and scope) a machine [DEC PDP-8/e] where "compare greater or equal" instruction was only funcioning as a "compare greater". A single diode had burned out.
These days there is a "micro-code" engine that is executing which translates the instructions as they are read from ram [or cache] into the appropriate operations (often reordering parts and pipelining others).
The "cross-platform" capability is becuase of the standards of having an information set which can be processed on different machines to achieve identical results from identical source material. Fundamentally it is no different than any other 100% standardized language. Unfortunately (nearly) every vendor will add one or more "Extensions" to their environment and reduce or eliminate this from a practical standpoint...
I just want to thank Eric for being so cool and linking to my blog.
Jesse - I've looked at your project before and I like it. I wouldn't mind discussing z-machine design if you're interested. I think our goals are very different, but there may be information to be gained for both of us.
Ok, come and ban me from programming (after 20 years I am sick of it anyway :-) ), but doesn't it all depend on the definition of a virtual machine? Take VMWare, or Virtual PC: aren't they, after all, just processes, or sets of processes, in the 100% native code, at the end of the day?
And the micro-code used by most of the modern CPUs - isn't it just an additional level of virtualization inserted between the actual binary commands executed by the hardware and the assembly language and the "good old" opcodes? So why can't we regard IL as yet another level, abstracting away the assembly and the opcodes, along with some of the operating system services (like memory management) that are largely responsible for the incompatibility between the operating systems and/or platforms?
It's true that, unlike the Java byte-code, the IL is not "executed" directly, but can this not be regarded as an optional implementation feature intended to improve the performance? This comes from the Wikipedia article on virtual machines ( http://en.wikipedia.org/wiki/Virtual_machine ):
A virtual machine (VM) is a software implementation of a machine (i.e. a computer) that executes programs like a real machine.
A virtual machine was originally defined by Popek and Goldberg as "an efficient, isolated duplicate of a real machine".
As we can see, nothing tells us HOW does a VM execute its programs (maybe, compiling them into the 100% native code?) or what the degree of "isolation" from a real machine should be (and is there any compromise between the "isolation" and "efficiency") but, since I am already banned from programming and summarily excommunicated, I had better stop preaching my heresy. :-D
The fact that **in the Microsoft implementation running on Windows** the IL is translated to bytecode is immaterial. That doesn't change the CLI; for example, .NET Micro Framework is an IL interpreter; would you be happy to call that a VM? And if so, why not regular .NET? It is in many ways sand-boxed...
in the previous reply I meant to stress "native" in the first (Windows) bit...
It reminded me of when I was 10 (1980, I’m a bit older than Eric) and I realized that I could write x = x +1 which is a mathematical non-sense (how could x be equal to x+1) but opens up all sorts of new possibilities in Basic. Believe it or not, all the "programs" I wrote before that where immutable (no variables whatsoever). Maybe I was ahead of my time and ready for multicores ;-)
Thanks, Eric. Whenever I see a nod to infocom, it brings back those great geek-shared memories of mapping out dungeons and marveling at the storytellers that created those fantastic worlds. And the humor was second to none. How many video games have you played in the last 10 years that made you burst out with laughter? Few, in my case. Anyway, really enjoy your blog. Best regards.
@Eric - The "banning from programming" was INTENDED to be "over the top" and not taken seriously. MY abologies to anyone if my "sense of humor" did not translate well. As far as the other items, I stand by my statements, and have republished a post on my blog about this topic [so as not to distract this topic furhter]. The material can be found at: http://geekswithblogs.net/TheCPUWizard/archive/2009/09/24/the-clr-does-not-a-virtual-machine-make.aspx
@Marc - Please see the post I just referenced. To specifically answer your question, I would possibly consider the Micro Framework as a Virtual Machine for the reasons covered in my blog post...
David V. Corbin [MVP]