Monday, March 29, 2021

The final official release of Classilla

An apology is owed to the classic Mac users who depend on Classilla as the only vaguely recent browser on Mac OS 9 (and 8.6). I've lately regretted how neglected Classilla has been, largely because of TenFourFox, and (similar to TenFourFox in kind if not degree) the sheer enormity of the work necessary to bring it up to modern standards. I did a lot of work on this in the early days and I think I can say unequivocally it is now far more compatible than its predecessor WaMCoM was, but the Web moves faster than a solo developer and the TLS apocalypse has rendered all old browsers equal by simply chopping everyone's legs off at once. There is also the matter of several major security issues with it that I have been unable to resolve without seriously gutting the browser, and as a result of all of those factors I haven't done an official release of Classilla since 9.3.3 in 2014.

Now that I've announced TenFourFox is winding down, let's just recognize the inevitable and officially declare that Classilla is no longer supported. I may still do minor work on it for my own purposes (for example, like issuing updates to stelae that I think might be helpful to other people, and if so I will post announcements about that here), but I don't make any guarantees on when or in what fashion or even if I will publicly release such work, and I will not be accepting bug reports or feature requests. Effective immediately classilla.org is now a placeholder with static documents. Files have been placed on a new permanent location on the Floodgap Gopher server, though files on SourceForge will remain as a faster HTTP mirror. Report-A-Bug is disabled and the Github project (which was never really used) is read-only as of now and will eventually be removed. Today Classilla is once again just a "hobby" project and I'm sorry I couldn't make it more than that.

Naturally the browser has always been open-source. The build instructions are intimidating but they do work, and I've collected the build prerequisites on the gopher server, which Classilla can access, of course. If you decide to make your own build of Classilla, all I ask is that you change the name to something else so people don't ask me about it.

To sort of make it up to folks, today I'm also releasing the incomplete work I have done towards 9.3.4. I'm calling this a "beta" since it hasn't had a great deal of testing, but you can use it or 9.3.3 (there are no substantial differences in security content). 9.3.4b has updates to layout, eliminating the old manual "slow scroll" option and automatically doing a more conservative repaint on sites that used to scroll incorrectly. Unfortunately this is sometimes also slower and occasionally dramatically so, and some sites will flicker as well, but no site will render worse than 9.3.3. It also has a contributed fix to JavaScript to fix a problem with high-precision math (due to a compiler bug in the version of CodeWarrior Pro 7 I use), and adds a convenience hot key (Command-Shift-Z) to toggle between "no style" and the default style sheet so that a badly rendering page can immediately be destyled.

Finally, as a last-minute thing, I also updated some of the built-in stelae and added a couple more for SourceForge, this blog and the TenFourFox Development blog. While the latter renders fine (if slowly), this blog doesn't, so I sped up both, and fixed visual problems and download issues with SF at the same time. I did this after I'd certified the source, though, so just copy them from the revised binary archive if you want to roll them into your own builds (I didn't feel like doing several more hours validating the source archive again for plain text files which can simply be copied).

However, these changes don't help much on modern pages for which the majority require TLS 1.2 to access at all. Although 9.3.3 added support for SHA-2 certificates and SNI, it's still limited to TLS 1.0, which was recently deprecated and which many servers no longer offer. Adding TLS 1.2 (and, for that matter, 1.3) capability needs sizeable updates to both Necko and NSS which are technically possible but non-trivial. However, now that we have Crypto Ancienne, an easier route is to modify Necko's proxy support to use carl as a backend, which I also implemented in 9.3.4b. If you run Classilla under Classic (as you might on 10.0 through 10.3), or Rhapsody's Mac OS mode, or run Power MachTen, then you can even self-host crypto support without a second system. Here's how.

First, set up carl, Crypto Ancienne's combination proxy and command line demonstration application, either locally or on a machine on your local network (I'll explain why this is important in a second). For Rhapsody/Classic and Power MachTen users, I have pre-compiled binaries available on the Floodgap gopher server that also include micro_inetd configured to bind to localhost. You can download these directly from Classilla or any other compatible Gopher client. The Power MachTen version runs on 4.1.4 and possibly earlier versions. The Rhapsody version runs on any Power Mac running any version of Mac OS X or Rhapsody 5.6/OS X Server v1.2, and possibly earlier versions. Source code is included.

  • Download the binary archive. On Power MachTen, put it into the root folder of the drive you are running Power MachTen from. On OS X or Rhapsody, you can leave it in your home directory or any other desired location.
  • On Power MachTen, log into the virtual machine; on Rhapsody or OS X, start a Terminal session.
  • On Power MachTen only, dfork //carl-machten-414.tar.gz ~/carl-machten-414.tar.gz (yes, two slashes). Change ~ to the desired destination if you want it anywhere else. This copies the archive from the Power MachTen root and strips any resource fork it may have accidentally acquired.
  • cd ~ (or where you put/copied the archive to)
  • gunzip carl-machten-414.tar.gz or gunzip carl-rhapsody-56.tar.gz
  • tar xvf carl-machten-414.tar or tar xvf carl-rhapsody-56.tar
  • You will now have a new folder cryanc with the binaries, so cd cryanc

Now bring up the proxy. On OS X with Classic or Power MachTen (assuming you are tunneling Power MachTen through Open Transport, which is the default), start micro_inetd listening to port 8765 like so: ./micro_inetd 8765 ./carl -p

Don't forget the ./s and the -p, or it won't start or listen correctly on the socket. If for some unexplained reason you are already using port 8765, then change that number in the command line and everywhere you see it below.

On Rhapsody, your instance of "Blue Box" Mac OS may be set up to use a separate IP address (this is the case on my Wally G3), which means connecting to localhost won't work. If you have only one IP address assigned to your main Rhapsody installation, but this address is different from what Mac OS is using, then run ./micro_inetd_any 8765 ./carl -p instead to listen on that interface. Be careful if your Rhapsody machine has a publicly routable IP address; this will make your system into an open proxy! If your Rhapsody install has multiple IPs, however, you really should be handy enough to modify micro_inetd.c and recompile it to listen on the right one.

Regardless, with micro_inetd running, now configure Classilla 9.3.4b. Classilla is based on an earlier version of Mozilla that allowed separate proxy definitions for "regular" HTTP proxies and special "SSL proxies" that supported the CONNECT method, since in those days doing so was not necessarily guaranteed. (Today every modern HTTP proxy supports CONNECT and the distinction is no longer relevant.) Here we have set both proxy settings to localhost on port 8765 — if you are on Rhapsody or using a non-standard Power MachTen configuration, or running carl on a separate machine, substitute that IP or hostname for localhost as necessary — though you don't have to proxy unencrypted HTTP traffic through Crypto Ancienne (that said, it will politely pass such traffic through).

The reason the browser prefers to use CONNECT is so the connection between the server and the browser is encrypted end-to-end (all the proxy is doing, in this case, is shoveling data back and forth). However, this means the browser is doing the encryption, which is not what we want. 9.3.4b adds a new preference called network.http.proxy.use-http-proxy-for-https which says that the browser should make an unencrypted request for an encrypted resource and defer the encryption to the proxy. Find this preference in about:config and set it to true.
Now view any https:// URL. The request will be forwarded to Crypto Ancienne, which will do the encryption for you. Here is Classilla 9.3.4b accessing Hacker News.
You'll note that the padlock icon which would ordinarily indicate a secure link shows an insecure one, and if you click on it Classilla will indicate that the connection is not encrypted. This is correct and intentional: the connection between you and the proxy is not encrypted. It just so happens that the proxy is the same computer via the internal loopback, so nothing can get in the middle. However, if you place Crypto Ancienne on another system on the local network, other systems on the network can potentially snoop you, and if you use this method to connect to a proxy that's not on your local network ... well, that's just dumb. Don't do that.

