Tuesday, September 3, 2024

Programming the Convergent WorkSlate's spreadsheet microcassette future

In this particular future, we will all use handheld spreadsheets stored on microcassettes, talking to each other via speakerphone, and probably listening to Devo and New Order a lot. (Though that part isn't too different from my actual present.)
It's been awhile since we've had an entry for reasons I'll talk about later, and this entry is a doozy. Since we recently just spent a couple articles on a computer whose manufacturer insisted it was mostly a word processor, it only seems fitting to spend some time with a computer whose very designer insisted it was mostly a spreadsheet.

That's the 1983 Convergent WorkSlate, a one-of-a-kind handheld system from some misty alternate history where VisiCalc ruled the earth. Indeed, even the "software" packages Convergent shipped for it — on microcassette, which could store voice memos and data — were nothing more than cells and formulas in a worksheet. The built-in modem let you exchange data with other Workslates (or even speak over the phone to their users), and it came with a calculator desk accessory and a rudimentary terminal program, but apart from those creature comforts its built-in spreadsheet was the sole centre of your universe. And, unlike IAI and the Canon Cat, I've yet to find any backdoor (secret or otherwise) to enable anything else.

That means anything you want to program has to be somehow encoded in a spreadsheet too. Unfortunately, when it comes to actually programming the device it turns out the worst thing a spreadsheet on an 8-bit CPU can be is Turing-complete (so it's not), and it has several obnoxious bugs to boot. But that doesn't mean we can't make it do more than balance an expense account. Along the way we'll examine the hardware, wire into its peripheral bus, figure out how to exchange data with today's future, create a simple game, draw rudimentary graphics and (with some help) even put it on the Internet with its very own Gopher client — after we tell of the WorkSlate's brief and sorrowful commercial existence, as this blog always must.

Convergent Technologies was founded in 1979 in Santa Clara, California by former employees of Intel and Xerox PARC, led by the flamboyant cigar-chomping Allen H. Michels as president and co-founder. Unusually for the time, Convergent's plan was to be a behind-the-scenes designer and manufacturer, at least initially selling little under its own brand. Instead, Convergent focused its efforts on large corporate vendors like Burroughs, NCR, Prime and Bull by developing machines for distributed processing arranged around those vendors' large systems. This gave partner vendors a complete product line for their customers, drove purchases and service contracts for their more profitable big iron, and helped to insulate Convergent from the instability of retail.

To make this work, Convergent had to constantly drive the cutting edge to stay ahead of the market, which gained them the nickname of the "Marine Corps of Silicon Valley." Management candidates were told up front to expect sixty-hour-plus work weeks or be disqualified, and some designers reportedly worked close to 100 during crunch times. Within one year it had its first product ready, the 1980 IWS "Integrated Workstation" based on a 5MHz 8086 with up to 640K of RAM running CTOS ("Convergent Technologies Operating System"), a multiprocess microkernel operating system with support for IPC, networking and netbooting (over RS-422). Among other companies, Burroughs rebadged the IWS as the B22 and called the OS "BTOS" (for Burroughs). Convergent followed the IWS in 1981 with the AWS "Application Workstation," a cut-down diskless IWS with less sophisticated graphics that Burroughs sold as the B21, and the more powerful Intel 80186-based NGEN in 1983 that could also boot MS-DOS. This was sold by many companies, Burroughs most notably as the B25, but also Prime as the Producer 200 and NCR as the Worksaver 300. In its MS-DOS form Microdata/McDonnell-Douglas sold it with their Pick-based Reality database as the M1000 and Datapoint as the Vista-PC. These systems were notable not only for their power but also their unmistakable boxy modular expansion scheme developed by designer Mike Nuttall. By 1982 Convergent had 800 employees and sales of $96 million (about $313 million in 2024 dollars).

Convergent also produced midrange systems for their clients. The biggest at that time was the appropriately-named MegaFrame in 1983, consisting of up to ten Motorola 68010-based processor boards ("application processors," in Convergent jargon) wired to as many as 28 (!) 80186-based I/O boards. Every board was an independent node with its own operating system and its own memory: the 68Ks ran at 10MHz with up to 4MB of RAM for Convergent's UNIX System III derivative CTIX, while the '186s ran at 8MHz with up to 768K of RAM and custom versions of CTOS depending on their task (filesystem, storage, cluster management or terminal services). A fully-configured system took six enclosures of six cards each and could handle as many as 128 simultaneous users, but the system was perfectly capable of running just on the '186 cards, and Burroughs renamed CTIX to "CENTIX" and sold it both ways as the XE500, XE520 and high-end XE550. Motorola, of course, preferred to sell the '010-based variety, offering it under the recently acquired Four-Phase Systems as the System 6600. In 1984 Convergent updated CTIX to System V and made a single-68K eight-user version dubbed the MiniFrame using custom I/O and supporting up to 2MB of RAM. In typical fashion it was ready after just eight months of nearly round-the-clock development. It was rebadged by both NCR and Burroughs, as well as Motorola, who sold it as the System 6300; subsequently, the MiniFrame was further refined into a workstation and sold by AT&T as the 7300 UNIX PC in 1985.

In their continued efforts to anticipate market trends, Convergent started evaluating the portables market in 1982 through its new Advanced Information Products division, then based out of the Great American Technology Center in Santa Clara (their former building at 2441 Mission College Blvd is now occupied by Sutter Health). At that time portables were seen as premium products (compare with the Data General/One) and handheld units even more so due to their novelty. AIP noted in particular the 1982 Epson HX-20's small totable form factor, long battery life and light weight, as well as built-in microcassette storage, but also its cramped screen and lack of application software. AIP management determined that a purpose-specific series of portable systems with built-in applications could do better and carve out its own unique higher-end market. This strategy seemed to be vindicated by the early 1983 introduction of the Kyotronic 85, a portable Intel 80C85 system with a nearly full-size keyboard, 40-column screen (the same as many home computers and portable PCs), BASIC, simple text editor, and built-in modem and terminal program. Although a slow seller for Kyocera in its native Japan, it was immediately licensed by the Tandy Corporation as the iconic TRS-80 Model 100 in April (as well as, among others, NEC as the NEC PC-8201A, which I once used as my sole computer for a month on Penang Island in Malaysia) and went on to sell strongly in Tandy's Radio Shack stores. Codenamed "Ultra," AIP's proposed handheld line used high-end but largely off-the-shelf technology on a common hardware platform, differing primarily in on-board software, RAM and built-in peripherals.

To Convergent's dismay, their usual partners were unenthusiastic about the entire concept. Even a high-end model would clearly sell for much less than their workstation systems, necessarily with lower margins and possibly requiring entry into lower-end markets where those companies then had little or no presence. There were also questions about how much money such a system could truly make given its high manufacturing costs and the need to sell in volume, something new for Convergent who had so far produced only smaller quantities of relatively niche hardware. In an abrupt deviation from their usual business model, marketing manager Karen Toland convinced Convergent leadership that consumer sales could still be profitable — that is, if they chose the right consumers. Should they make a splash in the market, she reasoned, the Ultra line could be further expanded, and proposals existed to make a writer-oriented version or a low-end cheaper unit for students.

For launching the line, AIP decided to initially concentrate on a single high-end model. They convened focus groups in New York City, Chicago and locally in San Francisco where sixty management, consultant and executive volunteers talked about their daily tasks and what features they felt would improve them. From these groups the idea emerged of using spreadsheets as the interface for executives who would be unfamiliar with or uninterested in doing their own programming. Although this high-end variant also had a keyboard and could be used for text entry (though not its strong suit), the volunteers also suggested the internal microcassette drive for voice dictation using a built-in microphone ("executives don't type," Toland observed), and the device's built-in 300bps modem could additionally autodial telephone numbers and serve as a speakerphone. The ROM even provided functions to exchange spreadsheet data with another device or a connected mainframe, which we'll explore. The logic board and accessory PCBs were produced in Japan by Oki Semiconductor using Oki and Hitachi chips, with final assembly States-side. The focus group members got free units off the assembly line in appreciation.

In August 1983 Convergent officially unveiled the WorkSlate at $895 (in 2024 about $2800), initially only through the premium American Express Christmas catalogue, alongside an optional battery-powered "MicroPrinter" for $295 ($910). Nevertheless, Convergent hadn't long to wait for market partners. New York securities firm EF Hutton had recently announced their Huttonline service providing investment information, account data and E-mail on their IBM mainframe and quickly added the WorkSlate to the pre-configured computers they provided to subscribers, along with their porkier IBM PCs and Wang systems. For $1194 (about $3750) Hutton would send you a WorkSlate and MicroPrinter with Portfolio Analysis software on microcassette and, of course, a monthly bill. By November 1983 the WorkSlate was already in ComputerLand and Businessland retail stores, two months ahead of Convergent's originally scheduled rollout.
And here's what you could have bought: the WorkSlate package came out of the box with two audio tutorial microcassette tapes, a bonus blank microcassette for your own stuff, the five-hour NiCad rechargeable battery pack (which needs to be re-celled, but AA batteries will do just fine and last at least twice as long), WK-102 AC adapter (which can charge the battery pack), a full set of manuals, RJ-11 phone cord and soft leatherette carrying case. The case is durable and stylish with a front pocket and a nice crisp Velcro closure. The entire unit minus batteries weighs a svelte two pounds nine-and-a-half ounces (1175g), and measures the same as a 8.5x11" standard piece of letter-sized paper, though it's about an inch thick. This package is not quite complete as it is missing the advertising material for Dow Jones, Official Airline Guide and CompuServe, all of which were billed as "compatible," plus the phone keyboard overlay for the numeric keypad, both of which appear to have been lost. Next to it is the MicroPrinter and its own carrying case and peripheral cable, and on top is my only "Taskware" software package, Loan Analysis.

I own two WorkSlates, though neither are in completely working order, and my second unit is in worse physical condition than this one. Both have non-functional microcassette decks, apparently a common problem with this machine, and this "display" unit also seems to have a bad speaker. The other unit has a working speaker but a very dim LCD that needs to "warm up" before use. You'll meet this second unit soon enough because we'll be doing some hardware experimentation on it.

The WorkSlate's striking industrial design now looks thoroughly like a product of its era, but in 1983 it must have seemed like it fell out of a future timewarp. Here I show it with the battery pack, AC adapter and phone cord — pretty much everything you would need on the road, though the charger/adapter won't fit in the case. The AC adapter is negative-tip and supposedly 6 volts, but this one measured 9 volts, so I'm not using it just in case (my trusty Radio Shack multivoltage adapter powered it for this article instead; the Texas Instruments AC 9201 adapter should also work). It uses a typical 5.5mm barrel jack for the power connector.

The keyboard on the unit is quite unusual, consisting of hard plastic mostly circular keytops that are all but impossible to touchtype on, though this wouldn't have necessarily been considered a grave fault by an intended audience unlikely to type much anyway. Other than being a QWERTY keyboard, the layout is otherwise totally unique. It has no Enter or Return key, replaced instead by the functionally equivalent Do It; while there is a conventional alphabetic Shift, there is no number row (moved instead to the side numeric keypad), requiring a green Special key for most symbols acting like symbol Shift, Control, Alt and Command all rolled into one. These symbols are silkscreened on the keyboard in green, with other special keys printed in yellow.

Function keys at the top select or enable menus, along with special dedicated keys for Cancel (Escape), Options and selecting a Worksheet on the left of the keyboard, and a cursor diamond pad in the centre (reminds me a bit of the Commodore 116). The numeric keypad is more or less conventional, but has extra mathematical operations, and is also used for more symbols with Special. Finally, there are separate On/Off and Eject buttons, though the Eject button is actually mechanical and not part of the keyboard matrix.

Even the box itself screams 1980s. While hardly Memphis Group, Nuttall's textured slab motif with its perpendicular contours and distinctive keytops was designed to attract the eye of the Reagan-era executive as something fashionable and trendy instead of staid and nerdy. Yes, this is the kind of computer murderous yuppies like Patrick Bateman might have used to make Dorsia reservations and schedule ordering their business cards. While to my great disappointment it didn't turn up in that particular movie, it did make several appearances in Airwolf as part of the eponymous hi-tech superchopper's navigation system:
Now, when's the last time you saw a spreadsheet do that?
The most important ports on the WorkSlate are on top: two RJ-11 phone jacks (one for the phone line and one for an optional handset, if you choose to connect one; both jacks will work for either purpose), the barrel jack power connector, and the GPIO port, which is marked "Peripherals." We're going to spend quite a bit of time with the GPIO port later on.

Other controls are a potentiometer dial on the left side of the unit for adjusting LCD contrast and another one on the right for speaker volume, plus 1/8" jacks for connecting a monoaural earphone or external microphone. These route to the tape deck and can only be used for voice recording and playback.

The underside of the machine has the backplate, battery doors and the flip-up stand. Don't remove that stand unless you absolutely have to: putting it back in will almost certainly break one tab or the other due to the age of the plastic, and both of my units' stands were already broken and had to be repaired with high-strength cyanoacrylate (I like JB Weld's light-cured type; cheap superglues aren't sufficiently strong). The large battery door slides off to accommodate either the NiCad pack or four AA batteries, while the smaller door is pried up to hold two 186 (LR43) 1.5V button cells used as backup. The button cells last about a week even if there are no main batteries at all, but you don't need either set of batteries to power it with the AC adapter, and you don't need to remove the stand to access the main battery door (just flip the stand up).

Both of my machines call themselves model WK-100 with the same FCC ID and their serial numbers differ by nearly 2,000, so it seems unlikely there are other variations given the WorkSlate's short seven-month production run. (However, see my footnote about this when we disassemble one.) Because of a lawsuit that was filed at its cancellation, we know there could have been at most 67,000 WorkSlates that ever existed, and sales data suggests that Convergent sold no more than a few thousand. Only a small number of working systems persist today, of course. More about that at the end.

A tour of the WorkSlate

Let's take a little tour of the machine. I apologize for the variability of these photographs since it was very hard to get a good straight shot of the screen with no reflections. In particular they don't do the LCD much justice, which is in reality quite sharp and contrasty for a 1983 panel even though it lacks a backlight.

When you power it on for the first time, it asks you for your name for occasional use in prompts and warning messages, and the clock defaults to September 26, 1983 at 8:00am. I don't know the significance of this date other than the Oko nuclear false alarm incident, in which Soviet Union lieutenant colonel Stanislav Petrov correctly determined the Oko early warning system had erred in claiming a total of five U.S. nuclear ICBMs were inbound. No missiles ever arrived and a later investigation concluded Oko had indeed malfunctioned. By disobeying Soviet protocol, Petrov prevented sending a mistaken retaliatory strike and possibly triggering nuclear war. He was not punished, but he was not rewarded either and retired early the following year. The incident was never publicly acknowledged until 1998 — which makes it impossible for Convergent's programmers to have known. Most likely this was just the particular date when its firmware was finalized. Sorry to wind you up but I never miss a chance to tell one of my favourite Cold War stories.

The display is in the unusual dimensions of 46 characters wide by 16 rows high using 6x8 glyphs. Although the panel is theoretically dot-addressible with a resolution of 276x128, the firmware doesn't allow for bitmapped graphics. The 46-character width may have been chosen to allow a typical 40 columns in the cell region of the spreadsheet (using the left six columns for the row number, cursor and padding).

The spreadsheet layout is the standard lettered columns and numbered rows format first popularized by VisiCalc. However, the WorkSlate probably came by this layout via Microsoft Multiplan, which Microsoft had licensed to Convergent in 1982 to port to CTOS for the AWS and IWS. Although Lotus 1-2-3 had come out in January 1983 to nearly immediate success, the WorkSlate's development was well underway by then and it likely had little or no influence on the firmware. Each sheet supports up to 128 rows and 128 columns (labeled A-Z and AA-DX), though how many can be simultaneously populated depends on available memory, of course. A cell can hold no more than 128 characters.

The top row of the LCD is reserved as a status line and divided into multiple fields. The leftmost field is the sheet name, which defaults to " empty " (spaces are part of the name), and can be no longer than eight characters. The second field shows the contents of the cell, which is naturally blank on a cold start. Following the contents field is the available memory, given as a percentage, which does double duty for the tape counter (where appropriate) and the machine's progress when doing calculations. The blank field after that is where status icons for the phone appear when enabled, such as ringing, off-hook or on-hold, and the rightmost fields are of course the date and time. If the timer is enabled, a little clock icon appears after the time.

The status line is periodically replaced by flashing announcements, most often "Replace backup batteries" on my units since I don't have the backup lithium batteries installed. Alarms and error messages appear here as well.

Basic numeric, string and formula types are supported, as you would expect. Strings are limited only by the size of the cell and appear left justified by default. Numbers are right justified by default and internally stored as floating point, even integral values. There is no way to know the exact internal representation but we'll be able later to determine each number consumes seven bytes of memory, suggesting something like double precision arithmetic. However, the WorkSlate predates IEEE 754 and the internal format obviously differs. For example, entering a value like 99999999999999999 comes out as 99999999990000000, while both a Microsoft Binary Format 40-bit float (as tested on a Commodore 64) and an IEEE 754 double would represent that number slightly rounded up as 1x1017. The WorkSlate doesn't display exponents, and there is no way to enter a purely numeric quantity as a string.

WorkSlate formulas are a curious mix. The general format also descends more or less from Microsoft Multiplan, which to the best of my knowledge was one of the earliest spreadsheets (if not the first) to indicate formulae with an equals sign, and does not mark built-in functions with an @ as VisiCalc did. However, unlike Multiplan which uses R1C1 relative notation for cell references, the WorkSlate uses VisiCalc A1 absolute notation, and also uses VisiCalc's three-dot operator for ranges (...).

That said, the most notable difference about WorkSlate formulas is that they use "proper" mathematical symbols: division is entered as ÷, not /, and multiplication as ×, not *. There is also a true inequality operator (≠).

Entry of strings and numbers is fairly straightforward, and they can be edited with the cursor diamond and backspace, though entering many common typographical characters requires awkward Special combinations. But editing a newly entered formula can be even more irritating because on initial entry the cursor diamond is used to indicate the cell you want, not to edit the line.

The WorkSlate has a very unique set of built-in functions and we'll be exploring these in great detail. Here's one: bar graphs. The WorkSlate has a primitive charting capability using the Line() function which takes a character and a quantity, and emits that number of characters. The quantity can be another expression, including a Total() over a range as we've done here. Since they're formulas, change the underlying value and the generated bar changes too.

The lines and boxes on the display are generated using the WorkSlate's simple palette of graphics characters, many of which are accessible from the DRAW option (but it's possible to access the entire character set — more soon). However, you can't enter these in the formula the first time around because the available menus aren't orthogonal; you have to enter a placeholder character and then change it. Some indicator characters cannot be entered directly at all which was likely on purpose to avoid confusion.

There are no other chart types built-in, but we'll fix that in the second example program.

Date and time arithmetic are also possible, with some limits, and as they are treated as numbers internally can be used in formulas too. Here, I've added 365 to various dates to advance the year (in cells A2, A6 and A8) and 12 to the time in B1 to advance it by twelve hours (B2). The 1983 default date and time does not appear to be an epoch: the WorkSlate will accurately almost represent any four-digit year from 1 to 9999, though you may need to explicitly indicate the century — two-digit years are treated as 19XX, which is why A2 shows the year 2000 — and all dates seem to be treated as Gregorian. Although I didn't exhaustively test its leap year logic (I know, right?), 2/28/1899 plus 366 is 3/1/1900, but 2/28/1999 plus 366 is 2/29/2000, both of which are correct.

I said almost because there is a curious bug in that years ending in 00 (2000, 1900, 1800, etc.) cannot be entered as such; they come out as 20, 19, 18, and so forth. Calculations on those dates are similarly affected because the year in memory really is 20, 19, 18, etc. (e.g., x/x/20 + 365 is x/x/21), but if you add 365 to a correctly accepted year like 1899 you still get 1900, so it seems to be a problem with parsing the year on entry rather than the internal representation. This glitch would have become very obvious as the millenium approached but was likely never noticed during the short window in which the WorkSlate was commercially sold and used. More serious bugs are yet to come.

In a (presently) less obnoxious vein, I should note for completeness that the firmware also has a Y9999 problem in that 1/1/9999 plus 365 days ends up 1/1/9900 as shown in A8. If you are reading this document from the year 10,000, may I be the first to congratulate you on keeping your unit running that long and suggest that you simply take years mod 10K for date storage.

The Options key at the lower left brings up the set of main options applicable to most any instance, three pages' worth cycled through by repeatedly pressing it. You select the desired option with the function keys under the screen or you can just press Cancel. The most important of these options is probably CHANGE, which is actually necessary to do some types of editing versus just changing cells directly on the sheet itself. In this mode the cursor diamond is used for motion and not indicating cells.
These Options, among others, are also where you can do things like change the alignment and format of cells, such as whether they can overlap, their justification and their width. Again, by default numbers are right justified (including dates and times) and strings are left justified, but you can change that here. The decimal and whole formats are of arguably lower utility, since they enforce a fixed two decimal place and no decimal place display format (rounded), mostly of use for financials where you might only work in integral cents or dollars.

Edit operations like cutting, copying and pasting are also done from Options, as is system setup. If you copy a formula to another cell, unless you make the reference absolute with an @, cell references are treated as relative and automatically adjusted for you. Oddly, you can only cut and paste within the sheet you're in.

The WorkSlate can handle up to five simultaneous worksheets, which exactly maps to one side of a microcassette which can hold up to five worksheets as well. Computer history lore holds that Boeing Calc (yes, that Boeing) was the first spreadsheet to implement tabbed sheets in 1985, but I think this gives Convergent a credible claim to having implemented something like it earlier, even if it wasn't recognised as such. Only the first two are "standard" (i.e., can represent any sort of spreadsheet). However, each sheet is otherwise independent and the other three by default are reserved for each of the WorkSlate's "derived types," the memo pad, phone list and calendar. These are globally available but to conserve space are not actually constructed in memory until the first time you use them. It is possible to overwrite them and recover the sheet slot and any memory they were using, but they will not be available to you again in their default form until you cold-start the unit.
The Memo Pad allows you to dictate to microcassette or type text strings directly into large cells. By default the Memo Pad displays a set of tape control options accessible with the function keys, replacing the available memory percentage with a tape counter. The WorkSlate has full software control of the tape motor and automatically rewinds and stops as appropriate. However, the designers didn't think the Memo Pad feature through fully because you can't write both voice and binary data to the same side of a microcassette, even though the Memo Pad will let you record and type simultaneously. As part of the recording process, the WorkSlate writes a small binary header at the beginning of a cassette saying if the tape is voice or data and the firmware will prevent you from mixing the two. Instead, you dictate on one side, then save the text on the other. You can play back the audio through the WorkSlate's internal speaker or using the side earphone/mic ports.
The Phone List is a simple three-column phone book of which only the first 14 rows are constructed automatically; more can be added by copying empty rows or creating the cells by hand.
Analogously with the Memo Pad, by default the Phone List maps phone options to the function keys such as answering incoming calls on the connected phone line or putting the call on hold, turning on the speakerphone, or opening a terminal connection. The feature that makes the Phone List a "phone list" is the ability to autodial the number in the current cell using the modem. Strictly speaking the dial feature would work from any cell with digits in any worksheet, because the Phone List is "just a spreadsheet" like everything else (see also the Dial() built-in function, which will dial a number), but the Phone List at least made the concept explicit. If you wanted to dial a number manually, it was possible to put the numeric keypad into a "phone" configuration (that's what that missing overlay was for, since it moves the numbers around).
Finally, the Calendar, which displays date and time options beneath a constructed datebook. Similar to the Phone List, this only builds two weeks of entries starting with the current date and only for 8am to 5pm (oh, how I wish I only had to work 8-5), though you can change the first date in B1 and the others will update, and you can change the hour numbers manually as you like. Simplistically, you could just enter appointments right into the cells and use it as an organizer; the layout as constructed gives you half-hour slots.
But the WorkSlate also has an alarm feature. Let's say you want to meet Patrick Bateman for, uh, lunch. He doesn't like it when you're late. I mean, he'll kill you for that. Here, your WorkSlate can save your life by reminding you five minutes before. Our lunch is set for 3pm, so we'll set the reminder for 2:55pm.
You can then add text to the reminder which will appear when the alarm goes off. The WorkSlate will put the text into the current cell, overwriting any previous entry (so you can just set alarms to enter schedule entries if you like; theoretically the number of alarms is limited only by available memory).
What this has actually done is create an unusual formula in that cell. We see our alarm text as a string, but then followed by a function Alarm() with a date and time parameter which is the real trigger. Notice that the alarm time, or date for that matter, doesn't have to have anything to do with the actual "time cell" you're using: the Calendar is also all "just a spreadsheet," so Alarm() works in any cell in any spreadsheet in exactly the same fashion too. Conceivably you could even put an alarm in your phone book, say. The idiom of "adding" (with a plus sign) the string to the alarm function is how the cell's text contents are set simultaneously with the alarm, which in turn are used as the alarm's label.
And indeed, true to form, the alarm goes off at 2:55pm (audio beeping and in the status bar), and so we hurry to our lunch at Dorsia and listen to him rant on about Huey Lewis, after which we walk back to our condo safely. Another life saved by the WorkSlate. Bret Easton Ellis missed a literary opportunity here.
The Memo, Phone and Time buttons also pop up those functions apart from their dedicated worksheets, letting you use them in a standard spreadsheet as well. The Finance button affords quick access to simple wizards for things like various methods of depreciation, loan value and payments, and Net Present Value on future receipts given a discount rate. These options are all implemented based on a set of built-in functions and the wizard generates the formulas for you.
The Calc button, however, pops up one of the WorkSlate's two "desk accessories." These tools can run simultaneously with a worksheet. The Calculator lets you do basic arithmetic but also can load and store values to the current cell, or directly add and subtract with it. A running history is displayed; with a MicroPrinter attached and the print feature turned on (Special-Print), a ticker feed like a desktop calculator is generated with negative numbers in red. After any arithmetic operator, you can also enter arbitrary text to annotate the entry which will also be printed.

What makes these two subprograms (the second to come presently) true desk accessories is that the spreadsheet remains live. While the keyboard is primarily occupied by the Calculator, you can still move around in the spreadsheet with the cursor diamond, and you can still enter and modify cell values by using the CHANGE option (i.e., via the Options key) while the accessory is running.

It is also possible to divide up the screen between two worksheets and link them together. This is of sometimes questionable value with wide cells and this small of a screen, but you can do it, and you can do it horizontally or vertically. Here, we have our phone book at our fingertips as well as our test spreadsheet.

Okay, you want to see the hardware now. Let's switch to the "beater" unit, turn it over and crack it open.

Removing the visible screws and taking the back off, the first thing that greets us is a laminated non-conductive copper sheet held on by clips and remnants of battery crud in the main compartment. We can also see the back of the tape motor, the reset pushbutton, and at the bottom the microphone.
We can also see the back of the GPIO port next to the battery compartment, with one of its lines going to the cathode (serving as ground) and two to the anode. This will be important later. No, I don't know why there's Scotch tape on the board either except possibly to hold that tiny surface mount component on, so I did not disturb it. The nicer of the two doesn't have it.
To actually turn the board over and see the electronics requires lifting the mic out of its little hidey-hole (it can stay connected), removing a couple more screws and then carefully worming the cassette door out (eject it first).
The main logic board then can flip over like a book page. The three main PCBs are the LCD, keyboard and logic board, all connected by two relatively robust ribbon cables which you can leave connected for this purpose. We aren't going to open up the keyboard since I've always hated fishing for keycaps.
The LCD display board ("Rev 2") is all Oki chips. Although each line of the TN LCD pancel is 276 pixels wide, each group of seven MSM5839GS chips at the top and bottom can drive a line of 280 dots (40 dots per chip), four of which are simply not displayed. These chips act as column drivers. The row drivers are the two M5238GS chips in the centre, both being 32-dot common drivers that using both groups of column drivers can each refresh 64 rows of the screen (i.e., 32 times two). With two such chips, one for the top half and one for the bottom half, we have all 128 rows for our observed panel resolution of 276x128. The refresh rate, according to the partial service manual on Bitsavers, is a very stable 69Hz.
The logic board contains everything else including the speaker, ports, microphone, pots, microcassette mechanism and primary electronics. However, its most noticeable feature is the surprising number of bodge wires, which is rather unexpected in a high-end device. There may well have been PCB yield problems — or possibly late-breaking hardware bugs — that required manual rework but it's notable that the rework itself is so extensive. It's not clear whether Oki did this at the factory or if it was done on final assembly in the United States. Jeff Birt, who has done a particularly detailed disassembly of the WorkSlate (YouTube video) and looked at several units, told me that no two were alike with different boards and correspondingly different bodges, though both of mine are the C-60-00124-01 version with similar board work.
Most of the action is at the top of the board (rotated 90 degrees clockwise in this view). The CPU is at U20, an 8-bit Hitachi HD63B03RF, and my original WorkSlate is in fact the first Motorola 6800-based system I ever owned. The Hitachi 6303 (here in the bug-fixed "R" variant) is an enhanced clone of the Motorola 6803, itself a ROM-less variant of the Motorola 6801 microcontroller. The 6801 started with the 6802 CPU, a Motorola 6800 with an on-chip oscillator and 128 bytes of RAM, but then also reduced the cycle time of several key instructions, added ten additional instructions including 16-bit addition, subtraction and (further enhanced in the 6303) multiplication, and implemented a 16-bit timer, TTL serial port and 31 parallel GPIO lines. However, the 6803 only supports 29 parallel lines, and on the 6303 many of those pins are marked n/c, exposing only 13 which are divided into an 8-bit port and a 5-bit port. These GPIO lines are additionally multiplexed with part of the address bus and part of the address bus is multiplexed with the data bus, or a standard 6800 bus configuration can be selected using the 8-bit port as the LSB of the address.

The primary CPU clock comes from the crystal at X1, a 4.9152MHz oscillator. This is internally divided down by four to yield a nominal internal clock speed of 1.2288MHz, but for power savings reasons the CPU is rarely running full-tilt: besides additional memory-to-memory operations and a register-swap instruction, the 6303 also adds a SLP "sleep" instruction different from the existing 6803 WAI "wait for interrupt." In sleep mode, the 6303 actually powers down its bus and runs no instructions but keeps the contents of the internal RAM and its registers active and maintains the serial port and timer, running on about one-sixth the wattage, until it is awakened by an interrupt or reset. When otherwise idle the WorkSlate firmware sleeps the CPU between its 10ms keyscan timer interrupts, effectively reducing the average system speed to as low as roughly 125kHz. On top of that, the 6303 can also enter a standby mode where the registers and CPU state are dumped to internal RAM and then the chip halts completely, powering only the internal RAM (the WorkSlate maintains power to the rest of memory and critical components like the gate arrays and real-time clock). When you press On/Off, this runs a ROM routine that saves the CPU state along with a ROM checksum, then signals the tape gate array at U29 (not visible here) to assert the CPU's standby line and power down. Pressing On/Off to resume will cause the tape gate array to deassert standby and send a reset to the CPU instead. The CPU powers back on and enters its normal reset vector, which looks at the internal RAM to see if the ROM checksum still matches. If it does, then power must have been maintained, and the CPU duly restores the rest of the processor state to resume operation. Otherwise, a cold start occurs. This detail will become relevant when we talk about a particular critical bug in the WorkSlate firmware.

Serial data from the GPIO port, microcassette deck and modem data lines are fed to the on-chip 5-line serial port; the on-chip 8-line parallel port is wired to the modem control and ROM banking lines. Otherwise, except for the on-chip peripheral registers at $0000 and the internal RAM at $0080, all other memory and memory-mapped I/O in its address range is manipulated via the decoder gate array at U13 (with the blue sticker on it). Like most 8-bit CPUs, the 6800 family can access a total address space of 64K. Main memory consists of eight Hitachi HM6116LFP-2 static RAMs in U1 through U8, each containing 2K for a total of 16K. The RAM at U1, however, has a special purpose: 736 bytes of it provide the screen memory as ASCII characters which are then turned into dot data by the LCD controller at U11, a Hitachi HD61830. The HD61830 has built-in character glyphs, but these are only 5x7, so to implement 6x8 glyphs to paint the LCD with no gaps, external character data is provided by a small ROM at U12 next to it. A special decoder gate array mode lets the CPU manipulate the HD61830's registers directly with its address bus lines. Once initialized, the LCD controller then regularly fetches characters from U1 and uses its external character ROM to generate the pixel stream for the row drivers. All the CPU has to do is change characters in the screen memory as needed and the LCD is automatically refreshed. In addition to the LCD controller, the decoder array can also select RAM, ROM, keyboard, tape gate array functions, DTMF dialer, or the real-time clock.

There is mention in the service manual of a 32K RAM option, though it describes it as "four 16K-bit by eight bit static CMOS RAMs" which would actually be 64K. I'm not aware of any production WorkSlates with more than 16K of RAM, 32K or otherwise.

Other major chips visible here are the HD146818FP real-time clock at U21 that handles the clock, calendar and upcoming alarm based on a 32.768kHz crystal at X2, the Mostek MK5089N DTMF touch-tone dialer at U22 with its 3.579545MHz crystal at X3 (using the NatSemi MM74C374N octal latch at U23), and the 300bps modem-on-a-chip Motorola MC14412 at U24 with its own 4.0MHz crystal at X4.

The lower left corner of the board (here rotated 180 degrees to put the ROM markings right-side up) is where the ROMs and the tape gate array live. The tape gate array is the large IC at U29, though it handles more than just the microcassette deck: it has lines for the tape motor control and data (with special circuitry for decoding tape signals to serial data), system power (both from the On/Off button and the alarm), battery voltages, audio input and output routing, and selecting the data source for the 6303's serial port. The decoder array treats it as a device that the CPU can select and manipulate.

The two system ROMs are at U16 and U15, both 32K Hitachi 23256 equivalents. Their date codes indicate second week 1984, which was well into the WorkSlate's brief commercial lifespan and makes them likely the last, if not only, production ROM revision that exists. The decoder array banks the appropriate ROM into $8000-$ffff as specified by lines on the CPU parallel port (so as currently configured the machine can support no more than 32K of RAM, even if more were physically present). Pads for a third ROM are at U14 nearby which would likely have been used in one of the other Ultra models, and the decoder array already has support for banking this ROM in too should one be installed. If one of my units quits completely, my next task will be to desolder and dump these ROMs, since I'm not aware of any copy of any version of them anywhere.

The cassette mechanism is a miracle of early 1980s miniaturization which also makes it a nightmare to work on, especially since tape drive issues appear to be the most common defect in surviving WorkSlates and the microcassette failures on my two systems are different. The "nice" unit has a stuck tape head that will neither reliably retract nor extend, making reading tapes vary between unreliable and impossible. Jeff's video series I linked previously examines a similarly failed unit which he determined was due to a small pin that fractured and prevented the actuator from moving the head properly. This failure has the side effect of preventing you from pressing the eject button and locking the cassette door closed unless you can pry it up or push down the head. Unfortunately his repair required rebuilding this tiny pin in his shop, something non-trivial for an idiot like me who has to make sure he doesn't solder his own fool fingers together. This "beater" unit, on the other hand, simply won't spin the reels at all. It's possible that both its motor and head are bad.
The tutorial tapes included with the WorkSlate are "just" audio (with a data header telling the WorkSlate that the cassette is "just audio"). They will play back with any standard microcassette recorder at the faster 2.4cm/s speed and I have archived them in their entirety, though the audio quality is merely adequate as I suspect they were recorded on a prototype WorkSlate as well.

However, the Loan Analysis tape is all data, with an effective baud rate (as with all WorkSlate data tapes) at a fixed 2400bps, 8N1 — exactly like serial data, complete with start and stop bits. The trick is converting it from its frequency modulation encoding (yes, the same as the early floppy format, unlike other cassettes that use another digital format or analogue encodings like FSK) to regular bits; FM is designed to allow clock recovery despite jitter, but there seems to be more of it on a tape than there is on floppy media, and the real unit has a voltage-controlled oscillator at U19 to compensate which locks the speed of the tape gate array's decoder to the true speed of the tape. It should still be possible to decode this and other Taskware cassettes from an audio recording with some manual work, though that by itself doesn't help us with actually getting data in and out. Some people have figured out tricks like whistling into the speakerphone with a modem but that limits you to 300 baud.

The MicroPrinter and the GPIO port

Fortunately there's an even better way of exchanging data with the unit or this whole article might not have been possible. Interestingly enough, that way starts with the MicroPrinter.

The MicroPrinter was the first of two peripherals produced for the WorkSlate and the only one available at launch; the second, the $195 ($600 in 2024 dollars) CommPort, was a combination serial-parallel port device that was not available until January 1984, and is comparatively rare. The CommPort was the only supported means for using a conventional parallel printer. That said, even though this entry will use serial access heavily, we don't actually need the CommPort to communicate with the WorkSlate — or even the modem to do so, for that matter, though I'm getting ahead of myself.
The MicroPrinter is in fact a small portable plotter that uses four coloured pens to draw on a 4.5" (11.5cm) paper roll. (Plotter dweebs will have already guessed what's under the hood just from that description but please don't spoil the disassembly for everyone else.) It defaults to a 40 column width line, exactly what the WorkSlate would display in the content area, but can also draw text in a "compressed" form for a full 80 characters or render spreadsheets sideways with up to 24 or 48 rows. The colour settings in the WorkSlate only apply to the MicroPrinter; the margin and paper settings only apply to a connected parallel printer.
A coiled 8P8C cable connects the MicroPrinter to the WorkSlate via the GPIO "Peripherals" port. This cable carries both data and power: while the MicroPrinter can be powered by the same AC adapter, AA batteries or NiCad battery pack that the WorkSlate uses, you can power both units from the other (the AC adaptor here could have been connected to either device, and they can also share battery power, though running the printer off the WorkSlate will probably kill its batteries rather quickly). While there is a hollow in the MicroPrinter case that the cable can snuggle into, the cable's degenerating plasticizer has made it so grotty that I keep it in a Ziploc bag in the leatherette case instead.

Only one peripheral can be attached to the port at a time, likely because of the limited power budget. That makes our task simpler because we don't have to worry about daisy-chaining. One thing I noticed with the WorkSlate propped up is that the profile of the main unit and the printer both match, another nice Nuttall touch.

I only have one example of the printer. It identifies itself as a model WP-100.
As before a laminated copper sheet covers the rear of the main circuit board, but the plotter mechanism is instantly recognizeable as an Alps DPG-1302. This was used in a huge number of contemporary printer-plotters and is best known in the Atari 1020 and Commodore 1520, but also appeared in the Texas Instruments HX-1000, Tandy Radio Shack CGP-115, Mattel Aquarius 4615 and Astor MCP-40, rebadged by Oric and (though unreleased) Tomy, among many others. The factory nylon gears in this mech are notorious for stripping (replacements exist) and the pens are long dried out, though we're not here to print on it today.
When we flip up the board we see its main CPU, a Hitachi HD63A01V1F. If this sounds like another 6800-family CPU, you're right: the Hitachi 6301 is a clone of the 6801 microcontroller, and like the 6801 has its own built-in ROM (4K) and RAM (128 bytes), though it only has 29 GPIO lines like the 6803 divided into three eight-bit ports and one five-bit. It has the same additional instructions as the 6303 including a "sleep" mode which is also used for power-saving when the printer is idle. The crystal at X1 is rather illegible but photographing it at the right angle shows it to be a 4.9152MHz oscillator as well, which is also divided down by four internally to yield the nominal clock speed of 1.2288MHz, same as the WorkSlate.

However, there is a second processor in the MicroPrinter, arguably making it more powerful than the computer it's connected to. The HD44860 next to it is a little 4-bit microcontroller with its own 128 bytes (as 256 nybbles) of RAM and 4 kiloword ROM using 10-bit instructions (hello, decle!), plus a timer/counter, on-chip oscillator, 44 lines of GPIO and two interrupts. Unlike the 6800, the 44860 is a strict Harvard architecture CPU. All but one instruction run in a single 5 microsecond machine cycle for an effective clock speed of 200kHz. Not unlike other 4-bit CPUs we've seen, the program counter treats the ROM as two banks of 32 pages each containing 64 words, and program execution stays in the same page until changed with a long jump. To make it wackier, though, the lowest six bits of the program counter are actually an infinitely repeating polynomial sequence, so subsequent instructions are not contiguous in ROM (and you thought COMP-X was weird).

After following some of the traces, my guess is the 44860 is responsible for directly driving the mechanism. The 6301 acts as the master, receiving instructions and data from the WorkSlate on the peripheral bus and sending commands to the 44860, which controls putting pen(s) to paper. (Jeff Birt disassembled a CommPort and showed that it actually has two 6301s in it, so it's even more powerful than the printer. They both run at 1.2288MHz also. Convergent's hardware designers must have found a special on 4.9152MHz crystals.)

But all this would be academic if it weren't for a unique feature of the MicroPrinter: the WorkSlate supports using it as a typewriter, where you can simply type characters to be printed immediately. That brings us to the second WorkSlate "desk accessory," the Terminal. Ordinarily the Terminal is for the modem, where you can dial into a bigger system and set it up to interchange data. As well as acting as a simple dummy terminal, it offers the ability to send and receive whole sheets.

However, if the MicroPrinter is connected to the GPIO peripheral port, the Terminal instead ignores the modem and sends data directly to the printer. That gives us an opportunity: if we can act like a MicroPrinter and do whatever dance it does with the WorkSlate, we can trigger the Terminal through the GPIO peripheral port and send and receive data directly — without a CommPort. Encouragingly, the partial technical description describes the data as serial 9600bps TTL, 8N1. That's just a UART. Anything can talk to that!

There's one problem, though:

It's that rotten coiled cable. It's 8P8C, but in the same dimensions as a 6P4C RJ-11 telephone jack.
That means something like a regular Ethernet or RJ-45 type cable is too fat: it physically won't fit in the port.

I dithered over shaving down an Ethernet cable to make it fit, and actually tried doing that with some files for a couple hours, though I eventually abandoned the idea because I was worried the two sides wouldn't come out even (and my arms were tired). A tip of the hat here to Lamar Owen on classiccmp who suggested one of the narrow "universal modular" connectors sold by Sandman (not affiliated, just satisfied). I made an order with them and we'll talk more about that in a second.

In the meantime, we'll need to figure out how the GPIO port is even wired — something else that's also not in the partial service manual. Additionally, since the MicroPrinter's ROMs are all internal to its microcontrollers, we will only be able to determine its behaviour by snooping the serial lines.

Connecting the coiled cable to a tester showed that it was wired straight-thru except for pins 4 and 5, which are swapped. This sounded suspiciously like a null modem swapping receive and transmit lines. We saw by the board traces where the GPIO port receives power and ground, so on the back end of the "beater" GPIO port connector I soldered jumpers to ground and pins 4 and 5, and a couple of the others that weren't obviously connected to the power rails. (Spoiler: at the end of this section we'll have an easier way of hooking up devices without having to modify the unit, so don't worry — you won't have to crack your WorkSlate open to use the example programs.)

However, we also know that the GPIO port is not always connected to the 6303's 5-pin serial port because the tape gate array can hook up other sources. Walking the traces with the 6303 data sheet, we can see some processor lines get brought out to a header at the top of the board. I soldered a couple more jumpers to the data lines for the 5-line serial port just to see what those were doing.

After fiddling with various permutations I was able to monitor serial data going back and forth, and those demonstrate the WorkSlate can ask an external device to identify itself in a completely old-school standard way. With picocom in partial hex mode (picocom -b9600 --imap tabhex,crhex,spchex /path/to/your/serial/port), upon starting the Terminal the WorkSlate sends an XON (ASCII 17, ^Q) followed by an ENQ (ASCII 5, ^E) out the GPIO port, which is the standard transmission control character to command a remote station to send its answerback string. The 6301 in the MicroPrinter responds with

1 MicroPrinter.

followed by a CR-LF. This is an interesting string to receive because the number suggests multiple printers could have been connected, either giving a total number of devices or saying what their "device numbers" were.

I don't have a CommPort to figure out how that would have acted, (update: it replies with 2 I/O Box., so the number is just a device specifier) but after some experimentation I determined all the WorkSlate is looking for is a single line feed character — if you send that in response, and quickly since it only seems to wait about three seconds before reporting an error message, the Terminal opens. Likewise, if you were wondering if a connected device can command the WorkSlate to identify itself, it can, and the WorkSlate responds even if the Terminal isn't running. Send an ENQ character yourself to the WorkSlate (Control-E) and it will answer with

Workslate

plus a CR-LF. Very logical. In fact, this is exactly how you can connect two WorkSlates together directly with the GPIO port, since they will both respond to the other's ENQuiry, and they will even share power. It doesn't seem like any of the other GPIO lines are used (other than power), so everything is in-band signaling with all the advantages and disadvantages that implies.

Here's what I bought from Sandman: a banjo with the "universal modular" cable, part #TOO6G. For those unfamiliar with telco work, banjos connect to phone jacks and break out the individual lines so you can connect a lineman's handset ("butt-set") to a pair with just the handset's alligator clips. The name comes from older banjos which were in fact circular and had a permanently affixed cable somewhat resembling the musical instrument. Getting the banjo kit seemed convenient because I'd need to break out the wiring anyway, so I could just use alligator clips of my own to wire up the USB-TTL adapter instead of having to crimp a cable and peel off the lines.

Parenthetically, the design of this particular banjo is such that it can be connected in series and used to tap the connection between two devices on either end. We've already found out what we needed to know without doing that, but this trick could be useful to you for something else.

The universal modular cable was the right width but it still didn't fit. The reason is that there's also a little key divot we have to carve out. (The outcropping next to it is there to prevent the coiled cable from being put in anything but a WorkSlate-family device, but it isn't needed to get the banjo's cable to mate.)
I put the cable in the vise in the workshop and, carefully with a Dremel rotary tool at medium RPM, took a couple swipes off that side of the modular connector. Now it fits!
We can demonstrate that the connection is good by looking for the 6 volt line we know is present from what we saw with the logic board. Six volts is not TTL, so a line with 6V on it can only be the power rail. It didn't take me long to find ground on pin 1 and a strong 6V on pin 2. To ensure we don't fry anything we'll leave that completely unconnected. Pin 8 also appears to be connected to the power rail (more than 5V), though at a lower voltage.
With alligator clip jumpers on and consulting my notes from the test connections, I ran pin 1 to ground, pin 4 to receive (RXD) and pin 5 to transmit (TXD) on my 5V USB-TTL adapter.
Ta-daaaaa. We now have serial communications with the WorkSlate directly through the GPIO port with our own cable. It's time to program this sucker.

Spreadsheet storage and operations, or, Turing completeness is overrated

I commented in the introduction that the worst thing a spreadsheet on a 8-bit CPU could be is Turing-complete, because being Turing-complete implies the possibility of unbounded loops. (We're going to ignore macro facilities for this discussion and most 8-bit spreadsheets didn't have them anyway.) Even if your software were properly coded with a means to halt a potentially infinite cascade of computations, it could make your hard work all but unusable especially if the spreadsheet formulas were highly complex.

The most common way this happens in a spreadsheet is with circular references, where one cell's calculated result depends on the calculated result of another cell which depends on the result of the first one. VisiCalc infamously had a strict left to right and top to bottom evaluation order which could cause aggravating errors if there were interdependent references. For at least a couple years the only spreadsheet that could correctly resolve them was Sorcim's CP/M VisiCalc clone SuperCalc. Former product manager Wally Feigenson posits this example which also did not compute correctly in contemporary versions of Lotus 1-2-3: given a particular dollar amount of gross sales (he used $100 as an example), calculate your cost of sales as a percentage (example, 60%) and calculate your gross profit (gross sales minus cost of sales), and then calculate your profit sharing as a percentage of net profits (example, 10%) and your net profits as your gross profit minus the profit sharing.

That last part is an obvious circular reference but is nevertheless a plausible business calculation. On paper you'd end up doing the numbers twice to resolve the problem, and that's what SuperCalc did. Though Feigenson cites this example to show off the new "iterative calculation" feature in SuperCalc 3, his example works just fine in SuperCalc2 for MS-DOS:

The correct answer for the example values is actually a net profit of $36.36 and profit sharing of $3.64, but this is only due to the default displayed precision and the answer is accurate internally. Unless you came up with a macro to force another round of recalculation, however, Lotus 1-2-3 would show zero.

Microsoft Multiplan had its own solution to this problem. Its evaluation scheme builds a chain of dependencies and works down the list ("compute-until-done"). If the process started hitting the same cells again, it would conclude a loop was present and abort with an error message. However, it could optionally run in an "iterative" mode where effectively the loop limit was removed and it ran until the values "stabilized" (defined as an absolute sum of changes between runs of less than 0.001). You could even create a custom test condition for termination, which could include a different delta value of its own or a cap on the total number of iterations. This mode was very useful for mathematical operations in particular (Newton-Raphson comes to mind) but also allowed the possibility that a run could go infinitely. Likely because of the pitfalls this could pose to an incautious user Microsoft chose to remove iterative calculations for Excel and did not reimplement it for almost a decade.

As the WorkSlate's strongest influence was Multiplan, it adopts the same "compute-until-done" strategy, but with a twist. It will detect and report a circular reference after the first run, yet it will allow processing to continue for a fixed three iterations before terminating. Consider this spreadsheet with A1 being =B1+1 and B1 being =A1+1:

On the first run B1 is evaluated as zero, so A1 becomes 1 and B1 becomes 2. This demands a second iteration, so A1 becomes 3 and B1 becomes 4. This demands a third iteration, so A1 becomes 5 and B1 becomes 6, at which point the WorkSlate cuts off further evaluation and chides you by name in the status row. You can manually press Recalc (Special-N) to kick off another three iterations, yielding 9 and 10 and so forth, but it never does more than that per run.

Regardless, this means that Feigenson's example will also work on Multiplan (if iterative calculation is enabled), and by extension on the WorkSlate:

The point of all this is to say that our WorkSlate programming endeavours can't depend on any computation where the number of necessary iterations is unknown (and therefore potentially unlimited). On the WorkSlate, this is a good thing. If you've ever worked for a clueless executive, you know they will ask the impossible of you, and they will ask the impossible of their toys too (but I repeat myself). It is entirely plausible that someone got themselves in trouble during testing with this and it would have had obvious impacts on battery life and responsiveness. Because the WorkSlate was never meant to run arbitrary programs, even though it makes this article more complicated and there will be many computational operations we simply can't express in its spreadsheet's terms, these limits are absolutely okay.
With that introduction, next we should figure out how worksheets are sent and received so we can start writing our own in a more efficient manner. Along with hanging up, pausing a transfer and the irrelevant-to-us option for talking over the phone line we're not using, the Terminal has options for exchanging data (Send and Receive). Let's take the silly little spreadsheet we have onscreen and send it to the computer I'm typing this on.
We press Send and select the sheet we want, and the WorkSlate will transmit it over the serial port via the connected banjo. Marking control characters and trailing spaces, it comes out like this (all lines are separated by CR-LF):

^R^R^Q\S C2 A1 A3  A E c[space]
\M 202
\N " empty  "
\W 13 9+2[space]
A1-"Allen"
B1-"Loves"
C1-"Cigars"
A2-1
B2-2
C2-3
^C^Q

The control sequences are two DC2 (ASCII 18, ^R) characters, which on TTYs would turn on the tape punch to start receiving data (again, very logical), followed by an XON (ASCII 17, ^Q), and then the spreadsheet data. At the end it sends an End-of-Text ETX (ASCII 3, ^C) character — and here you thought ^C just meant "break" — and another XON, ending the transmission. Sheet data always consists of the first four lines in order (\S \M \N \W), followed by rows and columns going left to right and top to bottom, though it will accept them unsorted. After some fuzzing and experiments, here's what I think everything means.

The \N line is quite obviously the name of the sheet, demarcated by quote marks. It is always eight characters and padded with spaces.

The \S line describes the maximal dimensions of the sheet (here going from A1 to C2) with the cursor to be placed in A3. Notice that the cursor position does not need to be within the bounding coordinates. In fact, other than the cursor position itself, the WorkSlate doesn't appear to make use of these values on receiveback and will freely put subsequent cell data outside of the stated dimensions. Similarly, the last three characters (we'll call them "flags") don't appear to do anything either. For a typical generic sheet they are usually rendered A E c, in that order and with that capitalization. However, the Memo Pad and Phone List, should you send those, are marked with a e c, and the Calendar is marked with a E c. The only pattern I've found is that sheets where events may be present are tagged with a capital E, and sheets with alternate cell alignments are tagged with a lowercase a. Likely these were hints about what types of features the sheet uses, maybe as a way to reduce power further by turning off unneeded interrupts or chips, but since you can use pretty much any built-in formula feature in any sheet, they don't appear to actually gate any particular attribute. If you alter the flags in a sheet and send it back to the WorkSlate, it will preserve their values but the sheet still apparently acts the same. For our code we will still set the bounding box correctly and use the most common A E c flags on the off chance they're salient, somewhere.

