Skip to content

HxC: support for 80-track DSSD .hfe -> .dsk conversions

TI-99/4A There's a bug in the HxC floppy image conversion program that prevents conversion from .hfe back to .dsk for 80-track DSSD disk images. In my opinion, the geometry selection logic is flawed -- it decides what the disk image format is based on disk size, and there's a size overlap between 80-track DSSD FM-encoded images and 40-track DSDD MFM-encoded images.

I posted a rough patch last night in another thread, have worked out a cleaner solution this morning, and have opened a ticket with the upstream developer on GitHub.

The geometry should be parsed from sector 0 of the .hfe (trying FM first as it's most common, then falling through to MFM) but that involves more code churn than is necessary to fix this particular use case and I sort of used up my internal energy budget for the week yesterday.

Edit: scratch that, I went ahead and refactored the code, so it parses the geometry as described above. I've replaced the patch below with the new code because ...

With luck the patch will be accepted and integrated into a near-future release of the software. In the meantime, the patch to libhxcfe is at the end of this post.

Edit: the patch was accepted and will be in the next release of the software. The latest, all-singing-all-dancing version, has not been accepted yet.

(There are many changes that need to be made to the HxCFE suite of interdependent libraries just to get it to build. Makes me wonder what sort of environment the developer uses for releases ... internal svn, maybe?)

diff --git a/sources/loaders/ti99v9t9_loader/ti99v9t9_writer.c b/sources/loaders/ti99v9t9_loader/ti99v9t9_writer.c index ba0d0e4..8359f8f 100644 --- a/sources/loaders/ti99v9t9_loader/ti99v9t9_writer.c +++ b/sources/loaders/ti99v9t9_loader/ti99v9t9_writer.c @@ -48,13 +48,11 @@ int TI99V9T9_libWrite_DiskFile(HXCFE_IMGLDR* imgldr_ctx,HXCFE_FLOPPY <strong> floppy,ch   int32_t nbsector,imagesize;     int32_t numberofsector,numberofside,numberoftrack; - int32_t bitrate; - int32_t density; - int32_t interleave; + int32_t density = ISOIBM_FM_ENCODING;;   int file_offset; - int32_t sectorsize; + int32_t sectorsize = 256;   unsigned char </strong> diskimage; - int error; + int error = 0;   HXCFE_SECTORACCESS* ss;   HXCFE_SECTCFG* sc;   @@ -64,183 +62,116 @@ int TI99V9T9_libWrite_DiskFile(HXCFE_IMGLDR* imgldr_ctx,HXCFE_FLOPPY * floppy,ch     imgldr_ctx->hxcfe->hxc_printf(MSG_INFO_1,"Disk size : %d Bytes %d Sectors",imagesize,nbsector);   - numberofsector=9; - numberofside=1; - numberoftrack=40; - bitrate=250000; - density=ISOIBM_FM_ENCODING; - interleave=4; - sectorsize = 256; - - switch(imagesize) + ss = hxcfe_initSectorAccess(imgldr_ctx->hxcfe, floppy); + if (ss)   { - case 1*40*9*256: - numberofside=1; - numberoftrack=40; - numberofsector=9; - bitrate=250000; - density=ISOIBM_FM_ENCODING; - interleave=4; - break; - - case 2*40*9*256: - // 180kbytes: either DSSD or 18-sector-per-track SSDD. - // We assume DSSD since DSSD is more common and is supported by - // the original TI SD disk controller. - numberofside=2; - numberoftrack=40; - numberofsector=9; - bitrate=250000; - density=ISOIBM_FM_ENCODING; - interleave=4; - break; - - case 1*40*16*256: - // 160kbytes: 16-sector-per-track SSDD (standard format for TI - // DD disk controller prototype, and the TI hexbus disk - // controller?) <strong>/ - numberofside=1; - numberoftrack=40; - numberofsector=16; - bitrate=250000; - density=ISOIBM_MFM_ENCODING; - interleave=9; - break; - - case 2*40*16*256: - // 320kbytes: 16-sector-per-track DSDD (standard format for TI - // DD disk controller prototype, and TI hexbus disk - // controller?) - numberofside=2; - numberoftrack=40; - numberofsector=16; - bitrate=250000; - density=ISOIBM_MFM_ENCODING; - interleave=9; - break; - - case 2*40*18*256: - //  360kbytes: 18-sector-per-track DSDD (standard format for most - // third-party DD disk controllers, but reportedly not supported by - // the original TI DD disk controller prototype) - numberofside=2; - numberoftrack=40; - numberofsector=18; - bitrate=250000; - density=ISOIBM_MFM_ENCODING; - interleave=5; - break; - - case 2*80*18*256: - // 720kbytes: 18-sector-per-track 80-track DSDD (Myarc only) - numberofside=2; - numberoftrack=80; - numberofsector=18; - bitrate=250000; - density=ISOIBM_MFM_ENCODING; - interleave=5; - break; - - case 2*80*36*256: - // 1.44Mbytes: DSHD (Myarc only) - numberofside=2; - numberoftrack=80; - numberofsector=36; - bitrate=500000; - density=ISOIBM_MFM_ENCODING; - interleave=11; - break; - - default: - imgldr_ctx->hxcfe->hxc_printf(MSG_ERROR,"Bad image size!.."); - return 0; - break; - } + sc = hxcfe_searchSector(ss, 0, 0, 0, density); + if (!sc) + { + density = ISOIBM_MFM_ENCODING; + sc = hxcfe_searchSector(ss, 0, 0, 0, density); + if (!sc) + { + imgldr_ctx->hxcfe->hxc_printf(MSG_ERROR, "This disk is neither FM nor MFM.  Exiting."); + return HXCFE_FILECORRUPTED; + } + }   - error = 0; - imagesize = numberofsector </strong> numberoftrack <strong> numberofside </strong> sectorsize; - diskimage = malloc(imagesize) ; - if(diskimage) - { - memset( diskimage ,0xF6 , numberofsector <strong> numberoftrack </strong> numberofside <strong> sectorsize); + // sc->input_data should contain the disk geometry + + numberofside = sc->input_data[0x12]; + numberofsector = sc->input_data[0x0c]; + numberoftrack = sc->input_data[0x11]; + + if ( (numberofside < 1) && (numberofside > 2)) + { + imgldr_ctx->hxcfe->hxc_printf(MSG_ERROR, "Image claims it has %i sides, which is clearly wrong.  Exiting.", numberofside); + return HXCFE_FILECORRUPTED; + } +  + if ( (numberoftrack != 40) && (numberoftrack != 80)) + { + imgldr_ctx->hxcfe->hxc_printf(MSG_ERROR, "Image claims each side has %i tracks, which is clearly wrong.  Exiting.", numberoftrack); + return HXCFE_FILECORRUPTED; + } +  + if ( (numberofsector != 9) && (numberofsector != 16) && (numberofsector != 18) && (numberofsector != 36)) + { + imgldr_ctx->hxcfe->hxc_printf(MSG_ERROR, "Image claims each track has %i sectors, which is clearly wrong.  Exiting.", numberofsector); + return HXCFE_FILECORRUPTED; + }   - ss = hxcfe_initSectorAccess(imgldr_ctx->hxcfe,floppy); - if(ss) + if ( (numberofsector </strong> numberoftrack <strong> numberofside) != nbsector )   { - for(i=0;i<numberofside;i++) + imgldr_ctx->hxcfe->hxc_printf(MSG_ERROR, "Disk geometry is %i sides, %i tracks per side, %i sectors per track, but does not match disk length of %i sectors.  Exiting.", numberofside, numberoftrack, numberofsector, nbsector); + return HXCFE_FILECORRUPTED; + } +  + imgldr_ctx->hxcfe->hxc_printf(MSG_INFO_1, "Disk geometry is %i sides, %i tracks per side, %i sectors per track.", numberofside, numberoftrack, numberofsector); + + imagesize = numberofsector </strong> numberoftrack <strong> numberofside </strong> sectorsize; + diskimage = malloc(imagesize); + if (!diskimage) + return HXCFE_INTERNALERROR; + memset(diskimage, 0xF6, imagesize); + + for(i=0;i<numberofside;i++) + { + for(j=0;j<numberoftrack;j++)   { - for(j=0;j<numberoftrack;j++) - { - hxcfe_imgCallProgressCallback(imgldr_ctx, j + (i*numberoftrack),numberofside*numberoftrack); + hxcfe_imgCallProgressCallback(imgldr_ctx, j + (i*numberoftrack),numberofside*numberoftrack);   - for(k=0;k<numberofsector;k++) + for(k=0;k<numberofsector;k++) + { + sc = hxcfe_searchSector(ss,j,i,k,density); + if(sc)   { - sc = hxcfe_searchSector(ss,j,i,k,density); - if(sc) + if(sc->use_alternate_data_crc) + imgldr_ctx->hxcfe->hxc_printf(MSG_ERROR,"Warning : Bad Data CRC : T:%d H:%d S:%d Size :%dB",j,i,k,sc->sectorsize); + + if(sc->sectorsize == sectorsize)   { - if(sc->use_alternate_data_crc) - imgldr_ctx->hxcfe->hxc_printf(MSG_ERROR,"Warning : Bad Data CRC : T:%d H:%d S:%d Size :%dB",j,i,k,sc->sectorsize); - - if(sc->sectorsize == sectorsize) - { - if(i==0) - { - file_offset=(j*numberofsector)*sectorsize + ( k * sectorsize ); - } - else - { - file_offset=(  numberoftrack      <strong>numberofsector*sectorsize) + + if(i==0) + file_offset=(j*numberofsector)*sectorsize + ( k </strong> sectorsize ); + else + file_offset=(  numberoftrack      <strong>numberofsector*sectorsize) +   (((numberoftrack-1)-j)*numberofsector*sectorsize) +   ( k </strong> sectorsize ); - } - memcpy(&diskimage[file_offset], sc->input_data, sectorsize); - } - else - { - error++; - imgldr_ctx->hxcfe->hxc_printf(MSG_ERROR,"Bad Sector Size : T:%d H:%d S:%d Size :%dB, Should be %dB",j,i,k,sc->sectorsize,sectorsize); - } - - hxcfe_freeSectorConfig(ss,sc); - } - else - { - imgldr_ctx->hxcfe->hxc_printf(MSG_ERROR,"Sector not found : T:%d H:%d S:%d",j,i,k); + memcpy(&diskimage[file_offset], sc->input_data, sectorsize); + } else { + error++; + imgldr_ctx->hxcfe->hxc_printf(MSG_ERROR,"Bad Sector Size : T:%d H:%d S:%d Size :%dB, Should be %dB",j,i,k,sc->sectorsize,sectorsize);   } - } - } - }   - if(!error) - { - ti99v9t9file=hxc_fopen(filename,"wb"); - if(ti99v9t9file) - { - fwrite(diskimage,imagesize,1,ti99v9t9file); - hxc_fclose(ti99v9t9file); - } - else - { - free(diskimage); - hxcfe_deinitSectorAccess(ss); - return HXCFE_ACCESSERROR; + hxcfe_freeSectorConfig(ss,sc); + } else { + imgldr_ctx->hxcfe->hxc_printf(MSG_ERROR,"Sector not found : T:%d H:%d S:%d",j,i,k); + }   }   }   }   - free(diskimage); - hxcfe_deinitSectorAccess(ss); -   if(!error) - return HXCFE_NOERROR; - else   { - imgldr_ctx->hxcfe->hxc_printf(MSG_ERROR,"This disk have some errors !"); - return HXCFE_FILECORRUPTED; + ti99v9t9file=hxc_fopen(filename,"wb"); + if(ti99v9t9file) + { + fwrite(diskimage,imagesize,1,ti99v9t9file); + hxc_fclose(ti99v9t9file); + } else { + free(diskimage); + hxcfe_deinitSectorAccess(ss); + return HXCFE_ACCESSERROR; + }   }   } - else - { - return HXCFE_INTERNALERROR; - } + + free(diskimage); + hxcfe_deinitSectorAccess(ss); + + if(!error) + return HXCFE_NOERROR; + + imgldr_ctx->hxcfe->hxc_printf(MSG_ERROR,"This disk have some errors !"); + return HXCFE_FILECORRUPTED;  }

TI web/FTP site mirrors

TI-99/4A So the WHTech FTP site (the main major repository of all things TI) was down for a day or so. Reason given was "They had a catastrophic drive controller failure due to a power issue. They say we will be back within 24 hours."

I've seen sites go away permanently for various reasons (dead hardware and no backup, host provider went out of business and no backup, maintainer loses interest and no mirror). I don't want to see that happen with WHTech.

So, I'm mirroring the FTP site here. Mirror jobs will run daily, in the middle of the night in the US to minimize traffic.

I'm mirroring a few other sites as well. They are listed (and accessible) via the "Related Links" folder on the right-hand side of this blog.

Hopefully we won't need to have this mirror, but it's here in case Bad Things(tm) happen again.

Building a TI-99/4A cassette deck emulator

TI-99/4A Although many people use their PC (workstation-class) to transfer programs to their TI via playing a .wav into the cassette port, I went in a different direction.

I like small dedicated devices, and thus I built a tape recorder emulator out of a BeagleBone Black, a SparkFun display cape, a USB sound dongle, and a little bit of UNIX.

First, I installed Arch Linux on the BBB via the instructions here. Yes, I hate systemd as much as anyone else, but Arch Linux has it and it's the least distasteful of the Linux distributions at the moment.

Second, and this is very important, I disabled HDMI. If you don't do that, the display cape WILL NOT WORK. There seems to be a different method every kernel release (as is the norm for Linux -- I really wish FreeBSD supported capes), but I guarantee this method will work on Arch Linux as of March 2015. Log into the BBB, push to root, and execute:

mv /boot/dtbs/am335x-boneblack.dtb /boot/dtbs/am335x-boneblack.dtb-old
cp /boot/am335x-boneblack-emmc-overlay.dtb /boot/dtbs/am335x-boneblack.dtb

You need to do this because the capemgr (if present in the kernel) will not disable HDMI; u-boot has the dtb hardcoded into its environment, and on the BBBs that I have they cannot be overridden by uEnv.txt. You won't find this documented anywhere; I found it while poking around a storage-blanked BBB via serial console.

Next, install the necessary packages with pacman:

pacman -S lynx alsa-utils

Next, turn off the BBB and attach the display shield and USB sound dongle. I like the 4D Cape 43, as it's cheap and has movement buttons. Turn on the BBB and wait to see a login prompt on the display. If you've got a white screen, then HDMI is still enabled. Disable it with extreme prejudice per the instructions above.

Next, log in and create an account. You can continue using "alarmpi" if you want. Edit /etc/systemd/system/ and add "--autologin ${USER}" after "/sbin/agetty" in the line that starts with ExecStart.

Now go to that user's home directory and edit .profile to look like this:

export PATH=$HOME/bin:$PATH

TAPE_HOST="(web server)"

if test -z "${SSH_CONNECTION}"; then
lynx -cfg=$PWD/lynx.cfg \

... editing the TAPE_* to match your environment. You can rework it to use the local filesystem by using "file:///path/to/files" if you so desire.

Now edit .mailcap thusly:

application/x-gzip; cat %s | aplay 1>/dev/null 2>&1

... so that gzip files will be autoplayed when selected. If you're using local files, you will need to use this .mailcap instead:

application/x-gzip; cat %s | gzip -d -c | aplay 1>/dev/null 2>&1

(why gzipped .wavs? gzip -9 losslessly compresses the output of my TI-to-sine-wave generator the best, better than even flac, thus I highly recommend doing this)

Now reboot. If all went well, you'll see a list of tape files. Select one with the buttons on the shield and press the enter button. The .wav file should be played through the USB sound dongle. Verify that it sounds okay via whatever method you desire (powered speakers, headphones, and so forth); ssh in and adjust the output volume with alsamixer if necessary.

That's it. Standalone cassette tape player construction complete, for roughly the price of a decent tape deck back in the 80's.

HRD+ 74259 Daughterboard verified good

TI-99/4A The subject says it all, really. I got ahold of a couple of 74LS259D SMD chips, soldered them into the adapter board, and replaced the '259 stack in the HRD+ with the daughterboard.

Since nothing ever works correctly the first time, I botched a solder joint on one of the chips and spent an hour with a microscope, a schematic, and a continuity tester to track down the problem.

Anyway, it works. Eagle .sch and .brd are here. Enjoy.

Daughterboard replacement for Horizon HRD+ chip stacks

TI-99/4A Earlier, in my article about refurbishing an old Bud Mills Horizon HRD+, I asked why TI gear (both official and homebrew) tended to stack similar chips on top of each other, straightening pins out, and using flying wire to connect to other stacked chips.

The question was mostly rhetorical because it was pre-electronic-CAD ... so it saves real estate on the board, saves time laying out interconnects on the board, and so forth, because a human was doing the layout. It doesn't make much sense now, especially that nearly every 74xx chip ever made is available in a form factor that's 25% the size and consumes 10% of the power of the original chip.

The Horizon HRD+ has three chip stacks -- one stack of three 74LS138s, one stack of two 74LS259s, and one stack of (obsolete in PDIP) 74LS154s.

I have designed replacements for all three that fit into the original socket, with small solder pads at the edge of the board to handle the flying wires that were originally attached to the chips.

The '138 replacement has been tested and seems okay. It incorporates the "hide" function as described in the HRD+ errata, so you'll need a 4.7k resistor and a small two-position switch to superglue up by the positive end of the batteries, with the switch actuator facing towards the rear of the PEB. Connect the middle switch contact to "HIDE" on the PCB, and either of the other two switch contacts to ground (but not both!), and you're good to go.

You may have clearance problems between the daughterboard and the surrounding socketed chips. Add an extra 16-pin socket between the HRD+ and the board, and again you're good to go.

A zipfile containing the Eagle .sch and .brd files for the '138 replacement are here.

The '259 replacement board will be forthcoming. I accidentally ordered N-suffix (DIP) parts rather than D-suffix (SMD) from Mouser, so it'll be a few weeks before I can have that ready and tested. After that, the '154 ...

... and maybe, just maybe, a daughterboard that uses two 512kx8 SRAM chips to bring the HRD+ up to one megabyte. No promises, but the hardware can certainly handle it. This would entail pulling all of the 62256 SRAM chips, plugging into one socket, removing a resistor on the board underside and running a couple of CS/address lines to the '154 daughterboard.

Better than stack-and-solder, anyway. I hope that someone else finds the Eagle files useful when refurbishing the Bud Mills Horizon HRD+.

(Repetition of the full product name for search engines :-)

Various TI python scripts on GitHub

TI-99/4A I should probably mention that I've put various TI scripts up on GitHub here.

Only two of the three are useful at this point: ti_bin_to_wav converts a TI binary file into a 48kHz stereo .wav file suitable for loading back into the TI via the cassette port, and does just what it says on the tin: converts a PC99 disk file into a regular V9T9 disk file. The former generates the .wav file using sine waves of the proper frequency (guaranteeing that the TI will be able to read the file), and the latter uses Thierry's TI disk track documentation to extract and re-order the sector data.

Before various well-meaning people from AtariAge comment that "(random utility) already does that, and it runs great under wine!" ... I'd like to respond pre-emptively with this:

Any utility that a) expects to be run on Windows (even via an ABI translator) and b) doesn't have source code available should not be trusted. Yes, cs1er and tape99 convert TI binaries to .wav files. Yes, TIDir converts between various disk formats. What if I'm running on a Raspberry Pi and therefore don't have Windows ABI capability? What if I'm ssh'ed into a file server and need to do batch conversions quickly? What if the Windows program just plain doesn't work and I haven't the patience to watch a twenty-minute YouTube video to find that one setting that is breaking the process?

What if I just plain don't have a Windows box, have no desire to build one, and know how to program? (Rhetorical questions are fun)

Hell, tape99 didn't even work for most use cases for well over a decade. It wasn't until I nudged a guy that was in contact with the original author that a "fixed" version was released ... and it still emitted .wav files that neither of my consoles could read.

That's why I write this stuff -- prior art exists, but it's non-portable, non-future-proof, non-CLI-friendly, and impossible to troubleshoot when it's broken. And TI people, out of the entire computing community, should know better than anyone about depending on a single source and undocumented code to run their gear.

(nothing personal, AtariAge folks, but some of you just don't get it and probably never will -- users and engineers are fundamentally different creatures)

Why I am against RAM-based DSRs

TI-99/4A Right, so there's a non-negligible number of TI users that use hardware with their DSRs (device-specific firmware) in battery-backed RAM.

This is a horrible, HORRIBLE idea ... but I'm apparently having difficulty communicating to various users exactly why it's a bad idea. "It's worked for me for thirty years, all I need to do when the system gets weird is to pop the battery and reload the DSR".

Well, that's nice, sunshine, but just because it kinda sorta works for you most of the time doesn't mean that it's a good idea to design hardware that way.

I understand the arguments for using RAM for firmware -- rapid development cycle and easy upgrades. And that's it. Let me enumerate the arguments against using RAM for firmware:

1) You've got to be able to bootstrap the system somehow. If the DSRs are involved in the boot process (whether simply initializing buffers, or actually being a boot device), and they're corrupted, you're screwed if you don't have an alternate method to boot the device and restore the DSR.

2) It is way, WAY too easy to corrupt the DSR. The HDX DSR, in particular, appears to use buffers at the upper end of what would be ROM space. It's RAM, though, so it doesn't have to worry about the buffers being overwritten by other programs. It does have to worry about overrunning those buffers and thus corrupting code, though, and it doesn't. The HDX has many flaws, but this is the killer -- it doesn't separate data from executable code, because (in the words of Raoul Duke) "the pension fund was just sitting there!"

3) Modified Harvard Architecture. Learn it, live it, love it. Keep your executable code separate from your data buffers and your system won't be vulnerable to buffer overflows. We learned this in UNIX a long, long time ago, and that's why NX pages exist.