In a like fashion, since Classilla never sees the server's certificate in this configuration, it can't verify its authenticity either. Crypto Ancienne may do this in a future version but for now, you may wish to find other means of confirming the host you have connected to is the host you want to connect to.

Pulling it all together, here is a screenshot from a running system, my dual 1.8GHz Mirrored Drive Doors Power Mac G4 (the system used to develop Classilla) running 9.2.2 and Power MachTen. Yes, my copy is legally purchased from Tenon, spank you very much.

You can also use the same installation of Crypto Ancienne for MacLynx; just change the proxy URLs in lynx.cfg to http://localhost:8765/ for HTTPS and/or HTTP as appropriate.

As a polite warning for Power MachTen users, Classilla can still unsettle your Mac, and if it does, that can corrupt your MachTen FFS volumes. Once you get this set up, you may want to back up your FFS images and consider running Power MachTen and Classilla from separate partitions or hard disks. At some point I would like to port carl into an MPW Tool so you can run it there, but I haven't even started on that yet, and I don't make any guarantees I'll ever do so. If I end up doing that, just like with stelae for other important sites that you can download and add to the Classilla `Byblos` folder, I will post about those things here and there is a "classilla" tag for them.

Regardless, Classilla still serves a basic purpose for me, and with judicious use of destyling serves as a very basic browser that's a little more than MacLynx and Netscape, a little faster than iCab, and now can access more pages than IEMac can. Hopefully this gives it a little longer lease on life because classic Mac OS still has an interface and user experience no other OS, even little-m macOS, has ever matched. I learned a lot from working on it. Thanks to everyone who said kind things about it.

Saturday, March 20, 2021

When you have too much memory for SheepShaver

When I first got my 133MHz BeBox (not new, sadly), it had "only" 32MB of memory and it had four more SIMM slots to fill. While Be only officially supported 256MB of RAM, I was blissfully ignorant of that, bought an additional 256MB of memory in four equally sized 72-pin SIMMs and installed it for 288MB of RAM. (It can actually take up to 1GB, I later learned.) Nice, I said! And then SheepShaver never worked again.

SheepShaver is a desperate pun and an unusual emulator: much like Classic on PowerPC Mac OS X, on big-endian PowerPC most of the MacOS and its applications run natively on the processor, in a form analogous to KVM-PR. In fact, SheepShaver on Leopard is pretty much the best way to run Classic applications on Power Macs that must run Leopard, though it also runs on Tiger and presents certain advantages there as well. It existed first on BeOS as a paid product before becoming open source, though multiple later forks fix various problems on modern platforms.

My original theory was that I had somehow broken something in the update or some other installation, and so I never did much with it (especially since I have plenty of real Power Macs around here). But while I was doing other work on the machine, after a game of BeOS Doom I accidentally double clicked on its icon on the desktop and ... it started up! What could have restored it, I feverishly wondered? Did something monkey around with the memory map? (Foreshadowing music plays here.) It only ran the one time, however, and I spent hours trying to retrace my steps to see if I could make it work again and I never could.

But this at least told me that the install was fine and the problem lay elsewhere. I had never closely looked at it in a debugger. Perhaps it was time.

The BeOS debugger isn't gdb, but you get the idea. The offending instruction was an stbu (store byte with update), but the effective address was ... really weird. It looks like it's wrapped around the entire addressing space back to 0! How did this program even work?

In the source code, for all supported platforms, SheepShaver (and Basilisk II, a 68K emulator it shares substantial code with) has a SIGSEGV handler for trapping segmentation faults; here is BeOS's. My initial thought was that somehow the handler wasn't being installed, but a couple debug printfs in the handler showed that not only was the handler being triggered, it was actually passing the segfault along to the system handler apparently on purpose.

A partial explanation appears in the Darwin (Mac OS X) port:

Under Mach there is very little assumed about the memory map of object files. It is the job of the loader to create the initial memory map of an executable. In a Mach-O executable there will be numerous loader commands that the loader must process. Some of these will create the initial memory map used by the executable. Under Darwin the static object file linker, ld, automatically adds the __PAGEZERO segment to all executables. The default size of this segment is the page size of the target system and the initial and maximum permissions are set to allow no access. This is so that all programs fault on a NULL pointer dereference. Arguably this is incorrect and the maximum permissions shoould be rwx so that programs can change this default behavior. Then programs could be written that assume a null string at the null address, which was the convention on some systems. In our case we need to have 8K mapped at zero for the low memory globals and this program modifies the segment load command in the basiliskII [sic] executable so that it can be used for data.

So, the handler expects to have actual memory mapped indeed at an effective address of zero for the MacOS's low memory globals, a holdover from the 68K days (and if I'd read the Basilisk technical notes, I would have realized that sooner). Since such a fault should never have gotten to the handler in the first place, it just passes it along and crashes. That kind of significant address space remapping clearly could not come from a user-level executable on BeOS; there had to be some sort of system component doing that remapping.

Turns out SheepShaver did in fact install a couple system extensions:

$ find /boot/home/config/add-ons -name 'sheep*' -print
/boot/home/config/add-ons/kernel/drivers/bin/sheep
/boot/home/config/add-ons/kernel/drivers/dev/sheep
$ find /boot/beos/system/add-ons -name 'sheep*' -print
/boot/beos/system/add-ons/net_server/sheep_net
The last one is used for tunneling emulated networking through the host machine; the sheep driver is the one we want (the two sheep drivers are actually the same file; the dev/ one is a symlink to the actual file in bin/). After a little digging in the source tree, I found the C source for it. It became rapidly obvious after a cursory readthrough that it manipulates the PowerPC page tables.