The \W line describes column width, out to the maximum column given by \S. The default layout, used here, makes column A 13 characters wide and everything else 9. (This default is also used for any cell outside the bounding box where the width isn't specified, by the way.) As a little bit of compression repeated values are designated with +d, so 13 9+2 indicates the sheet consists of one column of 13 characters (A) followed by two columns of 9 characters (B, C).

Special attributes like overlap and justification are not part of the header lines; since they're specific to an individual cell, they're appended to each cell entry after a space. The codes are, sensibly, "D"ecimal, "L"eft, "R"ight, "W"hole and "O"verlap. An interesting thing about overlap is that while the large cell is emitted with "O", the cells it overlaps are emitted also, just blank and empty.

Finally, the \M line is how many bytes the spreadsheet is expected to take in memory. Interestingly, the only use the firmware seems to make of this value is deciding whether to even attempt to load the sheet — if it's bigger than available memory, then the WorkSlate will immediately abort. Even if you make this value zero, which surprisingly it will accept, it will still abort reception if the sheet ends up exceeding available memory or the 128x128 maximum anyway, so you can't use such a trick to stomp on anything interesting (darn). This behaviour is nevertheless useful to us because it means we don't really need to sweat getting the value exactly right as long as we provide an acceptable one, though as a practical measure we'll still try to provide a proper estimate in our examples. Interestingly, you might think that you could have a value up to 16384 here (i.e., 16 kilobytes) and you would be wrong: remember that 736 bytes are required for the screen memory minus various work areas minus table attributes minus the 128 bytes built into the 6303. Experimentally I determined the largest value you can have for \M is 12868 bytes, and that only for a completely clean, empty system.

Memory usage for strings and numbers (and date and time values, which are emitted in their display form but stored as numbers) is straightforward to estimate. Every sheet starts at 144 bytes in size, plus 2 bytes for every cell in the bounding box. The bounding box here is 2 rows by 3 columns for six cells, so 12 bytes. Each number value takes up eight bytes (presumably a tag byte followed by the floating point value, see below for why the value itself appears to be seven bytes), and each string value is two bytes plus one byte for each character. If we tally this all up for the example, we get 144 + 12 + (2 + 5) + (2 + 5) + (2 + 6) + 8 + 8 + 8 = 202 bytes, the reported value.

Incidentally, if you put a quote mark inside a quoted string, it just gets sent inside the quoted string unescaped. The WorkSlate will figure it out.

Formulas are a little trickier. Let's look again at the WorkSlate version of Wally Feigenson's 1-2-3 torture test. I've removed the control characters and terminal spaces for didactic purposes but they are still present in the same positions.

\S B5 A1 B4  A E c 
\M 299
\N " empty  "
\W 15 9 
A1-"Sales"
B1-100
A2-"Cost of Sales"
B2=.6~$~B1
A3-"Gross Profit"
B3=B1-B2
A4-"Profit Sharing"
B4=.1~$~B5
A5-"Net Profits"
B5=B3-B4

What's with the tildes? They're how the WorkSlate's extended character set is implemented: paired tildes act like escape/de-escape sequences, where characters between them are interpreted as symbols. Consider this constructed sheet (I'll get to how I actually got it in the system in a second), where we escape all seven-bit printable ASCII characters in turn:

\S A3 A1 A1  A E c 
\M 0
\N "CharSet "
\W 40 
A1-"~ !"#$%&'()*+,-./0123456789:;<=>?~"
A2-"~@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_~"
A3-"~`abcdefghijklmnopqrstuvwxyz{|}~~"

Uploaded to the WorkSlate, it renders like this:

Most of the second and third row are not defined as anything except for the very last character (RUBOUT isn't printable), where a tilde immediately followed by a tilde is treated as a real tilde. Instead, almost all of the special symbols appear in the first row, where we see little icons, some control character representations and line drawing and graphics characters. Some of these characters can't be entered into a spreadsheet any other way than by sneaking them in here. If you trace along the list, you'll see that ~$~ is how we render a WorkSlate × on a system that may or may not have that character.

I haven't worked out all the pieces yet for formulas, but we can get close. This is a 10-cell spreadsheet (A1 to B5), so we start out with 144 + 20 = 164 bytes baseline usage. We add (2 + 5) + 8 + (2 + 13) + (2 + 12) + (2 + 14) + (2 + 10) = 236 bytes for all the strings and the single number in B1. To save memory and speed execution time, formulas are stored tokenized. My current theory is that each formula appears to have a minimum usage of 8 bytes, plus 7 bytes for every number and 2 bytes for every cell reference, and then one additional byte for every token (built-in functions, parentheses, operators, etc.). That means B3 and B5 are 8 + 2 + 1 + 2 = 13 bytes each and B2 and B4 are 8 + 7 + 1 + 2 = 18 bytes each. 236 + 18 + 13 + 18 + 13 = ... uh, 298 bytes. I'm not sure where the extra byte went. Fortunately the exact memory value doesn't really matter for what we're doing here as long as we give the machine something it can parse as a positive integer, with a sufficient fudge factor so it doesn't waste time loading something obviously too big.

We can use picocom to directly receive to a file by adding something like --receive-cmd "ascii-xfr -rnv", which will show progress, prevent line feeds from being mucked with and capture all the control characters for faithful transmission back. However, to get the receive operation to terminate, you'll need to manually send a ^Z from the Terminal which you can do by pressing Special-C, releasing them, then Z. Doing so doesn't cause any problems because ^Z will never be part of a spreadsheet transmission. Special-C works the same for all other control characters.

This format is completely different from Microsoft Multiplan's, by the way, which is pretty clear evidence that while the WorkSlate was (strongly, in some cases) influenced by Multiplan it is definitely not descended from it. In particular, it bears almost no similarity to Multiplan's SYLK format, and cannot read a sheet rendered as such.

Now, how about sending something back to the WorkSlate? You can overwrite any of the spreadsheets in memory, even the generated ones, but actually transmitting data properly turned out to be trickier than I thought. The problem here is flow control. I said that all the signalling was in-band, and I could find no activity on the other lines, so XON/XOFF software flow-control is all you have. If you're connected with the 300bps modem, this is no problem, because the 6303 is more than fast enough to receive and process every byte as it comes. The CommPort was a rare accessory, so it's reasonable to assume most of its contemporary users were doing this sort of thing over a phone line, and that's the speed they'd be at.

At 9600bps, though, it's a problem. It was a problem for the Canon Cat to receive and reliably process every character into the document as it arrived at that speed, and the Cat is a 5MHz 68000, so a 1.2288MHz 6800 (even an enhanced one) has no chance whatsoever because there's no ACIA or UART and everything here is bitbanged. On top of that, since there is no hardware flow control in the WorkSlate and XON/XOFF signalling is in-band, the buffering we take for granted with modern serial interfaces is really going to ruin your day. Usually by the time my code saw the XOFF, the WorkSlate was already dropping characters and the receive would fail for all but the most trivial transmissions. (I should note that the CommPort does have hardware flow control, but it does it in "software" — i.e., one of its 6301s is dedicated to the task and bitbangs the flow control lines; there's no UART or ACIA inside it either.)

With the Canon Cat, we solved this with a custom file sender tool that introduces an intercharacter or intercharactergroup (?) delay. We can use that here too and we'll use a variation of it later on, but ascii-xfr has a similar delay option, so we might as well just use that for consistency with picocom. After lots of fiddling, the fastest reliable transmission rate seems to be with a 3 millisecond intercharacter delay; counting start and stop bits (it's 8N1), we get about 1.042 milliseconds per character at 9600bps, increasing latency to 4.042 milliseconds per character for an effective speed of 2474bps. Why not just transmit at 2400bps? Because the internal serial port expects 9600bps, and that can't be changed, so there.

Other than overruns, there are two minor bugs when sending sheets to the WorkSlate. The first is that it doesn't seem to correctly display the new name of the sheet when receiving it (i.e., the \N field), even though the new name appears correctly after the transmission successfully completes. The other one, to be relevant to the third program example, is that upon its selection in the Terminal the receive option sends two DC4 characters (ASCII 20, ^T) and then waits for you to select the destination sheet. DC4 historically would signal teletypes to turn on the punch tape reader, so again this is a logical signal byte to send. However, while in theory this should alert the remote system to have a sheet ready to go, in practice you have no idea when the transfer will actually begin because the user could take any amount of time deciding where to put the received data. Even more strangely, if the remote side sends DC2 DC2 like the start of a spreadsheet while the Terminal is active, the Terminal will automatically interpret this as a Receive request and immediately ask you to select the destination sheet, but it will not send DC4 DC4 in response. That oversight seems like a bug, though admittedly another one most people wouldn't have cared about at the time. I'm just not "most people."

Nevertheless, with all that in mind, our command line for the next couple examples will be picocom -b9600 /path/to/your/serial/port --send-cmd "ascii-xfr -snv -c3" --receive-cmd "ascii-xfr -rnv" and you can adjust that for your own terminal program such as minicom, etc.

Let's bring on the programs. Everything here is in this Github project and the WorkSlate WorkSpace.

Hack No. 1: Rock, Paper, Scissors

We did Rock Paper Scissors for COMP-X, the first game I know of written for the Tandy Pocket Computer PC-5/6 Assembler mode, so on this occasion of what I believe to be the first game for WorkSlate we'll do the same. So that you could potentially play this while the Calculator or Terminal are open, I made it just four rows high and best out of three.

To make our programming tasks a little easier we're going to write a simple one-pass "cross-assembler" (a pre-processor, really) that will take a soup of cells, organize them and then emit the proper headers and control characters. It will also let us comment and annotate. As usual, the "WorkSlate Spreadsheet Crossassembler" (henceforth WSSC) is written in Perl primarily to annoy people. Pass it an assembler source file as an argument or on standard input and it will emit the ready-to-upload sheet on standard output. It accepts cells written in the same way we've seen above, but cell width can be optionally added to any cell with a dot (like B1.13-"hello world"). The cross-assembler will keep track of what cells were what width and emit the proper \W header (or stop with an error if there are inconsistencies, since width is per column). You can attach attributes like justification or number format to a cell in the same way we saw above (e.g., a space followed by R for right justification). Pseudo-ops manage starting cell, default column widths and worksheet name, and lines can be commented out by starting them with #.

There is no documentation out there anywhere on the WorkSlate's unusual set of built-in functions other than the infrequently encountered (and maddeningly incomplete) Reference Guide, so I've taken it upon myself to create the WorkSlate WorkSpace as a supplemental resource. You can refer to it for more details on some of the functions we'll use here and in the next two hacks.

This and the next hack will depend, completely in the second hack's case, on the If() conditional function. This function is almost exactly the same as in Multiplan, and even modern Excel: given three parameters, it returns the second parameter if the first is true, or the third parameter if it's false. Also like Multiplan, you can combine a list of conditions with And() and Or(), invert them with Not(), and chain If()s together by using another If() as one of the arguments. Note, however, that the WorkSlate uses plain tokens True and False instead of Multiplan's functions True() and False().

The WorkSlate also has Multiplan's same limitation on string comparisons: there are none. This is very clear in Multiplan's documentation but not in the WorkSlate's. In particular, we can't do moves in this game by entering Rock, Paper, Scissors, or even the letters R, P, and S, because while If(A1="P","Paper","Not Paper") doesn't result in an error (and I do consider that a bug), it doesn't ever evaluate as true. Comparing ASCII values doesn't work either, for that matter, so the moves you will enter must be numeric.

Since this is a Turing-incomplete spreadsheet, it can't "wait" for your answer because that would be an unbounded loop, but part of the game is to reveal the computer's move only after you do and we want (at least to make it look like) it's doing it one at a time. The IsNA() function tests if the cell is not a number ("NA" is short for "not available for calculation"), which also evaluates to true for blank cells, so if our moves are in column A and the computer's are in column B, we'll have B1 be B1=If(IsNA(A1),"",F4) to wait for a valid number in A1 before revealing our computed move from F4.

How do we compute those moves? Ordinarily I would reach for a simple pseudorandom number generator like Xorshift to generate it, but we have no bit math operations at all in the WorkSlate, just basic arithmetic and some statistical functions. In particular, that means we have no exclusive-OR. Off-screen columns F, G and H take a seed value in E1 and compute out really pseudorandom numbers from it. The sucky-pseudo-pseudorandom number function I made up for this demonstration is excruciatingly bad, sufficient to give D. J. Bernstein a really nasty twitch and just enough to basically work for some four digit seeds (provable improvements solicited — be prepared to show distributions). Since the computer's moves are supposed to be random anyway, we just precompute them upon a change in seed value, and reveal them one by one as the player column cells start having valid digits in them.

Computing the winner ran into another problem. The numbers are set so that higher numbers beat lower numbers, but paper covers rock, so we check for that specifically. On each turn the player gets a score of 1 for winning, 0 for a tie or -1 for losing. Unfortunately, a perfectly reasonable single-cell formula for this like C1=If(IsNA(A1),"",If(And(A1=0,B1=2),1,If(And(A1=2,B1=0),-1,If(A1>B1,1,If(B1>A1,-1,0))))) is too big for the WorkSlate, even though it fits in 128 characters. The limit on formula length, again experimentally determined, seems to be about 108 characters. Instead, we calculate the second set of conditions (which one is greater) in off-screen column I and do the check for paper-rock in column C, using the value in I if not.

To make a somewhat responsive interface, I ended up scattering IsNA() tests all over the place as signals. As long as any one of the three player spaces (A1-A3) are incomplete, then there will be no score in column C for that row, and there will be a blank cell in I4. Once all three moves are made, though, we sum column C to I4 and report a winner, replacing the instruction text, and directing the player to clear column A to play again. For example, D3's formula is D3=If(IsNA(I4),"0=PAPER",If(I4>0,"YOU WON",If(I4=0,"WE TIED","I WON"))), which replaces that part of the instructions with the winner.

Nothing prevents you from entering bogus values or playing in the wrong column or playing out of order, because we don't control the interface. Winners don't cheat (or do drugs, for that matter). If you don't want to play nice, then the only person you're hurting is yourself. You don't want to hurt yourself, do you?

Here's a video. The WSSC source is in Github, with a ready-to-upload sheet. We demonstrate a full game from start to finish, ending in clearing column A as a player would. Notice the recalculation progress indicator in the status row when we enter our moves, showing the number of cells yet to be evaluated counting down as computations complete.

Hack No. 2: Pie Charts on the WorkSlate

Remember those bar charts I showed you way, way back at the beginning? Good times, but they aren't enough. If we're going to have business cards with watermarks, then by golly we'll have to have better charts here in the 1980s office or heads will roll. Literally, perhaps. Plus, would Stringfellow Hawke put up with this from his nav computer?

What I really wanted to do here was figure out a means of primitive graphics. The firmware does not support treating the screen as a bitmap, so everything would need to be character cells. If we set up a matrix where every column in the, cough, "frame buffer" is one, gurgle, "pixel" (the rows are already), then by assigning a formula to every single cell we can draw something at the highest, urgh, "resolution" the WorkSlate firmware will permit. That's not outrageous, or at least not by my standards of outrage.

Still, you should have already guessed the pitfall here: memory usage. My first attempt was to draw a Mandelbrot set off a set of coordinates. The typical algorithm requires you to iterate on each pixel until you can decide if it's actually in the set or not, or you've exceeded an upper bound on number of times through the loop. Pixels that are not part of the set typically get a colour based on how many runs it took to figure that out, but the set itself is black, so it can be drawn in monochrome. Experimentally it took at least six iterations to yield something that looked like the set should look on a screen this blocky, and much below 14x10 (yes, really) it became unrecognizeable, so that was the lower bound on detail.

By also reducing the, er, "colour depth" (i.e., the extent of our graphics character palette) we didn't need to save the result of every computation and it was possible to collapse the six iterations into a collective double iteration, a test, a triple iteration and then a second test. I modeled this in Perl and got a (barely) credible result:

   ......8..  
  ......88... 
 ......88888..
 ...88888888..
 ..888888888..
888888888888..
 ..888888888..
 ...88888888..
 ......88888..
  ......88... 

So I wrote a script to spit out all the formulas as WSSC source. Unfortunately, this reduction still generated huge formulas for every displayed cell, and since we have limits on formula length, it ended up soaking up nine off-screen cells for each of the 140 on-screen "pixels" (A1, A2, B15 and B16 contain part of the coordinates and some pre-calculated ratios):

D51=A1+(B15~$~2)
D52=A2+(B16~$~5)
D53=((((((D51~$~D51)-(D52~$~D52))+D51)~$~(((D51~$~D51)-(D52~$~D52))+D51))-((2~$~D51~$~D52+D52)~$~(2~$~D51~$~D52+D52)))+D51)
D54=(2~$~(((D51~$~D51)-(D52~$~D52))+D51)~$~(2~$~D51~$~D52+D52)+D52)
D55=((((D51~$~D51)-(D52~$~D52))+D51)~$~(((D51~$~D51)-(D52~$~D52))+D51))+((2~$~D51~$~D52+D52)~$~(2~$~D51~$~D52+D52))
D56=((2~$~D53~$~D54+D52)~$~(2~$~D53~$~D54+D52))+D51
D57=((((D53~$~D53)-(D54~$~D54))+D51)~$~(((D53~$~D53)-(D54~$~D54))+D51))-D56
D58=(2~$~(((D53~$~D53)-(D54~$~D54))+D51)~$~(2~$~D53~$~D54+D52)+D52)
D59=(((((D57~$~D57)-(D58~$~D58))+D51)~$~(((D57~$~D57)-(D58~$~D58))+D51))+((2~$~D57~$~D58+D52)~$~(2~$~D57~$~D58+D52)))

Notice that this doesn't include actually setting the pixel itself — this is just the computations to determine what the pixel should be. Incredibly some tests with a couple runs of pixel math all parsed but when I tried to load the whole thing the WorkSlate completely ran out of memory about halfway through. Given we were already scraping the bottom in terms of rendering quality, that was the end of that.

But the idea was sound, so I thought about something else we could implement where the necessary math for each pixel could be lighter. And what would be more appropriate for a spreadsheet machine than another graphing option? Enter ... the WorkSlate Pie Chart!

For simplicity we will assume a spherical cow simple two-value pie where they sum to 100% (viz., the percentage that is and isn't Pac-Man), so we can just work with a single value. Here, a percentage will be most convenient. The portion of the pie that should be painted can be made up of a sufficient number of lines emanating from the centre of the circle to touch the given percentage of the circumference. For example, if we provide the value 47%, then we will start at a point on the circumference of the pie chart and travel an arc covering 47% of that circumference, drawing radii to every point we reach and moving along the circumference at intervals sufficient to ensure painting all the pixels in the sector covered by that arc.

I wrote a script that does exactly this for every integer percentage between 1 and 100 with an adjustable radius, plotting a disc with its center at (radius, radius) so that we aren't dealing with negative coordinates. Arbitrarily we'll start the walk at (2r, r) and move clockwise. For each percentage value, we use a simple Bresenham's line algorithm to draw a virtual line and examine each of the cells it would pass through. What we want is to record the lowest percentage value that cell would be painted for. If subsequent lines touch that cell but it would already have been painted by an earlier value, we don't change it. At the end it spits out WSSC source that we can assemble.

A radius of six turned out to be a nice value and with rounding on cells this large yielded a 13x13 pie with an actual radius of about 6.6667, about as big as could reasonably fit onscreen anyway. Each pixel formula ended up looking like this (WSSC source):

C5.1=If(IsNA(A1),"",If(A1>=55,"~:~","~?~"))
C6.1=If(IsNA(A1),"",If(A1>=52,"~:~","~?~"))
C7.1=If(IsNA(A1),"",If(A1>=49,"~:~","~?~"))
C8.1=If(IsNA(A1),"",If(A1>=46,"~:~","~?~"))
C9.1=If(IsNA(A1),"",If(A1>=44,"~:~","~?~"))
D10.1=If(IsNA(A1),"",If(A1>=41,"~:~","~?~"))
D11.1=If(IsNA(A1),"",If(A1>=39,"~:~","~?~"))
D3.1=If(IsNA(A1),"",If(A1>=60,"~:~","~?~"))

Our percentage value is in A1. If it's not set, we display no pixel at all. If it is, then if it's over that cell's lowest critical value we display a filled cell and a non-filled cell otherwise (using tilde escapes). Because we only emit formulas for cells that could be part of a 100% pie, we don't waste any memory defining cells outside the disc. There are no other built-in functions involved — it's all If().

The last step is to pretty it up a bit, so I drew a little half box around the percentage indicator and added a prompt which disappears when a valid number is entered. There is also a field for you to title your fabulously autodrawn pie chart.

I also want to point out that on this totally clean machine with nothing else in RAM but the pie chart sheet, we're already using 51% of its free memory!

I thought about graphing 51% to make the point, but eh, here's a quarter.

And here's a video. The Perl generator is in Github, with ready-to-assemble WSSC source and a ready-to-upload sheet for radius "six" as displayed. We test 1%, 2%, 10%, 47%, 82%, 100%, 9999999999% and -3%.

Every time the pie is recalculated, you'll notice that the progress indicator always starts at 138. That's because there are exactly 138 cells in the circle, which is its precise on-screen area after rounding:

% perl -e 'print ((355/113)*((20/3)*(20/3)));print"\n"' # that is, (pi)r^2
139.626352015733
% grep -c -F 'If(' pie6.ws
138

And, indeed, every possible pixel is recomputed every time the percentage value is changed, a very direct measurement of the disc's area. QED.

Hack No. 3: The WorkSlate Surfs Gopherspace

I promised you bugs, and now you shall have bugs. Bugs you must live with. Bugs that make Naked Lunch look like Snoopy with writer's block. I'm not kidding, either: the bugs we've encountered up to this point were annoying, and some seemed just plain sloppy, but all of them were correctable or could be worked around. The bugs in this section, however, can cause data loss or even make the WorkSlate crash so hard it will think it lost power. The worst part is I can't find anything exploitable about them, so for the misery they cause they give us nothing in return.

In the slavering bestiary that is the WorkSlate's built-in functions inventory (again, see the WorkSlate WorkSpace for more detail), we've seen unusual functions like primitive bar graphers and phone dialers and alarm setters. But recall that Convergent was unusually sensitive to where the market was going because the products they were developing for their partners had to be relevant when they finally hit retail. The new age of the microcomputer inspired wild feats of fantasy about a pervasively connected future where we might read completely virtual articles on computer screens, transmitted on wires and hosted on distant servers (ha, like that would ever happen). An exec who could afford a WorkSlate probably also had enough dough, or worked for a corporation with enough dough, to have an account on a service like The Source or CompuServe, and these were the very people Convergent was trying to entice.

To that end Convergent added a whole class of built-in functions that handle serial communications. Among other things, you could set up a series of spreadsheet cells that dialed an external computer, sent credentials, waited for a command prompt, sent a command, pulled data and then hung up, jumping from cell to cell automatically. If you set an alarm on the first cell, it would even turn itself on and do it unattended overnight when connect charges for those services were substantially less. This kind of thing was exactly why EF Hutton immediately added it to their supported systems list for Huttonline, for example — out of the box, their subscribers could now use the system in the sci-fi super-connected manner everyone expected the new wave of computers to do.

Well, theoretically, anyway. Here's an example combining two real sessions from the manuals. Consider a system where you enter a login ID and password, and then at a command prompt, ask for a stock quote. On a typical dumb terminal, that session might look like this (totally plausible, mainframe systems like GEnie worked this way too), with what you would have typed in bold:

ENTER ID: JOE
ENTER PASSWORD: 793AZY702
! STOCK QUOTE

This string of ten cells would pull the results of the stock inquiry down for you into a sheet, assuming the remote system could speak in WorkSlate sheet format. CR is one of the WorkSlate graphic characters intended to visually represent a carriage return (there was also an LF for line feeds).

A1="Wake Up"+Alarm(9/5/2024,1:00am)+GoTo(A2)
A2="LOGIN"+Dial("5551212",Data)+GoTo(A3)
A3=Delay(2)+WaitFor("ENTER ID: ")+GoTo(A4)
A4=Send("JOECR")+GoTo(A5)
A5=WaitFor("ENTER PASSWORD: ")+GoTo(A6)
A6=Send("793AZY702CR")+GoTo(A7)
A7=WaitFor("! ")+GoTo(A8)
A8=Send("STOCK QUOTECR")+GoTo(A9)
A9=Keep(B1...C1,1,1,20)+GoTo(A10)
A10=HangUp(0)

The alarm in A1 would run this for you at 1am on September 5th, but you could run it anytime by starting the sequence from A2. You could even consolidate these into fewer cells; the + conjoins the functions, and then GoTo() jumps to the next. Now, you're screaming at your screen reading this completely virtual article that this is ludicrous because how could you prevent this script from running automatically every time the spreadsheet gets recalculated? Simple: it doesn't run automatically every time the spreadsheet gets recalculated.

All of the functions in this class, GoTo() included, display a little key icon next to them. This is another graphics character you can't enter directly from the keyboard. On those cells, to kick off a sequence, you press Special-Do It and then it goes down its list. Except for GoTo(), if there's no active connection, it will try to establish one (either with the Terminal or the modem, depending).

(Side note: does that mean you could use GoTo() to implement an infinite loop after all? Yes, but only a completely useless one. While execution of these functions is deferred until you give the start signal with Special-Do It, other calculations are not, which is how the string labels worked in the example above. In particular, something like the trivial circular reference example we had before

A1=GoTo(B1)+B1+1
B1=GoTo(A1)+A1+1

still immediately evaluates to 5 and 6 and gives you an error even before you hit Special-Do It. When you press Special-Do It, it loops, all right, back and forth between A1 and B1, but their values never change.)

I said "theoretically," and I mentioned bugs, so let's talk about our first one. The Keep() feature is designed to parse ASCII formatted text into cells. You pass it a range, the first line of data to start parsing, the first character position of data to start parsing, and the maximum length of each line. Go right ahead and try it. Even if you pass it data formatted as instructed in the manual's example, Keep() will faithfully import it, but then just sits there. I tried sending lots of permutations of every control character I could think of, even the ETX XON at the end of regular sheets, and Keep() just keeps on keepin' on until it times out (90 seconds), you press Cancel, or you get kicked off. If you're interactively logged onto a service and able to cancel when the transmission completes, maybe that was enough in the day, but it doesn't seem to work as described in the manual.

However, that's more obnoxious than serious. Here's the bad part: Keep() does terminate properly if you pass it sheet data instead of plain ASCII text. Unfortunately, it then proceeds to ignore the cell range you gave it and overwrite the entire sheet like you did a receive. But not all of it, by golly: it will filter formulas out in some misguided way of protecting you from mal-Taskware, I guess, meaning you can't even pass it the same formulas back to reinstate them. You'd better hope your beautifully written login script was written to tape because if it gets what looks like a sheet, that script and the rest of the current worksheet are toast.

Well, then, let's see what alternatives we have, and there's a really encouraging-looking one in the manual called SheetIn(). This takes a single dummy argument, officially zero. "Theoretically" (that's called foreshadowing, kids), it should be able to just automatically replace the sheet we're viewing with the sheet it receives. I wrote up a test program that sends almost the exact same sheet data twice, differing only in the string so that we can see we completed the transmission. The first time, we'll send it

A1=SheetIn(0)
A2-"Hello World"

and the second time

A1=SheetIn(0)
A2-"Yello Wyrld"

which will both compute out to the same dimensions and same memory usage. We're replacing nothing but the string and even that is the same length, so if any contrived setup can work, it should be this one. After opening the Terminal, we press Do It, signaling the test program to send the first set of cells after a delay. We then load into the current sheet using the regular Receive option, which works fine. So far so good.

Now we press Do It again in the Terminal to signal the test program to send the second version, again after a delay, and press Special-Do It in A1 to receive it with SheetIn() this time. The receive completes successfully, A2 changes to the new string — and the machine crashes. In fact, it crashes so hard that all data is lost! This is such an explosive bug I just have to show you a video of it:

To make sure this wasn't a hardware failure, I tried it on both my units, and they both crash in the same way.

What on earth happened? Without knowing the contents of the ROMs I can only theorize, but my best guess is that the action of loading the new sheet clears caches — including whatever internal representation of the formula the CPU was executing, causing it to either hit a bogus instruction directly ($00 is invalid, for example) or a bogus code pointer or token that leads it to a bogus instruction. An alternative theory is that the load process alters the processor stack, causing the CPU to hit a bad instruction returning from whatever routine handles SheetIn(). Like the 6502, the 6800, 6801 and 6803 don't trap undefined opcodes (something I had to handle manually when virtualizing the 6502), but the 6303 can, and has a specific vector for it.

Recall that during a normal power-down, the CPU checksums the ROM and stores this value in its internal RAM before signalling the tape gate array to put it in standby. Powering down the system would require the CPU to send an explicit signal to the tape gate array, so either the vector was rigged deliberately as a fail-safe on the assumption an illegal instruction could never occur, or it merely ends up doing so erroneously. Either way, because the system got powered down without properly checksumming the ROM to its internal RAM, when we power it on again the CPU sees the checksum is wrong and assumes power was lost. This causes it to execute the cold-start routine and all data is cleared.

This isn't a "killer POKE" because there's no (apparent?) harm done to the hardware, but it's still stupendously catastrophic. I suspect Convergent actually knew about this because while SheetIn() gets a brief mention in the Reference Guide it appears nowhere else in the documentation. Its exact converse SheetOut() does (which works fine), and our not-so-functional friend Keep() appears many places, but SheetIn() goes otherwise completely unacknowledged. Likely the problem was discovered too late in development to rewrite the firmware and they just decided to mostly pretend it didn't exist.

But we know the regular manual Receive option works and can load entire spreadsheets with formulas just the way we want them. This requires the user to do some manual work when receiving generated data from a remote system, but if we send the two DC2 characters while the Terminal is active, we can immediately put the user into the Receive option and they just have to pick the desired destination sheet from there. We won't know when they do, but we can warn them in the Terminal and implement a reasonable delay before starting the transmission.

So, to get the WorkSlate on the Internet, we'll create a proxy that turns Gopher menus into spreadsheets. No, stop laughing and hear me out. Since Gopher is line by line, that corresponds very well to rows, we can use the scrolling display to break up each line into full screen width cells, and we can use the parts of the serial communications package that do work to send commands to the proxy and wait for results. This isn't as clean as Keep() and SheetIn() would have been, but we'll get to keep our data, and we won't crash loading the very first site. To make the most of the WorkSlate's limited capacity, the proxy will manage history and keep an internal copy of the current menu so that the WorkSlate only has to transmit short codes for selection, and cap menus and documents to 128 lines and the 12,868 byte limit.

Since the Terminal is a critical part of this project (it's how we signal the user to start the Receive process), we'll need it to be open the whole time but we still want a decently sized content area to actually see the Gopher site we're visiting. Unlike the Calculator, the Terminal's on-screen footprint is adjustable from Options, SET UP, Terminal, Display and then select # Lines. For these screenshots we'll reduce it to four lines instead of the default seven.
We start the proxy running on the connected computer, and then enter the Terminal. The proxy sees the ENQ from the WorkSlate, sends a linefeed to acknowledge, and then displays a prompt. These prompts are how we synchronize the WorkSlate and the proxy.
When we press Do It, the proxy tells us transmission is imminent, and sends DC2 DC2 to the Terminal. This opens the Receive option automtically and we select the sheet we'll use. I think most users can handle pressing one button in five seconds. The transmission is sent and the WorkSlate itself indicates success in the status row.
The default menu is built-in to the proxy; you could call it your bookmarks if you like. I just hardcoded Floodgap's Gopher into it since obviously I use my own stuff mostly. Each row of the returned sheet starts with a single character column where the serial control formula sits (or nothing, for info rows), then a 39-column cell, then 40-column cells up to a 159 character row.

While waiting for a selection, the proxy goes into a loop where it sends a linefeed to the WorkSlate about once a second. This allows you to exit the Terminal and then immediately return to it, like for example if you were dealing in a document where you wanted more screen space to view it.

So that we don't have to insert a complete request on every single valid row, the proxy sorts the menu into lines and determines what to do for each line. Each active line (i.e., each selectable option) has a formula of the form =Send("###CRLF")+WaitFor("*") (the CRLF is represented in tilde notation as ~-*~, which the WorkSlate translates to a real $0d $0a), where ### is the line number. We can have no more than 128 lines, so three digits is sufficient.

When we're ready to pick a menu option, we go to it with the cursor diamond and press Special-Do It. The WorkSlate sends the number command and line ending and waits for an asterisk in response. This is a signal from the proxy that the menu is ready (or there was an error, which is turned into a menu). The proxy backspaces over it, prints a new message to the user to get ready to accept a new sheet, and sends DC2 DC2 to open the Receive option. The new sheet is sent after the usual five-second delay. The "Quit" option is implemented by sending a special line "000" which causes the proxy to gracefully terminate, leaving the Terminal open.
As of this writing the Floodgap root gopher menu turns into 94 lines, the progress of which is shown in the status row.
And here we are. We can navigate around the document with the cursor diamond ...
... including to the right for long rows, meaning we can read standard 80-column-formatted documents, no problem!
For item types that the proxy doesn't yet support, instead of a formula we'll just emit a × in that column as a marker. For obvious reasons we don't support images, HTML, PDFs, binaries, etc., but I couldn't think of a good way to support indexed search servers like Veronica-2 yet even though I really wanted to. I'll talk about that at the end.
At the top of every menu is a Quit option, and if this isn't the root, a Back option above that. If I ever make the default menu into a real bookmarks menu, it would make sense to put it in the history as well. The WorkSlate lets you jump to the top of a sheet with Shift-Up and to the bottom with Shift-Down, so I put those options at the top for easy access. Backing up is implemented with a history maintained by the proxy and sending special line number 999.
If you overflow a menu with too many rows, the last row is replaced with a × and an "Out of rows" message (the same applies to "Out of memory" when the proxy calculates the sheet will exceed the maximum bytes available). Again, Shift-Down will easily let you see if the Gopher menu or text file got truncated.
And here we'll just quit, because I want to show you a video of all this instead. This clip is about five minutes, longer than my usual quick takes (because it takes a bit to transmit all that data), so I had to put it on YouTube.

The Gopher proxy comes in two pieces: the Gopher proxy itself, written in Perl, and a C program that allows you to run the proxy over a given serial port and implements the intercharacter delay. This descends from usb2ppp in BURLAP, so it will work on the same systems and accepts the same arguments (port speed, path, program and arguments) even though the only supported speed connected directly to the GPIO port is 9600bps. You start it with something like ./serport /path/to/your/port 9600 perl ./gopher.pl and then start Terminal on the WorkSlate. A running log for debug purposes is written to standard error on the computer running the proxy. Since the proxy feeds the WorkSlate all the sheet data it needs, there's no bootstrap you need to load on the device itself.

There's a couple improvements I was mulling over, but this entry has taken too much time and gone way too long, so I'll ponder them later. The first is tabbed browsing (I said stop laughing). Since you can select which of the five sheets you want to load Gopher data into, you should be able to run multiple sessions. However, because the proxy maintains what it thinks the state of the current menu is, you can't load one menu in one sheet and another menu in another sheet because the proxy doesn't know which one is active. Solving this would require tracking a much larger history and adding more state information to the Send() string, maybe a hash key or something, but that would also make the sheet data bigger and reduce the memory available, though in practice I ended up exceeding the 128 row limit much more often than the 12K memory one.

The second is accepting input from the user, on a first pass to allow things like searching Veronica-2, but also to enter arbitrary hosts and selectors without having to modify the proxy or manually hunt for the right menu item. While SheetIn() exceeds uselessness verging on active harm, SheetOut() works just fine, so we could potentially send a very brief sheet with a form that the user fills out and then runs a formula to ship it back. The problem here is the Terminal: you can't directly type into cells while it's open. Either the user would have to modify the cell with Options-CHANGE or exit the Terminal and go back into it, neither catastrophic or particularly onerous, but also making an already clunky experience worse. I suppose the user could just type into the Terminal but the point is to make this work with the WorkSlate's native interface. It's a hack already, though, so I might implement input forms anyway just for fun in the future.

Why there wasn't a WorkSlate on every 1980s executive desk

By now you're probably wondering why every boardroom denizen didn't have one of these things back in the day. Certainly for those who did own one, they really seemed to like it. William H. "Bill" Millard, who launched IMS Associates (builders of the WarGames-famous IMSAI 8080) and ComputerLand, one of the earliest retail vendors of the WorkSlate, openly raved about his. In a May 1984 interview with InfoWorld, he said, "The spreadsheet is the most exciting product I have ever seen personally. ... Listen, I'm bananas about this WorkSlate. I carry it with me everywhere." He further elucidated: "It's no bigger than a three-ring binder, it's plastic, has a workable keyboard, 128 by 128 spreadsheet, memory, tape cassette, power backup, built-in modem. You can sit on the airplane and be doing spreadsheets. To me this is a blockbuster product."

Initial demand indeed got so high for the WorkSlate and its manufacturing costs were so sizeable that Convergent management soon started reconsidering the price point. The company announced a "WorkSlate System" package deal in early 1984 for $1495 (in 2024 dollars about $4500) with your choice of a MicroPrinter or CommPort, started selling both peripherals for $395 each ($1200) and increased the base price for the WorkSlate alone from $895 to $1195 ($3600). However, likely due to other more general-purpose portables like the TRS-80 Model 100 and allies, sales had already slowed markedly by then, and the new MSRPs eroded them further. By mid-year, while reviews still gave it solid marks for its portability, internal software and ease of use, the power-limited CPU and 16K RAM started becoming liabilities and the keyboard was never popular. Convergent marketing managed to make the WorkSlate "the official computer of the 1984 Democratic Party Convention" at the Moscone Center in San Francisco and donated 57 units, one for each delegation, but much like voters and Walter Mondale most of them didn't have any use for it. "The capacity is too limited for our office," complained the Oregon Democratic Party chair, and some delegations simply raffled theirs off for funds. The Washington state Democratic Party reportedly made $9000 from their own WorkSlate lottery.

Internally the company blamed Karen Toland, the manager who came up with the consumer sales idea in the first place, and she departed the same year. That spelled the end for any expansion of the "Ultra" line and the company pivoted internal development to Unix systems back along their vendor-first model. Convergent "paused" production of the WorkSlate in July 1984 and officially halted it outright by August, provoking a lawsuit from Oki Electronics who said Convergent had contracted to buy 253,000 circuit boards and only purchased 67,000 (making this figure the absolute upper bound on total machines manufactured). Oki further alleged that Convergent never paid for nearly $430,000 ($1.3m) worth of boards that did get shipped and complained they had thousands of custom boards in their warehouses they couldn't sell to anyone else. Convergent started marking down remainder units with deep discounts and even sold them directly to Comdex attendees from their booth for $399 ($1200). Reports from 1985 found WorkSlates in liquidation selling for $300 or less.

In the end, Convergent and Oki settled out of court and the company eventually took an $8.4 million ($25.2m) write-off on the product. If the estimate of $3 million in WorkSlate sales in the November 5, 1984 InfoWorld is accurate, then Convergent could have sold no more than a few thousand of them. Allen Michels left the company in 1985 and Convergent never sold another direct consumer product again. Disappointing sales of the AT&T 7300 UNIX PC which its Special Projects division developed caused further financial problems for the company: by 1986 AT&T had only sold 10,000 of the 50,000 systems Convergent produced, and the 7300's incompatibility with existing software ironically made AT&T's earlier 6300 PC (a rebadged Olivetti M24) a much better buy. While simultaneously upgrading the MegaFrame to the CTIX-based MightyFrame with newer 68020 and 68030 CPUs, Convergent returned to its x86 workstation roots with the 80386 Server PC which could run CTOS, CTIX and MS-DOS. Its most noteworthy technical achievement was being able to run CTIX and MS-DOS applications simultaneously using the 80386's virtual 8086 mode.

Unfortunately, neither of these products appealed to their traditional vendor base, who largely believed Convergent's era as a market driver had passed. Convergent tried to get into the networking space by acquiring 3Com in March 1986 for $135 million ($385m), but the deal was scuttled at the last minute by 3Com shareholders who protested that the price undervalued the company. Only Unisys, newly formed in 1986 when Convergent's old partner Burroughs bought out Sperry Rand, still regarded Convergent's technologies favourably and bought them out too as part of an expansion strategy in 1988 for $350 million ($930m). All of Convergent's former intellectual property, including the WorkSlate, remains with Unisys today.

Everything in this entry is on Github, and supplemental technical documentation is in the WorkSlate WorkSpace.

2 comments:

  1. Wow, now that was an epic article. Very in depth look at the history and inner workings of a forgotten machine. Some good humor sprinkled on top as well.

    I almost bought a WorkSlate back in '85 (I think?) when they were being liquidated for $300. Loved the look, big screen and portability. Even though I was okay with spreadsheets, when I found out that it didn't have a traditional programming language I bailed. I preferred Pascal at the time, although BASIC, Assembler or C would have worked at the time.

    I still like the look of it. Wish I had the time to make a look-alike with a Raspberry Pi, e-ink display, and a more traditional keyboard layout--but keep the round buttons and color, or maybe keys that look like the Sinclair QL?

    ReplyDelete
    Replies
    1. Hey, thanks! It was a fun article to write. I managed to land a whole bunch of additional WorkSlate stuff, so there's going to be a sequel when I finish analyzing it all. Spoiler alert: there *is* a way to load arbitrary code on the unit ... but it may only work from the tape deck. More later.

      Delete

Comments are subject to moderation. Be nice.


See more of my general vintage computing projects,
mostly microcomputers, 6502, PalmOS, 68K/Power Mac
and Unix workstations, but that's not all. Be kind, REWIND and PLAY.

Buy Me a Coffee at ko-fi.com

Old VCR is advertisement- and donation-funded, and what I get
goes to maintaining the hardware here at Floodgap.
I don't drink coffee, but the Mr Pibb doesn't buy itself. :-)
Thanks for reading. -- Cameron Kaiser