Saturday, November 11, 2023

The Apple Network Server's all-too-secret weapon (featuring PPC Toolbox)

Most of my systems are microcomputers (and commensurately sized), though I do have some moderately larger beasts: you've met homer, my 1987 HP 9000/350 rack system, and Floodgap is powered by uppsala, a 2U-in-a-tower IBM POWER6 520 running AIX. But my first "large" machine, and indeed the first Unix server I ever personally owned, was this Apple Network Server 500. Its name is stockholm.
A mini-fridge-sized server with its famous translucent blinkenlight-friendly front sliding door and oodles of drive trays, this $11,000+ box (almost $22,000 in 2023 dollars) sat forlorn and unused at the University I was employed with as an IT working stiff in 1997. The bookstore had bought it at a substantial academic discount for their UniVerse-based (a Pick descendant, now Rocket U2) point-of-sale system, but the vendor wouldn't support the hardware anymore after then-CEO Gil Amelio cancelled the ANS line, so it got dumped off as surplus in the service bay where it lurked in a corner.
As it was just sitting around, I got to use it as my personal server, shown here circa 1998 in my old office on a bad scan from a bad Polaroid. In this picture it's acting as a terminal server for my Commodore SX-64 with a CMD SwiftLink 6551 ACIA serial cartridge (the SX-64 is sitting on a parallel port switchbox because its handle got busted).

About a year later the University said they'd throw it in with my consultant compensation because they wanted to get rid of it anyway, so it became officially mine, and I was delighted to have it. That machine, later upgraded to 200MHz and 512MB of parity FPM RAM, variously powered my E-mail and the Floodgap gopher and webservers from 2000 to 2012, and still does backup duty when the POWER6 has to be down for repairs.