On PowerPC (prior to POWER9 which introduces a higher-performance radix MMU), the mapping between virtual addresses and physical addresses is maintained by a set of hashed page tables, divided into page table entry groups, or PTEGs. (There is an alternate pathway using block address translation "BAT" registers but I'm going to ignore that for the purposes of this discussion.) The low memory globals region is 8K in size, so (with 32-bit PowerPC) we need two 4K memory pages to map to 0x0000 and 0x1000, which needn't be contiguous in real memory since we'll set up mappings for each page individually. The driver allocates three pages with malloc() and takes a page-aligned slice of two pages within it, then tries to find where in physical memory those pages got mapped to using get_memory_map(). Now we want to make those pages' effective address mapping in SheepShaver point to 0x0000 and 0x1000 instead.

To find a real address in 32-bit PowerPC, the top four bits of the effective address select one of 16 segment registers mapping each 256MB effective address block. The segment register's low 24 bits (the Virtual Segment ID) is combined with the 16-bit effective address' page number and 12-bit byte number within that page to generate a 52-bit virtual address. The VSID and the page number then get hashed and combined with the storage description register SDR1 to yield the address of the PTEG, the correct PTE is found within it, and the real page number within it then becomes the upper 20 bits of the resulting real page address. We're going to work this in a similar fashion to find the PTEG that would contain the mapping for these lowest page addresses.

Traditionally the number of PTEGs is optimally half the number of real pages to be accessed, and since the next highest power of two in a 288MB BeBox is 512MB, that means 229 addressable bytes in (divided by 4K, or 212) 217 pages. Halving that yields 216, or 65536, 64-byte PTEGs to equal a total size of 4MB. BeOS has a specific memory area for this, appropriately named pte_table, that we can look up with find_area() (thus giving us the effective address of the page table pointed to by SDR1). We find the relevant PTEG for each page by doing the same hashing steps the processor would do to resolve the address. In that PTEG, each PTE's highest bit is whether it's valid, followed by the 24-bit VSID, one bit for the hash type flag, five bits of the effective address called the Abbreviated Page Index, the 20-bit Real Page Number, and protection and access control fields.

We won't know the VSID without looking at the segment registers, but we can just walk the entire page table instead since we only have to set this mapping up once. When we find a valid PTE that matches the API, then we know this is a candidate PTEG and derive the VSID from that. We can then either directly modify an existing PTE within it or take advantage of the fact that each PTEG essentially offers up to eight hash collision resolution slots to add a PTE of our own. If we do this to the first place the CPU will look, we will take over that memory mapping for the life of the process.

The memory mapper conveniently has debug logging support for a simple tool called PortLogger that I patched up for BeOS R5. I compiled it with debugging on, restarted, ran PortLogger, started SheepShaver (it crashed, of course) and looked at the output:

$ ./PortLogger 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    PortLogger version 0.4.1
   Cameron Kaiser    - 14/02/21
   Simon Thornington - 14/02/97
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
init_hardware()
init_driver(3)
control(10000) data 0xfd001bb8, len 00000000
3 pages malloc()ed at 0x0202b228
Address aligned to 0x0202c000
Memory locked
get_memory_map returned 0
PTE table seems to be at 0x30000000
PTE table size: 4096KB
Found page 0  PtePos 58b84 V1 VSID c70 H0 API 08 RPN b11a R0 C0 WIMG2 PP0 
Found page 1d PtePos 58ba4 V1 VSID c70 H0 API 08 RPN b11b R0 C0 WIMG2 PP0 
Found page 1d PtePos 178580 V1 VSID d37 H0 API 2c RPN b11b R1 C1 WIMG2 PP0 
Found page 0  PtePos 1785a0 V1 VSID d37 H0 API 2c RPN b11a R1 C1 WIMG2 PP0 
Trying to map EA 0x00000000 -> RA 0x0b11a000
PTEG1 at 0x30034dc0, PTEG2 at 0x303cb200
 found 80069b80 00000010
 existing PTE found (PTEG1)
 written 80069b80 0b11a012 to PTE
Trying to map EA 0x00001000 -> RA 0x0b11b000
PTEG1 at 0x30034d80, PTEG2 at 0x303cb240
 found 80069b80 00001010
 existing PTE found (PTEG1)
 written 80069b80 0b11b012 to PTE
The driver seemed to properly reserve memory and find the real address (and thus real page number) for its mapping, and was able to resolve and walk the page table. But one problem jumped out immediately: we only have two pages (here 0 and 1d). Why is it that it found four? Notice that the "fraternal twin" pages have matching RPNs, but the VSIDs are different and we don't know which VSID is right. Did our algorithm effectively cause its own hash collision?

Continuing on, when we look at the existing PTE we found, the RPN is the first through fifth hex digits in the second word and both effective addresses match their real ones (80069b80 00000010 and 80069b80 00001010). That seems hinky.

My first thought was maybe we had a stale TLB and our PTE change didn't stick, because on the PowerPC 603 and 603e the code doesn't do a tlbsync to synchronize the translation lookaside buffer (which caches all this work) and this BeBox has two 603e CPUs. However, despite the code and Metrowerks saying it's 604-only, tlbsync is listed as a valid instruction in my copy of the 603e User's Manual Appendix A. I forced it to do a tlbsync by commenting out the check, compiled it again, restarted, ran PortLogger and started SheepShaver. Unfortunately, while it didn't do anything worse, it didn't work either.

My next guess was to see if maybe we were working on the wrong "twin." Assuming we really did have two sets of colliding hashes, what if we used the other one? A line of code to stop the search at the first page pair rather than the second was added and I tried again:

$ ./PortLogger 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    PortLogger version 0.4.1
   Cameron Kaiser    - 14/02/21
   Simon Thornington - 14/02/97
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
init_driver(3)
control(10000) data 0xfd001bb8, len 00000000
3 pages malloc()ed at 0x01587bb8
Address aligned to 0x01588000
Memory locked
get_memory_map returned 0
PTE table seems to be at 0x30000000
PTE table size: 4096KB
Found page 0  PtePos 33f04 V1 VSID c70 H0 API 05 RPN 45a8 R1 C1 WIMG2 PP0 
Found page 1f PtePos 33f24 V1 VSID c70 H0 API 05 RPN 45cd R0 C0 WIMG2 PP0 
Trying to map EA 0x00000000 -> RA 0x045a8000
PTEG1 at 0x30031c00, PTEG2 at 0x303ce3c0
 found 80069b80 00147010
 found 80076280 082b5190
 found 00000000 00000000
 free PTE found (PTEG1)
 written 80063800 045a8012 to PTE
Trying to map EA 0x00001000 -> RA 0x045cd000
PTEG1 at 0x30031c40, PTEG2 at 0x303ce380
 found 80069b80 00146010
 found 80076280 082b4190
 found 00000000 00000000
 free PTE found (PTEG1)
 written 80063800 045cd012 to PTE
Success! Now we actually have a free PTE, instead of modifying a questionable one, and we alter that. The mapping now takes precedence over anything else for that effective address and SheepShaver starts and runs normally. It also fixed Basilisk II, which would not run for the same reason, though SheepShaver seems to run 68K applications rather better than Basilisk II does.

Why was this never noticed? Well, like I say, Be never advertised support for more than 256MB in the BeBox, and in 1997 that would have been a significant amount of memory (my Power Mac 7300 in 2000 had 192MB and I thought that was a lot). Most PowerPC systems running BeOS probably had substantially less. Like many other bugs due to clock speed and RAM, no one ever dreamed future users would have such a surfeit of them.

The patched binary and source code are on Be-Power.

Sunday, March 14, 2021

Those mysterious Toshiba T-chips (plus: VTech madness with the Laser 50 and Type-right)

One of my wife's favourite possessions from childhood is an Australian Dick Smith Electronics (she's an Aussie, I'm a half-breed) Type-right. Much like Tandy Radio Shack in the United States, Dick Smith would happily rebadge anything he didn't have to engineer or build from scratch, and a number of his early computers were actually rebadges from Hong Kong company VTech. For example, the 6502-based VTech Creativision was sold in Dick Smith stores as the Wizzard [sic], and several of their Laser computers (notably the Z80-based 200, 210 and 310) were sold as the VZ line.

It won't come as a surprise to learn that this one is a rebadge too:

Yes, it's another VTech unit. In the United States it was sold (as the VTech Type-right) with an instruction manual and a tutorial cassette, but my wife only remembers it coming with a spiral-bound manual she is no longer able to find. Let's power it on.
The display is a simple 8-character LCD. (Cheap is the name of the game here. More on that in a moment.) We can select lessons "CLASS" or a "GAME," which simply shows a queue of letters you have to clear from the screen by typing. She liked it, and it was kinda fun, in a Typing Tutor III Letter Invaders kind of way. The class mode, however, offers multiple lessons:
Yeah, okay, you want to see the hardware, too. This is where my wife turned a little paler than usual when I went after it with a screwdriver. Don't worry, it still works!

Another thing you won't be surprised by: there's not much inside. It's pretty much mostly keyboard.

These cheap circuit boards are typical of the time. Everything in sight is covered in belched-up strands of epoxy. Although the sticker on the underside of the case has a 1985 copyright date, the actual board seems to have a manufacture date of "20 JAN 1988" with a yellow sticker "U.K." (Hong Kong was of course still a British territory then, but who knows what the sticker actually means).
There is a chip visible, but if we peel back the "T R" sticker,
we see it's just an Intel NMOS 8K 2764 ROM. (Best guess from the visible markings is that it was manufactured in the 35th week of 1985.) So where's the CPU?

If we look back at the boards, we see a dotted area outline with the legend "U1" under that folded length of stiff ribbon cable. Time to pull out more screws and turn the board over.

There is one big chip here in that position. Close up,
it's a surface mount Toshiba chip labeled T7951. What is this thing? Clearly it controls just about everything on the board!

The first clue comes from another VTech computer called the Laser 50, though not one that Dick Smith Electronics is known to have rebadged, which was sold in several countries including the United States. It resembles a Casio Pocket Computer and functions much the same way (even with the same P0 through P9 BASIC program spaces), but has a full keyboard and a carrying handle. Hmmmmm, does this form factor look familiar?

The underside also carries a 1985 copyright date.
Cracked open to reveal, again, an obscenely cheap design. Besides the liberal application of epoxy, also notice the cardboard washers where the screws go in the left PCB to prevent them from shorting traces. There are card edges for a 16K memory expander and a cartridge/peripheral bus, but I've never seen them used, and I'm told they mated incredibly badly. External power (though it does do marvelously well on batteries) and cassette connectors are on the top.
On the right board we again see silkscreened outlines labeled U1 and U2. This unit is non-functional and you can see they're definitely not meant to be repaired, so we're not going to make anything worse trying to get the board out. This turned out to be an unexpectedly difficult undertaking because the chips were literally stuck with (now degenerating) adhesive foam pads to the back of the LCD and it took a spudger to pry them off. They don't seem like they were there for cooling and in fact probably made things worse. I scraped off one so you can see.
Zooming in, this chip is labeled Toshiba T7813. I scraped the pad off the other chip after, and logically it's labeled Toshiba T7812. Otherwise they look identical.
The only other chip we saw was a Sharp LH5116-15, which is a 2K (16Kbit) static RAM chip. It has a manufacture date of 33rd week 1988.

There is precedent for subsuming multiple functions into a single chip; it makes manufacture cheaper (and these things are all about cheap). Many of the small Pocket Computers used similar all-in-one chips that contained the CPU, LCD driver, I/O, mask ROM and RAM. Armed with this information, we can comfortably conjecture that there are two in the Laser 50 because it has a 16 character display, so they must each run one half, and the Type-right just has one because it only has eight characters. Likely the only difference between the T7812 and T7813 are the contents of the mask ROM.

That just leaves what the CPU is. The Laser 50 does not have any facility for directly writing machine code programs, and we can't get into its ROM/ROMs (though if I wanted to wreck my wife's precious toy and be doomed to sleep outside forever I might be able to get the ROM out of it), but there are other Toshiba T-chips serving as CPUs in other machines. The ones I could find after a few hours of Googling include:

  • T7775 (MSX). This contains a Toshiba Z80 clone CPU with Intel i8255 PPI clone I/O and clock-bus-mapper-glue, with separate VDP (video), PSG (sound), ROM and RAM. It only appeared in four systems and they all dated from 1985.
  • T7826. This turns up in another cheap-as VTech device, the Whiz Kid. This unit has a copyright date of 1984. It seems even more deprived than the units here, but later, related-in-name-only toys in this series were more functional and some even had speech synthesis.
  • T7937 and T7937A (MSX-Engine v1). This also contains a Z80 clone CPU (the TMP84C00A), plus other notionally discrete peripheral T-chips on die, namely the Toshiba T7766A (PSG clone) and Toshiba T6950B (VDP clone). It also carries a TMP82C55A (8255 PPI clone) and clock-bus-mapper-glue, but uses separate RAM and ROM.
  • T9763, T9769 and T9769A,B,C (MSX-Engine v2). These are upgraded versions of the T7937 series with MSX 2/2+ capability, but are otherwise the same, and also use separate RAM and ROM.