4) Ready availability of cheap EPROM programmers. Back in the eighties, an EPROM burner was a valuable device. My Data-I/O burner cost nearly four digits. They're USD$30.00 now on eBay and any fool can use the Windows point-and-drool interface to burn the DSR to EPROM.

In summary, using RAM for DSRs on the TI is (in my educated, non-humble opinion) a gigantic goddamned design error.

There. That's off my chest :-)

Refurbishing a Horizon HRD+ RAMdisk Card

TI-99/4A I recently came into possession of an ancient Horizon HRD+ RAMdisk card for the TI-99/4A PEB. Not the 2000, 3000, or 4000 -- the original model, designed by Bud Mills and Ron Gries circa 1986.

It's a variable-size RAM disk with battery-backed RAM. There's a rudimentary charging circuit onboard, so that NiCad batteries can be used and charged while the PEB is on. This particular card hadn't been turned on since 1989, based on the calendar taped to the front of the PEB that it was in ... so, needless to say, the batteries were not only dead but had leaked into the chips below.

First order of business was to replace the corroded AAA battery holders at the top of the card and assess the damage. Lucky for me, the original owner decided to put gigantic ceramic 0.1uF decoupling capacitors directly beneath the batteries, and they blocked most of the battery goop from the chips below ...

... which was a good thing, because this was a) fully-populated with 62256s (which are getting hard to find) and b) using a couple of 74LS154s soldered together to do the address decode, and .600 '154s aren't made anymore as far as I can tell.

