Showing posts with label 68000. Show all posts
Showing posts with label 68000. Show all posts

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.

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).

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.

Sunday, September 27, 2020

Hacking a gopher client into the Alpha Micro

I have great personal affinity for the Alpha Micro multiuser systems, which were the first multiuser computers I ever experienced (an AM-2000 and a battery of terminals at the Salvation Army corps my family and I attended as a kid). These blue-collar machines were famous for their high level of vertical integration; many ran back offices from churches to veterinary offices, and I'll bet a substantial number still do, sitting in a closet quietly continuing to do their job. It is estimated that around fifty or sixty thousand Alpha Micro systems were produced based on known serial numbers. My two main systems came from a party store and a emergency dispatch center, and the primary one (a 1994 AM-3500 Eagle 300, front panel closeup shown above, and the subject of a recent Refurb Weekend) has a webserver and you can access it.

AMOS, the Alpha Micro Operating System that they run, is a curious beast especially to modern sensibilities, largely because of its strong DEC influence via TOPS-10. Although AMOS is not a descendant, it adopted many of its conventions to the point where DEC actually (though unsuccessfully) sued Alpha Micro over it in 1984. The filesystem is 6.3 and case-insensitive, and instead of subdirectories files are divvied up by octal project-programmer numbers (like [100,7]) on specific devices; all file access is by device and PPN, or aliased pseudodevices called ersatzes that point to specific locations. On the CPU side, AMOS is populated by process-like entities called jobs (at least one is attached to every terminal or pseudoterminal) with a system limit on the number of jobs that can exist. On startup jobs have a fixed memory partition allocated to them to store their data and program code, and their permissions are generally determined by the particular PPN they started logged into (for example, [1,2] is the operator and essentially "root"). Some components can be loaded into a systemwide memory partition and be available to all users, though each user does not get a separate copy (so things like COMMON.SBR that implement a primitive key-value store may allow users to see other users' data, unless they are loaded separately into individual user partitions and/or the program is specifically engineered to be "reentrant").

AMOS is also one of a few operating systems (in fact, I struggle to think of any, though there probably are some) that effectively runs the 68K CPU in little endian mode. The CPU is still big-endian internally but the bus lines are swapped in order to preserve data file compatibility with earlier version of AMOS that ran on customized WD-16 CPUs. Opcodes in particular are little-endian, which throws off the ability to easily disassemble code, and Alpha Micro's somewhat non-standard mnemonics don't help. AMOS also does not use nor require an MMU and thus is a strictly real-memory OS with no concept of virtual addresses. The kernel (which is called the "monitor") can still preemptively multitask between jobs on the system, but to deal with memory partitions occupying different addresses, programs are required to run from any location they are placed ("relocatable"). Memory protection is likewise relatively limited in scope, meaning a program that goes awry may occasionally require the machine be forcibly rebooted, although fortunately the OS and filesystem are tolerant of this. The gemisch of components and jobs loaded on system startup is very reminiscent of classic MacOS and while they tend to be better coded and conflicts are rare, once in a great while they can clash just as badly. Making a wrong move with the startup script AMOS32.INI is a great way to render your machine unbootable.

One collection of these components is AlphaTCP, effectively a port of SpiderTCP (and enough of SpiderStreams to implement it), an earlier TCP/IP stack whose most famous implementation was as part of Windows NT 3.1 (replaced in 3.5). However, until the very last version of AlphaTCP it had no means for programs to directly open network connections of their own -- and this particular API, if you can call it that, is pretty baroque anyway -- and my workhorse Eagle 300 still runs an earlier version that doesn't have it regardless. That means relying on what preexisting functionality is already present to support the widest variety of machines. Depending on release AlphaTCP comes with device drivers, servers for HTTP, SMTP, FTP, Telnet, POP and rwhod, the usual utilities such as ping and traceroute (which it calls TROUTE), and clients for Telnet, FTP, mail, HTTP, TFTP and ... finger. Hmmm.

You see, one of my other great personal affinities is Gopherspace. Especially with the "TLS apocalypse" severely limiting the ability of older browsers to access modern Web sites, I think Gopherspace is a better fit for old machines, and clients can be written even for slow memory-constrained 8-bit platforms. (That's an upcoming post, by the way.) The protocol is trivial and interpreting a menu is much, much, much less complex than even relatively simple web pages. And interestingly, the finger protocol and the gopher protocol are in broad strokes identical: send a selector followed by CRLF, get a result, close the connection. The finger client is also well-behaved and can emit files to disk with redirected I/O. Hmmmmmm.

The original way I solved this problem was something I called the "fingerproxy," a modified finger daemon that runs on a proxy host and forwards or parses commands. But this is clunky and isn't self-hosted, and eventually the limitations of this approach became excessive, so a couple weeks ago I decided to make the AlphaTCP finger client do the work itself. At least at first blush it seemed like converting FINGER.LIT (the .LIT extension in AMOS means a "literal" binary file, i.e., an executable) to access a Gopher server instead would just be a matter of changing the port number. (NARRATOR: It wasn't.) So I decided to find the presumed port number and patch it in the binary. The 68000 notionally deals in 16-bit shorts, so I looked for various permutations of 4f (79, the port number for finger) such as 004f or 4f00.

Unfortunately FINGER.LIT is a "huge" 52K binary, the reason for which will become apparent shortly, and this byte appears in lots of places. One of them, however, was part of a string (because it is also the letter "O"). So I ran it through strings and this one popped out:

finger/tcp unknown service

This means it must look up the port number out of the services list! AlphaTCP's equivalent of /etc/services is TCP:SERVIC. (and written in the same format). I edited that file and changed finger's TCP port to port 70, and ...

.finger /@gopher.floodgap.com
3 '/W /' doesn't exist! error.host 1
i This resource cannot be located. error.host 1

It worked!

The next experiment was to copy to a new binary GOPHER.LIT and change all strings in the new file that had finger to gopher so it would use that service entry instead. There weren't a great deal of these and since it was exactly the same length string, it was an easy in-place substitution. This wasn't quite as successful:

.gopher
?GOPHER.RTI not found.

RTI files are "runtime initializers" (essentially the AMOS equivalent of a data segment in a separate file). This is a possible clue it was written in C, not in hand-coded assembler as many Alpha Micro tools are, and being written in C also explains the large(ish) size of the file. I assumed nothing in the RTI depended on these changes and just gave it a copy of FINGER.RTI.

.dir finger.*
FINGER LIT 105 DSK0:[1,4]
FINGER RTI 6
Total of 2 files in 111 blocks

.copy gopher.rti=finger.rti
FINGER.RTI to GOPHER.RTI
Total of 1 file transferred
.gopher /@gopher.floodgap.com
3 '/W /' doesn't exist! error.host 1
i This resource cannot be located. error.host 1

Success!

At this point those of you unfamiliar with the finger protocol will wonder where the /W is coming from. This is documented in the protocol description in RFC 1288 (Gopher's, for the record, is RFC 1436), stating it is sent "to [request] a higher level of verbosity in the user information output." Since by now I strongly suspected this was a compiled C program, I located the string /W and turned it into nulls, effectively making it a zero-length null terminated string. This successfully suppressed the string completely and now I could send selectors and view raw Gopher menus and text files.

This was, however, essentially a single-protocol lunkheaded equivalent of cURL, not an actual console shell. Since lots of strings would be getting slung around I decided to implement a front-end shell using AlphaBASIC, which virtually every AMOS machine comes with and has unusually good string handling for a BASIC dialect. It would create a .CMD file (analogous to a Windows-DOS batch file) that would call GOPHER.LIT with a selector and host, dump the resulting data to a file, and chain back into the front end. This worked great for text files and it paginated them perfectly. It also displayed gopher menus raw on screen just fine, including with what appeared to be the typical tab delimiters between gopher menu item fields (read the RFC for details). But when I tried to actually parse those menu items, it couldn't find any tabs at all.

When in doubt, look at what's coming over the wire. What was coming over the wire (according to my local network snooper) was intact, but when GOPHER.LIT wrote the output to a file, a hex dump of the actual file showed the tabs were getting automatically expanded out into spaces. Weirder still, instead of the typical CRLF line endings I was expecting to see in a gopher menu, I was seeing CRCRLF. What gives?

Because of the little endian byte ordering I mentioned before, disassembling the code turned into a marathon process of flipping 16-bit word byte order and feeding it in sections to a 68K disassembler expecting big endian opcodes. Sensible code resulted but for the life of me I could not find any code that obviously turned tabs into spaces. I found the code that parsed whitespace in the services file (in fact, I found that in a couple different places), but nothing that actually generated white space or, for that matter, doubled-up carriage returns.

The remaining variable was the redirection of the output to a disk file. In AMOS, this is done by intercepting terminal output routines. What if this was something the terminal driver was doing? I wrote a tiny little assembler program to test.

SEARCH SYS
SEARCH SYSSYM

PHDR -1,,
MOVB #9.,D1
TOUT
TYPECR <Hello, world!>
EXIT

END

68000 assembly language programmers will have recognized exactly one opcode in that file (and even that is atypical: you probably would write it as MOVE.B). The rest of it are directives or macros; writing assembly programs on AMOS is actually a real pleasure because of the rich number of macros and monitor service calls to make your life easier. More about that in a second, too, but what this is doing is emitting a tab character and the string Hello, world! followed by CR. I assembled it with the trusty M68 assembler, emitting this tiny object file (the first four bytes are a disk pointer and are not actually part of the file):

000000: 00 00 00 00 FF FF 00 01 00 00 00 00 00 00 3C 12
000010: 09 00 06 A0 0C A0 48 65 6C 6C 6F 2C 20 77 6F 72
000020: 6C 64 21 0D 00 00 12 A0

You can see there is no line feed or spaces, and you can see the CR (0x0d) and tab (0x09). I ran it, redirecting its output to a disk file. The file dumped like so:

000000: 00 00 00 00 20 20 20 20 20 20 20 20 48 65 6C 6C
000010: 6F 2C 20 77 6F 72 6C 64 21 0D 0A

The file had spaces instead of the tab character, and had CRLF at the end instead of CR. The terminal driver was indeed munging the output.

Monitor calls on AMOS are implemented using 68K A-line traps. Whenever the processor sees an instruction with a first (most significant) nybble of 0xA, an A-line exception is triggered and sent to the operating system. (Most 68K operating systems exploit this feature for system calls; 68K Macintoshes implement Toolbox calls this way. There are also F-line traps for FPU instructions which notionally allow emulation on systems that lack them, though interestingly the Atari ST uses both A-line and some F-line traps as GEM calls.) In the bowels of the Alpha Micro monitor calls documentation is a table for a terminal status bitfield called T.STS:

The T$DAT bit enables "complete data transparency." This sounds like exactly what we want! It says we should manipulate it only with the TRMWST (terminal write status) and TRMRST (terminal read status) monitor calls. These are macros and their definitions in SYS.M68 look like this:

DEFINE TRMRST DST,PORT
        IF B,PORT,      SUB     A6,A6
        IF NB,PORT,     LEA     A6,PORT
        SVCA    171,,,,D7,DST
ENDM
DEFINE TRMWST SRC,PORT
        IF B,PORT,      SUB     A6,A6
        IF NB,PORT,     LEA     A6,PORT
        SVCA    172,,SRC,D6
ENDM

The B and NB mean "blank" (not provided) and "not blank" (provided) with respect to the parameters. We don't need the port argument since we are only interested in talking to the default terminal. The source and destination parameters are for sending and receiving the new value respectively. That gets passed to another macro called SVCA, which is the supervisor call macro, shown here:

DEFINE  SVCA    ARG,A,B,C,D,E
        IF      NB,A
         NTYPE  ...X,A
         IF NE, ...X-^O26, LEA A6,A
        ENDC
        IF      NB,B
         NTYPE  ...X,B
         NTYPE  ...Y,C
         IF NE, ...X-...Y, MOV B,C
        ENDC
        WORD    ^H0A000+^O'ARG*2
        IF      NB,E
         NTYPE  ...X,D
         NTYPE  ...Y,E
         IF NE, ...X-...Y, MOV D,E
        ENDC
ENDM

Without getting too deep in the weeds here (too late?) this macro looks at the types of the arguments and if they aren't suitable issues instructions to get everything in the right place, since the 68K instruction set is not fully orthogonal. The real magic here is the WORD pseudo-op: this is what emits the A-line instruction. The default radix for M68 is octal (I told you this was heavily DEC-influenced), so punching a couple figures into the ol' scientific calculator, the A-line instructions that TRMRST and TRMWST respectively generate are 0xA0F2 and 0xA0F4.

The next thought was where to sneak this in. I figured I could mash this into a few shorts and trim down one of the messages for space; I'd only need to do this when the executable starts because this bit is helpfully reset for us when returning to the AMOS command prompt. However, it would be easy enough to check first if the code was already making these calls for some other purpose, since it seemed very unlikely to see an F2 A0 (to read the status word) near a matching F4 A0 (to write out the modified one).

% xd gopher.lit | grep -A2 'f2.a0'
00000690 4d 21 22 01 ce 9d f2 a0 07 20 40 00 10 00 ce 9d
000006a0 00 2c f4 a0 fc d7 00 00 00 80 7c 2b 00 00 00 10
000006b0 a8 84 7c 2b 00 00 00 04 a4 84 7c 2b 01 00 00 00

Jackpot (boldface added for clarity). This only occurred once in the entire file. Byte-swapped and disassembled into standard mnemonics, the relevant instructions are

suba.l d6,d6
.short 0xa0f2
move.l d7,d0
ori.w #0x10,d0
suba.l d6,d6
move.l d0,d6
.short 0xa0f4

The program was already manipulating the status word to turn on lower case input (with T$ILC), since most fingerds on the other end wouldn't be expecting uppercase. We can see what we have to do: if we add 0x08 to the 0x10, we get that extra bit turned on at the same time. I made this in-place change and now the AlphaBASIC shell could see and parse the tabs!

However, files when downloaded were not checksumming. Indeed, this fixed the tab problem but not the CRCRLF issue, and there was an extra CRLF at the end. There is no status bit to disable that; it turns out that turning LF into CRLF is intrinsic to the standard output routines, so it would faithfully turn any CRLF already being received into CRCRLF, and the output redirection added the extra line at the end as a final insult. Since we are using a driver script and emitting to a file anyway, the solution here was simply to write a postprocessor in assembly (AlphaBASIC would be unacceptably slow for this, and isn't very adept at binary file handling) that the driver script calls afterwards to get rid of the extra CRs and remove the extraneous line at the end. I chose to do this as separate utilities for each individual task ("one thing well") and they might actually be useful utilities in their own right in any case. At last: every file I downloaded in this fashion checksummed correctly!

With the rudiments now properly functioning, it was time to finish the front end. A couple feverish days of coding later, drum roll please:

This is GAMBLE.BAS, the Gopher Alpha Micro Browsing-Linking Environment, displaying Floodgap's root gopher menu on the Eagle 300 with its beautiful colour AM-75 console (a modified Wyse WY-370). Gopher works very well from the keyboard and because everything is stored as a file, GAMBLE can display arbitrarily large menus and text documents limited only by disk space. Another nice side effect is that saving binaries and other resources is merely a matter of renaming the temporary file. If you have an Alpha Micro with AMOS 2.3A and AlphaTCP 1.3C or 1.5A you'd like to run this on, source code and binaries are available on the Floodgap gopher server (or via the Public Proxy).

As an enticement, I have uploaded the old Alpha Micro Users' Society network library archive to the Gopher server as well, and GAMBLE can access it directly. Check it out!

I should note that the approach I have chosen here is not without its drawbacks. AMOS has a maximum command line length of about 92 characters, and since .CMD files drive the command line in the same fashion, they have the same limitation. This means that with all the other pieces of the command, the combined length of the hostname and Gopher selector is limited to about 80 characters making certain portions of some sites inaccessible (the front end checks this, and won't let you select them as a precaution). In addition, a selector with quote marks or @ characters would similarly be inaccessible because these are reserved by the shell and (ultimately) FINGER.LIT, though these characters are not commonly found in Gopher selectors, and of course the dependence on the port number being in TCP:SERVIC. means it will only ever communicate over port 70. A TAMED (using the socket API) approach in AlphaBASIC would avoid the selector and port limitations but it would impose others, notably speed as well as handling binary data and possibly memory capacity, and it wouldn't run on AlphaTCP 1.3C. A lot "just works" this way on the widest variety of systems, and I might think of better ways to tackle these deficiencies over time.

Most of all, though, this entire endeavour probably taught me more about AMOS than all my puttering around with it had as a toy over the last decade or so. For one of my favourite and certainly more obscure machines, this was a stimulating and actually vaguely useful project, and I'll be thinking about other things I could be doing with it now that my appetite is whetted. Meanwhile, this isn't the last gopher client we'll be writing in this blog: stay tuned for some other machines that I really need to be tunnelling through Gopherspace with.

Tuesday, June 23, 2020

Refurb weekend: Alpha Micro Eagle 300

One of the more unusual 68000-based systems -- even a few powered by ColdFire CPUs -- are the Alpha Micros. They run a DEC-inspired multiuser operating system called AMOS and popped up in lots of vertical markets as diverse as point of sale, veterinary and medical, and even churches (I encountered my first Alpha Micro in a Salvation Army corps, where it ran the business end using its terminal-based office suite and customized menus). I have a particular fondness for these systems due to a story that I'm not quite sure I'm past the statute of limitations on, but suffice it to say I find them to be entertaining oddballs and I serve my own homage site actually on an Alpha Micro, the Eagle 300. Here it is with its rather nice AM-75 colour terminal (a modified Wyse WY-370) and an AM-401 external CD-ROM (a rebadged Toshiba XM-5401B).

This machine does 24/7 non-stop service and it occurred to me one day walking past my house's server room that there was a rather loud whine coming from it, as if a cooling fan was stripping its bearings or something. A little investigation determined that it was coming from the E300, but it wasn't the power supply fan or the single 80mm case fan: it was the hard drive, which was slowly cooking itself to death because both of those fans, the only case ventilation, had failed. The power supply was a cheapo AT (not ATX) power supply I bought off the shelf a few years ago, but the case fan looked like the original one that came with the unit when it was manufactured in the mid 1990s. It's time for a Refurb Weekend!

The first order of business is to solve the cooling problem temporarily and order new parts. Let's just take the case off so the system can run as cool as possible until supplies arrive.

Replacing the PSU fan isn't prudent in this situation because there's probably other marginal parts in it as well; at this point it's safer just to replace the whole thing. Although I could jam another AT supply in it, they're getting rarer than ATX PSUs, the currently available stock are usually of poorer quality, and AT supplies have the singularly inconvenient attribute of having an integrated hard power switch that you have to weasel into where the old one went. (Notice the bag of mounting parts from the last time.)

For the new fan I picked a high-flow Noctua NF-A8 fan (the single-RPM FLX version, since this machine is too old to have PWM control) because the concept of a high-end fan in an old school workhorse amuses me conceptually. However, for the power supply I got an off-the-shelf ATX PSU and an AT-to-ATX converter.

The fan was the easy part: the old fan has a Molex power connector, and the Noctua conveniently includes a converter for that in the box, so done and done.

The AT-to-ATX converter is a little more complicated. This takes an ATX power connector and has leads to the existing switch instead of providing one of its own, and provides power on traditional AT P8/P9 connectors. You slip its switch leads out and connect the ATX connector to the PSU, and then connect the P8/P9 connectors to the motherboard.

The advantage here is that once wired in, you don't have to deal with the motherboard power supply connectors anymore or the power switch; you just switch out the ATX side like any recent system. This is important because many later AT supplies (and, it appears, this cable) don't properly key the P8 and P9 connectors. Inserting them backwards is guaranteed to let out the magic smoke and a lot of cussing, so remember the old nerd saying that you should never have red and red together. With this cable you just get it right once and you don't have to risk stressing the connectors or shorting out the machine next time.

With the rubber mounts in, the Noctua pulls a prodigious amount of air through, and combined with the now properly functioning power supply our Refurb Weekend was completed in less than a Refurb Hour. Best of all the hard disk is no longer shrieking from heatstroke. Still, maybe a SCSI2SD is the next project for this machine.