That's because the POWER6 runs everything the ANS did — because the ANS also runs AIX. The ANS 500 and 700 were not Apple's first Unix-specific servers (that would be the Apple Workgroup Server 95, a Quadra 950 using a special PDS card that only worked with A/UX, Apple's own Unix with a bolted-on Mac compatibility layer), but they were Apple's first Mac derivatives that could not boot classic Mac OS at all and natively ran a non-Apple operating system. Indeed, most people treated it as exactly that, a big Unix server from Apple, and at the time I did too.

However, there was a secret weapon hidden in ANS AIX most of us at the time never knew about. Built-in to the operating system was a fully Unix-native AppleTalk stack and support for receiving and sending Apple Events, surfaced in the form of Apple's disk administration tools and AppleShare. But Apple had a much more expansive vision for this feature: full server-client "symbiotic" applications that could do their number-crunching on the ANS and present the results on a desktop Mac. Using the Program-to-Program Communication Toolbox ("PPCToolbox"), and because AIX's throughput far exceeded anything the classic Mac OS ever could ever handle, an ANS could augment a whole bunch of Macs at once that didn't have to stop to do the work themselves.

Well, today we're going to write one of those "symbiotic" applications doing something this little Mystic Color Classic could never efficiently do itself — accessing and processing a JSON API over TLS 1.3 — and demonstrate not only how such an client application looked on the Mac side, but also how the server component worked on the AIX side. If you're lucky enough to have an ANS running AIX too, you can even compile and run it yourself. But before we do that, it might be a little instructive to talk about how the Apple Network Server came to run AIX in the first place.

IBM AIX is nearly as old as the Mac OS, originating for the IBM RT PC in 1986, though early versions also ran on PS/2 systems and System/370 and /390 mainframes. AIX started as an early port of UNIX System V and is thus a true Unix descendent, but also merged in some code from BSD 4.2 and 4.3. The RT PC was powered by the IBM 801, considered to be the first modern RISC design, and in 1985 IBM Austin embarked on the AMERICA project to develop a successor. In 1990 IBM introduced the first RISC System/6000 machines, the desktop 7012-320, deskside 7013-530 and server 7015-920, all using a POWER1 CPU at 20MHz to run the new AIX 3.x.

Over in Cupertino, Macintosh System 7 had just emerged in 1991 from shorter-term upgrades implemented by the Blue team, with longer-term ideas deferred to Pink, each famously named for the coloured index cards the concepts were written on. In July, industry rag front pages frothed breathlessly over IBM and Apple's partnership to turn Pink into a new object-oriented operating system, which would run on chips based on the RS/6000 POWER architecture via Motorola as a junior partner (yes, the AIM alliance was, at least initially, not the biggest news). This new architecture, of course, was what would become PowerPC. Apple promised a PowerPC-based Mac in less than three years (i.e., 1994) between $2000 and $3000, running Mac OS, A/UX and the "Pink OS" (what would become Taligent). At the same time, it would continue developing 68K Macs, and enhance the Mac OS using object-oriented features from the C++-based MacApp framework.
There were also wild ideas about their operating systems cross-pollinating each other. IBM incorporating AppleTalk seemed logical, but on the outlandish side Pink was supposed to be able to run OS/2 applications, likely on the questionable advice of IBM Grand Unified Theory of Systems insiders in their quest for a unified microkernel OS. (This particular concept eventually became the Workplace OS, culminating in the doomed OS/2 prototype for PowerPC that never ran on Apple hardware, and its cancellation in 1995.)
The other weird announcement at the time was IBM and Apple codeveloping a new version of AIX that incorporated the Macintosh Toolbox. If that sounded a lot like a new A/UX instead, you get a prize. In November 1991 Apple announced A/UX 3.0 and detailed their plans for A/UX 4.x, intended to use an OSF/1 Mach kernel running on PowerPC, and by employing the same ABI (application binary interface) the new operating system could incorporate AIX compatibility. While IBM and Apple would continue to sell products named A/UX and AIX, they would instead be rebrands and value-adds ("personalities") on top of the same core OS, internally named PowerOpen. (Note the QuickTime racecar video in this screenshot appropriately named "openpower.")
Subsequently, in late 1993 Apple made their first public announcement of a PowerPC Apple server. Up to this time the Workgroup Servers, produced by the Server Group under John Sculley, had all been rebadged desktop Macs with additional software and peripherals such as tape backup (or, in the AWS 95's case, its fast PDS cache-SCSI card). While Apple still planned to create such a server to run System 7.x, a higher end model would run PowerOpen A/UX 4 as a direct successor to the AWS 95. Early announcements like this one even claimed it would be Apple's first 64-bit system by using the new PowerPC 620. (The 620 was ultimately a disappointing performer and ended up eclipsed by the 604.)
Around this time Apple's choice of server operating system made a sudden detour into, unexpectedly, Novell NetWare. In 1989 Novell introduced Portable NetWare, a "userspace" version of their network operating system that instead ran as an application on other operating systems. It provided all the standard NetWare file, print and network management services to NetWare clients, but was written in portable C and could be compiled from source with minimal changes by an OEM. The first ports were for Prime's 80386 Unix systems as well as NCR's port to Motorola 68000 machines. In 1991, IBM jumped on board, porting it to the new RS/6000s under AIX.

Novell had previous dealings with Apple, including as partners on the 1992 Star Trek project intended to "boldly go where no Mac has gone before" by porting System 7.1 to x86 on top of DR-DOS. Team members Fred Monroe and Fred Huxham later went on to work on the Apple Interactive Television Box. Star Trek was technologically successful but widely derided by industry observers, and when Michael Spindler became CEO in 1993, he cancelled it as a potential threat to the looming PowerPC transition.

The first Power Mac was the PowerPC 601-based Piltdown Man, descended from the Cognac project's famous RISC LC prototype, which became the Power Macintosh 6100 in March 1994 along with its sibling 7100 and 8100 systems. These first 601s were fabricated on a 600nm process with 2.8 million transistors; the later 601+ was a process shrink to 500nm. The 6100 and 8100 were reworked into the Workgroup Server 6150 and 8150 respectively ("Starbucks"), running System 7 as promised, while the AWS 95 successor ("Green Giant") was forged by merging a similar case and slot layout with the fastest 80MHz 601 then available, along with its characteristic low-mounted floppy to accommodate the two 5.25" drive bays and a top-mounted CD-ROM.

Unfortunately for Apple, the new AIX-a/k/a-PowerOpen A/UX was nowhere near ready in late 1993, and Pink had metastasized by then into various Taligent platforms (OS/2 and AIX, and Apple's Copland) which weren't at all suitable either. Instead, Apple used their existing relationship with Novell to fit the Green Giant prototype with a PowerPC build of Portable NetWare, using some of IBM's porting work but running under System 7. Christened Wormhole, Mac Portable NetWare bombed with testers who were openly sceptical of the choice, and the Server Group launched Green Giant along with Starbucks in April 1994 as the Workgroup Server 9150 — running Mac OS. Nevertheless, Apple promised at the same time to port Portable NetWare's successor, Processor Independent NetWare, to the Power Macintosh, and Spindler vowed at least one system would support it.

But Apple had not forgotten about AIX. Although Gary Davidian's 68K emulator was intended to run the bulk of existing Mac software, there was no way it could efficiently run A/UX 3.x. The future was still (supposedly) the new AIX in whatever form that ended up taking, and native PowerPC apps thus used the same PowerOpen ABI as AIX in System 7 such that when (if) the new OS became available, PowerPC Mac applications would "just work." While all of the new PowerPC Workgroup Servers ran System 7.1.2 as shipped, Apple promised at rollout that both Starbucks and Green Giant would be able to run AIX 4.1 when it became available, and would continue to provide the AWS 95 for customers who wanted old school A/UX.

In the meantime, IBM had become impatient with Apple and determined that development delays were stunting the technological growth of the RS/6000 line. On top of that, Apple's headlong rush to market had fractured IBM's attempts to build a common PowerPC Reference Platform (PReP), leaving market-majority Power Mac hardware only able to run System 7, and market-minority IBM hardware only able to run everything but System 7 (including Windows NT and OS/2). To recover control IBM decided to unilaterally retake the reins.

In February 1994 IBM announced AIX 4, which would add symmetric multitasking to the kernel for the first time and was designed to run on anything from PowerPC desktop workstations to large POWER servers. Additionally, as the industry was moving en masse to Intel's new PCI standard, IBM also made the jump from MicroChannel to PCI, starting with the Type 7020 series running the PowerPC 601 in October 1994. While MicroChannel would remain supported in AIX and PReP, PCI was the future, and IBM wouldn't support NuBus. Apple followed suit and announced they would move to PCI as well (not without controversy; an InfoWorld columnist in October 1994 argued that compared to NuBus "PCI is a cleverly disguised trap [by Intel] and Apple is walking right into it"), introducing the "Tsunami" Power Macintosh 9500 as the first PCI Mac in June 1995. It came with the new high-performance PowerPC 604 up to 150MHz, fabricated on a 500nm process with 3.6 million transistors.

While desktop users were delighted, Apple's enterprise customers started grumbling even louder. Apple still had open promises to them about AIX, Taligent (as CommonPoint, shown here in a beta screenshot) was furthest along on AIX and Apple still had an arrangement for AIX, but neither Copland nor new-A/UX were happening, and IBM made clear that AIX on NuBus wasn't going to happen either. At this point Apple simply abandoned PowerOpen and concluded it would never ship as such: if this mythical high-end Apple server was going to run AIX at all, it would have to run it straight up. There was no way to magically turn Green Giant into a PCI box, and phoning in a "Workgroup Server 9550" (no such model ever existed, for the record) would cause a buyer revolt, so the Server Group took Tsunami back to the shop and reworked it instead.
This task was primarily done in Austin, where the new machine picked up its codename "Shiner" after the developers' favourite brand of beer, along with additional work at DeAnza 3 in Cupertino. Shiner's on-board hardware was reorganized to boot AIX directly and Apple contracted design firm Lunar to design its unique chassis with hot-swappable drives, fans and redundant power supplies, a backlit status LCD, a sliding drawer for the logic board, and a front locking translucent door. The top image is my 500 in operation; the bottom was the bare chassis when I had to strip it down a number of years ago to figure out a hardware problem. It's big, beefy and beautiful.

Unfortunately, other glitches delayed Shiner's release. As AIX was SMP-capable, Apple planned to offer Shiner with multiple processors, but the original 604 was found to have a serious hardware bug that impaired cache coherency when more than two processors were present. While this didn't affect designs like DayStar Digital's asymmetric multi-processor Power Macs, it seriously affected IBM, Groupe Bull and Zenith, who retreated to the 601, and stalled out Shiner. Motorola did not start sampling a fixed CPU until October 1995.

However, the software turned out to be a far bigger problem. Spindler's ill-advised promises around Processor Independent NetWare bloated the development schedule, as PIN was an actual operating system the machine had to boot instead of userspace server daemons like Portable NetWare was, and the arrangement wasn't cancelled until late in 1995 when Novell decided to develop for the new CHRP PowerPC machines instead. (This was the Cygnus port of NetWare to PowerPC, and ended up little used, since it never ran on Macs. Novell abandoned PIN completely with NetWare 5 in 1998.) [UPDATE: However, an early alpha actually existed of PIN on the Mac, codenamed Cyberpunk, and allegedly did run on prototype Shiners. I got a copy.]

After that, AIX remained the leading choice for Shiner, but IBM and Apple continued to fight over how Macintosh applications would work under Shiner's specific flavour of AIX, codenamed "Harpoon." While Apple's existing Macintosh Application Environment (MAE) was well known for running 68K Mac applications in emulation under Solaris and HP/UX, its absence on AIX had long been considered conspicuous. As promised, an early prototype environment called Macintosh Application Services for AIX (MAS) could run some Power Mac applications natively on Harpoon using a ported Toolbox and system libraries — keep that fun fact in the back of your head for a moment — but Apple could not sustain the development of MAS and cancelled it in March 1995. Meanwhile, Apple internal sources reported in August that neither Apple nor IBM were willing to pay the development costs even for a straightforward MAE port. As a trial balloon Apple ended up developing a version of Mac OS specifically for Shiner and actually demonstrated it, but the great confusion it elicited in potential buyers caused Apple to scrap it before the machine's début. AIX was the last OS standing, so Shiner ran AIX applications, and nothing but.

Eventually Shiner prototypes made it to testers — I have a non-functional EVT ANS prototype that used to be at Netscape — and Apple announced it in two flavours, the "low end" Shiner LE (minus extra bays and with only a single power supply) that became the ANS 500 and the canonical "high end" Shiner HE that became the ANS 700. (I have an ANS 700 as well, which is quite logically named holmstock, but the 500 is the system in normal operation and the one we'll demonstrate here.) Prices for the configurations ranged from $10,969 for a low-end (ahem) 500/132 with 32MB of RAM (about $21,000 in 2023) to $14,999 for a 700/150 with 48MB of RAM (about $30,000), plus the two-user AIX license adding another $1,498 ($3,000), and $1,399 ($2,800) to upgrade that license to unlimited users if you needed it. Alongside the full-size machines Apple developed a rackmount prototype called Deep Dish intended to become the ANS 300, but decided not to release it, even though I know of at least two that still exist and casually drive by their houses a lot.

The ANS 500 and 700 finally reached market in February 1996 as one of the first new products under Gil Amelio, who replaced Spindler that month after his removal by the Apple board of directors. The systems got very positive reviews in the press, but their exorbitant price tags did not go unnoticed — a story we'll pick up at the end.

I gave you all that history to point out that RS/6000 AIX and the classic Mac OS, at least for a period of time, ended up influencing each other in surprising ways (just wait until we get to what snuck into IBM's C compiler), and that there was also some period of time in which AIX was going to be the next Mac OS. The history helps us understand it's no accident they use the same ABI for registers and calling conventions, slightly different from the PowerPC SysV ABI used by operating systems like Linux and the BSDs, and support the same XCOFF objects, at least at first (MPW used it as an intermediary in early versions). Although XCOFF was gradually obsoleted in MacOS as a binary format, New World Macs can still boot XCOFF from Open Firmware, and the PowerOpen ABI persisted into the Mac OS X era through the very last day Leopard on PowerPC was supported. This sort of low-level integration would not have made any sense, even as a colossal coincidence, unless both AIX and Mac OS were intended to have the same underlying foundation, and that observation makes it much less strange that the ANS would have ended up running it.

ANS AIX runs virtually everything that IBM AIX can run with the exception of certain device drivers and stuff that depends on MicroChannel — it ran all the binaries the local 3.2.5 systems had at the University without recompiling them, for example — but the reverse may not be true, even for userland applications, if those applications take advantage of what Apple added to it. Indeed, the work that went into making a Macintosh Toolbox that could run on AIX, and porting core Mac OS libraries to also run natively on AIX, did not go entirely wasted. Aside from Mac-specific hardware support like ADB, three specific portions ultimately shipped on every Harpoon CD.

The first portion was AppleTalk. For those unfamiliar with AIX, this is smit on the ANS in TTY mode ("smit happens," as we say in the biz), the System Management Interface Tool used as AIX's central configuration interface. It comes in both this text version for terminals or in a native X11 version that's point-and-click to at least some degree. Apple created an entire menu within SMIT to support AppleTalk so that AIX administrators could manage it in the fashion to which they were already accustomed. Here we're on the ANS' console, which AIX calls the Low Function Terminal or LFT; I took these screenshots from the ANS's VGA port using an INOGENI VGA2USB3, cropping and slightly correcting the aspect ratio.

The implementation of AppleTalk in ANS AIX was mostly complete, including support for NBP (AppleTalk Name Binding Protocol), AFP (AppleTalk Filing Protocol, including modern AFP-over-TCP in Harpoon 4.1.5), and ADSP (the AppleTalk Data Stream Protocol) among others. Daemons provided AppleTalk routing and printing services, though a glaring deficiency was direct support for AppleShare, which required a third-party package. Apple included two demonstration disks with the ANS, IPT uShare and Helios EtherShare.

Notably, the AppleTalk version in Harpoon 4.1.5 is given as 2.0. The AppleTalk stack could run on any port (here on et1, the 10/100 PCI card connected to the secured wired network) or route between multiple ports. Besides the daemons, Apple included an AIX AppleTalk library applications could link with, as well as a subset of Apple's Universal Interface headers tweaked for AIX.
The other two portions were a full send-and-receive implementation of Apple events, also implemented as a native AIX library with headers, and running on top of that an AIX native version of the Program-to-Program Communications Toolbox called ppcd. In this article we'll make use of all three.

The PPC Toolbox, which has nothing to do with PowerPC and works on 68K Macs as well, was introduced with System 7.0 and allows applications to exchange messages with other applications, either on the same Mac or across the network. To this end it runs on top of Apple events and AppleTalk (the actual transport) and provides a browser to find available systems and willing applications running there, plus services for authenticating to those remote systems.

PPC Toolbox's primary purpose on the Mac itself was as the underlying mechanism for program linking, an unfortunate term, since this feature was distinct from simply running an application present on a shared volume. When program linking was active (from the Sharing Setup control panel), it was possible to receive Apple events from another Mac to control applications running on your Mac. When a PPC Toolbox-aware application is running and registered, it becomes visible to other Macs when they ask through their local PPC Toolbox browser about what network services are available. WebSTAR was one such example, where the WebSTAR webserver was often running on a separate machine, and when the WebSTAR Admin application started up it asked on the network what WebSTAR servers were present that it could control. As unrestricted remote access of that sort would pose an obvious security hole, the administrator had to first authenticate to the desired server and then could manipulate it remotely. Many power users wrote AppleScript to accomplish something similar, since AppleScript to this day is all about sending Apple events to applications, and the core concept is still part of modern macOS.

Harpoon ppcd expands on this notion, almost like an Apple event-aware inetd. It presents itself just like any other remote Mac and responds to queries with its list of available services; it's just that these services are actually entries in its configuration file, along with provision for more sophisticated daemons that can deal with clients directly. When a client Mac requests a service, it authenticates to the ANS just like it would authenticate to any other Mac, and then ppcd executes the desired service which exchanges data with the client over AppleTalk — just like any other Mac.

Some of you will have asked whether A/UX 3.x can do such a thing, and the answer is yes, but only in the same way another remote Mac can; i.e., there's no special A/UX Unix-side support like there is in ANS AIX, and the PPC Toolbox is only available on A/UX if you're running the A/UX Finder. The A/UX documentation makes that plain: "The A/UX Program-to-Program Communications (PPC) Toolbox is identical to the Macintosh OS PPC Toolbox." A/UX had its own style of hybrid applications that could offload work to a separate Unix process but those sorts of applications all ran locally. Only ANS AIX was capable of what we're going to show here.

Apple included several clients and servers with ANS AIX, mostly demos, but a couple of eminently useful ones. The Mac clients themselves were on a floppy disk that came in the ANS' accessory box, though fortunately if yours didn't come with one Apple also includes them under /usr/lpp/apple.remoteutils/clients. Irritatingly, not all of them provide source code.

To install and run them, only one extension is absolutely required: the Network Server Passwd Tool, which handles authentication to your ANS. Drop it into your Extensions folder and reboot, and when prompted by the client, enter your regular ANS AIX login and password. Note that your password is not transmitted encrypted — everything is cleartext — and thus you should only play around with these programs on a secured local network. In particular, do not run the ANS AppleTalk stack on an interface connected to a WAN. You don't know where it's been, so don't put it in your mouth.

I should also note before we continue that these clients don't appear to work properly with Mac OS 9 (nor Classic except possibly 8.6 under Rhapsody Blue Box), which added support for the PPC Toolbox to run over TCP/IP as well as AppleTalk. Harpoon's implementation only supports AppleTalk and interacting with it seemed to unsettle my OS 9 machines, so you should run these clients on Mac OS 8.6 or earlier, or A/UX 3.x.

Let's demonstrate how this works in practice by going back to our little compact friend. While our usual 68K workhorse is my hard-truckin' clockchipped Quadra 800, we're going to constrain things a bit more with this Mystic Colour Classic. Now, this is still a fairly beefy 68K Mac, since a Mystic is actually a Colour Classic with an LC 575 logic board swapped in, and this one was further upgraded with a 25MHz full '040 and 64MB of RAM (though mostly wasted right now due to 24-bit addressing for its Apple IIe Card).
But I picked this one specifically because it has no Ethernet connection: everything we'll do on it is over LocalTalk, where AppleTalk is its raison d'être. I'll have up the Network control panel so that you can see I'm not cheating with MacIP, and I've chosen to take actual photographs of the screen instead of screenshots (except in one specific case) to prove there's nothing up my blogger sleeve. To allow the Mystic to actually see the ANS, the LocalTalk segment is bridged to EtherTalk by a Dayna EtherPrint-T, though things like a Shiva FastPath, GatorBox or even a software LocalTalk bridge will also work for this purpose.
Of the two actually useful client-server services Apple included, probably the most useful is the CommandShell. This one requires the included CommandShell VT102 extension to be installed, but you might have it installed already as this is the same one many other applications use, and it's not required at all on A/UX because it already has it for A/UX's own command line.

When we start the Command Shell, the PPC Browser window pops up first and prompts us to "Select a CommandShell server" (this message is configurable). A list of running Macs or suitable Mac substitutes participating in PPC is populated in the upper left corner, and if you had any AppleTalk zones to choose between (I don't), you'd see them on the lower right. We see two here, the Colour Classic itself ("CC") and stockholm.

When we choose stockholm, a list of services that answer to the desired signature (CmdS, an OSType-style FourCC) appear. There is just one that ppcd offers, the CommandShell service itself.
We select that and are prompted for our AIX login and password.
And, lo and behold, a terminal window pops up. But this connection isn't over Telnet or even traditional serial: it's ADSP, using the pseudo-hostname cmdshell. You can even open multiple connections, which ppcd will multiplex.
A less practical, but actually rather more impressive, demonstration of what this mechanism is capable of is the Fractal Demo. Some of you on the other side of the pond may remember the Mandelbrot Construction Set (auf Deutsch) from 64'er October 1991, which computed the Mandelbrot fractal set on a Commodore 64 while using the 6502 in a connected Commodore 1541 disk drive as a coprocessor. But the C64's 6510 and the 1541's 6502 ran at approximately the same clock speed (1MHz), whereas here we're going to shunt the work from a 25MHz 68040 to a 200MHz 604e.
For these shots I've switched to screen grabs so you can see the generated image at full quality. The Colour Classic ordinarily has a 512x384 screen (the Apple IIe modes notwithstanding) but the application will generate at nearly any window size or one of the prespecified ones. You can also adjust the lookahead and the square size of each transaction from the server.
When a screen is rendered, you can zoom in by drawing a box around it, and the ANS will send back a zoomed view.
This particular client-server pair has full source code for both ends. It seems to date from rather early in development: the Mandelbrot generation code is credited to Mark Maxham at the Apple Advanced Technology Group, dated February 1992, while the rest of the server is credited to Scott Mulligan and dated 1993 and 1994, though the actual file modification times are 1995 and 1997 (the latter from the 4.1.5 upgrade).

Once the server gets a request from the Mac client, it confirms that both the client and server are speaking a compatible protocol version (there was only ever one publicly released), and then receives a request specifying the square size, which square, and over what interval, to which it replies with a byte array of graphic data corresponding to the requested square. Zoom requests are received as new intervals. These are all structured Apple events, even the array of bytes, which is simply transmitted as a block of raw data within the Apple event reply. A system of periodic heartbeat events roughly every 30 seconds lets the two sides know that the other is still present.

To give you an idea how quickly this scheme could operate, even over LocalTalk, here's a realtime (not time-lapse) video of the process. Initially we use the smallest square size, but at this size the overhead of handling the Apple events dominates compared to the amount of data actually generated, and the process is relatively slow. When we jump to the largest square size, however, we generate a much larger set per transaction and the window fills up in seconds. Let's hear it for making someone else do the work!

This demonstration also points out something important about the architecture. The classic Mac OS, as beloved as I find its interface, is a well-known dog's breakfast under the hood with little provision for memory protection and relying on MultiFinder-era cooperative multitasking. Even in the era of the 8.6 nanokernel and Multiprocessing Services, where you could at least theoretically have preemptive tasks, those tasks still had to have a presence in the UI and that part of the system remained cooperative.

If we were doing the math locally, the calculations would have dragged the system down and impaired its ability to service its own interface effectively, let alone any other application running at the same time. But by deferring the calculations to the ANS, we've almost completely fixed this problem: the ANS does it faster, the ANS does it asynchronously (the system gives you a callback when the data arrives and until then you can just blissfully yield with WaitNextEvent, keeping the rest of the user interface responsive), and the ANS can service multiple clients because it does preemptively multitask, meaning your entire LAN is covered.

Remember, in the 1995 timeframe when all this was being developed, Copland (and therefore Taligent) was a Bataan death march and PowerOpen-A/UX 4-whatever was waiting for its death certificate, and while BeOS had SheepShaver, it was third-party software on a low-volume OS made by a quirky company best known for a strange blue machine. If you were willing to forgo Mac software compatibility completely, Windows NT and Solaris could run on PowerPC but not on Power Macs, the OS/2 port is best not mentioned, running stuff on/with NetWare was a cruel joke (and it was cooperatively multitasked, too), and if you dared suggest Apple could buy NeXT, remember that NeXTSTEP and OpenSTEP never ran on PowerPC until Rhapsody. Plus, Be and NeXT carried additional institutional baggage by having controversial ex-Apple execs at their helms.

Here, on the other hand, was a way you could solve the entire problem by just grafting libraries onto an existing operating system Apple already had an arrangement to use. When you think about the idea in that context, "symbiotic" applications like this one looked like an easy transitional solution to the classic Mac OS's deficiencies.

For the next couple demonstrations, having made the point on the Mystic, we'll move back to the Quadra so that we have a little more visual real estate.

The next, more practical use of this technology (and also lacking source code) is the Disk Management Utility. That's like it sounds: you can manipulate JFS logical volumes, physical disks and AIX volume groups natively from a controlling Mac. Naturally you'll have to authenticate as root (remember there's no encryption!), and once you do, there is a minor pause while the client gets the system information. It will not allow multiple administrators to connect simultaneously. This machine has only one partitioned disk, a full-height Fujitsu 18GB SCSI-2 drive which was quite large for the time, but the Utility could reputedly handle fairly complex topologies.
Given a particular disk, you could drill down to individual partitions and adjust their permissions and sizes or do other administrative tasks, including those on logical disks hosted on the supported Apple RAID solution. I have a long personal history with AIX, so even in 1998 I did all my admin tasks from the LFT console, but this would have been very attractive to Mac admins new to the operating system. Of course, the chief problem with this otherwise well-designed application is that it cast the fact you had to do literally everything else in smit into very sharp relief.

The copyright date in the About box is 1992, though I can't determine from this if that was for MacApp generally (that is, Apple's attempt at their own object-oriented framework for the classic Mac OS) or for the Utility itself. If the Utility actually hailed from 1995 as other services do, then it would have been a very late application even for C++ MacApp, as CodeWarrior's equivalent PowerPlant framework was already eating its lunch by then and Apple was itself using PowerPlant in some of its own software. Although MacApp 3.0 apparently had very good Apple event handling, our project for this article will use PowerPlant.

That brings us to our last demonstration, and the one we're going to hijack, because both client and server source code exists for it too. The server source code on 4.1.5 is credited to Chris Jalbert with a "1996-1997" copyright date, updated from the earlier 4.1.4.1 release of what was called javelind. It's purportedly a basic system monitoring application, but the concept is generalizeable to something much more than that.

There are actually two applications left, the Status-Who Demo ("trident") and the Status Demo ("javelin"), but the Status Demo is completely subsumed by the Status-Who Demo, so we'll show that as the client. You can choose the desired view from the File Menu, either the "who" view (which shows a multiline, automatically refreshed list of current users and their locations, much like /usr/bin/who), or the "uptime" view (which shows the time, number of users and system load, much like /usr/bin/uptime). Both can run at once. The Status Demo solely displays the "uptime" view.

For the "who" view, the signature will bring up a Login Demo service. (Notice the PPC Browser window asks you to "Select a Unix Server.") In this mode, the server pushes the current userlist to the client as an array of strings in an Apple event, updating that list if it sees that /etc/utmp has changed. On the client end, it idles, yielding to other apps, until it gets that Apple event with which it updates the text in the window.
However, the "uptime" view brings up two services with matching signatures. They appear to work the same, but there's an internal difference between them we'll get to when we discuss the backend.
The chief difference between the uptime view and the who-view — between the single line of text and the multiple lines, that is — is that you can specify the interval for the uptime view, whereas the who-view rate is determined by the server. Both can run at once, though you can have multiple uptime views, and the system will multiplex their events. The uptime view also maintains a history so you can scroll back through the load averages and see when your box was blowing up.

Let's have a look at the backend now.

Harpoon ppcd has its own SMIT menu where you can start and stop the daemon or modify its services. This can all be done from the command line, and you can just edit /etc/ppcd.conf and bounce the daemon to add or remove services, but I'll show you SMIT's interface to service configuration for completeness here.
I mentioned previously that there are two ways a service can be set up. As it happens, the two services we saw for the uptime view are the same daemon built both ways. The most complex, but also the most flexible, way is to register yourself with ppcd and open your own listening ports (specified by name and either a matching string or a type and creator; we use the latter to specify the OSType signature). This is the only way to have multiple port types registered to a single daemon, and both the who-view (creator "signature" is JVLN and type is who with a trailing space) and the "New Status View" for uptime (JVLN updt) are handled by this one, appropriately named /usr/bin/flexibled.

On the other hand, it also requires your daemon be running all the time when ppcd is, and ppcd in this configuration basically only acts as your bootstrap. So that you don't have to busywait, however, the select() call is expanded in Harpoon to not only terminate with time left for regular POSIX signals but also on Apple events (which you retrieve with WaitNextAppleEvent and dispatch with WaitNextAppleEvent to handlers you previously defined with AEInstallEventHandler), and additionally works with things like ADSP connections as well as more typical TCP/IP network sockets.

So where did the other Status View service answering to JVLN come from? It came from the other way we can set up a service.

The easier way to write a service is to let ppcd do the work of looking for events for the signature you're watching for and managing authentication. You can only specify one signature and you can only handle a single generic port type for it, but your daemon doesn't need to run until ppcd finds something you're looking for. At that point your daemon is started, you handle events and AppleTalk connections for as long as you want to, and then you terminate.

This screen shows the configuration for the Fractal Demo service. The service name is what is used in the port the Harpoon ppcd advertises, as well as the signature (here, Mndl is reported as the creator, with an implied wildcard for the port type in these simplified servers). Authentication is required if the guest connections without password are not allowed, or if you removed the guest user account, as I have since it was a production system. If you authenticate and you're in the list of privileged users, the specified daemon will be started with the uid and gid you provide as if it were setuid; if you're not, then it assumes yours. Once all that happens, then ppcd starts your daemon.

In most cases, a given Mac application's creator code and its PPC Toolbox signature were the same OSType FourCC. While AIX daemons wouldn't have a creator code, the general correspondence of PPC Toolbox signatures to creator codes meant you were also supposed to register your desired signature with Apple like you would for an application. Somehow I don't think Apple got a lot of requests for this. A generic signature of UNIX was always available and explicitly defined as free for all.

By default, there are five services listed in /etc/ppcd.conf.

CommandShell:0:NONE:/usr/sbin/cmdshld:CmdS:noguest:*
Disk Management Utility:0:NONE:/usr/sbin/diskmand:DSCS:guest:NONE
Status Demo:100:100:/usr/sbin/simpled:JVLN:guest:*
Fractal Demo:100:100:/usr/sbin/fractald:Mndl:guest:*
:::/usr/sbin/flexibled::guest:

You can see what the Fractal Demo SMIT screen corresponds to in the actual configuration file, including its port name, default uid and gid (always used, since the set of privileged users is an asterisk), path to the daemon, signature, whether guest is allowed, and the privileged user list (or an asterisk for everybody).

You can also see the simpler Status Demo (/usr/sbin/simpled, naturally), which responds to JVLN as well, but only can offer a single port type and its programmers chose the uptime view. flexibled, on the other hand, specifies no port name and no signature (or anything else for that matter), so ppcd always brings it up so that it can open its own ports and listen for its own Apple events. It responds to JVLN too, but that's because it itself created its own PPC Toolbox port for it. However, all of the other PPC services are "simplified," including the major ones like the Command Shell and Disk Management Utility.

The source code for the Status-Who and Status Demos is actually a single pile of source that's quadruple-headed: it can be built as either the flexible daemon (both who and uptime) or simple (just uptime), and either for AIX or for Mac OS (in which case most of the status updates are just dummy strings but the app allegedly works). We'll only deal with building it on AIX. Although I've placed a slightly tweaked version up on Github you can follow along with, here's the relevant portion from the original Makefile:

PROGRAMS        = flexibled simpled

FOBJS           = fobjs/tridentd.o fobjs/ppcstuff.o fobjs/client.o \
                        fobjs/status.o fobjs/handlers.o
SOBJS           = sobjs/tridentd.o sobjs/client.o sobjs/status.o \
                        sobjs/handlers.o
LDFLAGS         = -L../../../../export/power/usr/lib
LIBS            = -lae -lat
HEADERS         = tridentd.h debug.h AIXAESuite.h
INCFLAGS        = -I/usr/include/mac

DEBUG           = -D_DEBUG=0 -w
OSVERSION       = -DAIX4_x

CFLAGS          = ${DEBUG} ${OSVERSION} -D_XOPEN_SOURCE -DSYS_V \
                -qcpluscmt -qextchk -qfullpath -qinfo -qmacpstr -qproto \
                -qlanglvl=ansi -qtune=604

libae.a and libat.a are the Apple events and AppleTalk libraries respectively, and AIXAESuite.h (part of the project) contains the FourCCs for the Apple event messages and their subparameters this daemon understands, namely ones for quitting, heartbeats, version check (accepts version, returns error or no error), interval (for uptime only, specifies the refresh frequency), and actual message events where data is passed.

The C compiler defaults to generic cc, but those -q* options betray what compiler was actually expected: IBM's own C compiler for AIX, xlC. To link against, Apple included a tweaked subset of its header files with Harpoon in /usr/include/mac, though most of the files are generally unchanged, and only a few reference AIX specifically:

/usr/include/mac/% grep AIX *
AppleEvents.h:/*    AIX can't deal with enums that large - DJP */
AppleEvents.h:/*    AIX can't deal with enums that large - DJP */
AppleEvents.h:/* Required call for AIX implementations. */
AppleEvents.h:#ifdef _AIX
AppleEvents.h:#endif        /* _AIX */
ConditionalMacros.h:        for copiling code to be run under AIX
ConditionalMacros.h:/*      There is no pascal linkage in AIX. Null out the keyword
PPCToolBox.h:/* These are additions specific to the AIX implementation. */
PPCToolBox.h:#ifdef _AIX
PPCToolBox.h:#endif /* _AIX */

This means that very few of the C structs defined in those files were altered from their Mac OS originals. To be able to emit Apple event and AppleTalk structures compatible with Mac OS, xlC has to be able to pack structs in the way a 68K Mac would pack them, or they won't be compatible over the wire. In general, such 68K-packed structs are smaller than the way a PowerPC-native compiler would pack them for maximum efficiency on PowerPC.

And, well, it turns out it can. I mentioned before that there were weird things in xlC related to Mac compatibility which could have only come about from AIX and Mac OS being entangled for so long, and those things are -qmacpstr, which allows the \p shorthand for turning a char * literal into a Pascal string, as well as support for #pragma options align=mac68k, which causes the compiler to obey Mac 68K alignment rules until turned off with #pragma options align=reset. Those options are of no value on regular AIX; they would only be good for writing AIX programs that interact with classic Mac data structures, and they would only have had a reason to exist if AIX and Mac OS were to be more tightly intertwined. This compiler support doesn't seem to have been specific to Apple, either: the xlC you got for the ANS was vanilla IBM, and to this day IBM still documents and supports Mac 68K alignment and Mac Pascal strings in the most recent version of xlC, 16.1 as of this writing.

What do we do if we don't have xlC? I used to have access to it at the University but I never had a license for it on my own hardware as I never needed it; gcc was more than sufficient. Unfortunately, gcc on non-Darwin platforms (because I guess Apple and IBM didn't read RMS into the grand plan) doesn't support Mac Pascal strings or 68K alignment. You can work around the Pascal string problem by hand-encoding them (a Pascal-compatible Str255 simply encodes the length into the first byte instead of being null-terminated), but there's no way, or at least not with the old gcc/egcs 2.x for AIX 4, to make it pack things the way you want them without manually spelling the alignments out with things like char[]. I did actually try doing that, but to get the alignments from my Tiger G4, I ran into the fact that even good old 32-bit Carbon didn't support any of this nonsense by then:

% cat /Developer/SDKs/MacOSX10.4u.sdk/Developer/Headers/FlatCarbon/PPCToolbox.h
#warning PPCToolbox.h is not available on Mac OS X
% cat /Developer/SDKs/MacOSX10.4u.sdk/Developer/Headers/FlatCarbon/AppleTalk.h
#warning AppleTalk.h is not available on Mac OS X

I ended up installing the AIX Mac headers on the G4 and managed to fish out the corrected alignments of all the AppleTalk structures, and it compiled, but it still didn't work. I've left this attempt as ppchacked.h and ppchacked.c for messing with another day.

Fortunately, it turns out we don't really need it for the simple form of this daemon. Only the flexible form needs to talk directly to clients and thus must create and populate Apple data structures, and you'll notice that in the Makefile for the simple form there's no ppcstuff.o (that's Program-to-Program Communication, not PowerPC), which is where all of that lives. Everywhere else only deals in Apple events, and those functions treat it as an opaque data type using accessor methods like AEGetAttributePtr and AEGetParamPtr instead of manipulating the struct directly. As such code doesn't depend on having its own bytewise understanding of the struct, that much we can compile with gcc. As long as we only require one port type for this service, the simple form will suffice, so we can still create the backend with a freely available compiler.

So what are we going to create? The Status-Who demo is the most straightforward shell to take over since it displays text to the client. What we want to do should be textual, approach some level of utility, and be something that would take the local Mac much more memory and/or CPU capacity to do itself than it would ordinarily have available.

With that in mind, let's use the existing Firebase API to display an auto-updating list of the top stories on Hacker News. (Attention Lobste.rs and Two Stop Bits: if you have a functioning API I'll support that too.) I think this is an excellent test case, because why not have it in the background while you do your distraction-free writing or something, nothing running on native 68K Mac OS speaks TLS 1.3 (well, not yet, anyway), and even if there were it would take a good few seconds to actually download and parse every single API call because of the crypto overhead on a 68040. For that matter, on the 200MHz 604e it still takes a few seconds to get all the article titles together, but there are ways to make that more efficient, AIX handles the tasks, and the client Mac isn't throttled up doing all this while you're trying to do something else.

We want this to be multi-line, so the first order of business is to write a new simplified daemon that responds with the who-list instead of uptime, which I've grandiosely called hnd. The original source code has a define SIMPLIFIED to elide all the unneeded flexible code but I've stripped it down to only run as simplified in this tree. It assumes all requests it gets are for the who-view and responds accordingly.

The function GetWhoString in status.c emits the who blob, limited to BUFSIZ bytes (for the classic Mac OS, this value is 1024 by default, or 1KiB), which in our stripped version is now called GetString. I've left hooks for making the interval irregular or more efficient (see StringUpdated in status.c, which currently always returns true), but right now it does a full check every time the recheck interval comes around.

Speaking of intervals, there are three timing intervals running to keep everything happy. HEARTBEAT_CHECK, originally in client.c and now in tridentd.h, is the expected frequency at which we would get Apple events from a connected client telling us it's still there. HEARTBEAT_SEND, in client.c, is how long after the last Apple event we'll send a heartbeat to to the client, if no events have been passed in the meantime. Finally, DEFAULT_INTERVAL in client.c is how often we'll actually try to update the string we send (i.e., check the Hacker News API for what's on the front page) — whatever we do should optimally take less than HEARTBEAT_CHECK seconds to complete so we don't queue up too many client heartbeats while we're blocked on an operation. In hnd, these values are 30 seconds, 120 seconds and 60 seconds respectively.

Finally, we'll put it into /etc/ppcd.conf with the signature HCKN (you could change kSignature in tridentd.h just to make it clean, but this is never actually used in the simplified version of the daemon since ppcd handles all the signature stuff). We'll run it under my uid, though you could also run it under the guest user if you still have that. On my system this will always authenticate but there's no reason not to let a guest on your network query this ... right?

Hacker News:1000:1000:/usr/src/hnd/hnd:HCKN:guest:NONE

Restart ppcd, either from SMIT or the command line, and it will now be available for that service.

Now for the client. Apple provided source for Fractal Demo, Javelin (i.e., Status Demo) and Trident (Status-Who Demo) in inconsistent formats, respectively MPW 3.3/MacApp 3.0, CodeWarrior Gold 11 with PowerPlant and MPW 3.3/MacApp 3.0, and just plain CodeWarrior Gold 10 (not 11) with PowerPlant, which is the one we're using. The first order of business is pulling up the CodeWarrior version to the version of the compiler I use locally on the Q800 for MacLynx, which is CodeWarrior Pro 2, and generating it as a 68K build instead of a fat binary since it's a Quadra, after all. This particular version of CodeWarrior is available from a veritable Garden of various Macintosh software sites but it should also work with later compilers.

We'll first get it to compile and running against the regular flexible daemon. I've provided this version separately as "New Trident Demo"; just unstuff and open the project. The main changes, besides completely reconstructing the project file from scratch, were slight adjustments for the later PowerPlant and rewriting CodeWarrior Gold's C++ inherited:: idiom, which CW Pro 2 doesn't seem to understand.

The major guts of the MacOS client are in CTridentwhoView.cp and CTridentCmds.cp. There's lots of commented-out code in here, but that's as it came, so don't blame me. Fortunately, to switch it over to the Hacker News daemon, all we need to do is change the JVLN signature to HCKN.
Back in hnd (we can now make changes to it live and have them reflected in the output: remember, ppcd is like inetd, just with Apple events) I modified GetString to return a static multiline string as a proof of concept ... and, with the client in "who" mode, it worked! (If you do this with "uptime" mode, you get a note saying it got back a chunked response.)

The next step is to make this a little easier to work on. I'd rather have the C code call out to a script that it can take the output of rather than juggle all this work in the daemon itself (it's probably safer that way, too), so we'll add a little section of code to popen() a pipe to Perl and have Perl run a script. The initial sample script will just display the local time to show that the updates are happening regularly.

And they do, leaving our last step on the server side to actually write something that parses the API. In this Perl script, using carl from Crypto Ancienne as a HTTPS-enabled curl substitute and some trivial regexes to pull out fields from the returned JSON, we can populate the top ten list of stories with their titles and current point level.
Ta-daa! And we're still using the Quadra normally. All it has to do is redraw the window when it gets the reply from the ANS; otherwise the Mac isn't tied up every 60 seconds while we have the live display in the background.

For the final run, we will remove the option to open an uptime window (it won't work), change the names, add a proper "about" window, and add some code for a proper title bar on the window. To change the resources, first go into ResEdit and remove the ckid resource (it will complain it's checked out read only from an MPW project; ResEdit will let you change things but PowerPlant Constructor won't), then open the resource file in Constructor to make the other changes so that everything remains up to date with the source code.

Let's take it for a spin!

Dear server: I would like to snarf Hacker News top stories in the background plz thx bye
Select your symbiont! (Though there's only one.)
And after a few seconds to gather the data, we have a display!
I copied it over to the Color Classic and it works there too. Naturally both the Quadra and the Mystic can be fetching at the same time with no interference. An enhancement might be to have a common cache so that multiple sessions don't need to be pulling the same data over and over on the same box, but I'll leave that as an exercise to the reader.
As a parenthetical note, look at how little memory this requires. You could run this easily on a much less powerful Mac; it should run just fine on a Macintosh Plus as long as the Plus has enough RAM to boot System 7, since that's the earliest OS version that has the PPC Toolbox.

Unfortunately the source code as given is only known to work on Harpoon and I'm not aware of anything that emulates a Network Server, not even QEMU, so you'll need the real hardware to run this right now. That said, it should be possible (mutatis mutandis) to mimic a similar setup on early versions of Mac OS X still supporting EtherTalk with appropriate modifications, and with Mac OS 9's PPC Toolbox over TCP/IP you should still be able to do it on more recent ones as the Apple Event Manager remains part of macOS and supported in both Objective-C and Swift. I may do this myself if I get some time to, but I look forward to people exploring the concept more. The calls this backend uses should be easy to translate to modern code with few changes.

But the symbiotic application concept, which will be completely new to many of you, didn't set the world on fire back then either. I know of very few third-party programs that took advantage of it on the ANS; one exception was IPT uShare, which had a scriptable AppleShare administration tool (IPT ScriptableAdmin:0:0:/ushare/Admin/iptosad:Sndr:guest:*). Part of this was no doubt because the ANS was incredibly expensive and Apple ended up selling very few of them. This was made even more acute by the fact that the planned multiprocessor ANS, which might have appealed to the well-heeled buyers who could afford it, was only prototyped and never sold; I know at least one of these cards is out there and wouldn't mind getting my hands on another. As a poor substitute, Apple offered the 200MHz 604e single processor version as both an upgrade card and as a complete 700/200 system with 48MB of RAM and two 4GB drives for $16,129, or about $32,000 in 2023. On the software side Harpoon only got a single upgrade to 4.1.5, which is the last version that you can run on an ANS (IBM AIX doesn't work, nor do many IBM APARs, which Apple released separately in ANS-specific versions on their own FTP site fixdist.apple.com, now lost). It's hard to say if the line was actually profitable, and I've heard arguments about this both ways because there was a lot of sunk RandD despite having big margins on a per-unit basis, but it's beyond dispute that the installed base was very small.

Meanwhile, Amelio hired his old comrade Ellen Hancock away from National Semiconductor in June 1996 as CTO and tasked her with finding Apple's new operating system direction. At Comdex in November 1996, Hancock announced to shocked Vegas attendees that the Network Server would run Windows NT and MacOS after all, as well as AIX, and planned to ship a ROM upgrade to make this possible. It's not clear the idea was ever technically feasible, but it didn't end up mattering anyhow: Apple bought NeXT just a month later in December (against Hancock's recommendation), and in April 1997 Amelio, on the advice of the newly returned Steve Jobs, cancelled both OpenDoc and the Network Server line on the same day. That's sad and the end of one of the weirdest chapters during Apple's weirdest period, but I suppose I have Steve-O to thank for me getting my first big server, because I probably wouldn't have wound up with it otherwise.

Fortunately, even after their cancellation many of the ANSes at Apple remained in operation serving content, some as late as 2005, and happily some of these old warhorse systems have also wound up in the hands of collectors. An Apple Network Server isn't a small system to house, but it's hardly a white elephant, and it's capable of tricks no other Apple system at the time could match. Plus, isn't it interesting to think of what an AIX Mac might have looked like? (Hey, wanna send me an SMP card for my 500? Please?) Apple would not have a true enterprise-grade server in its lineup again until the Xserve G4 in 2002.

The source code for the server daemons (original and Hacker News) and the clients (original and Hacker News) is on Github. Compatible versions of Perl and gcc for AIX 4, which are prerequisites, are on our partial Gopher mirror of the former AIXPDSLIB, and Crypto Ancienne carl is available on Github.

2 comments:

  1. Wow. What an amazing blast from the past.

    I worked in the Apple Toolbox group and later Taligent (along side Razz the Wonder Dog) and thoroughly enjoyed this trip down Memory Lane. Thanks so much for putting it all together. (You must have a very full garage, basement, and attic? ;)

    One note: I also worked on MacApp (the OO framework that you mention came before Taligent and stayed with Blue), and it was coded in Object Pascal (not C++).

    ReplyDelete
    Replies
    1. Wasn't MacApp 3.0 intended for C++, though? That's where I first encountered it (irrespective of what it was actually implemented in) and I used it only for C++ projects. Yes, definitely the earlier ones were Object Pascal, no question.

      I'd love to hear any war stories about Taligent - I think there's a lot of history there that's yet to be discovered. I had a chat with someone who worked on the language and character side. If you're willing, drop me a line at ckaiser at floodgap dawt com ?

      Delete

Comments are subject to moderation. Be nice.