I pulled the top row of chips (including the '154s) and scrubbed everything with isopropyl alcohol anyway. Good thing that I did, too, as the top right (power) pin on the '154 stack came off as I pulled the chip. The stub was okay, but the bit that went into the socket had corroded away. Easy enough to fix with a bit of extra stiff wire left from an LED from another project.

For some reason, mid-80's TI gear used a lot of stacked chips. There were wires flying from pins bent up on the stack, and some of those wires weren't connected anymore. So I needed a schematic ...

... and the go-to place for TI documentation (the Horizon subdirectory) didn't have a manual for the plain HRD+.

A few days of searching led me to a post on AtariAge, where a person with the handle "schmitzi" posted a low-quality scanned version of the construction manual, including schematic (source here).

I fixed the wiring to match the schematic, powered it up, and it appeared to be okay. All memory tests passed, it could be formatted as a 384k RAM disk, and all was well ...

... until I replaced the (ancient, power-wasteful, hot-to-the-touch) linear 7805 with a Minmax switching 7805. Now, suddenly, it didn't work. I could see only a few volts across the power rails. That was not right.

It turns out that the 7805 ground connection isn't really connected to ground. It has to go through a diode first (which isn't usually a problem) and is fed back into the supply voltage via resistor R10 (which is a problem). The fix is to remove R10 and connect pin 2 of the 7805 directly to ground. Problem solved.

