Thursday, December 24, 2020

Unboxing the best gift of 1983: the Commodore SX-64

Happy holidays and to those of you that celebrate it a very merry Christmas. As a holiday-appropriate entry, let's unbox ... a Commodore SX-64!

The Commodore SX-64 has the distinction of being the first portable colour computer. Originally part of an entire family of portable Commodore 64 systems, it was supposed to be the midrange model between the black-and-white SX-100 and the dual drive DX-64 as announced at Winter CES 1983, but only the SX-64 was ultimately released by May of that year. (There was even an SX-500, based on the Amiga 500, but it never got past the prototype stage.) Portable in this case is used advisedly, as it weighs about 23 pounds, but it has a 5" monitor which isn't terrible, a built-in 1541 5.25" floppy drive and a detachable keyboard all in one tank-like enclosure that can be lifted around by the handle (which doubles as a stand). Power usage is too much to run on an external power source any smaller than your typical car battery but in the age of systems like the Osbourne 1 such a machine wasn't implausible as a luggable. It did not sell particularly well but the sheer number of SX-64s that survive and see regular use today (I actually own three others) attests to their residual popularity. I acquired this SX-64 as NOS still in its original box and packaging a few months ago and decided to get it out of storage as a fun little exploration.

Getting it out, we experience our first Christmas emotion: anticipation. This box was only opened by the previous owner to check the contents, though it got flattened a bit by sitting stacked in the storage unit.

Obviously Commodore didn't lose a lot of sleep making the box pretty (not like the regular C64 or other packages); it's plain jane cardboard in the same look as their printer boxes and certain other sub-sexy accessories. The indication "PHILADELPHIA" on the label suggests this was stocked at their West Chester, PA headquarters. Although it's likely some stores did sell the SX-64 at retail, at least as a kid in Southern California I didn't see any retail sales at the usual suspects of the day, including where we bought our 64 and 128 and peripherals (Target, Sears, Toys R Us, etc.). We will verify the serial number shortly.

And now the second Christmas emotion — delight — as we see the SX-64 inside. On top is (in protective foam wrapping) the manual, and (in protective plastic) the "saddlebag" accessory that is very precious to SX-64 owners that is often separated from used units. I have put the manual aside. Let's get the saddlebag out of the plastic.

The "commodore" and logo are silk-screened onto the fake fuzzy exterior, which on my other units is worn or faded, but not so here on this absolutely pristine one. Inside the saddlebag are two boxes of the type 5.25" floppies used to come in. One contains a totally ordinary grey IEC power cord but the other contains a beautiful new DB-25 keyboard connector cable. (A straight-thru DB-25, suitably machined to fit, will do if your SX-64 doesn't have one.)