By 1988, VTech seems to have abandoned the Toshiba T-chips and used a discrete and genuine Zilog Z80 in its PreComputer 1000. With this in mind, while we don't know what was in the T7826, all the other CPU-like T-chips I could find incorporate Z80s and VTech kept using the Z80 even after they stopped using T-chips, so these systems are probably Z80-based too.

Last but not least, which came first, the chicken Type-right or the Laser 50? Given the tendency of cost-reduced designs to be follow-ons, and that T-chip numbers appear to be more or less sequential, the Type-right appears to have been a cost-reduced design based on the Laser 50. And, well, probably a better design as well. After all, hers still works.

Sunday, March 7, 2021

Over the weekend: Commodore 128DCR keyboard extension, updates to Be-Power

Just some potpourri over the weekend. While working on other stuff and doing laundry, I finally put that keyboard extension on my Commodore 128DCR so I can put the keyboard in my lap if I want to. Any straight-thru DB-25 will work, so I used a 3-foot parallel printer extension cable.

Also, I was pointed to some additional BeOS PowerPC files, so I deduped and sorted them, and they are on Be-Power. Among other things there are now many more apps and also some of the OS updates. I also heard from someone who claims to have the old ftp.be.com archive completely mirrored, though for obvious reasons I'm mostly just interested in the PowerPC stuff. Haven't heard back from him yet when I asked for more details, but that's very encouraging for having a nearly complete mirror.

Wednesday, February 24, 2021

So long, Fry's

I can't say I'm surprised to hear that Fry's Electronics is closing their doors. Back in the distant pre-pandemic year of 2019, I went to a Fry's in Fremont to pick up some replacement parts since it was on the way. The store was dead. Dead, dead, dead. I counted more staff than customers and I figured that store was ripe for closure; now that everyone is ordering everything online, and particularly their tech-savvy customer base, it was inevitable that COVID-19 would kill them too. The stores were too big and had too much stuff that people only needed intermittently.

But I do have very fond memories of Fry's. I grew up in San Diego, and they took over the Incredible Universe location off Murphy Canyon Road when Tandy got rid of the brand in the 1990s. I bought a lot of classic Mac peripherals and upgrades there, many of which I still have, including hard disks, processor cards and 3D accelerators. If I needed a SCSI cable or a modular jack or some wacky board, they probably had it. I also spent a lot of time in their CD section; for the last few years they were blowing them out at $5 a pop, which was great, since I'm apparently the last person in America to actually buy discs. They also had 3D Blu-rays and were one of the few stores to keep carrying them in any quantity even after the major film studios largely stopped producing them domestically. And of course who can forget the long junk food aisle of temptation we all had to walk along to check out?

My other favourite Fry's include Roseville, CA, with the locomotive sculpture bashing through the front (probably my favourite store display). Here's a couple from there in 2018:

Also North Sacramento, CA, ended up there a lot when I was still regularly going to Sacramento on official business; Sunnyvale, mostly because of pleasant memories going to Halted Electronics down the street which also closed, of course; and Las Vegas, with the big slot machine. I also remember the Fry's in Anaheim with the space shuttle and my wife's new favourite book:
but all I have of the San Diego store was this blurry picture I took of their lock screen showing everlasting disdain for the competition,
plus Fountain Valley, San Marcos, Woodland Hills (all Southern California) and Fremont.