It also turns out that Bud Mills published errata for the HRD+ that fixed a data-corrupting reset-on-powerup bug, and added a switch to lobotomize the card in case the DSR somehow became corrupted. That document was found (in PDF form) in the TI-99/4A Yahoo! mailing list archives.

(Side rant: what is it with TI people and DSR-in-RAM? I've seen three projects now that put the DSR in battery-backed RAM, with predictable results. Did they all sleep through the electrical engineering class where they were taught that critical code needs to be in non-volatile memory unless you have a damned good reason for not doing so?)

Digression aside ... I have OCR'ed and cleaned up both the construction document and the errata and placed them here and here.

I have taken the liberty of correcting typos in the former; the latter didn't appear to need correction.

As far as I can tell, there was only one source each for these documents on the net (schmitzi's post on AtariAge for the construction manual -- and that place is sort of a walled garden regarding web spiders), and I've therefore posted cleaned-up versions of both here with the intention that the various search engines can find them easier.

I hope these documents help the next poor fellow that finds one of these cards and decides to make it go.

Why won't the TI-99/4A cassette port work with my PC sound-out port? Here's why ...

TI-99/4A TL;DR: the OEM TI cassette cable tape-audio-1-input jack is wired incorrectly for equipment available now. Leave pin 9 floating and run pin 3 to CS1 plug sleeve and things magically work. You can cheat by amplifying the signal a lot (and expect it to have issues from time to time) or fix your wiring. Your choice.

Problem description:

Under most circumstances, when wired straight into either the headphone or line-in jack, the signal heard from the TI speaker is low. Really, really low. Much, much lower than I remember it being. And, thus, the TI doesn't see the signal at all. Plugging the microphone jack in sometimes helps, but it's still twitchy.

If you screw around with the cable (ground pin 4, plug the mike jack into headphone out and the audio-in jack into line out, pull the plug out about three millimeters, and so forth), suddenly the incoming signal volume dramatically increases and the TI sees it. That's ... not right, it's very wrong, so I looked deeper.

I've spent two days poring over schematics (the official TI schematics, the SAMS schematics, with an eye towards Thierry's cassette port description) ... and I think I found the problem.

Let's start with Thierry's tape description. This description appears to be the same description that is reproduced throughout the net, but it's slightly wrong (or, at least, cryptic). It looks like this (omitting the ASCII DB9 diagram):

# I/O Use
- --- -------
1 > Cass 1 motor control
2 > Ditto (negative)
3 > Output to tape 1 or 2 (neg)
4 > Audio gate
5 > Output to tape 1 or 2
6 > Cass 2 motor control
7 > Ditto (negative)
8 < Input from tape 1
9 < Ditto (neg)

According to the schematics, that's mostly correct. There's one thing that's glaringly missing, though -- a ground connection.

The way Thierry's diagram should read is thus:

# I/O Use
- --- -------
1 < Cassette 1 motor control (switch input)
2 > Cassette 1 motor control (switch output)
3 x Ground (connected to cassette 1 and 2 microphone sleeve)
4 > Audio gate (unused, signal not present on cable)
5 > Audio output to both cassette 1 and cassette 2
6 < Cassette 2 motor control (switch input)
7 > Cassette 2 motor control (switch output)
8 < Audio input from cassette 1
9 < Audio input from cassette 1, connected to ground through RC filter

Note pins 8 and 9. 8 goes to the tip of the 3.5" jack -- that's the signal. 9 should be grounded, according to the TRS convention where tip and ring carry signal, but sleeve is always grounded.

This would be why I was never seeing signal without the mike line connected to something; there is no ground continuity between the recorder and the console except through the microphone connector.

I say again: If the microphone connector isn't connected, there is no ground connection. The TI depends on the microphone jack ground connection to ground the headphone/line-out jack.

I haven't done the math to figure out what band that RC filter on pin 9 is passing, and the schematics have different opinions about what else is wired into that part of the circuit. Maybe cassette 2 was originally supposed to have output capability, and there was a last-minute cost-cutting measure

The fix is simple: connect the sleeve of the audio-in jack to pin 3, as it's supposed to be grounded anyway.

That, however, is going to also ground the ring on the cable jack, and depending on how the audio source is wired, that may cause issues/damage the ring audio source. There are two possible fixes here: replace the male mono jack on the cable with a stereo mono jack (and leave the ring floating), or build a work-alike interface.

I chose the latter, mostly because my TI cable is extremely beat-up and therefore has questionable electronic reliability, but partially because it's a cleaner solution.

I took a small project box and used a nibbler to cut a hole for a female DB9 connector. I then installed two female stereo 3.5-inch jacks. Pin 5 on the DB9 is connected to the tip of one of the 3.5-inch jacks (microphone), pin 8 to the tip of the other (audio-in), and pin 3 to the sleeve of both (ground)

You can wire in cassette 2 and/or motor control (I'm doing the latter, as the display shield on my BeagleBoneBlack has capability for this), but that's the magic signal fix.

Electrical engineering bits that the uninterested can skip:

What IS going on with that circuit hanging off of pin 9? page 23 of this tech ref makes it clear that it's an RC filter. The QI circuit on page 30 makes it look like it's boosting the impedance (which might actually work, but I don't have a QI). The SAMs looks very much like the RC filter variant, except it has another resister in series before the filter that's probably boosting impedance.

Why hasn't anyone noticed this problem before? I believe that it's because the problem can be mitigated by throwing enough amplification at the problem. You'll see recommendations on forums like AtariAge to run the signal through amplified speakers, thence through the speakers' headphone jack. Others recommend using the line-out jack only, or the headphone jack only, at specific OS-side volume levels.

Each method succeeds to varying degrees, depending again on the hardware that's being used. None address the root issue: the TI cable isn't grounding both plugs, and worst-case is feeding audio backwards into the RC filter on pin 9.

It's that simple. You don't need to mess about with specific volume control settings or specific brands of USB audio dongles: it's a TI design flaw that assumed that the cable would a) always be plugged into a mono source and b) always have the microphone plug connected.

So, in summary, here's the behavior summary as I see it:

1) The TI cassette cable has mono plugs,
2) The audio-in plug therefore shorts the ring (right channel) and sleeve (ground),
3) The TI cassette cable connects the microphone sleeve to console ground,
4) The TI cassette cable connects the audio-in sleeve to ground via (at least) an RC bandpass filter,
5) If the TI cassette cable microphone plug is not plugged into the sound source, there will be a ground loop through the audio-in sleeve,
6) Behavior of the system beyond that point depends very highly on the quality of the sound source design,
7) Anecdotally, adding a large amount of amplification (and possibly equalization) to the signal overcomes the ground loop.

