Skip to content

Internal power / disable switch for the Axiom Parallax printer interface.

TI-99/4A The Axiom Parallax sidecar Centronics printer interface has only nine chips inside, and they're all small LS TTL glue.

The console +5VDC supply should be able to handle the additional (minimal) current draw, especially if the console has an F18A and the now-useless 4116s were removed. One less wall wart to get in the way, plus the Axiom runs cooler because the linear regulator isn't dumping ~+4VDC as waste heat.

So I tried it. It works. Here's what you have to do:

  • Ensure that you've actually got +5VDC on pin 1 of the expansion bus (lower left as you're looking at it). If you have a speech synthesizer, you don't, because TI didn't run the line. If that's the case, crack the synthesizer open and run a wire between pin 1 on the socket and pin 1 on the card edge. Be careful not to oversolder the card edge -- solder it to the very edge and clean up with desoldering braid.
  • Disassemble the Axiom. There are a lot of screws and standoffs. Keep track what goes where.
  • Desolder and remove the 7805 voltage regulator. Note the orientation of the 7805 -- pin 3 is the pin on the right-hand side as you're facing the regulator, and that's the one we care about. The board quality is just a tad above crappy; if I had to do it over again, I'd snip the leads and pull what's left out of the hole rather than do a full desolder, because I lost the through-plating on the (now unused) voltage supply pin.
  • On the bottom of the board, solder a wire between pin 1 on the socket and the solder pad of where pin 3 of the 7805 used to be. The pad may not have been run through to the bottom of the board; if so, poke the wire through the hole and solder to the pad on the top of the board.
  • Remove the old power jack, because you'll need that hole for the disable switch.

  • There's another modification that the Axiom should have if you're using an UberGROM.

    As documented, the UberGROM uses spacebar-at-powerup to bring up the recovery code.

    Well, the Axiom does the same thing -- it goes into diagnostic mode when spacebar is held at powerup, and keeps outputting chr(0x01) through chr(0xff) until you release it. And the UberGROM never sees the spacebar held down, so it never invokes the recovery code.

    And if you're like me, you probably have your sidecar connectors wired down with something like double-sided tape to keep them from shifting and crashing the console. Thus, unplugging the Axiom is a PITA when building up UberGROMs on real hardware.

    Luckily, disabling the Axiom at boot-time is simple. Do this:

  • locate pins 20 (!CE) and 18 (!OE) on the ROM/EPROM,
  • disconnect them. There's a trace on the bottom of the PCB that wires them together -- take a razor knife and break that connection.
  • prepare a single-pole dual-throw switch (i.e., two positions, three wire lugs). Solder a 4.7k resistor and a length of wire to the leftmost lug (which we'll call 1), and regular wire to the middle and right lug (which we'll call 2 and 3).
  • Wrap electrical tape or heat shrink tubing around all of the switch solder joins (both sides of the resistor especially). You don't want anything shorting out inside.
  • Feed the three wires in through the top of the PCB. There are a bunch of viable holes: pick one that feels suitable.
  • Connect 1 to +5VDC. There's a nice big lug to the left of pin 24 on the ROM/EPROM; that's a safe place to hook up.
  • Connect 2 to 18 (!OE).
  • Connect 3 to 20 (!CE).
  • Test before reassembly. If it all works, reassemble. Put the switch where the power input used to go.

  • What you're doing is forcing the EPROM output-enable signal to be logic-high all the time when the switch is engaged, essentially disabling the EPROM. When the switch is disengaged, !OE gets the same signal as !CE (the chip-select line) and the EPROM is enabled when the decode glue kicks in. This method takes advantage of the old ROMs having separate chip-enable and output-enable, and makes it easy to turn off the EPROM without messing with the chip select logic.

    (Also of interest: the Axiom can use either a 2716 or a 2732. Pin 21 (A11 on 2732) is grounded on the PCB, so both will work. Just put the firmware in the bottom half of the 2732 and you're good to go -- no need to fill the ROM by double-copying.)

    Printing from a TI to a modern printer

    TI-99/4A So, obviously, the TI-99/4A doesn't support printing to USB printers, nor can it print to network printers.

    With a little help, though, it can print to modern printers via a helper device running UNIX/FreeBSD/etc. This leverages the BeagleBone Black-based cassette emulator I described in an earlier web log entry.

    Here's how it's done:

    Step 1: Decide if you're going to use the TI's parallel port or the serial port. I recommend the parallel port for the sake of speed -- the serial port lacks any sort of handshaking, so you will drop characters if you choose serial.

    Step 1a (parallel): obtain a parallel-to-serial converter. They're about fifteen bucks on eBay. Pretty much any unit will do, so long as it has a Centronics port on the input and a DB25/DB9 on the output. Configure it to output as fast as it can (typically 38400, which we'll refer to as ${SPEED} for the rest of this procedure), 8N1.

    Step 1a (serial): obtain a straight-through DB25 <-> DB9 cable. DB25 will be male, DB9 will be female.

    Step 2: obtain and connect a USB serial adapter to the BeagleBone Black. You will probably need a small USB hub if you're also using it as a cassette deck emulator -- passive hubs are fine. Run "dmesg" on the BBB's console until you see it detect the serial adapter and assign a device name (typically "ttyUSB0"). Note the device name; we'll refer to it as ${DEVICE} for the rest of this procedure.

    Step 3: connect the TI to the BBB. If you went with step 1a, verify that you can successfully send characters through your parallel converter -- i.e., print a file while running minicom on ${DEVICE} at ${SPEED}. If you went with step 1b, no further actions are necessary for this step.

    Step 4: download my TI utilities from GitHub here ... you care about everything in the printer_listener subdirectory.

    Step 5: on the BBB, make an incoming printer spool directory wherever convenient. We will refer to it as ${SPOOL_DIRECTORY} for the rest of this procedure.

    Step 6: inside the printer_listener subdirectory is an "epsonps" directory. Enter it, and execute "make; sudo make install". The Epson-to-PostScript converter (and its PostScript preamble) should be installed to /usr/local/bin.

    Step 7: on the BBB, as root, ensure that the python2-pyserial package is installed.

    Step 8: on the BBB, copy the script from the printer_listener subdirectory to someplace convenient in your path. You can also leave it in-place, but you'll need to explicitly specify its location when started.

    Step 8a: on the BBB, look at the sample filter scripts and optionally choose one to use. One will send the printed output to a networked printer as HP PCL, the other will leave a PDF in /tmp. If you choose to use a filter, copy it to someplace convenient in
    your path (hereafter referred to as ${PATH_TO_FILTER})

    Step 9: on the BBB, execute " -d ${DEVICE} -s ${SPEED} -f ${PATH_TO_FILTER} -n ${SPOOL_DIRECTORY} (with the "-f" and "-n" options being, well, optional). You will see "printer listener listening" after the serial port is initialized.

    At this point, anything you print will appear either in the directory that you ran from, or the ${SPOOL_DIRECTORY} if you specified that option. If you specified a filter with "-f", the output will be processed per the directives in the filter file.

    That's about it. You'll probably want to wire this into a startup script; I despise systemd, so I'm leaving that as an exercise for the student.


    Finished and verified: SMD replacement boards for Horizon HRD+

    TI-99/4A As previously mentioned, I've been working on an old Horizon HRD+ RAMdisk card for the TI-99/4A.

    Three of the chips (a 74LS138, a 74LS154, and a 74LS259) had other chips stacked and soldered on top of them, with leads bent out and wires connecting to various places on the board. One chip (the 154) is no longer available in .600 DIP form.

    For obvious reasons, I decided to replace these chip stacks with plug-in boards. Each board is electrically equivalent to the chip stack that it replaced, with the exception of the 138 replacement board incorporating errata wiring later published by Horizon. All chips are SMD, and are currently available as HCT.

    The Eagle schematic and board files are available here.

    All three of these boards designs have been tested and are currently in use in my HRD+.

    Enjoy. Hopefully this will help someone out there that's trying to get one of these working, but is stymied by the stacked chips and/or can't find a package-exact replacement for the 74LS154.

    Two UberGROM images: Logo/Logo-II/Multiplan/TurboForth and Plato/Return to Pirate's Isle

    TI-99/4A Thanks to Tursi, Ksarul, and a few others, it's now possible to create bankswitched GROM/ROM cartridges for the TI-99/4A.

    Most of the game cartridges were converted long ago to disk-loadable EA/5 images (most needing the 32k RAM expansion). The languages, courseware, and things that wouldn't fit into a RAM expansion have not been converted.

    The aforementioned guys built a GROM simulator around an AVR, added a bank-switching 512k EEPROM for the ROM side of the cartridge, and released it as the UberGROM cartridge board.

    Therefore, I went ahead and created two multicarts. The first one contains Logo, Logo II, and Microsoft Multiplan. The second contains Plato and Return to Pirate's Isle. They can be found here.

    Update: the first image now also contains TurboForth v1.2.2.

    To convert these to cartridges, first obtain two UberGROM cartridge boards. The link above goes to ArcadeShopper's web site, but you can also purchase them directly from Ksarul on AtariAge.

    The AVR must be prepared as per Tursi's instructions here. The instructions expect you to have a cheap eBay TL866 EPROM burner, but it's also possible to program with avrdude via SPI. Contact me for details if you decide to go this route.

    Next, extract the files from the archives you downloaded from the links above. If you're building both cartridges, extract into separate directories.

    Next, burn the "eprom.bin" that corresponds with which of the two multicarts you want to create onto the EEPROM that you should have received with your UberGROM and install it onto the board.

    Next, copy "cart1.tifiles" or "cart2.tifiles" onto a TI-readable media. I use and recommend the HxC Floppy Emulator, which uses disk image files on a SD card to emulate a Shugart-compatible floppy drive. Note: these files are (obviously) in TIFILES format; do the needful when copying them onto the target media.

    Next, copy "gromcfg" from Tursi's website onto the same TI-readable media. Insert that media into the floppy drive. If HxC, be sure to mount it.

    Next, insert the prepared UberGROM cartridge into the TI. Hold down the space bar and turn it on. After the rainbow screen, you should see a menu that includes "Run Program". Choose it.

    The file you run will be "DSKx.GROMCFG", where "x" is the number of the drive that contains the prepared media. After several seconds, you will see the GROMCFG browser.

    Hit control-L. Say yes, you really want to do this, then feed it the name of the cartridge file ("DSKx.CART1" or "DSKx.CART2".

    Now go get a beer, this takes about five minutes.

    When it's finished, powercycle the TI. After the rainbow screen, you'll see an option for "Multicart". Choose that, and you'll see the selection menu for the multicart that you just created.


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