This is part of a series of posts for the Retro Challenge 2018/04. See my index page for the other posts.
Yesterday I figured out a workflow for getting CPM software onto disk images that I could run on my Osborne computer.
Now to the final part of my personal ‘retro challenge’. Which was:
“Conclude by playing a classic CPM game on my portable computer! Preferably Colossal Cave Adventure or The Hitch-Hiker’s Guide to the Galaxy :-)”
As an aside, I’d already come across a BASIC version of Colossal Cave on one of the pre-made disk images I’d downloaded from the internet.
But I really wanted to get the Hitchhiker’s game running.
I loaded them onto an Osborne disk image and gave it a go:
Well, it works, sort of. There are loads of random characters, presumably control codes to manipulate screen layout that are designed for a different terminal type than the Osborne.
Researching the subject, I found this 22-year old thread discussing Infocom game interpreters!
It seems all Infocom adventures use the same interpreter, with a separate data file for each game.
The only difference is the name of the data file is embedded in the binary, so you have to patch the binary to point to your game file.
I found another generic CPM version of Zork 1 at retroarchive.org, so tried the interpreter from that.
First I had to patch the filename in the binary. I used Hex Fiend on macOS:
That worked much better (no random characters) but the score/status line no longer appears, and also the text wraps at 63 columns, so didn’t fit into the Osborne’s 52 columns. I could force the Osborne to wrap at 52 columns, but that leaves loads of partial lines, which is very ugly.
Next I tried to patch the binary further.
I had a bit of confusion from the instructions in the above blog post, until I realised all the hex addresses mentioned were out by &0100 from my file. (I later found out that CPM .COM files are loaded into memory at &0100, so those are actual memory addresses, not offsets in the file.)
Here are the locations that can be patched:
&0103 (offset &03 = 3) one byte: screen width
&0105 (offset &05 = 5) eight bytes: filename of data file (padded with spaces if shorter than 8 characters) – this is the base name, the interpreter will add .DAT for the data file and .SAV for game save files
&0110 (offset &10 = 16) up to 32 bytes: screen init (first byte is length of string)
&0131 (offset &31 = 49) up to 32 bytes: screen uninit on exit (first byte is length of string)
&0152 (offset &52 = 82) up to 32 bytes: start of status line (first byte is length of string)
&0173 (offset &73 = 115) up to 32 bytes: start of prompt line (first byte is length of string)
The blog post suggested some character strings for the Osborne, which I tried. However some (such as characters for reverse video) don’t actually work on the Osborne.
I looked up the Osborne manual for which screen control codes it supports. For convenience I’ve reproduced the list here:
07 BEL rings the bell 08 BS moves the cursor left one position (no erasure) OA LF moves the cursor down one line OB VT moves the cursor up one line OC FF moves the cursor right one position OD CR performs a carriage return (no line feed) 1A SUB clears the screen and homes the cursor 1E RS homes the cursor 1B 29 ESC ) begins half-intensity video display (dim) 1B 28 ESC ( ends half-intensity video display (bright) 1B 45 ESC E inserts a line at cursor position 1B 67 ESC g starts graphic character interpretation 1B 47 ESC G ends graphic character interpretation 1B 6C ESC l starts underlining all characters 1B 6D ESC m ends underlining of characters 1B 51 ESC Q inserts a character at cursor position 1B 52 ESC R deletes a line at cursor position 1B 54 ESC T deletes from cursor to end of line 1B 57 ESC W deletes a character at current cursor position 1B 3D y x ESC = <y> <x> move cursor to x-32, y-32 1B 53 y x ESC S <y> <x> scroll screen so x-32, y-32 is in upper left
Apparently it’s based on the TeleVideo 920C standard, although codes other than those listed above don’t seem to work.
The x/y coordinate codes initially seem a bit weird, requiring a 32 offset from the actual position. But it makes sense, as starting counting from 32 means the x and y encodings are always visible ASCII characters rather than control codes themselves!
Using this code guide, I developed my own Infocom patches for the Osborne:
10h / 16 = screen init 05 (5 bytes) 1A (clear screen and home) 1B 3D 37 20 (move to y=23, x=0) 31h / 49 = screen uninit 00 (do nothing) 52h / 82 = status line 08 (8 bytes) 1B 3D 20 20 (move to y=0, x=0) 1B 29 (dim) 1B 6C (underline) 73h / 115 = prompt line 08 (8 bytes) 1B 3D 37 20 (move to y=23, x=0) 1B 28 (bright) 1B 6D (cancel underline)
Which looks like this in the binary:
This gives a rather pleasing version with a dim underlined status line at the top of the screen divided from the main text:
I also patched in a game file name of simply “GAME”, so I could use it for multiple Infocom games without further patching (by renaming HITCHHIK.DAT etc to GAME.DAT).
Finally I created 6 different versions, for the 3 different Osborne screen widths (52, 80, 104), and either generic CPM character encoding or with the Osborne control code characters. The different widths would allow me to use the wider version on the external monitor.
GAME52.COM GAME52G.COM GAME80.COM GAME80G.COM GAME104.COM GAME104G.COM
The CPM boot disk I have tries to load a file AUTOST.COM after it boots, so it’s possible to just rename one of those files to AUTOST.COM to have it start automatically.
Here’s a zip file with these files.
Finally I achieved my ultimate goal!
As a final task, I wanted to add a menu to select between the different screen widths (and change the Osborne screen resolution accordingly). So far the only way I’d found to change the resolution is to use the SETUP utility to change which mode a specific disk boots up in.
The manual for the Screen-Pac graphics card says you can change the screen size in MBASIC:
I’d tried that before, but couldn’t get it to work then. I tried again, and double-checked my BASIC code was correct:
I also tried the (deprecated) DEF USR method to call machine code:
Neither had any effect.
I decided to check just what code was at location &E4E5 by disassembling it:
Turning to my trusty 1980s books on Z80 opcodes:
I disassembled the code the old-fashioned way:
I always used to assemble code this way (and using these very books). Disassembling was harder though as the opcodes in the book are in alphabetical order not numerical order!
The manual states that this routine should look in &E1A5 and use the value to change the screen resolution.
But it’s clearly doing nothing of the sort. It seems to be checking DE is non-zero, then adding DE to HL, using the result as an address and dereferencing the single byte at that address, then returning that value in HL.
So nothing to do with the screen hardware.
Perhaps the ROM is a different revision. Perhaps the manual has a misprint. Perhaps MBASIC doesn’t page in the correct memory bank to access the ROM. (But the manual does give this as a method for MBASIC to change the screen resolution.)
Hmmph. Oh well. Maybe I’ll try again another day.
But I’m not going to let it detract from the main event.
HITCHHIKER’S ON MY OSBORNE FOR THE WIN :-)