From an electrical engineering (and, thus, physics-derived) standpoint, here's how I see it:

1) There are at least three different versions of the circuit schematic for the goop connected to the audio-in sleeve, which in turn means ...
2) ... there will be variance in what works for each user, depending on the goop in that particular console and the sound source design,
3) The incoming signal is amplified by a pair of op-amps to +5VDC in the console, so significant signal amplification should not be necessary,
4) In general, ground loops are bad and should be avoided if at all possible,
5) In general, significant signal amplification is wasteful and should be avoided if at all possible.

So, here are the possible fixes (in ascending order of desirability) as I see them:

1) Run the signal through a large amount of amplification to overcome the ground loop,
2) Plug in the microphone jack and use a mono-to-stereo plug converter to isolate the ring from the sleeve on the audio-in jack,
3) Replace the mono-intended cable with a stereo-intended cable wired per the first post in the thread.

The first one works for some people, but is electrically incorrect and will not work in all cases. The second and third are functionally equivalent.

Converting TI disk images to 80-track double-sided single-density (DSSD)

TI-99/4A The stock TI disk controller ROMs support 40-track double-sided single-density, which comes to a little over 180k per disk. The controller, as shipped, expects track 0-39 on side 0 and 40-79 on side 1.

If the ROMs are replaced with modified EPROMs, the capacity can be doubled. We can't do much about the single-density limitation, as the WD1771 controller didn't handle double density, but we can increase the track count per side to 80. There's a note on AtariAge that describes how it's done, but I can't seem to find the post at the moment. If memory serves, we change the code to increment the disk head used (side 0 to side 1) from > 39 to > 79. A simple but elegant hack. (Update: here is the post in question)