I don't think any of these are likely to remain retail stores. In the future post-pandemic days, this sort of boutique electronic sales will entirely be ruled by Pacific Rim dropshippers sending directly from Guangzhou, and it'll be the same stuff for less. But I'll miss the experience, though. Kids in a candy store never had it so good.

Sunday, February 21, 2021

MacLynx beta 3 -- really!

Yes, I'm back to further hijinx with the port of Lynx 2.7.1 to the classic Mac OS. I have great fondness for it because it was the first browser I ran on the first Mac I personally owned (a IIsi) and I'm delighted to be dusting it off. No, it's not just a monkeypatch like "beta 2" (in scarequotes) was: this is a real rebuild off the real original source code once I did some housecleaning and refactoring. Here it is, running in A/UX:
Or, here's some actual proof it's different. Even with Crypto Ancienne providing bolt-on TLS 1.2 capability, you couldn't view Hacker News with MacLynx previously because it was sent as text/html;charset=utf-8. Now you can (admittedly System 7 doesn't understand what UTF-8 is, but let's just handwave that away for now). There's no easy way to monkeypatch in a fix like that because it's new logic and new strings, not just changing old ones, so only a code change will fix it.
And, symmetrically, here's lobste.rs, though the reason you couldn't view it before was a bug in Cryanc that was fixed in current 1.5:
That's not the only change, either. Besides accepting UTF-8 (or at least not refusing), it also has a proper fix for beta 1's inappropriate Content-Encoding header, has a "live" scrolling area (click the top or bottom halves of the scroll area to advance or go back a page), and reduces the event loop's constant redrawing of the screen. There are no changes to the rendering core in this version.