Serial number checks out. This unit has a manufacture date of September 1983 and was "made in Japan." SX-64s have the full complement of ports except for cassette; the SX-64 Kernal ROM instead defaults to device 8 (the internal disk drive), but has a bug that overruns the input buffer when using the SHIFT-RUN/STOP shortcut to load from disk. The Kernal also uses different, higher-contrast colours. You can connect an SX-64 to an external monitor, but do it when the power is off or you can kill the SID (I've actually done that once, to my great chagrin).

The other major glitch with the SX-64 was the top-loading expansion port. Its ribbon cable to the mainboard was so inadequately shielded that devices like REUs would not work reliably, forcing Commodore to issue a service pack to correct the problem.

Each side of the main unit's Styrofoam packing has "SX-64" and a Commodore logo stamped on it.

Portrait with the foam sheet protection still on the handle.

Foam protects the screen.

... well, sort of. Degrading as polyethylene foam of this age often does, the foam had also left a little "extra" but it was easily wiped off.

With the disk drive and monitor control door open. The 1541 disk drive still had the original head protector, which is a cardboard insert to prevent the disk heads from clattering themselves to death during transport. Please, if you're shipping a 5.25" drive, put a head protector in. A throwaway floppy disk will do nicely.

And finally, all plugged in and ready to rock, we now experience the last and most bittersweet Christmas emotion: disappointment. This unit does not power on. After some cursory checks of the fuse, it appears to be the power supply, which while more reliable than the obnoxious C64 potted epoxy brick is still subject to the capricious ravages of time. This unit will thus become a spare.

(The unboxing experience isn't exactly contemporary Apple, but I think I still would have been excited.)

Sunday, December 13, 2020

Stereoscopic computing: anaglyph sprites on the Commodore 64

This article is part of a series -- you can read other entries

In our first 3D article we talked about the various types of stereoscopy available on computers. Modern systems can both generate a 1080p image and/or (with most video cards) a high-refresh-rate image, and most 3D displays or 3D-capable TVs are 1080p, so depending on whether you have an active or passive display system you can either use fast refresh (like my 120Hz DLP projector, which delivers a full 60Hz to each eye with active glasses) or an interlaced image and polarization (like my Vizio 3D TV and Mitsubishi Diamondcrysta monitor). This generates a high-quality, (usually) flicker-free high definition 3D picture.

However, classic computers invariably don't have either of those options, so we must resort to less satisfactory approaches. While some systems implemented a spinning shutter wheel as an active 3D display option, many older systems lack sufficient refresh rates to be sufficiently smooth and very old machines can't update the screen fast enough between eye views anyway. (That gives me a headache just thinking about it.) For most situations the typical choice will be either anaglyph, i.e., red-cyan glasses, or exploiting the Pulfrich effect. The latter, though not general purpose due to how the effect is generated, can be sufficiently convincing with the right application and we'll look at that in a later article. A third option is possible with modern displays but it, too, will be the subject of a later post. Today we'll try to get a primitive anaglyph effect working on the Commodore 64, and we'll do it with the classic and widely available red-cyan glasses you can get on Amazon or from specialty shops like Berezin (no affiliation, just a satisfied customer).

The basic concept with anaglyph is that the coloured glasses filter certain wavelengths of light, delivering different views to each eye. Since the red filter is over your left eye, your left eye only gets red (primarily), so the image that should be delivered to the left eye is tinted red. Likewise, as the cyan filter is over your right eye, the cyan filter should optimally admit only what is part of the right eye image. In practice, as anyone who's looked at anaglyph images knows, the strategy is imperfect: most full colour images will have some bleed-through and while colour selection and processing can reduce differences in brightness between the eyes, some amount of ghosting and retinal rivalry between the two sides is inevitable. Still, anaglyph 3D images require no special hardware other than the glasses and some well-constructed images can be very compelling.

To make objects stick out, the left (red) channel is separated further and further to the left from the right (cyan) channel; to make them recede, the left channel is separated further and further to the right. When the channels overlie exactly, they are seen at a neutral distance from the viewer as appropriate to the image's composition. The mnemonic is thus the three R's: red right recedes.

Unfortunately, conventional colour anaglyphs are difficult on a system like the C64 because there is only one fixed, limited palette and the shades available may not correspond well with the lens colours. You may be able to make the displayed colours more appropriate for your glasses by messing with the display settings or colour balance, but this naturally has other side effects. Additionally, there is no alpha channel, so overlaying objects (which is necessary to deliver two views in one image) just obscures what's behind them. Usually you would use a proportional shade of purple to deliver an appropriate level to each eye but the C64 has but one shade of purple, and you would have to manually figure out when to use it.

A way around the latter problem is to either dither or (as a special case of dithering) interlace. This reduces resolution but eliminates having to do costly alpha calculations and screen updates. One way of doing a 3D anaglyph display on a C64 is alternating red/black and blue/black lines in multicolour mode, as the red (VIC-II colour 2) and blue (VIC-II colour 6) shades are the closest shades to most red-cyan glasses. This gives you effectively a 160x100 monochrome image. For greater dynamic range you could also consider red/pink/black and blue/light blue/black on alternating lines, using black as the common background colour, and some of the CPU-assisted modes like FLI and Hires FLI can do even better. But this requires substantial precomputation to generate the image and thus is only generally useful for static images. How might we do this for a dynamic display?

While computers like the Amiga have playfields for overlaid elements, the VIC-II chip in the C64 really only has one option: sprites. Sprites do effectively have an alpha channel, but it is a single bit, so there is no translucency. Thus, if we want a dynamic 3D anaglyph display on the C64, a straightforward means is to interlace a blue sprite and a red sprite, yielding a composite 3D plane that can move in the Z-axis by changing their relationship to each other. And that's what we'll do here.

The illusion of depth is enhanced not only by the shift between the left and right channels, but also the size of the object, so we will need a routine to scale a sprite. For simplicity we'll just use a solid square block, which we can generate on the fly. Sprites on the C64 are 24x21, each row three bytes in length, up to 63 bytes in size. We will write a quick little utility routine that will turn a number into a string of bits, and then copy that to the same number of alternating lines, clearing the rest of the sprite so we can grow and shrink it at will.

The assembler source for this quick interlaced sprite scaler is on Github and can be cross-assembled with xa. We'll put the sprite at 832 ($0340) in the cassette buffer, which appears as SPRITE in the text. Here's some highlights.

        jsr $aefd
        jsr $ad9e
        jsr $b7f7
As a convenience for playing around in BASIC, these calls accept a comma after the SYS statement followed by an arbitrary expression, and then convert the result to a 16-bit integer and store it in $14 and $15. We only care about the low byte, so $14 is the entirety of the value. We clear the top row of the sprite, and if the parameter is zero, just copy that to the rest of the sprite (clears it). Otherwise, let's make enough one bits in the top row of the sprite by setting carry and rotating it through:
        ; turn x into x 1-bits
lup1    sec
        ror SPRITE
        ror SPRITE+1
        ror SPRITE+2
        dex
        bne lup1
We then duplicate it on alternating rows (unless it's 1x1 or 2x2).
        sbc #0 ; ror cleared carry, so -1
        lsr
        beq clrs ; no copies

        tay
        clc
lup2    lda SPRITE
        sta SPRITE+6,x
        lda SPRITE+1
        sta SPRITE+7,x
        lda SPRITE+2
        sta SPRITE+8,x
        txa
        adc #6
        tax
        dey
        bne lup2
And then we clear the rest of the sprite. We assemble this to 49152 and load it, then set some parameters. After you load the binary into VICE from wherever you assembled it to (and remember to type NEW after loading), you can cut and paste these POKEs into VICE.

poke53281,0:poke53280,0:poke646,12:poke53287,6:poke53288,2
poke2040,13:poke2041,13:poke53271,3:poke53277,3
poke53248,150:poke53249,100:poke53250,150:poke53251,102:poke53264,0
sys49152,10:poke53269,3

You'll get this display.

We have set the sprite colours, made them double size for ease of use, and positioned them so that they are interlaced. If you put on anaglyph glasses at this point, it's just a block at neutral distance. Let's write a little BASIC code to move them around as a proof of concept. You can cut and paste this into VICE as well:

10 rem midpoint at x=10
20 forx=0to20:poke53250,160-x:poke53248,140+x:poke53251,110-x:poke53249,108-x
30 sys49152,x:for y=0to50:next:next
40 forx=20to0step-1:poke53250,160-x:poke53248,140+x:poke53251,110-x
50 poke53249,108-x:sys49152,x:for y=0to50:next:next
60 goto 20

With glasses on, you will see the block swing from receding into the distance and protruding into your view by sliding and scaling.

There are two things to note with this primitive example. The first is that the steps end up separating the red and blue components quite a bit; combined with the afterimage from the bleedthrough, we end up seeing two blocks at their furthest extent. We can solve that problem by separating the sprites at a slower rate (say, half) than the scaling rate. This limits how far the composite plane can be moved in the Z-axis, but there are usually other optical limits to this generally, so we're not losing as much as one would think.

The second thing to note is it's kind of jerky because it's not updating the sprite registers fast enough (the delay loop is just there so you can see each step of the animation; the lack of smoothness is because of the computations and POKEs necessary on each "frame"). We'll solve this problem by rewriting the whole thing in assembly language. For style points we'll add a background (a crosshatch) as a neutral plane, and flip the sprite priority bits for the composite plane as it moves forward and back so that it also has proper occlusion.

The assembler source for the "complete" demo is also on Github (and also cross-assembled with xa), but notable parts are discussed below. We will write it with a small BASIC loader so we can just LOAD and RUN it.

        .word $0801
        * = $0801

        ; 64738 sys2061

        .word $080b
        .word $fce2
        .byte $9e, $32, $30, $36, $31
        .byte $00, $00, $00
The motion routine is more or less a direct translation of the BASIC proof of concept except we will separate the sprites by only half of the scaled size for less of a "double vision" effect, so we change the constants to match. Here VALUE is still $14, which we're still using merely as a holding location even though we are no longer servicing BASIC directly, and HVALUE is a free zero space location for the temporary result of the math.
mlup    lda VALUE
        clc
        lsr
        sta HVALUE

        lda #150
        sec
        sbc HVALUE
        sta 53250
        lda #110
        sbc VALUE
        sta 53251

        lda #108
        sbc VALUE
        sta 53249
        clc
        lda HVALUE
        adc #140
        sta 53248
At the end we wait a couple jiffies so that the animation is actually visible, check for RUN/STOP, and if not pressed cycle the position in VALUE back and forth. MODE is $15, since we don't use it for anything else either here, and is initialized to zero when we start.
        ; wait two jiffies per frame
        lda #0
        sta $a2
waitt   lda $a2
        cmp #2
        bcc waitt

        ; check run/stop
        lda 203
        cmp #63
        bne cycle
        lda #0
        sta 53269
        lda #147
        jmp $ffd2

cycle   lda MODE
        bne decr

incr    inc VALUE
        lda VALUE
        cmp #21
        beq *+5
        jmp mlup
        sta MODE
        ; fall thru
        
decr    dec VALUE
        lda VALUE
        cmp #$ff
        beq *+5
        jmp mlup
        lda #0
        sta VALUE
        sta MODE
        jmp mlup
Here is an animated GIF of the result you can view with anaglyph glasses, though the result in an emulator or on a real C64 is smoother.

As you can see, the composite sprite recedes and protrudes appropriately, and depth cueing is helped by flipping the priority bits as it goes past the "zero" point (the crosshatch) where VALUE, in this coordinate system, is defined as 10.

While an anaglyph composite sprite approach clearly has drawbacks, it's still in my opinion the best means for independent motion of planar objects in the Z-axis and gives the most flexibility for "true" 3D on classic machines of this era. But the Pulfrich effect doesn't have its colour limitations and can be useful in certain specific situations, so we'll look at that in the next article.

Sunday, November 29, 2020

Modern console, old protocol

Not much, just a note someone put a Gopher client on the Nintendo Switch. It may well be the first homebrew browser for that platform. Seems fitting.

Thursday, November 26, 2020

Not a refurb weekend: Apple IIgs

The most advanced Apple II computer was, of course, the one Apple insisted would be a dead end: the 1986-1992 Apple IIGS. And the reason was simply that the GS was too good a computer, offering Apple II compatibility, 16-bit performance, a 4096-colour palette with up to 640x200 high resolution graphics, and one of the best sound chips since the SID in the Commodore 64 (in fact, Bob Yannes designed the SID, then went on to found Ensoniq who produced the GS'). It was so good, in fact, that it posed a real risk during its development of cannibalizing sales from the Macintosh. Apple thus made sure it couldn't be a threat by deliberately hobbling the 16-bit 65816 CPU to just 2.8MHz when it was capable of up to 14MHz and at minimal additional cost, and intentionally never did more with the system publicly other than token ROM upgrades and pathetic bumps in memory. It takes some truly inspired buttheadery to hold back a fabulous system in that fashion, but that's what Apple was capable of in those days, and arguably still is.

This particular computer in my collection — and yes, that's a Canon Cat next to it, a topic for another day — is a Frankenstein assembled from three partially working units. It has a ROM 03 motherboard (the last revision not counting the 8MHz "Mark Twain" prototype) but with a Woz Limited Edition topcase just 'cuz, a 1MB Apple RAM expansion card (for 2.125MB of memory), an original 7MHz Applied Engineering TransWarp GS CPU accelerator with the 8K cache daughtercard, and a 20MB Applied Ingenuity InnerDrive consisting of a SCSI card plus a unified drive enclosure and replacement power supply. It is connected to an ADB keyboard and mouse, a LocalTalk network box, an AppleColor RGB display, an Apple joystick and 3.5" and 5.25" floppy drives, and boots into GS/OS 6.0.1.

A few months ago after I got LocalTalk up and running and was transferring some other things to it, it started to get flaky, and would invariably crash into the Apple machine language monitor after sitting at the GS/OS Finder for a few minutes. This machine was certainly produced in the "crap caps" era and bad capacitors are in the differential diagnosis, but with so many things installed a more systematic approach would be required. Today it's the Thanksgiving long weekend during a pandemic, and I've got only dayjob work and take-out dinners to look forward to for the next several days. Sounds like an opportunity for another ... Refurb Weekend!

First, let's pop the hood and check for obvious damage. I blew out some dust with an air can, but everything looked pretty shipshape otherwise.

Looking overhead you can see the combination drive cage-power supply for the AI InnerDrive plus its ribbon cable to the controller card. Between the controller card and the enclosure is the TransWarp GS, and the Apple RAM expander card is furthest away.
Closeups of the InnerDrive and TransWarp GS cards.
Closeup of the Apple RAM Expansion card (670-0025-A).

To check for board damage, you also need to remove the power supply, since a couple large capacitors sit under it and, inconveniently with the larger size of the AI combo cage and PSU, the PRAM battery.

Clean as a whistle. I even got out the multimeter and checked the PRAM battery, and it still reads a full 3.65V.

I'm not a fan of prophylactically replacing capacitors that are not obviously bad, especially since you have a non-zero chance of accidentally breaking something in the process of fixing what ain't broke to begin with. At this point I resigned myself to having to test components independently: remove or disconnect something, fire it up and see if it crashes. I was strongly suspecting either the TransWarp card or the RAM expander, since those would certainly cause random failures. For that matter, it might even be heat related. Anyway, to get a baseline, I went ahead and disconnected the LocalTalk network, the joystick and the external drives, rebooted it and waited for it to crash.

After an hour it was still cheerfully sitting at the Finder. I fired up a game of Crystal Quest GS (ported by Becky Heinemann, who also wrote the firmware for the InnerDrive and had very nice things to say about TenFourFox). No crash. I fired up Wolfenstein 3D (ported by Logicware, which Heinemann co-founded). No crash. I played some Tunnels of Armageddon, one of my favourite GS games (no known connection to Heinemann). Still no crash.

I reconnected the joystick and let it sit for a few minutes. The joystick doesn't work too good, but it didn't make anything crash.

I reconnected the disk drives (best done with the power off, just in case) and let it sit some more. The machine had gotten good and warm with all this burning-in, so if heat was a factor, it should have been by then. No crash.

I reconnected the LocalTalk box and let it sit a couple more minutes. Boom, straight into the monitor while it was sitting idle.

I powered it off, disconnected the LocalTalk box, rebooted and let it sit a couple more minutes. No boom, no monitor, normal operation. The LocalTalk network was crashing GS/OS. And, actually, this makes sense, because the last thing I was doing with it before it got "wacky" was copying stuff from the network. It had never been connected to the house LocalTalk segment before because it never needed to be.

So what's crashing it, and why doesn't it do so right away? My guess is that the delay is because something on my household AppleTalk network transmits a packet intermittently that GS/OS's stack doesn't like. The segment itself couldn't be the cause because there were no other LocalTalk hosts up (the 486 and the Colour Classic are both connected to the segment, but neither were on). A Dayna EtherPrint-T bridges the LocalTalk segment to the main Ethernet via EtherTalk; on the other end of the Dayna is the little Macintosh IIci running old Netatalk which services old hosts, this Raptor Talos II running modern Netatalk which doesn't, and the G4 Sawtooth file server running Tiger (not Netatalk). Any one of those could be sending packets that GS/OS 6.0.1 just doesn't know how to cope with, but a cursory Google search and a look through comp.sys.apple2 didn't come up with anything similar. A mystery to be further investigated in a later entry.

In any event, it's good to see it doesn't appear to be caps nor hardware, and there are other ways to get files to the GS, so not having LocalTalk isn't the end of the world. Turns out this wasn't a Refurb Weekend after all, but who's complaining? That said, however, now that it's back in its right mind there are some future upgrades to do. The first is to consider some other means of mass storage since even old battleaxe SCSI drives don't last forever, and the InnerDrive firmware is notorious for only being able to work with a very small number of specific drive geometries (forget using a drive much later or larger than the 20MB one it has). A Compact Flash card solution exists and would seem the obvious choice rather than faffing around with another SCSI controller. The second is to put the full 8MB of RAM in it; there are some third party cards still made by homebrewers that will do this. If nothing else, between the two of them I'll be able to shoot a whole lot more Nazis for a whole lot longer, and that's always a(n Apple II) plus.

Sunday, November 15, 2020

Fun with Crypto Ancienne: TLS for the Browsers of the Internet of Old Things

The TLS apocalypse knocked a lot of our fun old machines off the Web despite most of them having enough horsepower for basic crypto because none of the browsers they run support modern protocols. Even for Mac OS X, the earliest version you can effectively use for Web browsing is 10.4 because no earlier version has a browser that natively supports TLS 1.2, and for most other old Un*ces and the like you can simply forget it.

To date, other than the safe haven of Gopherspace, people trying to solve this problem have generally done so in two ways:

  • A second system that does the TLS access for them, subsuming the access as part of a special URL. As a bonus some of these also render the page and send it back as a clickable image; probably the best known is Web Rendering Proxy which works on pretty much any browser that can manage basic forms and imagemaps. Despite the name, however, it is accessed as a special web server rather than as an HTTP proxy, so links and pages also have to be rewritten to preserve the illusion.

  • A second system that man-in-the-middles a connection using its own certificate authority; the request is then upgraded. Squid offers this feature, and can act either transparently or as an explicit HTTP proxy. Modern browsers defeat this with certificate pinning, but these browsers wouldn't have that, though you do need to add the proxy as a CA root.

The man-in-the-middle step is needed because most old browsers that are SSL or TLS aware don't want the proxy messing with the connection (it's supposed to be secure, dammit), so they open up a raw socket with CONNECT to the desired site such that all the proxy should do is merely move data back and forth. I imagine it is eminently possible on today's fast systems that an SSLv2 or SSLv3 connection's symmetric key could be broken by brute force by a transparent proxy and used to decrypt the stream, then re-encrypt it to modern standards and pass it on, though I couldn't find a public package obviously like that. (If you know of one, post it in the comments.)

There is a third alternative, however: configure the browser to send unencrypted HTTPS requests to an HTTP proxy. Most browsers don't do this because it's obviously insecure, and none do it out of the box, but some can be taught to. What you want is a browser that doesn't speak HTTPS itself but allows you to define an "arbitrary protocol" (with "finger quotes") to use an HTTP proxy for, and come up with an HTTP proxy on the back end that can accept these requests. Such browsers exist and are even well-known; we will look at a few.

But let's do one better: all these approaches above need a second system. We would like whatever functional layer we have to bolt on to run on the client itself. This is a retrocomputing blog, after all, and it should be able to do this work without cheating.

To that end allow me to introduce Crypto Ancienne, a TLS 1.2 library for the "Internet of Old Things" with pre-C99 compatibility for geriatic architectures and operating systems. What's more, Cryanc includes a demonstration application called carl (a desperate pun on curl) which, besides acting as a command-line agent, is exactly this sort of proxy. You can build it, have it listen with inetd or a small daemon system like micro_inetd, and point your tweaked browser's settings at it. If it's listening on localhost, then no one can intercept it either. Et voila: you just added current TLS to an ancient browser, and you didn't even burst any wineskins.

The browser that allows this nearly perfectly and with fairly good functionality is the venerable OmniWeb, prior to version 4.2. Although the weak HTTPS of the era can be added to OmniWeb with a plugin, and later versions even included it (I'll discuss that momentarily), it is not a core component of the browser and the browser can run without it. OmniWeb started on NeXTSTEP as a commercial product (it's now available for free); version 2.7 ran on all the platforms that NeXTSTEP 3.3 supported including 68K, Intel, SPARC and HP PA-RISC. We have such a system here, an SAIC Galaxy 1100 named astro, which is a portable ruggedized HP Gecko 9000/712 with an 80MHz PA-7100LC CPU and 128MB RAM.

carl builds unmodified on NeXTSTEP 3.3 (cc -O3 -o carl carl.c); the C compiler is actually a modified gcc 2.5. micro_inetd.c just needs a tweak to change socklen_t sz; to size_t sz; and then cc -O3 -o micro_inetd micro_inetd.c. Then we need to configure OmniWeb:

You will notice that we are running carl via micro_inetd on port 8765 in the terminal window at the bottom; the command you'd use, depending on where the binaries are, is something like micro_inetd 8765 carl -p (the -p puts carl into proxy mode). The URL we will use is thus http://localhost:8765/, though note that micro_inetd actually listens on all interfaces, so don't run this on an externally facing system without changes. We have assigned both http and https protocols to that proxy URL and OmniWeb simply assumes that https is a new and different protocol that the proxy will translate for it, which is exactly the behaviour we want. Let's test it on Captain Solo Hacker News:
Excellent! Self-hosted TLS on our own system! Let's try Lobste.rs!
OmniWeb 2.7 doesn't support CSS or JavaScript, and its SGML-to-RTF renderer (!) is not super quick on an 80MHz computer, but it's remarkable how much actually does work and it's even somewhat useable.

What about later versions? As most readers already know, NeXTSTEP 3.3 became OpenSTEP 4.0 (dropping PA-RISC, boo hiss), and then after Apple bought NeXT, or possibly the other way around, became Rhapsody 5.0. Rhapsody was a curious mixture of a not-quite-faithful facsimile Mac Platinum interface and OpenSTEP underpinnings and was eventually a dead end for reasons we'll mention, but Apple did turn it into a saleable product, i.e., the original Mac OS X Server. OmniWeb 3 runs on Rhapsody, and of course we have such a system here, the best laptop to run Rhapsody on: a PowerBook G3 WallStreet II "PDQ" named (what else?) wally with a 292MHz G3 and 384MB of RAM.

We built carl and micro_inetd on wally in the same way; its cc is actually a cloaked gcc 2.7.2.1. Configuring OmniWeb 3 is a little trickier, however:

You'll notice the non-proxied destinations and protocols are off. When these were on, it seemed to get confused, so you should disable both of them unless you know what you're doing. Again, note the terminal window in the background running carl via micro_inetd with the same command, so the URL is once again http://localhost:8765/, and both http and https are assigned to it. You can see Lobste.rs was already working but here it is without the settings window in the way:
And here's Hacker News:
There is still no CSS, but there is some modest improvement in the SGML rendering, and the G3 rips right along. I actually rather like Rhapsody; too bad not much natively runs in it.

Rhapsody was a dead end, as I mentioned: for the Mac OS X Public Beta, Apple instead introduced the new Aqua UI and made other significant changes such that many Rhapsody applications weren't compatible, even though they were both based on early releases of Darwin. Nevertheless, the Omni Group ported OmniWeb to the new platform as well and christened it OmniWeb 4. OmniWeb 4 was both better and worse than OmniWeb 3: it has a much more capable renderer, and even does some CSS and JavaScript, but it is dreadfully slow on some pages such that the 600MHz iMac G3 I ran it on seemed significantly slower than the Wally which ran at less than half the clock speed. In version 4.2 OmniWeb started using the system proxy settings instead of its own (ruining our little trick), and with the availability of Apple WebCore with Safari in Mac OS X Jaguar 10.2 a new and much faster OmniWeb 4.5 came out based on it. If it weren't for the fact I was already a heavy Camino user by that time I probably would have been using it too.

This leaves us with 4.0 as the last OmniWeb we can use carl for, but 4.0 was written for Cheetah 10.0 specifically and seems to have issues resolving domain names beyond Puma 10.1. Not a problem, though, because carl can do that work for it! Here we are working on christopher, a tray-loader strawberry iMac with a 600MHz Sonnet HARMONi G3 upgrade card and 512MB RAM running 10.2.8.

The Omni Group still kindly offers 4.0.6 for download. Drag the application from the disk image to /Applications, but before you run it, open the package in the Finder and go into Contents and Plugins. This is one of the releases that included HTTPS support, so drag HTTPS.plugin to the Trash, empty the Trash and start up the browser. Configuration is much the same as OmniWeb 3 but with one minor change:

Again, we have the Terminal open running micro_inetd and carl (it's actually running the binaries copied off wally!) on port 8765, but since OmniWeb 4 can't resolve domain names on 10.2, the URL is http://127.0.0.1:8765/. Non-proxied destinations and protocols are likewise off. With that, here's Hacker News:
And here's Lobste.rs.
The rendering improvements are obvious but so is the significantly increased amount of time to see them. By the way, if you're wondering where the window shadows are, that's because I run Shadowkiller on this iMac. Without it, its pathetic Rage Pro GPU would be brought to its knees in Jaguar.

So that's it for OmniWeb. What other browsers can we use for this? Surprisingly, an even more famous name: NCSA Mosaic!

NCSA Mosaic was available in multiple forms, including one also maintained by yours truly, Mosaic-CK, which descends from the last Un*x release (2.7b5) and the only NCSA browser for which the source code is known to survive. With the Mosaic-CK changes it builds fine on present-day macOS, Mac OS X, Linux and others. Like OmniWeb it treats HTTPS as a new and different protocol and you can tell Mosaic-CK to use a proxy to resolve it, so here's Mosaic-CK 2.7ck13 on my Raptor Talos II running Fedora 33 showing Hacker News.

You can set up the proxy rules with the interface, but it's simpler just to make a proxy file. If you run Mosaic-CK once, a preferences folder is created in ~/.mosaic (or ~/Library/Mosaic-CK for Mac). Quit Mosaic-CK and inside this folder, create a file named proxy like so:

https 127.0.0.1 8765 http
http 127.0.0.1 8765 http

Each line must end with a space before the linefeed. Then, with carl running as before, Mosaic-CK will access HTTPS sites through the proxy.

Naturally this doesn't extend the functionality of Fedora 33 very much though (especially since I'm typing this in Firefox on the very same machine), so what about systems that can run Mosaic-CK yet have no other options for modern TLS? One of those is the very operating system Mosaic-CK was originally created for, Tenon's Power MachTen.

Power MachTen is essentially OS X inside-out: instead of running Classic on top of a Mach kernel, Power MachTen and its 68K ancestor Professional MachTen run a Mach kernel on top of the classic Mac OS. I have it installed on bryan, my 1.8GHz Power Mac G4 MDD running Mac OS 9.2.2, which you earlier met when it chewed through another power supply (as MDDs do). Power MachTen has its own internal X server on which it runs AfterStep by default and includes Motif libraries. Here's the MDD viewing Hacker News; notice the classic Mac menu bar and the xterm running micro_inetd and carl.

However, even though Power MachTen uses gcc 2.8.1 and no modifications to the source code were required, some hosts consistently have issues. Lobste.rs, for example, throws a TLS alert 10 (unexpected message), and some other sites that appear to use a similar server stack do the same. Still, this is substantially more than OS 9 can do on its own. What if we moved this to a "real" Apple Unix -- A/UX?

Most readers will know what A/UX is, Apple's SVR2-based Unix for most 68K Macs with an FPU and MMU. It is notable in that it also includes System 7, allowing you to run both standard Mac apps of the day as well as compile and run binaries from the command line (or use the built-in X server), so we'll run it on spindler, a Quadra 800 clock-chipped to a 38MHz 68040 with 136MB of RAM running A/UX 3.1. There is a Mac version of NCSA Mosaic which for some reason uses a different version number, though the source code is apparently lost. It runs just fine in A/UX's Mac Finder, however, so we'll install NCSA Mosaic for Mac 3.0b4. Instead of the included Apple cc we'll use gcc 2.7.2, which is available from various Jagubox mirrors; carl builds unmodified and micro_inetd just needs the socklen_t fix.

3.0 is the only release of NCSA Mosaic that allows suitable proxy settings, at least on the Mac (2.x used "gateways" fixed to conventional protocols instead). We define http and https protocols, then point them both at localhost:8765 (use "Remote" so that you can fully specify the host and port). carl is already running under micro_inetd in the background (see the CommandShell window).

Here is 3.0b4 (trying to) displaying Google. I'd love to show you Hacker News, but it can't cope with the reflow and crashes. These crashes don't occur in Mosaic-CK, nor does the <script> spew; I don't know if the Windows version of Mosaic does this but I don't have a Windows port of carl currently.
3.0b4 also doesn't like getting HTTP/1.1 replies from servers that answer with /1.1 responses even to /1.0 requests. That's probably inappropriate behaviour for them but Mosaic doesn't even try to interpret the reply in those cases. The -s option to carl can fix this for some sites by spoofing /1.0 replies (though the headers are passed unchanged), but some sites won't work even with that.

So, since Mac Mosaic 3.0b4 is persnickety and crashy as heck, do we have an alternative that can be configured in the same way? Not the usual suspects, no: not Netscape, nor MSIE, nor NetShark, nor MacWeb. But incredibly, MacLynx works!

MacLynx is a port of Lynx 2.7 to 68K and PowerPC with (as befits Lynx) very light system requirements. The source code is available, though the binary I'm using here is monkeypatched to fix an issue with an inappropriate Accept-Encoding header it sends. Configuring it is very un-Mac-like: edit lynx.cfg and set http_proxy and https_proxy appropriately, as we are doing here in BBEdit 4.1.

Unfortunately MacLynx still has some other problems which will require a trip to the source code to fix, including not knowing what to do with text/html;charset=utf8 (so no Hacker News). Similarly, carl on A/UX has the exact same failures on the exact same sites in my internal test suite as it did on Power MachTen, which makes me wonder if something in Apple's lower level networking code is responsible (so no Lobste.rs either). But, hey, here's Google over TLS, and there's no script-spew!
This problem doesn't occur with apps running in Classic under Mac OS X talking to carl running in the Terminal, by the way. That said, if you just want TLS 1.2 on Mac OS X Tiger, you could just run TenFourFox and even get TLS 1.3 as part of the deal.

Anyway, this entire exercise demonstrates that with a little care and the right browser you can bolt modern cryptography on and put at least some of these machines back to work. I'm planning to do further ports to IRIX (it builds already but MIPSPro c99 miscompiles some sections that need to be rewritten) and SunOS 4 (needs support for the old varargs.h), and I've got an AlphaPC 164LX running Tru64 here doing nothing as well. I'll have to think about what browser would be appropriate for IRIX other than Mosaic-CK, but Chimera runs nicely on SunOS 4 and the source code is available, and it doesn't need Motif (so it could even be an option for A/UX or older HP/UX). For classic Mac, MacLynx works very well already, so if we can fix its minor deficiencies and make it a little more Mac-like I think it will do even better on 68K systems in particular.

Of course, a still better idea would be to simply integrate native HTTPS support into those classic browsers for which we do have the source code using Crypto Ancienne itself rather than carl as a proxy. That's an obvious goal for a future release of Mosaic-CK.

And, well, maybe this is an opportunity to make Gemini appropriate for retrocomputing. A Gemini client becomes possible now that we have a TLS 1.2 client, and its lighter weight document format would be an especially appropriate choice for these machines. We could even bolt it onto these browsers here by defining a new protocol gemini:// for them and writing a proxy to translate Gemini to HTTP/1.0 and its document format to HTML; you could start with carl itself and make the appropriate modifications. Anyone feel like a weekend project?

Crypto Ancienne is available on Github.

Friday, November 6, 2020

MacLynx "beta 2"

Only a few people remember there was once a native Lynx port to the classic Mac OS, circa 1997. A text-based browser on a GUI-only computer would seem contradictory, and certainly wasn't congruent with the HIG, but it was an honest-to-goodness Lynx 2.7 in a terminal-like window and it worked. It also has very low system demands (basically "just" System 7), making it one of the few web browsers you could even run on a Mac Plus. I used it quite a bit on a Mac IIsi, which was the first Mac I ever owned and for awhile the only web browser that machine ever ran.

Dusting it off as a test case for a secondary project, I discovered it has a problem which was not apparent during the days I used to use it: it inappropriately sends a Content-Encoding header that says it can accept gzip, but (possibly a MacLynx-specific bug) it is unable to decompress and view any pages sent accordingly. Back in the day not many servers supported that, so no one really noticed. Today everybody seems to. As a stopgap I figured the easiest way to fix it was simply to make that header incomprehensible and thus the server would ignore it, so I monkeypatched the binary directly to munge the gzip string it sends (the data fork for the PowerPC version and inside the DATA resource for the 68000 version). Ta-daa:

Now, credit where credit is due: it looks like Matej Horvat discovered this issue independently about a half-decade before this post. His solution was a bit better: he munged the Content-Encoding string itself rather than its value. This is not quite so straightforward in the 68000 version because of how strings are encoded in the DATA resource, but as a belt-and-suspenders approach I went ahead and implemented his approach too (after all, I suppose it's possible my munged string may match an encoding method that could be supported in the future).

Olivier's old site has not been up for years, so I resurrected it from a local copy and MacLynx is again hosted this time on Floodgap. Unfortunately I don't have his old French localization, but I do have the source code also (for CodeWarrior Pro 2), and you can download this unofficial "beta 2" from there as well. Both the 68K and PowerPC versions have both patches.

Sharp-eyed readers will have noted something a little odd about the screenshot. The secondary project works, but needs some polish and a couple minor bug fixes. More later.

Thursday, November 5, 2020

A Gopher view of Gemini

With the possible exception of Google, and I fully acknowledge the irony that this blog post is hosted on a Google property, everyone thinks the Web has gotten too big for its britches. And, hey, maybe Google agrees but it's making them money and they don't care. In this environment of unchecked splay, the browser has since metastasized to an operating system running on an operating system (you could even argue that simplified document presentations like Reader View are browsers themselves running on this browser-as-operating system), web services drive browser technology to facilitate greater monetization of users rather than greater ability to access information, and the entire client stack has reached the level of baseline bloat such that many operating systems and older systems are cut off completely.

As a consequence I think one of the biggest reasons for the Gopher protocol's return is as a reactionary backlash against the excesses of the Web. But this motivation is by no means universally central. When I started gopher.ptloma.edu in 1999 (which is now gopher.floodgap.com) I did it to preserve a historic technology that I always wanted to be part of (I even maintained news and weather through my .plan for awhile in university since I didn't have server hardware of my own back then), and many of us early Gopher revivers probably approached it from the same perspective. This affects our view of the protocol's niche as well, as I'll explain a little later.

Really, Gopher isn't a direct ancestor of the Web either; at most it can be argued to be an influencer. Gopher actually originated as an attempt to put a friendlier frontend onto FTP (the venerable File Transfer Protocol that Google is doing its level best to kill too), meaning Gopher menus aren't hypertext any more than a file directory listing is, and today's conceptualization of them as primitive hypertext is merely an artifact introduced by the early (surprise!) web browsers which subsumed it into their core metaphor. While it's become the dominant paradigm for modern Gopher holes and much effort is spent shoehorning menus into HTML-like documents, it's really not how Gopher menus are designed to function, despite such abuse even by yours truly who should know better (see, you really can get away with hypocrisy if you admit it).

All that wind-up was to offer a backdrop to contrast against Project Gemini. Gemini has caught a lot of people's fancy. Gemini finds Gopher too limiting and the Web too big, so it tries the Goldilocks approach. Rather than Gopher's strong menu-document hierarchy, Gemini lets you have documents with (horrors) inline links and formatted text, but the way in which you specify that document is intentionally bowdlerized compared to HTML so people can't keep adding features. Gopher has a trivial open wire protocol where the selector and a handful of item type characters do all the work, while heavy HTTP has a header and method for every kink and everything is encrypted, so Gemini splits the difference by requiring TLS 1.2 with SNI and MIME types but doesn't cache, compress or cookie. It's a vision of the web where if you want to view an image, you click on a link, scripting is for playwrights, and if you want to track a user across visits, it's hard and imprecise.

And that's the core difference, philosophically, between Gopher and Gemini: Gemini truly is a descendent of the Web, it's just one with an eye on the Gopher renaissance that happened inbetween. From the view of us inhabiting Gopherspace, it doesn't really occupy the same present-day niche: it isn't historical, because it's a new protocol, and it isn't retrocomputing, because the document format is non-trivial and its TLS requirement excludes even some relatively recent systems. It does this arguably intentionally, in fact. You can run a Gopher client on a Commodore 64 and I just wrote one for a 68030-based system; I don't see that happening for Gemini. Just like it won't displace the Web, Gemini isn't going to displace Gopher — though in fairness nor does it try — because it just simply doesn't scratch the same itches.

What Gemini will do, however, is essentially freeze Gopher in place by peeling off those who find it too constraining. Gopher+ was stillborne and merely a few consensus expansions have occurred like hURLs and CAPS, which are really just conventions anyway since they don't actually change the core protocol or how menus are parsed and handled. A few Gopher sites offer Gopher over TLS which only some specialized clients and proofs-of-concept can connect to. But since Gemini mandates TLS and every Gemini client already speaks it and some speak Gopher besides, plus the minimal work necessary to transcode Gopher menus into text/gemini, Gemini seems a more likely transport for a "secure Gopher" than bolting TLS onto a protocol that was never designed for it in the first place. These are the folks least likely to stick with Gopher because of its limitations anyway, so I don't see the phenomenon as anything that would harm the Gopher community. And freezing Gopher more or less as it was only enhances its historicity.

I've observed before that the Web and Gopher can coexist, and I don't see why Gemini and Gopher can't either. Despite their superficial similarities and some overlap in motivations, they don't really serve the same purposes and that's absolutely a-okay. From my view on port 70, anything that gets us to a point where people start thinking about alternatives to an increasingly out-of-control Web is a positive, and I'm glad both Gemini and Gopher are doing that.

Monday, October 12, 2020

Refurb weekend: Power Mac G4 MDD

I have the very first computer my parents ever bought (a Tomy Tutor, a strange relative of the Texas Instruments 99/4A). I have the first Mac I ever owned, a Macintosh IIsi which was a gift from a family at church; they got it as part of some pallet of stuff they bought and had no use for it. And I still have the first Mac I ever bought new. In fact, now that I think about it, I think this was the first computer I ever bought new.
Here's its sales receipt: a Power Mac G4 DP 1.25GHz (what is colloquially called a "Mirrored Drive Doors" or MDD variant). This was, at least officially and at that time, the last generation of Power Mac that could boot Mac OS 9 natively and the most powerful specification Apple offered that did so. In fact, I bought it specifically for that reason even though I was a starving student and really couldn't afford it because I still had, and have, a big investment in classic Mac applications. I don't remember if I got the education discount, but as specified with dual 1.25GHz 7450 CPUs, 512MB of RAM, 120GB hard disk, 64MB ATI Radeon 9000 Pro and an extra 512MB stick it cost me $3339 before tax in December 2002 (they comped me the install fee). The Quad G5 I bought a little over three years later cost roughly the same out of pocket, so in today's dollars the MDD was actually more expensive. I bought it on an Apple Loan, natch; the Quad I paid for in cash.

The MDD is still connected to my KVM along with my SGI Fuel, Quad G5 and the Raptor Talos II I'm typing on which is my current daily driver. The MDD was my daily driver for about three years, dual-booting Jaguar and Mac OS 9.2.2. Even after the Quad G5 replaced it as my primary machine, I upgraded it to 2GB of RAM (though Mac OS 9.2.2, now its sole operating system, only supports 1.5GB), a 256MB Radeon 9000 Pro and a Sonnet Encore/MDX processor card with dual 1.8GHz 7447A G4 CPUs. It also got a second SuperDrive and a second hard disk, a G4Port serial adaptor to replace the modem which I rarely used and a Formac ProTV tuner/digitizer, and it still has two slots and two more drive bays free. It's not survived perfectly -- the audio jacks got mangled in an accident, so I have to use a USB adaptor for audio in and a set of Pro Speakers -- but the important stuff still works dandy. Classilla is built on this machine, along with my work on Power MachTen and of course all those great late 1990s Mac game ports (though No One Lives Forever, which I bought with it, requires 10.1.4 so I play it on the G5 in Tiger).

On Sunday I was making progress on getting the classic Mac OS to speak TLS 1.3 with a little help (more about this soon). I pressed the power button to transfer my work over and test it and ... nothin'.

MDDs are great machines, but they're notorious for two particular failings: they can howl like anything (nicknamed the "Windtunnel G4") when they heat up, and their power supplies suck. The power supply was undoubtedly what failed, so that means ... a Refurb Weekend!

Ugh, dusty. The Post-It note on the front was to remind me of the Open Firmware path for the internal hard disks. Let's clean it up a little first. (By the way, I hate the speaker grille for these things, so I never installed it.)
The MDD was the last of Apple's "outrigger" Macs. This started with the Power Mac 7200, 7300, 7500 and 7600, and then evolved into the desktop Beige G3 and then reached its zenith with the Blue and White "Yosemite" Power Macintosh G3. These, along with the Power Mac G4, simply open by lifting a latch on the side and the entire motherboard swings down. No screws. Upgrades are a snap.

At this point we paused for a few minutes to blow out all the dust.

Naturally, the power supply (up top) is non-standard, just to be difficult. It is a modified ATX supply (in a weird case that reminds me of the Mini-ITX form-factor, at least in width) with different pinout that in addition to the usual voltages also provides 25V for the Apple Display Connector on the ATI GPU. They came in a 360W Samsung and a 400W AcBel. Fortunately I keep spares, so we will be replacing this 400W AcBel with the NOS 360W Samsung I had in my stock closet. (It is possible to rewire an ATX supply to provide the voltages sans the ADC's 25V line, but an ATX supply won't fit on the shelf inside the case to hold the PSU. We'll consider potential mounting options for the next time this thing chews through one.)
Giving the new PSU a quick test after unplugging the duff PSU's main power connector. You can see the power LED on the board and the fan for the Sonnet upgrade both activate and the Mac "bongs." Time to install it.
The main power connector and the power connector for the accessory drive bay run through the back of the case. This means the optical drives need to be pulled out to access them. Two screws, one at the top front facing the case front and one at the bottom rear facing you, hold the optical drive cage in place. Remove those, take the Faraday panel off the back, unplug the ATA and power cables and set the cage aside.
The main case fan doesn't need to be unplugged, though it might make this easier. Either way, lift it straight up from its little guides and put it aside. The main power cable is now exposed. To remove the power connectors to the hard disks in the rear drive bay, remove the screw from the drive cage's little eyelet (if the screw is there), then find the release lever on its left, pull it towards you and slide the drive cage up just a bit to free it from its socket. Remove the power connectors and, if you like, the ATA connectors as well and set it aside. If you choose to remove the ATA connectors, this is assisted by sliding off the flat black cable clip on the bottom of the case that keeps the ATA cable from bunching up.
A little aperture just in back of where the main fan sits is where the main power and the power lines for the accessory hard disk bay (unpopulated here) are threaded. Pull them out one at a time.
Two screws attach the power supply. The first is a 2.5mm hex screw. This usually needs some cranking to remove and replace.
The second is a Phillips-head screw that bolts a little metal eyelet on the end of the PSU to the case wall. Remove both, slide the PSU from the rear of the case towards the front and take it out.
Install the new PSU and bolt in both the front hex screw and rear Phillips screw. (The hex screw will spin uselessly if it doesn't mate. You need both in because without doing so the PSU will tend to wiggle or shift trying to connect the power cord.) Thread one of the power connectors and the main power connector back in through the rear aperture. Reconnect the main power.
Whenever I have one of my Power Macs open, I always check the 3.6V 1/2AA PRAM battery. This one was, not unexpectedly, dead as a doornail (that's 18.6 millivolts). Fortunately, I keep some of them in stock also.
Replace the main case fan, reconnect the hard disk and optical drive cages, and close it back up.
Back in action. I needn't point out that Mac OS 9 absolutely screams on a machine of this specification.
And just as pretty as ever. I actually do have an Apple Studio Display in storage with an ADC connector I've thought about pairing this with, but fortunately I also have the ADC converter box (basically a power supply that breaks out DVI and USB), so even if I did convert this to some sort of mutant SFF ATX (or SFX) power supply I could still use the Studio Display. In the meantime, I'll see about possibly getting this supply professionally refurbished. I have at least one other in stock along with a full body double but I'd like to keep this trusty machine going as long as I can.

Sunday, October 4, 2020

Stereoscopic computing: converting Quake and Doom

This article is part of a series -- you can read other entries

In our first 3D article, we talked about the basics of stereoscopic computing, including anaglyph rendering and active versus passive displays, and demonstrated a tool for turning a twinned set of webcams into a 3D image.

Games, of course, are the most obvious application, but early 3D games mostly dealt with the phenomenon as overlapping 2D planes due to the lack of 3D acceleration, limited CPU power and the predominance of 2D sprite hardware in contemporary video chips. These almost all used anaglyph colours (red/cyan glasses and others), but a few exploited the Pulfrich effect, and we'll look at some examples of these with the Commodore 64 in a later post. Even true stereoscopic systems like the Virtual Boy still largely used scaled planar images rather than true 3D graphics even though their separated video pathways yielded much better results (and eyestrain). As our previous article discussed, we try to avoid anaglyph rendering because all of them have some level of colour distortion (because they use colour differences to deliver a different picture to each eye) and ghosting (because that process is imperfect), and Pulfrich rendering, for reasons we will discuss, has only limited applications. For these examples, we will continue with my trusty Mitsubishi Diamondcrysta RDT233WX-3D, a passive polarized 3D display, though this should work for any passive 3D monitor or television. The general principles also apply to active systems like Nvidia 3DVision, though the details will be peculiar to each particular vendor-proprietary system.

One of the earliest games to have a true 3D mode built into it was id Software's Quake, and, perfect for alternating line polarized passive displays, it interlaced the left and right eye views. However, it was actually created for active glasses alternating between left and right views in sync with the alternating scans of a CRT monitor (here is one such scheme), though this method can still be simulated with a line blanker with modern display cards and LCD panels. This code persisted into early source ports of Quake when id released the code under the GPL, such as in the software-rendered SDLQuake, and is enabled by going to the console and entering lcd_x 1 (the higher the value, the greater the stereo separation). SDLQuake can still, with some effort, be built on modern systems but is strictly limited to 640x480. Naturally, interlaced images will not appear in 3D on a 2D monitor, and may need to be downloaded and moved to the right vertical position to match the polarization order of your screen. For reasons that will become clear in a moment, these were captured during demo playback and could not be paused, but they still serve for purposes of comparison even though the scenes are not exactly identical.

lcd_x 0 (default, 2D only)

lcd_x 1

lcd_x 5

The highest stereo separation yields great effects for most of the display but makes the weapon uncomfortably close, which even without 3D glasses you can tell by how widely spaced the left and right views of it are. For my money the game is really only playable at the lowest separation setting, except that on modern 64-bit systems, SDLQuake and other early releases are not playable at all; they crash immediately when trying to start a new game because they are not 64-bit aware.

Modern Quake ports have corrected this problem but in turn have eliminated much, if not all, of the original stereoscopy support. A few have secondarily added back 3D using alternative means. One such port is Quakespasm, a continuation of the older Fitzquake, which in turn descends from id's GLQuake. Unfortunately, it only has an anaglyph mode (set r_stereo to a non-zero value in the console) and the old interlaced mode is gone. Let's fix that!

Pure software rendering would deal with this problem by taking two images and going to bottom copying one view to odd lines and the other to even lines. A faster approach on systems with SIMD is to blit over one screen entirely using those nice fat vector registers and then line in the other appropriately, which is the approach we took in Camaglyph. However, this is using OpenGL, so we have an additional option of using the stencil buffer. A stencil buffer approach would draw alternating lines in the stencil buffer and then map the left view and right view depending on whether there's a line in the stencil buffer or not. GLQuake does not itself make use of the stencil buffer (too early) and Quakespasm only uses it to prevent intersecting shadows, so we can easily remove that small amount of code to have exclusive control of the stencil buffer since the shadow intersection code is infrequently of rendering relevance.

I don't claim this to be the best way of accomplishing it, but here's my patch against current versions. You will, of course, have to build it yourself from the source code. To enable it, go into the console (usually the tilde key) to set the appropriate options. r_stereo, same here as in regular Quakespasm, sets the stereo separation. I wouldn't make this much higher than 2.0; even that gave me a little bit of a headache, but since it's helpfully a float value 1.5 (in the console, type r_stereo 1.5) is a nice compromise since 1.0 doesn't really have enough depth. To enable interlacing, set the new cvar r_stereomode to either 2 (L-R) or 3 (R-L: my Diamondcrysta is R-L), or set it to 1 for the old red-cyan anaglyph. Because it allows larger screen displays the 3D-enabled Quakespasm is much more immersive and is really quite fun to play. For this screenshot I set the separation to a value of 2.0 so that the effect is a little more exaggerated.

This should work on any system (Mac, Windows or Linux, which is my usual environment) and on any graphics card of vaguely recent vintage.

I mentioned Doom in the title, which is more "2.5D," and mixes flat sprites (characters, objects) with 3D environments. There are many modern ports of this but the one I've come to recently enjoy is GZDoom, which as a built-in feature now has row interlacing as well as many other 3D modes in current releases of its renderer. To do this, pull down the console (tilde key) and enter vr_mode 12. Row interleaving in GZDoom is L-R, so I also add vr_swap_eyes true to make it R-L. You can also add this to gzdoom.ini wherever your system stores it (on Linux, ~/.config/gzdoom/gzdoom.ini). Because it tries to centre the window by default, however, I made a tiny patch to force everything to the 3D monitor which I use as a second, spanned display.

In future articles we will explore 8-bit ways of achieving 3D, and consider an alternate approach for when you want to convert a game to stereoscopic 3D but it's already using the stencil buffer.