I am holding in my hand the 1982 Radio Shack computer catalog, unaccountably saved from my high school years. Sold under the TRS-80 brand, the products look familiar: computers with displays and keyboards, printers and hard drives as well as games and productivity software. The prices are reasonable, if a little high by today’s standards: $800 for a basic desktop computer, $1,100 for one with extra memory, and a more powerful business system for $2,300. There are no laptops (although there is a “pocket computer”), and touch screens are far in the future, but everything else is recognizable.1
It’s the details that are jarring. Compared to current hardware, the capacity of these computers is microscopic. The $800 TRS-80 Model III, the entry-level model, has 4 kilobytes of memory—a mere 4,096 bytes. From the perspective of today, when a low-end computer might have 4 gigabytes of memory, it is hard to conceive that one with 4 kilobytes—one-millionth as much—could do anything useful at all.
The storage difference is similar. A hard drive selling for $5,000 holds 8.4 megabytes; today you can buy an 8-terabyte hard drive—larger, again, by a factor of a million—for less than $300. The floppy disks hold 170 kilobytes each (once you pay almost $1,000 more for a floppy disk drive and disk operating system software); today’s USB sticks hold a million or more times as much. Shrinking a modern storage device to the size of what was available back then would be the equivalent of reducing this entire book to half of one letter. A car that moved at one-millionth of the speed of today’s cars would move on the order of inches per hour; such a car would be considered, by normal human observation, to be standing still.
The year 1982 is also a milestone in my personal history: my family acquired our first home computer, an original IBM Personal Computer running IBM PC DOS (Disk Operating System) version 1.00.
Before the appearance of the IBM PC in late 1981, the personal computer industry was split between incompatible computers from Radio Shack, Apple, and Commodore. Anybody wanting to sell software for all three had, essentially, to write it three times. The IBM PC introduced a fourth platform, but for a variety of reasons—the facts that IBM was the most well-known computer company in the world whose name “legitimized” the personal computer industry, the IBM PC had a more expandable hardware design than its competitors, Microsoft sold a version of DOS to other hardware companies, and a company called Phoenix Technologies wrote a lawsuit-proof copy of the low-level software that IBM included on the computer, or possibly because of serendipity and other factors, and all these in unclear proportions—the IBM PC soon became the standard for all personal computers, supported by a robust marketplace of companies selling PC-compatible computers, consigning the other three to the recycling bin of history. With a standard platform to target, the personal computer software industry began its rapid growth. And if these slow, underpowered computers seemed like barren soil in which to grow such an enterprise, in terms of the programmer’s experience they were light-years ahead of what they replaced.
As a small child I had limited exposure to actual computers due to the fact that much of the technology would not fit through our front door. I prepared for my future career by playing with Lego bricks—a common thread in the life story of programmers my age. In the mid-1970s, computers existed in two forms: mainframe computers, the big ones you see in old movies, which were used for weather forecasting and whatnot, and tended to be owned by large companies, governments, and universities; and minicomputers, which were smaller and more self-contained, and used by businesses for tasks such as running their payroll. The notion that somebody might have a computer in their house was seen as both frivolous and ridiculous; computers were for dull, important things, and in any case, where would you put it? When Microsoft was founded in 1976, the corporate vision of “a computer on every desk and in every home” seemed, well, a vision.
The year 1977 saw the introduction of the first three broadly successful personal computers, the Commodore PET, Tandy TRS-80, and Apple II, along with the Atari 2600 game system (then, as now, game systems were also computers, with a different user interface and nominal purpose). Meanwhile, at a far remove from those scrappy upstarts, somebody in the math department at McGill University, where my father was a professor, convinced the department to purchase a minicomputer made by a company called Wang, to the tune of $20,000 plus $2,000 for the annual service contract. While mainframes tended to be the size of industrial refrigerators and installed behind glass in climate-controlled rooms, minicomputers were the size of smaller appliances and could be installed anywhere. The entire category of minicomputers would eventually be obliterated by the descendants of the original personal computers (which at the time were known by the vaguely insulting term microcomputer), and within fifteen years Wang Laboratories would suffer a precipitous Innovator’s Dilemma drop—to borrow the title from Clayton Christensen’s book—from thirty thousand employees to bankruptcy, but at the time Wang computers were considered quite capable.2
My childhood home did not participate in the first round of personal computer adoption—no Apple, Commodore, or TRS-80, or even a gaming system in our living room—but my father would occasionally bring me to the McGill University math department on a Saturday, where I could while away the afternoon on the Wang minicomputer playing games like Bowling, Football, and Star Trek—incredibly unsophisticated versions, with “graphics” lovingly rendered in text (the football game constructed the field markings out of dashes, plus signs, and capital I’s, and at halftime indicated the presence of the band by moving the letters B-A-N-D around the screen). I did this perhaps three times a year and can still remember the feeling of anticipation as a visit approached. As I write this I am on an airplane with two of my children. Looking over I see that—well, as it happens, they are both reading books right now, but half an hour ago they were playing games on handheld devices. The difference between my access to computer games and theirs, to say nothing of the quality of the games, is mind-blowing: as if I had observed evolution from trilobite to Tyrannosaurus rex in a single generation.
This introduction to playing computer games was roughly aligned with the invasion of the video game Pong into homes across the land, so I was not unique in spending time squinting at poorly drawn electronic entertainment, but my introduction to writing software came early for that era. The first software that I ever wrote was not on a computer. Hewlett-Packard had a line of calculators that could execute programs written by the user (as the manual for one of them states, “Because of their advanced capabilities, these calculators can even be called personal computing systems”).3 In the late 1970s, my father owned several HP calculators, including one with a built-in thermal printer (the HP-19C). The calculators were focused on mathematical operations, such as calculating mortgage payments, but I had no need for that. I was interested in programming for programming’s sake, so I wrote programs that were useless in the real world, such as one to print out prime numbers (I’ve never found myself with the need for a list of prime numbers, but I’ve written the solution in several programming languages).
Before I talk more about programming, I should explain a bit about how computers run programs. The processor, the chip inside a computer, has a set of registers that can each store a single number. The processor can perform operations on the numbers in these registers—addition, multiplication, and so on—and can also perform tests on them—to see if they equal to a certain value or if one is larger than the other, say—and jump to a different location in the program if the test is true. Finally, since the set of registers is small (eight to thirty-two registers is typical), the processor can move values from a register to the main computer memory, and from the main computer memory to the register, so that they can be preserved and brought back to a register when needed.
That’s basically what the processor can do: operations on registers, comparisons between those registers, jumps based on those comparisons, and moving data back and forth between registers and memory. There’s a bit more (such as the ability to perform mathematical operations directly on data in memory rather than having to move it into a register first), but essentially everything is built up from combinations of these instructions, as single processor operations are known.
The program that a computer is running is a series of instructions. For example, on the Intel processors used in many personal computers today there is an instruction called ADD
that can add two registers. To add the EAX
register to the ECX
one (and store the new total in ECX
), the instruction in human-readable form is written as:
ADD ECX, EAX
but for the machine, it is actually this sequence of bits:4
0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1
which is a “series of 0s and 1s” as used in the sentence “computer processors interpret a series of 0s and 1s.”5
The sequence of 0s and 1s is known as machine language, and the human-readable form
ADD ECX, EAX
is known as assembly language; a program called an assembler can convert assembly language into machine language so that the computer can execute the program.
Getting back to programming on the HP calculators, this was an assembly language experience: my programs had to move data back and forth from memory to the location where the processor could operate on it (which was technically a stack, not registers, but the effect was similar to programming on a processor that had only two registers).6 There are those who say that every programmer should learn assembly language first so that they know what is going on under the covers, but in truth these HP calculator programs, while notable for their time, are prone to simple mistakes and hard to read.
Toward the end of 1980, my father lugged home a device known as a terminal that allowed us to connect to the mainframe computer at McGill. The setup was archaic by today’s standards—it was archaic by the standards of a couple years later—but this is understandable given that it was the approximate midpoint between the dawn of computing and now. The terminal was of a type known as a teleprinter or teletypewriter, also called a line terminal. It had a keyboard but no screen, only a printer, so it looked like a large typewriter that consumed fanfold paper (that continuous folded paper with removable holes along the side, now only seen in old movies and car rental agencies). The device sat there in an unanimated state until we dialed the phone number for a mainframe computer at McGill and placed the phone handset into an acoustic coupler.
The acoustic coupler, which looked like two oversize headphone earpieces mounted on a box (Radio Shack was selling one for $240 in its 1982 catalog),7 cradled the handset snugly enough that it could transmit beeps and boops to the microphone and hear the same coming from the speaker. These sounds were used to exchange data with the McGill computer, which housed all the actual processing power; if you imagine a modern desktop computer with a keyboard, display, and system unit, the keyboard and display were in our house and the system unit was at McGill, attached not by a short cable but by the telephone connection. And a rather slow connection it was, with the speed being 300 baud, or about thirty characters per second, roughly (you guessed it) one-millionth of the bandwidth of a typical broadband connection today. The only good thing that I can say about the slow connection is that it made the slow printing speed of the terminal a nonissue; since a full line of eighty characters of text took about three seconds to transmit, the printer had no trouble keeping up with it.
On this system, sitting in my parents’ bedroom, I learned a programming language called WATFIV, a version of the grizzled computer language Fortran.8 Compared to assembly language, this was a higher-level language, providing useful abstractions such as the ability to give names to storage locations (known as variables) rather than requiring the use of processor register names such as EAX
and ECX
. A program called a compiler converted the higher-level language into machine language (conceptually the compiler converted it into assembly language that was then converted to machine language, but typically it blasted out 0s and 1s directly).
Since WATFIV was designed to run on mainframe computers that were often accessed from coal-powered line terminals like the one I was using, it had limited output functionality. There was no support for drawing graphics on a screen or playing sound out of a speaker, since terminals frequently had neither a screen nor a speaker. The input/output functionality of a WATFIV program was limited to reading in and printing out lines of text.
My formal training as a Fortran programmer consisted of the book Fortran IV with WATFOR and WATFIV, which I would eyeball during slow moments in high school history class. The book taught me the basic syntax of the language, but didn’t explain how to write a program that accomplished something useful, any more than knowing the syntax of the English language allows you to put together a sales pitch or marriage proposal.
Here is an example of a WATFIV program from the book—and if ever a programming language was well suited to being printed in uppercase in a fixed-width font (which I will use for all program fragments in the book), it’s a Fortran dialect (in fact, the entire book was printed in a fixed-width font):9
C EXAMPLE 6.3 - SUMMING NUMBERS
INTEGER X,SUM
SUM=0
2 READ,X
IF(X.EQ.0)GO TO 117
SUM=SUM+X
GO TO 2
117 PRINT,SUM
STOP
END
This program is not particularly hard to read; if you instinctively skipped over it, I encourage you to go back. The set of steps in a program is often called code, but not in the sense of an impenetrable mystery as in The Da Vinci Code; it’s just a series of instructions that follow certain rules.
The program declares two variables of type INTEGER
(meaning a number) named X
and SUM
, initializes SUM
to 0, reads in a value and stores it in X
, and checks if X
is 0. If X
is 0, then it prints out the total and stops; otherwise it adds the value of X
to a running total stored in SUM
and goes back to read another value.
The READ,X
and PRINT,SUM
lines refer to what is known as an API, which stands for application programming interface. APIs are functionality provided to programs in order to accomplish certain tasks—in this case, reading a value and displaying a value, respectively. We say that code calls an API—that is, it tells the compiler that it wants to jump to that API to perform an operation. The APIs take parameters, which are passed to the API, providing more detailed information. In this code, READ
is passed the parameter X
, telling it what variable to read the value into, and PRINT
is passed the arguments SUM
, telling it what variable to print.
The numbers to the left of the code—2
and 117
—are optional line numbers, which are needed as targets of the GO
TO
instructions; GO
TO
2
means “jump to the line numbered 2 and continue execution from that point.” Unlike in most modern languages, the spacing at the beginning of each line matters. Per Fortran’s rules, the line numbers appear in columns 1 through 5, and the actual program code starts in column 7. The first line, starting with a C
in column 1, is a comment, which is ignored by the compiler.
As for the data that will be read when this program calls the READ
API, understand that Fortran and its variants were designed to read programs off punch cards (one punch card per line of the program), run them, and print out the results. The data would normally follow after the program, on another punch card (there had to be a special card containing only the text $ENTRY
between the program and data). The READ
API was defined to read from the next punch card in the pile (the version of WATFIV running on the McGill computer had been enhanced to allow you to store the program and data on the mainframe computer disk drive rather than requiring that it be reread each time from cards, and could also read input data typed at the keyboard, if you wished).10 This particular program expected there to be a final card with the value 0, which would indicate the end of the data.
These were typical of the Fortran programs that people wrote—simple solutions to problems such as calculating the semester grades of students from their individual test scores (one test score per punch card, natch). In many cases, the people who wrote programs were the same people who used those programs, because the programs they wrote were specific to their exact situation.
In this day of Xbox gamers with their own reality shows, it’s hard to remember that not that long ago, playing computer games was still viewed as a geeky hobby. Many people who played computer games also dabbled in writing computer games, which was unquestionably geeky. You may not remember a British pop group called Sigue Sigue Sputnik, a one-hit wonder whose object of wonderment was the song “Love Missile F1-11”; what amazed me in 1986, looking at the back of the LP at our local Sam the Record Man store, was that these people, whose clothing and hairstyle unquestionably marked them as cool, forward-thinking types, listed “video games” as one of their hobbies—a signature moment for me. I am still slightly surprised when a student today tells me that they became interested in programming not because of the sheer thrill of 1s and 0s but instead because they enjoy using software and thought it might be interesting to learn how to write it. Nothing about Lego?
As with the HP calculators, I muddled through learning WATFIV without any particular goal in mind. I was writing simple programs; sorting a list of numbers (which in programming terms is known as an array of numbers) from largest to smallest was a typical example, or if I felt like an interactive experience, there was the old “the computer is thinking of a number; you guess, and I will respond with ‘higher’ or ‘lower’” game. In order to spend the time puzzling through how to make these things work on your own, for such a meager result, you had to have a personality that viewed solving the problem as valuable in and of itself—that understood that the journey, in this situation, was the reward. At the same time, there’s not much opportunity to make a mistake that can’t be fixed by trial and error. You would spend more time discovering that a line of Fortran code mistakenly started in column 6 instead of 7 than you would in dealing with bugs in the logic of your program.
For that matter, playing with Lego as a kid probably had been decent preparation. The pieces combining into a larger object, detailed directions that had to be followed precisely, and little “click” when you connect them together are all quite similar to writing short programs.
The IBM PC that we acquired in 1982 was a much more compelling software platform than the McGill mainframe. Beyond the convenience of not having to connect remotely via a modem (after waiting until nobody else in the house was using the phone), the IBM PC could display graphics and play sounds.11 Programmers took advantage of this to write word processing programs that displayed accurate formatting on the screen, spreadsheets that recalculated on the fly, and other marvels of the age. No more typing in text commands to a football game and then watching it respond; no more band represented by the letters B-A-N-D moving back and forth. Now you could write games (what I cared about) that were actually interactive!
Even better, the BASIC language included with the computer, since it was customized to run on the IBM PC, supported all this hardware. In fact, the IBM PC BASIC had advanced features that I have not seen on any other system. There was an API called PLAY
, which you could feed a “tune definition language” and it would play the notes; the following
PLAY "L8 GFE-FGGG P8 FFF4 GB-B-4 GFE-FGGG GFFGFE-"
would play “Mary Had a Little Lamb.”12 And the DRAW
API supported a “graphics definition language,” so
DRAW "M100,100 R20D20L20U20 E10F10"
would draw a box with a triangle on top.13
Suddenly I had moved from a fairly difficult programming environment on the McGill mainframe, with limited commands available, and that was only really useful for small “learning to program” examples that I wrote quickly and forgot about, to a rich environment with programs that I genuinely wanted to run and keep running, expand over time, and possibly even show to other people.
A cottage industry of books and magazines sprang up to serve the IBM PC community, but my main source of knowledge on BASIC was the printed manual that came with the computer. Once again, I learned by figuring it out myself—which I am pointing out not to highlight my own skill but instead to emphasize that reading a reference manual was then the standard way that people learned to program. At the same time, I was also writing BASIC code on a friend’s Apple II clone, learning the details in the same way (the Apple II BASIC also had graphics and sound support, but the specifics of how they were done in BASIC differed between the PC and Apple II, and for that matter, among the BASICs included with most of the personal computers of the day).
I was able to blunder along, teaching myself enough IBM PC BASIC to crank out inferior clones of arcade games such as Pac-Man and Q*bert. My crowning achievement in high school may have been writing a program that arranged the names of every member of my senior class into a giant 84, the year I graduated, with said design then being printed on a sweatshirt. Unfortunately, there were several factors that prevented me from learning the proper way to tackle larger programs, especially of the sort I would write in my professional career as a programmer.
The first problem was that whatever knowledge was out there about how to write “good” programs, it never made it into my consciousness. The BASIC manual had short snippets of code, each of which addressed the proper syntax and usage of a single part of the language—the language keywords and API that you would use to write BASIC programs. While there may have been clarification of what different parts of the sample were trying to accomplish, there was never any discussion of why a particular section of code had been written in a certain way. The code samples worked, and that was enough. The rest was up to you.
When you are dealing with small samples of code, which exist only to demonstrate calling one API, you don’t spend much time worrying about readability or clarity. This came through in various ways. For example, naming a variable I
or J
isn’t particularly helpful in a long program, where you want something more descriptive. But people often used single-letter variable names in these samples, and without much thought on the matter, this style would then be adopted in actual code. Most programmers wrote code for their own use and kept a mental image in their mind of how it worked, so they didn’t concern themselves much with how readable it was by other people; I certainly didn’t.
The second problem was the limited amount of memory in the computer.
The IBM PC came with three versions of BASIC in order to accommodate the resource limitations—it was possible to buy an IBM PC with as little as 16 kilobytes of memory. On such a machine you could only run Cassette BASIC, which was included with the computer, burned into a 32-kilobyte ROM (read-only memory) chip, which meant it didn’t take up any space in the 16 kilobytes of memory. It was called Cassette BASIC because you could order an IBM PC without disk drives, using a cassette tape for storage. In this situation, the computer would boot directly into Cassette BASIC; there was no DOS because there was no disk to operate (or to load DOS itself from).
I assume that a few people did order a cassette-tape-only IBM PC (I vaguely recall a letter to the long-defunct magazine SofTalk for the IBM PC discussing exactly this), but the vast majority ordered it with floppy disks, which meant you were running DOS and therefore could launch either Disk BASIC or Advanced BASIC, known respectively as BASIC and BASICA, after the DOS commands you typed to start them.
Disk BASIC was a superset of Cassette BASIC, adding support for reading and writing files to disk as well as communicating over a modem. Advanced BASIC included that plus the advanced graphics and sound APIs like DRAW
and PLAY
. Disk BASIC required 32 kilobytes of memory, while Advanced BASIC needed a whopping 48 kilobytes. Keep in mind that memory had to accommodate the BASIC interpreter itself (although the more advanced BASICs did rely on some of the Cassette BASIC code stored in the separate ROM), the code for your program, and whatever memory the program itself used to store data in variables.
Given this, it becomes more understandable that you would give all your variables one-letter names because that used the least amount of memory to store the code that used those variables. BASIC allowed you to add comments in your code to explain what it was doing by preceding them with the REM
statement or a '
(single-quote) character, but comments were also viewed as a space-hogging luxury. And a 16-kilobyte IBM PC wasn’t even the most limited environment in which you could run BASIC; recall that Radio Shack was selling a TRS-80 computer with 4 kilobytes of memory in its lowest configuration, running a BASIC so minimal that it barely even supported character strings—variables that held sequences of text (string is used here as in “string of letters,” not the thing you tie objects together with). Your program could have exactly two string variables in your code, named A$
and B$
.14
In fact, to save memory, some of those early BASICs (although not the ones included with the IBM PC) allowed you to leave out the space characters. A loop, a standard programming construct that allows statements to be repeated, is normally written in BASIC like this (BASIC programs back then required a line number on every line):
10 FOR J = 1 TO 10
20 PRINT J
30 NEXT
but you can write it in one line instead:
10 FOR J = 1 TO 10: PRINT J: NEXT
which could then be jammed together as
10 FORJ=1TO10:PRINTJ:NEXT
and that would be considered completely normal and arguably clever.
These memory limits were a step backward from the mainframe days. Those mainframe computers didn’t have a lot of memory either (I don’t know the specific details of the McGill computer, but a typical mainframe in the 1970s had between 256 kilobytes and 1 megabyte of memory), and it was shared between all users connected at any one time, but the operating system used a technique called virtual memory that let the disk drives function as extra memory. In such an environment, shaving bytes off your code was much less important, and programmers could splurge on the occasional comment line.
The third problem that prevented me from learning proper coding techniques on an IBM PC was that at the time, the BASIC language made it difficult to write large programs. Such programs are made up of layers and layers of code, often written by different people, connected together via APIs. Code you write is typically calling an API provided by a lower layer but also supplying an API for higher layers to call. While BASIC did allow your code to call the APIs offered by BASIC itself, such as DRAW
and PLAY
, there was no way for your code to provide its own named APIs to other code.
BASIC did have subroutines, which allowed code to jump somewhere else in the program and then return back to the calling code. These are conceptually an API, yet they were referenced not by name but rather by line number (similar to the way that GO
TO
statements worked in Fortran or, for that matter, BASIC). Furthermore, subroutines did not support parameters; they referenced variables directly. You called a subroutine using the GOSUB
(sometimes spelled GO
SUB
) statement, specifying a line number as the target. Consider this example (modified slightly for clarity) from the book Structured BASIC, from 1983:15
100 READ A
110 GO SUB 700
120 PRINT S
130 GO TO 999
700 S = 0 ! START OF SUBROUTINE
710 FOR I = 0 TO A
720 S = S + I
730 NEXT I
740 RETURN !!! END OF SUBROUTINE
999 END
On line 120, the program is calling the subroutine starting on line 700, which sums up the numbers from 0 to A
and stores the result in S
. Conceptually the variable A
is a parameter to that subroutine, because the subroutine uses A
in its calculations, and the variable S
is the return value from the subroutine, because setting S
is the net effect of the subroutine. But those variables have no particular connection to that subroutine except in how they were used. In software parlance all variables in BASIC were global variables, meaning that any code could access them, whether in the main program or a subroutine. The caller of the subroutine on line 700 has to “just know” to put a certain value in A
before calling the subroutine and also “just know” that the value that the subroutine calculates will be stored in S
when it returns. Not to mention, it has to “just know” that the subroutine that adds the numbers from 0 to A
starts at line 700, and not accidentally GO
SUB
to line 690 or 710.16 The comments on lines 700 and 740, starting with a !
, indicate to the reader that they are the start and end of a subroutine, but the compiler ignores comments and has no knowledge that line 700 is supposed to be the start of a subroutine.
This may seem like a minor detail, but in practice it makes calling other people’s code quite tricky (of course, I was programming by myself so there was no other programmer—another factor that prevented me from learning “real” software development). Imagine copying code from another programmer and trying to include it in your own BASIC program. In addition to needing to know the exact line numbers to call subroutines as well as the exact variable names to use to pass in and return values, you have no guarantee that the line numbers and variable names used inside another person’s code won’t be the same as the ones you’ve used, which would cause a conflict. BASIC requires line numbers partly because it was designed to work on an interactive system that might not have any kind of text editor available; if you wanted to insert a line of code between two existing lines, you chose a line number that was numerically between the numbers of those two lines. And if you chose a line number that was already in use, then BASIC replaced the old line, which meant that loading somebody else’s code would replace part of your code if the line numbers conflicted.17 Essentially you were restricted to writing programs that depended only on the built-in API available in BASIC, or if they did load in other snippets of code, they were two parts of the same program that had been carefully crafted to not overlap their line numbers, as opposed to the much more generic “call an API implementation that somebody else wrote” that defines modern layered programs.
In addition, as a BASIC programmer, you got no practice in defining a clean API interface for others to call. These connections are fragile points in a program because of the potential for misunderstanding at the API boundary, whereby the caller of an API may not realize exactly what the API does, especially if it was written at a different time by a different person (and in large programs, the source code—the uncompiled original version—that implements the lower API is frequently not available to look at). In BASIC, you didn’t have to ponder what was a clean set of variables to pass to a subroutine or return from it. Any subroutine could read or set any variable in the program. Even the subject of API naming didn’t come up because only the line number identified the subroutines.
And even if you were trying to maintain a self-contained program that did not depend on any other code, you still wound up with code that was hard to read. MS-DOS, the operating system for the IBM PC, included a few sample BASIC programs to demonstrate the functionality of the language and give people something to do while looking at the computer in a store. I remember one that showed a chart of the colors (all sixteen of them) available on the IBM PC, and another that sketched the “skyline” of a “city” by drawing an endless succession of randomly sized rectangles on the screen—a pretty good effect for what must have been, at its heart, about ten lines of BASIC code, centered on an API called LINE
, which would draw filled-in rectangles if you asked it properly.18
About half the sample programs were written by IBM, and the rest by Microsoft. According to Glenn Dardick, who supplied IBM’s contribution, he wrote most of his in a couple days while recuperating from an operation. His magnum opus was the Music program, which would play eleven different songs, including “Pop! Goes the Weasel,” “Yankee Doodle Dandy,” the “Blue Danube Waltz” by Johann Strauss, and “Symphony #40” by Wolfgang Amadeus Mozart, all while a musical note followed along on a rendering of a piano keyboard. Dardick enlisted a family friend, a staff conductor at the Metropolitan Opera in New York named Richard Woitach, to help him with the music.19
The most famous, or infamous, of these BASIC samples was called Donkey, or DONKEY.BAS, since that was the name of the file containing the BASIC source code. Donkey was a video game with extremely simple gameplay. The screen displayed a two-lane road running from the top to bottom of the screen; near the bottom, there was a race car, which could be in either the left or right lane. A donkey would appear in one of the lanes, moving down from the top of the screen, and you hit the space bar to move the car to the other lane to avoid hitting the donkey. Once one donkey was avoided, another appeared at the top of the screen, and the timeless battle was renewed. That’s the game! Videos of it in action do exist on the Internet, although don’t construe this as advice to watch them. In its defense, it was written during a late-night exercise (with Bill Gates as a coauthor) to show off the power of Advanced BASIC.20 It used both the DRAW
and PLAY
APIs as well as demonstrated the basics of how to write an interactive game, which was very instructive for somebody like me who was used to the noninteractive line terminal experience of a mainframe computer.
I will readily admit that I played Donkey a few times as an actual video game rather than merely to marvel at its simplicity. And one of my children, while reading a draft of this chapter, dug up a playable version of Donkey and found himself briefly captivated, especially since each successfully avoided donkey moves the race car closer to the top of the screen—a nuance I had forgotten that ratcheted up the excitement. The mobile game Flappy Bird became a sensation in 2014 with scarcely more compelling gameplay or more complicated controls than Donkey had back in 1981.
One benefit of Donkey’s notoriety is that the source code survives to this day, so we can see what an 8-bit era BASIC program looks like.21 It’s only 131 lines long, of which the first 45 are spent printing an introductory message and making sure you have the right hardware. You can follow the core game logic pretty well, but then suddenly you hit these two lines (in IBM PC BASIC, GOSUB
was written as one word):22
1480 GOSUB 1940
1490 GOSUB 1780
and you have no context for what those mean—what the subroutines do, what variables they depend on being set before being called, and what variables they modify while running. At least in Fortran, the code would look something like
CALL LOADDONKEYIMAGE(DNK)
CALL LOADCARIMAGE(CAR)
which provides a hint of their purpose—load the images of the donkey and the car into the variables DNK
and CAR
, so they can be more easily drawn on the screen later (which is what the BASIC subroutines at lines 1940 and 1780 do). Or failing that, the authors of Donkey could have added comments to the lines of BASIC code:
1480 GOSUB 1940 ' LOAD THE DONKEY IMAGE INTO DNK
1490 GOSUB 1780 ' LOAD THE CAR IMAGE INTO CAR
and then the code at line 1940, instead of jumping right into this sequence to draw the donkey,23
1940 CLS
1950 DRAW "S08"
1960 DRAW "BM14,18"
1970 DRAW "M+2,-4R8M+1,-1U1M+1,+1M+2,-1"
could instead have started with a comment line to indicate what it was doing, maybe like this:
1940 REM SUBROUTINE TO DRAW DONKEY AND LOAD IT INTO DNK
Meanwhile DNK
and CAR
, at three letters each, are tied for being the longest and most descriptive variables used in the whole program; other variables include Q
, D1
, D2
, C1
, C2
, and B
. Note that BASIC allowed variable names to be up to forty characters long.
Another line of code in DONKEY.BAS is this one:24
1750 IF CX=DX AND Y+25>=CY THEN 2060
This is the collision test between the car and donkey; CX
and CY
are the screen coordinates of the car, while DX
and Y
(not DY
, for no obvious reason) are the screen coordinates of the donkey. The math for these things is always a bit hard to puzzle through, so the complexity of the IF
statement is expected (it’s saying, if the on-screen x-coordinates are the same, meaning the car and donkey are in the same lane, and the on-screen y-coordinate of the donkey is within twenty-five of the car, then the bottom edge of the donkey is overlapping the top edge of the car, and we have a collision). The reason the code is confusing is because what the code does on collision (when the IF
test is true) is jump to line 2060 (that is what THEN
2060
does) as opposed to calling an API with a name like SHOWEXPLOSION
. And again, there are no comments to explain any of this. If you go to line 2060 to try to figure out what is going on, you see this:25
2060 SD=SD+1:LOCATE 14,6:PRINT "BOOM!"
First 1 is added to the variable SD
, which isn’t that informative (SD
holds the number of times the donkey hit the car, for those following along at home, so it looks like it’s an abbreviation of “score—donkey”), then the LOCATE
statement mysteriously moves the cursor to row 14, column 6, but then you see that it prints the word “BOOM!” and you can probably guess, especially if you’ve played the game, that this is the collision code. But having to jump back and forth, keep all this in your mind, and depend on recognizable PRINT
statements to figure out what you are looking at makes it hard to read the code.
You can imagine an alternate world in which the BASIC samples that came with MS-DOS had good variable names and helpful comments, and people took advantage of this to expand the games. I could have written SUPERDONKEY, with three lanes and two donkeys on the screen at once, except that due to the filename limitations in MS-DOS, it would have been called SUPRDONK.BAS. Still, maybe this would have incubated an appreciation of the benefits of writing code that others can understand, which we (we meaning “the assembled group of people that was inspired by IBM PC BASIC to want to go work at Microsoft,” of which I am a member) would have then carried off to our jobs, and who knows how software engineering would have developed. But the code was hard to read, and none of that happened. Nowadays, companies that are preparing to open source their code so that the public can see it may be stressed about people criticizing their variables names or code layout, but apparently no such concerns existed back then; the only concession in the BASIC samples was a three-line IBM copyright message at the top of all of them.
Another source of BASIC code was books with titles like BASIC Computer Games and More BASIC Computer Games—two collections curated by David Ahl, founder and publisher of the early magazine Creative Computing. The books consisted of source code for a variety of games; the only way to play these games was to type the code in yourself, hopefully without making any mistakes (possibly, if a friend had typed it in already, they could copy it to a floppy disk or cassette tape for you). This was actually helpful in learning the language, since typing in code gave you the opportunity to think about how it worked.
Every computer had its own dialect of BASIC, partly to handle specific features of the computer, and partly because BASIC had never been standardized.26 The net effect of this was that programs in the book would never do anything as computer specific as graphics or real-time play; they were all text based, and relied on the user typing commands and hitting “Enter” to interact with them, with lines of output being displayed one at a time. This made them suitable for almost any BASIC, whether running on an IBM PC or connected via a terminal. I realize now that the Star Trek game I played on the Wang minicomputer at McGill is closely related to the game Super Star Trek in the first BASIC Computer Games book; whoever ported it to the Wang had modified it enough that the star map in the game, while still rendered entirely in ASCII graphics, was displayed in the center of the screen rather than scrolling in imitation of a line terminal.
Less aggressive modification might be needed just to get the game working, if your version of BASIC was different from the version the book targeted. For example, many BASICs allowed multiple statements on one line, separated by a colon, which the book took advantage of, but some did not, which would require reworking the code. Or they allowed multiple statements, but separated by a back slash instead.27 And some differed in their handling of strings, especially the API used to extract subsets of strings. IBM PC BASIC was pretty much in line with what the book expected, except for a minor tweak needed to any code that generated random numbers. This is not too surprising since the standard for the books, which came out in 1978 and 1979, respectively, was Microsoft BASIC—not running on the IBM PC, which did not exist yet, but rather on earlier computers, offering a reminder that Microsoft was in business as a company selling languages for several years before making the deal to sell MS-DOS to IBM that ensured its long-term success. In fact, the reference version for the first book was Microsoft’s MITS Altair BASIC, revision 4.0—a descendant of the first product that Microsoft ever sold.28
The games were of varying quality. The lack of graphics support left the interactivity a bit lacking. These are the instructions for the Hockey game in the first book:29
QUESTION RESPONSE
PASS TYPE IN THE NUMBER OF PASSES YOU WOULD
LIKE TO MAKE, FROM 0 TO 3.
SHOT TYPE THE NUMBER CORRESPONDING TO THE SHOT
YOU WANT TO MAKE. ENTER:
1 FOR A SLAPSHOT
2 FOR A WRISTSHOT
3 FOR A BACKHAND
4 FOR A SNAP SHOT
AREA TYPE IN THE NUMBER CORRESPONDING TO
THE AREA YOU ARE AIMING AT. ENTER:
1 FOR UPPER LEFT HAND CORNER
2 FOR UPPER RIGHT HAND CORNER
3 FOR LOWER LEFT HAND CORNER
4 FOR LOWER RIGHT HAND CORNER
And that’s how the game went: when one player had the puck, the game would prompt to enter a number of passes (0 to 3), then prompt for the type of shot and area, then randomly generate a result (goal or save), then determine which player had the puck next, and so on until you ran out of fake time (the computer also decided how much fake time each play took).
Believe it or not, these games were compelling to play back in those days (and some of them, to be fair, were better suited to the noninteractive mode), even though you could read the source code and figure out the computer artificial intelligence (AI; from reading the code now, it looks like the slap shot was the best option to score, and the area you aimed at had no effect).
Still, they were examples of large BASIC programs, with Super Star Trek from the first book clocking in at over four hundred lines, printed in extra-small type, and Seabattle—written by a high school student in Minnesota—from the second book being over six hundred lines.30 Beyond the unfortunate coding style that BASIC mandated, with subroutines and GOTO
targets being line numbers, people continued to use poor variable names; the average number of characters in the book’s variable names was perilously close to one.
The author of Seabattle, Vincent Erickson, said that the version of BASIC he wrote it for had a limit of two characters for variable names—an obvious impediment to clarity. He wrote the game in 1977, and later entered it in the 1978 Minnesota state programming contest; he then submitted it to Creative Computing magazine, which selected it for inclusion in the book, paying Erickson $50 for the rights. To his credit, the program does have a decent selection of comments identifying the different sections of the code, but Erickson didn’t remember if he wrote it that way originally or added them when he submitted it for publication. He won second place in the programming contest; on the plus side, he later started communicating via e-mail with a female computer science student at another high school; her teacher had given her a list of entries in the contest. This led to what may be the first marriage between two people who met online.31
While anodyne variable names were apparently not enough to block Cupid’s arrow, they could be frustrating. There was a checkers game in the first book that contained the logic for the computer AI, which I would have been interested in, but it was buried in a thicket of single-letter variables, and also a maze generation program in the second book, another program whose logic I wanted to decipher—how did it ensure there was only one path through?—yet it was too hard to wade through the code. For the games like Hockey, you could use printed strings as a guide; the “somebody scored a goal” logic was clearly in the general vicinity of this:32
970 PRINT "GOAL " A$(7):H(9)=H(9)+1:GOTO 990
But for a game like the Checkers or Maze programs, those guideposts did not exist. And not surprisingly, comments were almost nonexistent (belated kudos to whoever wrote the Bowling program, which at least had a few helpful comments to delineate different sections of the code).
With all this, it is accurate to say that BASIC, in the form in which it most commonly existed in the early 1980s, was unusable for sharing code between people who did not know each other and did not collaborate in detail on the sharing. This is not a knock specifically on the BASIC that Microsoft wrote for the IBM PC, which was quite full featured; it’s a fundamental problem with a language that uses line numbers as subroutine and GOTO
targets. John Kemeny and Thomas Kurtz, the inventors of BASIC, recognized these problems, and by the late 1970s had come up with an improved version of BASIC that had properly named subroutines with parameters and other changes that made GOTO
mostly unnecessary.33 Unfortunately the versions of BASIC that shipped with personal computers, which is where most people learned the language, had already split off in their own directions. Kemeny and Kurtz were not happy with the IBM PC BASIC, partly because it reminded them of the earlier incarnations of the language. They described the way it handled numerical variables as “ugly” and “silly,” and criticized aspects of the graphics supports as the sign of a “very poorly designed language.”34 Yet there was no closing the barn door after that particular horse had escaped.
At this point in my story I should mention the name Edsger Dijkstra. Dijkstra was a Dutch computer scientist who was born in 1930 (he died in 2002), which positioned him well to invent some of the foundational concepts and algorithms in computer science. Despite looking like a central casting computer science professor, he had a knack for generating good quotations, and in 1975 wrote a letter titled “How Do We Tell Truths That Might Hurt?” in which he stated, “It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration.” He also labeled Fortran an “infantile disorder” and called it “hopelessly inadequate for whatever computer application you have in mind today: it is now too clumsy, too risky, and too expensive to use.”35 He was silent on the subject of HP calculators, although you should hear what he said about COBOL (another language dating from the 1950s). In any case, BASIC’s lack of sophistication made BASIC interpreters simpler, and therefore easier to fit into the minimal memory of early personal computers. Rather than heed Dijkstra’s warning, the personal computer industry ordained BASIC as the de facto standard language, contributing to the blight of another generation of programmers. Gates, incidentally, had an almost-identical first experience with computers about a decade before I did, when his school obtained a similar printer-based terminal that connected to a remote computer (except that his first programming language was a dialect of BASIC, not Fortran, so he had to deal only with the mental mutilation, without my hopeless inadequacy added on).
So there I was in fall 1984, a survivor of Fortran and BASIC, heading off to Princeton to major in computer science, preparing to soak up all the software engineering knowledge that had so far eluded me. What happened next, as they say, may surprise you.