Under the hood, although the alpha 5 source code (the only version that survives) claimed to have been built with CodeWarrior Pro 2, the projects are actually from an earlier IDE, and it isn't clear which version of GUSI or CWGUSI (GUSI being a shim library to map "conventional" Berkeley sockets and file handling onto MacTCP and MacOS) was used. In addition, even though CWGUSI 1.8.0 is included with CW Pro 2, MacLynx won't, er, link with it (it can't find stdout or _Stdout, depending on where the symbol occurs). I don't blame the GUSI authors for this; that's Metrowerks' fault because the Metrowerks Standard Library (MSL) made stdout a preprocessor define, not an actual compiler symbol, meaning the pre-built version of 1.8.0 included on the CD could not have been built with CW Pro 2 in the first place.

That said, because reproducibility with classic Mac OS homebrew is a problem (the cool kids now use Retro68 but this doesn't help much with old codebases), I'm sticking with CWGUSI 1.8.0 because it's already on the disk and for MACINTOSH software there's a veritable GARDEN worth of places to find CodeWarrior Pro 2. Rather than go to the inconvenience of rebuilding it again, this ugly hackery in the main MacLynx source file made the linker error go away and doesn't affect the SIOUX debugging window.

#undef stdout
FILE *stdout;

I also purged and cleaned up the filepaths in the project after converting it to the "new" IDE. The only other piece you'll need to build is Internet Config Programmer's Kit 1.4, which you can get from Info-Mac. Everything else is nice and relative to compiler and project so it's portable again:

There are now also separate prefix files for debug and release builds, too, so you can cut out debugging code completely from a release build. Right now this only excludes the SIOUX debug window from the release version, but I'll be looking for more targets to cut the fat in future versions.

That's not to say there weren't other significant problems with the toolchain upgrade, even after the application built successfully. As another symptom that CWGUSI 1.8.0 was mismatched, various errnos were wrong because GUSI's set originally encroached on other errnos used by the MSL and used a different set of values to compensate. That was no longer the case with CW Pro 2 and thus manifested as failure to open any network connection at all (because errno didn't correspond to any of the expected signals you would get on a non-blocking socket), and required forging new processor defines that matched up with the expected values.

With that fixed, HTML pages came up all smushed together, with no links. Gopher menus appeared fine, but anything that generated HTML from a Gopher menu (like a CSO search) also didn't work, as well as Lynx's internal interfaces for downloads and image helpers. Some flailing around in the CodeWarrior debugger traced the problem down to the binary search algorithm that finds SGML tags: it wasn't matching any, even though the tags were there and correct. The problem turned out to be Lynx's internal strcasecomp (not strcasecmp), which seemed to barf on the MSL's implementation of TO_LOWER() and was returning unexpected values. I rewrote it in an embarrassingly obvious form to the compiler and finally got sane results.

With the browser basically working again, I turned my attention to something which always used to annoy me in the day: if the pointer is over the MacLynx screen, it flickers. The issue here turned out to be how Olivier had originally merged the Mac event loop and Lynx's main loop. Lynx waits patiently for a key, but you have to keep spinning the Mac event loop to service the GUI and allow other apps to run. Thus, the Mac event loop, even if the user is doing nothing, goes through all the work to update the status line and screen as if the user had done something, and the Mac curses implementation is inefficient enough that this ends up requiring an entire screen refresh every. single. tick. of the main loop. That's why the pointer flickers, and why keystrokes lag.

To reduce the need for keystrokes, I expanded the mouse support (which right now is pretty much limited to selecting links and manipulating the pulldown menu) to make the previously unused scroll area live. You can now page back and forth in a document by clicking in the top or bottom halves, and then click on links, reducing waiting for the main loop to catch up with keying around from link to link. But the pointer still obnoxiously flickered, so I worked on several ways of reducing the screen update frequency. The Mac curses is rather dumb and I wasn't sure how effective making it do partial updates would be (an exploration for another day), but it seemed like I could short-circuit all that work if a key wasn't being pressed. This turned out not to be the case. Lynx desperately assumes you pressed something, and trying to skip whole portions of its event loop when you didn't would maddeningly cause it to drop or delay other keystrokes. Eventually I added a simple global hack to Mac curses that just turns refreshes on or off and I tried to batch them as much as possible, which besides improving responsiveness also cut out a lot of CPU load too. It might have been possible to extricate the two loops with a little thought using the Thread Manager and spinning both loops separately, but I didn't want to introduce a new dependency (I like it being able to run on very minimal systems, and Thread Manager wasn't a standard part of Mac OS until System 7.5), and I didn't want to add too much complexity by telescoping yet another event loop inside Lynx's wait-for-a-key routine, at least not this time around.

Now, after everything that's been added and updated, there is one thing that's been removed: the PowerPC native build. That might seem strange coming from a vehement pro-Power ISA bigot like me (typing this post on a Raptor Talos II POWER9 workstation, even), but the reason is simply because I don't want to deal with multiple versions of CodeWarrior on my MDD G4, so I'm building this on my Quadra 800 instead and only a 68K target is available. Doing so, however, also allows me to test it on the same machine in System 7.1, System 7.6, MacOS 8.1 and A/UX 3.1, and keeps any additional toolchain changes I may need to make localized. I may resurrect the PPC build in the future but Classilla with styles off is more functional than MacLynx PPC, and of course this 68K build will run just fine on a Power Mac anyway.

What's next? I thought about porting a later Lynx but later releases may incorporate changes I might not want, and I have no idea if they would even build. More to the point, I haven't even been able to enumerate all the other changes Olivier already made to get it working, all of which would probably have to be adapted or dragged forward. I think improving its handling of <div> tags, especially with respect to positioning, as well as tables and HTML entities will get us a large part of the way to parity without introducing other potential incompatibilities. There are unused menu resources in MacLynx; those should be hooked up. I did initial work on a dialogue box for entering URLs instead of using Lynx's prompt, which is much faster than typing into the curses-driven prompt and lets you use cut and paste and the mouse, but that isn't in this version yet. And, because Lynx does know how many pages long a document is, it should be possible to actually have a real scroll bar which works by sending events to Lynx instead of the up/down mouse shortcuts (for that matter, we also need a mouse hotspot for backing up -- which is actually in this release, but I don't know if I'll keep it in its present location).

Finally, there's the whole topic of TLS 1.2. MacLynx can only access TLS sites by using Crypto Ancienne as a proxy (or anything else that offers an HTTP-HTTPS proxy, but I haven't seen others). It might be possible to build this into Lynx, but also keep in mind that most 68K Macs aren't fast enough for modern servers. My Quadra 800 is clock-chipped to 40MHz and is able to keep up by running the proxy self-hosted in A/UX or MachTen, but in my experience systems start running into trouble below that speed: my 36MHz Solbourne S3000, a SPARC, times out on a couple sites, while my 25MHz '030 IIci with no cache card can sometimes take up to 20 seconds to do a transaction and failed on most of the sites I tried. With those numbers it's painfully obvious a 8MHz 68000 wouldn't have a chance. For these, it makes more sense to run Crypto Ancienne on some other system and have the Mac talk to it because only the fastest 68040s are swift enough to do so themselves. However, current versions of Lynx don't allow offloading TLS to another system using an HTTPS URL to an HTTP proxy anymore, another reason to stick with 2.7.1.

Anyway, visit the MacLynx page for updated builds and the source code, and post your comments. Watch this blog for beta 4 when I feel up to it.

Sunday, February 14, 2021

Make the BeBox great again: TLS 1.2, inetd and more for PowerPC BeOS R5

Many nerds are at least historically aware of the BeOS, which died an untimely death two decades ago this year (when parent Be, Inc. sold out to Palm in 2001 and self-liquidated). Established in 1993 by former Apple exec Jean-Louis Gassée, Be's new OS was meant as a media-savvy alternative to MacOS and Windows, but with POSIX compatibility (largely), a command-line shell option and pervasive cheap multithreading, which is probably its most notable technical feature. It survives in recreated spirit on modern PCs, if not a direct descendent, as the perennially-beta Haiku.

A few nerds, however, will recall that BeOS didn't originally run on x86. In fact, its original architecture was one almost nobody remembers, the AT&T Hobbit, a strange stack-oriented CPU specialized for running C programs. The Hobbit had few takers due to its cost and various technical issues (Apple eventually rejected it for the Newton, leading to the rise of ARM), and when AT&T decided to kill the project in 1993 it nearly killed Be as well, who were using it for their dual-processor prototype wonderbox. After all, the best way to show off your all-singing, all-dancing, all-threading new operating system is with extra CPUs to power it.

Be regrouped around the PowerPC 603, which led to some unique technical issues of its own because the 603 has only three cache coherence states (MEI), making it notionally insufficient for multiprocessing. (This was carried over to the G3 as well, which is really just an evolved 603; the 604 offered a fourth state, and the G4 the full five MERSI states.) With little choice to get a product out the door, Be had to get around this problem with extra hardware to forcibly keep the processor caches synchronized. Be ended up making around 2,000 of the striking blue-and-beige PowerPC BeBoxes, deliberately targetted at technical users, over half of them in the slower dual 66MHz version and later a 133MHz version in the minority. Touches like the zooming LED load meters on the front, built-in MIDI and the customizable Geek Port made them beloved machines by their few owners: author Neil Stephenson, famous for Snow Crash, wrote the essay In The Beginning Was The Command Line with his own BeBox in mind. Pointedly, he declares in the essay that "[w]hat holds Be back in this country is that the smart people are afraid to look like suckers."

Naturally, there's a BeBox here too at Floodgap, a dual 133MHz model with 288MB of RAM running BeOS R5, the last release for PowerPC. And with a little hacking to get around its non-POSIXisms, it now has its own port of Crypto Ancienne with TLS 1.2. The screenshot is what's on the monitor (just press Print Screen anytime and a Targa file is dumped).

The Power Macintosh 7300 under the monitor isn't running BeOS, though it could (not sure if I'd need to remove its 800MHz G4 upgrade card, but it's basically compatible). Aside from PowerBooks (maybe the 3400 could be tricked into booting), PowerPC BeOS would run on pretty much any PCI beige Mac with a 603 or 604 CPU, including the clones. It even boots on systems with aftermarket G3 upgrades. It wouldn't run on an actual beige G3, however, and it wouldn't work on any New World Mac that came after.

And that's the reason why PowerPC BeOS withered after R5: Apple wouldn't provide technical documentation on future models, and Be didn't want to make the company dependent on reverse engineering them. By 1997 the BeBox, only ever a niche product for a niche OS, was discontinued. While Power Computing and other vendors still offered BeOS with their Power Mac clones, the Mac clones were themselves dying out and Be proceeded full speed ahead on an x86-compatible BeOS, releasing the dual-architecture R3 in 1998. PowerPC users became quickly neglected: BeOS never released a "try before you buy" personal edition of BeOS for the Power Macs, and unlike the situation with NeXTSTEP where fat binaries for all architectures were the rule for most software, the majority of developers simply wrote for x86 alone. There was never another browser for PowerPC BeOS other than Be's own NetPositive (while x86 had Opera and Mozilla), which is why I didn't show any BeOS browsers magically empowered by Cryanc in the screenshot, and when BeOS R5.1d0 "Dano" was leaked after Be's demise featuring the improved BeOS Networking Environment (BONE), there was no PowerPC release. At the time LowEndMac observed, "If you feel like Macs are treated like second class citizens, wait until you switch to BeOS — you might soon get the feeling of a fourth class citizen."

Nowadays I'd beg to be a fourth-class citizen. All of the old ftp.be.com archives appear to be gone, along with most of their games and freeware ports. A few packages developed by third parties survive in their original locations, and a few more in the Wayback Machine. There was a egcs port to PowerPC BeOS, but it seems to have evapourated completely, leaving BeIDE and Metrowerks C/C++ as your only development choice. I don't have many software packages but what little I do have for PowerPC BeOS I put on the Floodgap gopher server.

And no Intel crap. Twenty years later x86 has Haiku, which on 32-bit can run all your old x86 R5 apps and new ones besides, so x86 BeOS doesn't need our help. Instead, let's make the BeBox (and PowerPC BeOS generally) great again. And, hey, any of the Hobbit BeBoxes still out there too, being personally aware of a couple. (Especially if anyone wants to send me theirs.)

In future posts I want to talk about some of the other things I've been doing on this BeBox, including patching the SheepShaver Power Mac emulator (fun with page table entries) and writing a gopher client in BeIDE. But today, let's talk about porting Crypto Ancienne to BeOS, writing the only currently existing inetd-like environment for PowerPC BeOS, and why I say R5 is only mostly POSIX compliant.

Crypto Ancienne's core crypto library, ultimately derived from TLSe and libtomcrypt, is written in pre-C99. In fact, version 1.5, the current release, not only adds support for BeOS but also Tru64, IRIX 6.5 and SunOS 4, plus contributed builds for 68K NeXTSTEP, Professional MachTen, Haiku and Solaris 9 along with its previous support for Mac OS X (PowerPC and Intel), AIX, A/UX, Power MachTen, PA-RISC NeXTSTEP and of course Linux and the contemporary BSDs. While gcc 2.x is the most common compiler on these platforms, we also added support for MIPSPro on IRIX, Compaq C on Tru64 and Metrowerks C on BeOS. The core is generally the easiest portion to compile once you find the way the OS likes types and prototypes specified, and Metrowerks C had a good reputation for standards compliance, so other than adding a hack to get function-local variable allocations under 32K (!) that much was uneventful.

The tricky part turned out to be carl, the Crypto Ancienne Resource Loader, the Cryanc demo application and a desperate pun. BeOS has some unusual aspects to its POSIX support, all of which were rectified in Haiku, which built with the default code pretty much unmodified. The needed hacks boil down to the fact that, like the Windows API, standard input, standard output and standard error aren't "normal" filehandles. Let's say you want to check if there's input on an arbitrary file descriptor. There are no less than three non-interchangeable ways in BeOS:

  • If it's a socket, you can use select() like normal right-thinking people. There is no poll(), but overall this works like you think it should. This is also true for Winsock.
  • If it's a file or pipe, however, you can't. Instead, while this isn't well documented, you can make it non-blocking (something like (void)fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);), and then busywait on the descriptor (return (read(fileno(stdin), &throwaway_char, 0) >= 0); will tell you if input is present). This is somewhat like PeekNamedPipe() in Win32, except that BeOS seems to lack any bespoke function for this purpose, and both require a similar combination of timeouts and alternating calls if you're waiting on a network socket and standard input.
  • But, if it's a TTY, it all goes out the window because there's an even more poorly documented ioctl you have to use instead (ioctl(fd, 'ichr', &numcharswaiting)). Haiku even preserves this ioctl for compatibility, though it is obviously discouraged. The non-blocking read() trick might also work but I ended up having to do a combination of both approaches, and even that doesn't work quite right.

For carl's loop where it transmits data from standard input and receives data from the socket, that had to be modified to check a utility function (stdin_pending()) and time-limit select() so that it could go back and forth between the two descriptors. This is ugly but it works, and the successful result is what you see in the screenshot (I grepped some lines from the HTML from lobste.rs as proof-of-doesn't-suck).