Tony Knerr (may he rest in peace) originated the modification. I've placed a zipfile containing the EPROM images and instructions on this site here. Pre-burned EPROMs can be purchased from ArcadeShopper here.

That, however, only solves half of the problem. This modification renders the drive unable to use 40-track DSSD images, and there are a lot of those floating around out there.

One solution is to use Fred Kaal's excellent TI99Dir program to create an 80TDSSD image and copy the files to it, but it's Windows-only (runs under Wine).

I wrote up the following code to convert files in batches on FreeBSD/MacOS/Linux. To use it, you'll need the very latest version (greater than 1.5.0) of from the excellent xdt99 tools. That is because we need to specify the 80TDSSD disk image geometry explicitly, and that support landed in the code only a few days ago.

I've uploaded the conversion wrapper (a simple shell script), along with a working version of, here.

I hope this helps others in the same situation. Note: this doesn't properly extract files with "*" prefixing the name. Everything else should be fine.

The TI-99/4A PEB flex-card "tune-up".

TI-99/4A The fellow at has had an article up for the past decade that recommends that few TTL chips in the flex-card that connects the TI-99/4A console to the PEB should be replaced with CMOS equivalents, and swap a few old electrolytic capacitors while you're at it.

The theory is sound. CMOS logic 0 and 1 are rail-to-rail (as opposed to TTL's "well, anything over three volts or so is logic 1"). The signals handled by the replacement chips should attenuate less as they move the meter or so from the console to the PEB interface card. The devil, though, is in the details, and this particular devil is the obsolete logic family that he recommends.

He states that the 74LS244s be replaced with 74HCT244s. That was good advice in 1982, when the HCT family was released. However, the fanout isn't improved and the propagation delay is doubled.

I recommend instead that they be replaced with 74ACT244 units. Fanout is quadrupled and the propagation delay stays the same as with the LS units, but draws about a quarter of the the power. ACT was introduced in the mid-nineties, if my memory is correct.

I'd also go one step further and replace the LS245s on the flex cable, and all of the LS244 and LS245s on each card in the system, with their ACT counterparts. The whole point of this exercise is to buffer the address and data lines; the original instructions buffered only the address lines, and only to the PEB physical bus. TI recommended that both address and data be buffered on each card as well, so these revised instructions follow TI protocol.

Definitely replace the capacitors, though, as they're going to leak if they haven't already.

If you decide to replace the chips on the cards as well, you should also replace the positive voltage linear regulators (7805 and 7812, typically) with new switching equivalents. I don't have part numbers off-hand (there are a lot of fabs making switching drop-ins for the 78xx series), but replacing a 7805 with 0.5A switching equivalent makes a significant difference in the heat generated by the card.

(I sent portions of the above to the maintainer of the mainbyte site, with no response. I suppose that's all that can be expected from a decade-old website ...)