That’s right – the very first IBM Personal Computer, the IBM 5150 – a business-oriented machine and a hallmark platform that started it all, shipped along with an integrated cassette port. Too bad it could only be utilized through the cassette BASIC contained in ROM… Or shall we say thankfully? Yes, this interface was abandoned soon after, for obvious reasons. But let’s fast-forward 40 years: reviving this legendary machine today, why don’t we give the cassette interface a go… in DOS ?
Since DOS by itself lacked any support to go with the interface, as you can see, this utility allows you to access the IBM cassette port directly together with DOS, to read and write raw data: be it the original IBM BASIC apps, or whatever you give it, utilizing the cassette port in the IBM PC, right next to the keyboard connector. The program can also be used together with the infamous IBM PCjr… that is, if you can scavenge out of nowhere (or make yourself) a cable for their proprietary “C”-marked connector.
And even though the ROM BASIC had been included in the successors of the IBM PC, e.g. the PC/XT and PC/AT, the cassette port was not. As such, the program will fail to load on anything newer than an IBM PC, or PCjr, for that matter.
So how would you use this contraption? Firstly, as the name implies, you need an original IBM 5150 PC or PCjr, with DOS, and a cassette deck. Or a reel-to-reel tape recorder, like I have used below. Or any sound device capable of playback and recording, in general… like a modern PC, or a smartphone.
And then you need a proper cable. On the 5150, it’s easy according to the DIN pinout and schematic above, similar to a Tandy TRS-80 datacable. You then need to provide these signals to your sound device, either mono or to the left channel. Note that if your device does not have a line input, but a microphone input instead, you need to set the P4 jumper on a 5150 mainboard accordingly. Or, as an alternative, use a voltage divider (e.g. a potentiometer) before the mic input of your recorder. Failure to do this can damage the mic input.
The pins 1 and 3 of the DIN connector on a 5150 are normally-open relay contacts. Before any I/O operation, this relay then actuates its contacts to switch the cassette motor on. If you don’t use these pins, either listen to the relay clicking and actuate your device accordingly, or use the /A command-line argument that asks and waits for you to press PLAY (or PLAY+RECORD).
With that in check, the application can be downloaded here. Or you can fetch it from my GitHub repository, although you will need the Turbo C compiler to compile this; ideally through the BUILD.BAT batch script.
Note that the maximum consecutive data size that can be loaded from tape is always 64K. This is a limitation of both real-mode addressing and the BIOS tape I/O routines. As such, the IBM Cassette BASIC, which resides in ROM, also won’t permit you to write BASIC scripts bigger than 64 kilobytes. Also note that if you don’t have enough memory (early 64KB mainboards), this value can go down too.
Now, the command line options are easy to follow. You need to seek the tape to the desired place and then use the /R command to read from the tape to save it to a file (or directly to a realmode RAM segment – take care not to overwrite something vital in memory!); /D to dump the tape contents to the screen as raw ASCII or /X to execute the read data as machine code, respectively. Whatever is read from the tape is not “parsed” in any way to preserve the interface, so there is no dedicated “LOAD” command to search for a custom application/data/whatever.
Even though specifying the number of bytes to read is optional, it is a good practice to note it down, and to always specify it after the read command. Otherwise, 5150CAXX does not know how big the data stream is going to be read from tape, and as such, it will continue reading until it reaches a “bad tape signal” or a CRC error, stopping the read.
In that case, the output file size might actually be a little bit higher, as the ROM BIOS might append zeros or broken data after the end of the actual datastream. This is because the read is continued past the valid stream, and this now-invalid read operation eventually fails on the CRC/synchronization chunk, stopping the tape. In this case, you can either cut out the “lard” with a hexeditor, or just ignore it altogether. See picture below.
Also note that if you use a real tape recorder and you get CRC or “bad tape signals” errors, with only a part of the data recorded (or read) right, make sure both the recording modulation and playback volume levels are loud enough and not distorted, there are no audible dropouts of the tape, the heads are pristine, recording bias is properly set, and there is absolutely no audible wow or flutter, caused by the mechanics or tape stretching. To rule these factors out for testing purposes, use a solid-state sound source, like an another computer or smartphone etc.
The “extra lard” that might occur at the end of the datastream, if the size in bytes is not specified; the file content is identical nevertheless.
Left window: original file, right window: file read from tape.
Back to the command-line switches: The /W command is used to load a given file from disk to memory, and then write it to the tape at the current tape position.
You can also record 64K real mode memory segments to tape this way: e.g. 5150CAXX /W 0xFE00 8192 dumps the 8-kilobyte BIOS, located at FE00:0000 and records it to tape; /W 0xF600 32768 saves the 32K ROM BASIC to tape, /W 0xB000 records a screenshot, and so on… 🙂
The /WB command performs the same operation as /W , but also creates a BASIC loader stub, allowing the binary in question to be loaded inside ROM BASIC using the LOAD command. This option requires a .COM or a .BIN file, or a memory segment, to be passed along as source, and it must not invoke any MS-DOS interrupts.
With this option, upon loading the BASIC stub from tape, there’s a routine which resizes the BASIC data segment to 8K, sets up new stack and loads the binary to RAM at address 12K (prepares a new segment at 0x300). The offset origin is either 0, if the original was a memory segment, a .BIN file or had no extension, or 0x100 for a .COM file.
Use /A to make the application wait for your response, if you don’t use the relay tape motor actuator, for all commands.
Now, the /X option is kind of a specialty, as it interprets any read data as x86 machine code directly. Any illegal instructions read from the tape will crash the system, requiring reboot. Note that you cannot read BASIC applications this way: they need to be saved into a file first, and then interpreted either through IBM Disk Basic or Microsoft GW-BASIC, as they are not valid x86 instructions, but of a special, “tokenized BASIC” format.
An example of a “Hello world” code that can be written to the tape and read by the /X option, can be found here. In the previous versions of 5150CAXX that occupied one segment only, this code needed to be “self-conscious” (like an old-school computer virus) to get its instruction pointer to address itself, as the offset of loading was directly above the application code. The recent revision of 5150CAXX is compiled as an .exe and uses DOS allocmem() to allocate a full 64K segment, if possible, but always starting at offset 0 (org 0).
Also, the code shall end with a FAR RET (RETF) opcode, in order to transfer control back to the operating system proper. Check the above example for details.
Reading the IBM Advanced Diagnostics from tape with 5150CAXX:
There’s not much of software that had been released for the IBM 5150 on a cassette tape.. apart from a few notable exceptions, such as the IBM PC Diagnostics, and later the IBM Advanced Diagnostics. Since the only way to launch any application for the 5150 that’s been stored on a tape is through the IBM ROM BASIC (well, until 5150CAXX had been developed, anyway), this Diagnostics bundle could be used to perform a quick check-up of the PC without being required to have any kind of a drive, or operating system, in the thing.
The Advanced Diagnostics cassette recording basically consists of three parts, all of them can be dumped with 5150CAXX to 3 files, using the /R command three times. The first two parts are created obligatorily by any application that has been written in IBM Cassette BASIC and then stored with the SAVE command, whilst the third part is a tad special:
- The first file dump is basically a 256-byte header of the data stream that follows afterwards. It contains an eight byte application identifier (in this case, ‘ldcass‘, the name that you would usually pass to the BASIC LOAD command), then the type of the following data stream (tokenised BASIC) and memory location where to load it to.
- The second file dump is a small BASIC stub that shows information about the Diagnostics tape being loaded. This BASIC program cannot be executed directly – you need to open it up in GW-BASIC or IBM BASIC. It ends with a special CALL instruction that executes the third part.
- The third file dump is actually x86 machine code, the diagnostic application by itself. This can either be loaded directly with the /X option, or it can be saved to a file, and then – with a small DOS stub “helper” binary – made into a DOS-launchable application.
Using FASM and BUILD.BAT and renaming the third file dump to CASSETTE.BIN, the result is a DOS-compatible Advanced Diagnostics application that can be downloaded from here. Just beware that the exit routine of this code is a reboot, as the application does not know it runs in DOS.
Works with cassettes, too – you don’t have to use a reel to reel tape recorder