On the Crypto Ancienne web browser demo we showed that those computers could self-host their own carl in proxy mode so that they were their own "crypto proxies," assuming a suitable level of web browser support (or coercibility). NetPositive, your only choice on PPC BeOS, resolutely insists on using its own state-of-the-art 40-bit encryption over SSL; I'll see about hacking that later. Still, carl doesn't listen on sockets itself and relies on inetd or inetd-like environments such as xinetd (hi, Rob!) and Jef Poskanzer's micro_inetd, my personal favourite mini-inetd. We demonstrated running it as a proxy with micro_inetd on pretty much every other one of the OSes Cryanc supports, so it would be nice for BeOS to do the same.

Well, it won't come as a surprise to you that BeOS R5 works with none of these. Back in the day, it was even argued it might not be possible to implement inetd at all because sockets aren't shared across fork() (typically, for most inetd-like environments, they fork(), connect the socket to the standard filehandles and launch the dependent program, but this approach is unpossible in BeOS for that reason). Furthermore, you might think that net_server, the team (i.e., process) responsible for sockets in BeOS, would implement something of the sort and you would be wrong; the telnetd and ftpd in R5 are implemented differently. BONE does have a classic inetd but only because it fixes this problem as part of the other significant underlying changes in Dano, none of which were made available for PowerPC.

So this post also introduces inetb (kneeslaps and guffaws), less a port than a heavily multithreaded reimplementation of micro_inetd. Near as I can determine, this is the only inetd-like system that can run on a pre-BONE system. How can we do this if we can't pass the socket to the process we fork() to? Easy: don't pass the socket with fork()! Download it from Be-Power, or follow along with this gist:

  • We start up inetb with the port number and the dependent program. Let's use ./inetb 8765 awk '/quit/{exit}{print $1+1}' as a nice interactive example: this takes input, quits if it's quit, and otherwise tries to coerce it to a number and add one to it.
  • We listen on the port, initalize our array of iothreadstates (a struct we use to track sockets in flight), set up signal handlers for SIGCHLD and SIGPIPE, and go into an accept() loop. So far, so standard.
  • When we get a connection, we assign a new iothreadstate and then use an implementation of popen2() to fork() the dependent process but using pipes, not the socket.
  • Now for the BeOS magic. With the dependent process now running and its standard filehandles connected to pipes, we then start two threads, one to read from the process and write to the socket, and another to read from the socket and write to the process. (I have intentionally not implemented standard error: it's convenient to see it for debugging in the terminal you're running inetb from. Exercise left for the reader, but it would be a third set of pipes and a third thread.) The main thread goes back to its accept() loop to take more requests.

In the normal case, let's say that we quit this miniature awk session properly with the quit command. How do the threads react?

  • awk terminates, sending a SIGCHLD to the main thread and triggering the signal handler.
  • The signal handler reaps the process and based on the PID finds its iothreadstate. It then launches a cleanup thread for that iothreadstate, and goes back to the accept() loop to take more requests.
  • The cleanup thread now has to make the threads quit cleanly, since killing them leaves a mess in net_server (killing teams cleans up resources, but not individual threads within a team). It does this by sending a message to both the read-from-process and write-to-process threads. Any message will make them quit.
  • For the read-from-process thread, this is sufficient to interrupt its blocking read(). It sees there is a message, and exits gracefully.
  • For the write-to-process thread, this is a little more complicated. Even though the socket read should be blocking, in practice signals regularly interrupt it, so we use a select() on the socket to ensure we really do have data to read. There appears to be a bug in BeOS, however, where sending a message sometimes doesn't interrupt select(). We get around this problem by having the select() timeout every 10ms so it can look in its queue, which is less elegant, but better than a tight loop. Anyway, it too sees there is a message, and exits.
  • After waiting for both threads to exit, the cleanup thread flushes the socket and closes everything, returns the now spent iothreadstate to the pool and exits itself. Meanwhile, the main thread has already gone on to service other requests. Ain't multithreading great?

What happens if the user just disconnects?

  • As in standard POSIX, the write-to-process thread sees that the socket is ready but there is no data. Assuming a signal hadn't arrived, this is treated as a disconnect. It kills the dependent process (this is an entire team, so it's safe) and quits.
  • awk has just been killed, so a SIGCHLD goes to the main thread, triggering the signal handler.
  • The signal handler reaps the process, finds the iothreadstate, and starts the cleanup thread as it returns to the accept() loop.
  • The cleanup thread takes down the read thread as well by sending it a message, flushes the socket, closes everything and terminates. Meanwhile, the main thread has already gone on to service other requests. Another stupendous day in Cheap Thread Land!

It's BeOS' ability to effortlessly spawn huge numbers of thread even on constrained systems (even in 1996 a dual 133MHz wasn't stonking fast, and certainly not the 66MHz version) that makes this arrangement work effectively. Want to handle something asynchronously instead of busywaiting? Make a thread! The thread can block (usually)! Perfect for UI or network! Even the cleanup is asynchronous in inetb so that as little happens on the main thread as possible. The kernel handles all this messing around for you as long as you play by the rules.

BeOS isn't perfect, though, as that last sentence will attest. During my testing of inetb I unsettled net_server a lot. You can restart networking from its preference window, but it seemed bad that I had to do this as often as I did. In fact, as an unrelated note, I was able to pretty much wreck the machine every time if I accidentally started CDBurner. I don't have a burner and you'd think it would handle that circumstance, but it doesn't. The machine goes haywire if I'm lucky; it locks up if I'm not. I eventually had to remove it from the Applications menu. More generally, the notion of uids and gids is a veneer and you're pretty much doing everything as the superuser. That means wrong moves hurt.

But don't forget that early Mac OS X had its own weird problems during its earliest versions. BeOS, at least superficially, gives you that similar experience of a POSIX-alike underpinning with better multitasking and memory management, and it was definitely lighter on system resources than early OS X was, too. What NeXT had was Steve Jobs and a longer history with Apple than Jean-Louis Gassée, and while it is variously said that Be's demanded purchase price is what turned Apple away from buying them, I've always thought it was just a cover story for the real deal to get an original Apple founder back. And that worked out handsomely for Apple. But I think BeOS could have served as the next Mac OS at least as well.

Our next BeOS entry will talk about SheepShaver, which you can think of as "Classic" for BeOS. It even runs PowerPC code natively for surprisingly useable performance. But it started crashing incessantly after I upgraded the RAM in my BeBox. Can we fix that? Of course! Find out how next time!

Friday, February 5, 2021

The quest for Wolfenstein 3D music on the Apple IIgs

Many people are unaware there is a port of Wolfenstein 3D to the Apple IIgs (my GS is currently still in working order). It's actually a surprisingly good one, derived in part from the Macintosh port with a custom soundtrack, with a strange history of its own. It plays well enough on my 8MHz TransWarp GS accelerator with the viewport about half size, but you really need a ripping soundtrack to mow down Nazis, and while I could revel in their screams the game never once played a note of music.

Naturally, because coronavirus, I decided to spend a weekend rectifying this. And yes, it's the latest 1.1 version, yes, the checkbox for Music was checked in the Wolf3D preferences (Command-P), and yes, other sound effects worked fine.

I like to think I have a modestly tricked out system (hard disk, GS/OS 6.0.1, accelerator, ROM 03 with a Woz Special Edition topcase) but one thing it was a little weak on was RAM. A ROM 03 IIgs has 1.125MB on board (1MB, plus 128K); I had a fully populated 1MB Apple RAM expansion card in, so I had 2.125MB total. Officially the game doesn't play on anything less than 4MB, but it clearly ran, so I figured RAM was the immediate problem.

There are many choices for RAM upgrades for the IIgs but one with a long history (albeit differing opinions) is the GGLabs RAMGS/8. This gave me 8320K (8MB plus 128K on board), the price wasn't exorbitant and I could nab one easily on eBay. Compared to the other cards in my system, it's rather diminutive (and doesn't need the strut prop the long Apple RAM card did).

Its sole installation direction is a silkscreened arrow saying "FRONT." This is a little nerve-wracking because the slot has no keys and the card could go in it either way, and the installation appears backwards. But that's how it's supposed to fit (the controller board for the InnerSpace hard disk is behind it):

With that, I started up GS/OS, admired the nice ample addressing space in Get Info in the Finder, and ran the memory test provided with AppleWorks GS. After many minutes I got a clean bill of health.

However, I still got no music out of Wolfenstein 3D. Trashing the prefs file made no difference. So what else was I doing wrong?

The classic Mac OS divides components up into control panels and extensions primarily but GS/OS deals in control panels and tools. On a ROM 03 system, about half of these tools are built into the ROM and the rest reside on disk. I had a vanilla GS/OS install with all Tools through 034, but music support needs Tool035, which is not installed with standard GS/OS.

After some digging I found the Tool as a standalone file on an unrelated disk image, but this disk image was in the newer-fangled .2mg format, which I can't directly turn into a floppy with DiskCopy or DiskDup on my Power Mac 7300. (Yes, you can use things like ADTPro to write the disk on the IIgs itself, but this Mac has a suitable floppy drive, and it seems like I should be able to use it rather than messing around with serial cables.) CiderPress will convert these, however, and while the GUI is Windows-only you can build some pieces of it on Linux. None of them would directly do what I wanted, which is to turn it into a DiskCopy image, but the source code gets you most of the way there. Edit Convert.cpp and find the line switch(18). This is the destination format. Since I knew this was an 800K image, I changed the 18 to 14, which specifies a DiskCopy 4.2 image, and used the badly-named iconv (not to be confused with libiconv) to generate the image. On the Power Mac 7300 I changed the type and creator codes in ResEdit to dImg and dCpy respectively so that DiskDup would accept it. Bang, a 3.5" floppy with the needed tool.

Triumphantly I returned to the IIgs, inserted the disk, copied the Tool over, restarted GS/OS, started Wolfenstein 3D, and indeed heard some very impressive music out of the wavetable synthesizer. Success! Unfortunately this is where I think I pushed my luck: the 8MHz TransWarp GS just can't keep up with music and the rendering. In fact, while it tries to load the game data you can intermittently hear the music grind to a halt (it even crashed out to the monitor once, presumably because the data didn't arrive quick enough), and you have to have the viewport at postage stamp size to make it playable. I'm glad I have 8MB of RAM and it works very well in everything else, but after all that I decided to turn music back off so I could actually play the game (but I kept the Tool, in case other games require it).

So, with the RAM situation licked, I guess now I'm in the market for a faster IIgs accelerator. In today's world, a man's gotta kill Nazis with a soundtrack, you know.

Friday, January 22, 2021

Things I've learned about A/UX

I've been working more frequently with A/UX as actually a user rather than merely a collector/tourist to improve support in Crypto Ancienne (by the way, git tip has fixed the known remaining issues with A/UX and it now passes my internal test suite). Here are a couple things that don't appear in the manual or in the otherwise comprehensive A/UX FAQ.

If you're puzzled why you can't Telnet into your A/UX machine, nfs0 needs to be set to wait and net9 needs to be set to respawn in /etc/inittab, or incoming connections like Telnet and FTP don't (or, depending on what inetd you're using, connections may just sit there and inetd fails to spawn the daemon, sometimes for as long as a half an hour). This means you need to be running /etc/portmap as well as /etc/inetd; you can't run just inetd. You should probably also upgrade to jagubox inetd. You might be able to get around this by not using portmap services in /etc/servers but I haven't needed to try that.

If you are sitting at the "Welcome to A/UX" dialogue box (i.e., you aren't logged into the machine and you have autologin disabled), you have to select Special, Restart to properly unmount the file systems. Selecting Special, Shut Down bizarrely leaves them dirty (forcing a long and unnecessary fsck on the next boot), and running shutdown from a root console doesn't consistently work right either. So now I have it rigged to not autoboot from the Mac boot partition, I select Restart from A/UX when I'm done, and then when the machine comes back up in the Mac boot partition, the A/UX filesystem is clean and I just shut down the Mac partition without continuing through the boot. The downside is I have to press Cmd-B manually to start the boot when I do want to be in A/UX.

This machine runs A/UX with my custom partitioning, which I document in more detail elsewhere.

I do have to say that on my clock-chipped Quadra 800 (to 36MHz), A/UX is a real pleasure. If they had ported it to PowerPC natively I bet it could have really been something spectacular even though it was sort of a dog's breakfast under the hood (but in that respect no worse than the classic MacOS).