Friday, January 3, 2020

ZX Spectrum TZX TAP ZIP Cassette Loader Using Raspberry Pi

Supratim Sanyal's Blog: Sinclair ZX Spectrum Cassette Loading using Raspberry Pi
Falcon Patrol II (Virgin Games, 1995) - Sinclair ZX Spectrum Cassette Loading using Raspberry Pi

This is a follow up post to "Powering up a British ZX Spectrum after 30 years in America - The PAL UHF Analog TV and 220V AC Challenge" where I described how to run a United Kingdom targeted 220V 50Hz AC powered Sinclair ZX Spectrum microcomputer with analog PAL UHF TV video output in the 110V 60Hz and modern ATSC digital HDTV environment of the United States.

In this post I will describe how I use a Raspberry Pi to load games and software from digitized cassette tapes on the ZX Spectrum in such a fashion that the ZX Spectrum thinks it is listening to a real cassette player.

Here is the hardware you will need:

1) Raspberry Pi 3B+ or later » 

4) Stereo Audio to Mono Splitter Cable »

As for the operating system, load up the usual latest Raspbian desktop operating system on your MicroSD (a 16GB MicroSD is more than enough for this project).

I initially tried to feed audio directly from Raspberry Pi's internal headphone connector to EAR jack of ZX Spectrum, but could not get the ZX Spectrum to load the software despite claims by a few others of such a setup working. Use the USB audio adapter because it outputs the cassette sound in a loud enough volume for the ZX Spectrum to load from it. The 3-ampere power supply is imperative for providing enough juice to power the Pi and the USB Audio Card. Attach the USB Audio adapter to an available USB port of the Raspberry Pi. Also make it the default audio output device from Raspbian desktops' Preferences -> Audio Device Settings screen.

Raspberry Pi USB Audio Adapter Setup Screen

The ZX Spectrum EAR/MIC connection is mono for both audio input (EAR on the ZX Spectrum) and output (MIC on the ZX Spectrum). Connect the male stereo end of the stereo-to-mono audio splitter cable to the audio output of the USB Sound card which is usually marked with a picture of a headphone and colored green. Then connect either of the two mono female ends of the splitter cable to the "EAR" jack of the ZX Spectrum. In my case, having a ZX Spectrum +3,  this is the mono black male jack marked "EAR" of the EAR/MIC cable.

Connecting Raspberry Pi to ZX Spectrum to work as a Cassette Player to load games

I left the red mono "MIC" jack from the ZX Spectrum left unconnected. Of course, as the next experiment, I will try to save files from the ZX Spectrum to the Raspberry Pi using some audio recording software and the Microphone jack on the USB Sound card, but that is another topic.

ZX Spectrum EAR and MIC jacks conneted to Raspberry Pi Audio Cassette Player

That's it for connections. Once your Raspberry Pi boots up to the operating system, take a look at this blogger's post ("Turn your raspberry pi into a virtual cassette to load games on your Amstrad CPC or ZX Spectrum") translating to English if you don't read Spanish, and follow the steps documented there to obtain the basic tool-set. Note that you will need to also install the git package in addition to the packages listed there. Here is a touched-up excerpt.

Build and install the playtzx tool from source:

$ sudo apt-get install audacious texinfo build-essential automake git
$ git clone
$ cd cpctools/playtzx-0.12b/
$ ./configure
$ make
$ sudo make install

After building and installing the the playtzx tool, the binary should be present in /usr/local/bin/playtzx:

$ which playtzx
$ ls -l /usr/local/bin/playtzx 
-rwxr-xr-x 1 root root 241260 Dec  5 19:15 /usr/local/bin/playtzx

The "advanced" script available here supports on-the-fly decompression and playback of zipped TZX and TAP files in addition to regular TZX files. It invokes playtzx to create a VOC audio file in a temporary directory and then runs audacious to play it back. Not liking unnecessary write cycles to the MicroSD card, I created a 128 MB in-memory ramdisk for writing temporary VOC files. To create the ramdisk, become root and create a directory /ram0, then add the following to /etc/fstab and reboot:

tmpfs /ram0 tmpfs nodev,nosuid,size=128M 0 0

Here is my touched-up "advanced" script, modified as follows:
  • added output volume control to set the playback volume to the optimal value that I found (90%) for nice equal-width red and white header loader border bands on the ZX Spectrum cassette loader
  • pointed the temporary directory to /ram0 ramdisk mount-point
  • changed playtzx sampling frequency to 32000 since this resulted in the loading bars in the border scrolling slowly upwards instead of downwards
  • added verbosity to audacious

# playcdt: script bash to play TZX/CDT tape images of Amstrad CPC & ZX Spectrum
# requires playtzx and audacious (see
# version 0.1 alpha - GNU/GPL 2 Jesus Basco 2016
# modified by Supratim Sanyal - see


# Set volume of USB sound card to 90%
echo ====
echo Setting volume
amixer -c 1 cset numid=6 ${volume},${volume}
amixer -c 1 cget numid=6
echo ====

es_zit=$(file -b "$1" | grep -i zip | wc -l)
es_tzx=$(file -b "$1" | grep -i tzx | wc -l)
#USE Ramdisk instead of wearing out SD
if [ $# -ne 1 ]; then
   echo "ERROR: Hay que poner el archivo TZX/CDT"
   exit -1
if [ -f "$1" ]; then
   if [ $es_tzx == 1 ]; then
      echo Reproduciendo archivo \""$1"\"
      playtzx -voc -freq 32000 "$1" ${tmptzx}/temporal.voc

      echo ==== Playing converted file  ====
      ls -lh ${tmptzx}/temporal.voc
      echo =================================

      audacious -pqH ${tmptzx}/temporal.voc
      #rm ${tmptzx}/temporal.voc
   elif [ $es_zip == 1 ]; then
      echo AVISO: El archivo \""$1"\" es un archivo ZIP... descomprimiendo e intentando sacar un archivo CDT/TZX
      mkdir -p ${tmptzx}/tzxtmp
      unzip -C "$1" -d ${tmptzx}/tzxtmp
      for i in `ls ${tmptzx}/tzxtmp/*.{tzx,cdt}`;
          playcdt "$i"
      #rm -rf ${tmptzx}/tzxtmp
      echo ERROR: El archivo \""$1"\" no es un CDT/TZX
   echo ERROR: El archivo \""$1"\" no existe
   exit -1

I saved this script in ~/playcdt-script/ directory under my home directory (and chmoded it executable, of course).

$ pwd
$ ls -l playcdt-script/
total 12
-rwxr-xr-x 1 pi pi 1486 Dec  6 10:14
-rw-r--r-- 1 pi pi 1143 Dec  5 20:57
-rw-r--r-- 1 pi pi 1090 Dec  5 19:19

As for TZX Cassette Games and Utilities, I found a huge collection at this web-site. The 285MB "Games Collections's TZX Format (7z Archive)", for example, has 12,466 TZX files! I decompressed all the .TZX files into a  ~/TZX/tzxgames/ directory under my home directory:

$ pwd
$ ls TZX/tzxgames/
007 De-Pulsar (19xx)(Tony Bryan).tzx
007 - Live And Let Die (19xx)(Encore)[Re-Release].tzx
007 - Lord Bromley's Estate (1990)(Domark)[a][Lightgun].tzx
007 - Lord Bromley's Estate (1990)(Domark)[Lightgun].tzx
007 - Lord Bromley's Estate (1990)(Domark).tzx
007 - Q's Armoury (1990)(Domark)[a][Lightgun].tzx
007 - Q's Armoury (1990)(Domark)[Lightgun].tzx
007 - Q's Armoury (1990)(Domark).tzx
007 Super File 2 (19xx)(-)(Side A).tzx
007 Super File 2 (19xx)(-)(Side B).tzx
100 KM Race (19xx)(Coyote Software)(It).tzx

With all the pieces now in place, you can now command the ZX Spectrum to load from tape the usual way - LOAD "" in 48K mode or using the Tape Loader in 128K mode - and then play back a TZX  cassette tape image on the Raspberry Pi using a command like this:

$ ~/playcdt-script/ ~/TZX/tzxgames/Cyclone\ \(1985\)\(Vortex\ Software\).tzx 
Setting volume
numid=6,iface=MIXER,name='Speaker Playback Volume'
  ; type=INTEGER,access=rw---R--,values=2,min=0,max=151,step=0
  : values=136,136
  | dBminmax-min=-28.37dB,max=-0.06dB
numid=6,iface=MIXER,name='Speaker Playback Volume'
  ; type=INTEGER,access=rw---R--,values=2,min=0,max=151,step=0
  : values=136,136
  | dBminmax-min=-28.37dB,max=-0.06dB
Reproduciendo archivo "/home/pi/TZX/tzxgames/Cyclone (1985)(Vortex Software).tzx"

ZXTape Utilities - Play TZX , TZX to VOC Converter and TZX Info v0.12b for Linux

ZXTape file revision 1.01
Number of Blocks: 6

Creating .VOC file using 32000 Hz frequency.

Block   1:    Program : Cyclone     Length:    19  Normal Speed ,Pause: 1.000s
Block   2:    --------------------  Length:   146  Normal Speed ,Pause: 1.000s
Block   3:      Bytes : loader      Length:    19  Normal Speed ,Pause: 1.000s
Block   4:    --------------------  Length:   514  Normal Speed ,Pause: 1.000s
Block   5:    --------------------  Length:  6914   Speed: 155% ,Pause: 1.000s
Block   6:    --------------------  Length: 40632   Speed: 155% 

==== Playing converted file ====
-rw-r----- 1 pi pi 5.5M Jan  2 18:31 /ram0/temporal.voc

ERROR [guess_element]: No suitable mixer element found.
WARNING [voc]: <0x728a0af0> Estimating duration from bitrate, this may be inaccurate
WARNING [voc]: <0x728a5580> Estimating duration from bitrate, this may be inaccurate
WARNING [voc]: <0x728a3f10> Estimating duration from bitrate, this may be inaccurate

The ZX Spectrum home computer now loads the digitized cassette in TZX format played back by the Raspberry Pi, as you can see in the video at the top of this post. I have even tried a bunch of Fast Loaders, they all work fine, too. Here is Falcon Patrol II - one of the most difficult games to load from cassette due to the fast loader (at a whopping 211% of normal ZX Spectrum microcomputer cassette data transfer speed) - loading fine with the setup described in this post.

Download: You can download » all the tools and TZX collection mentioned in this post from my google drive or dropbox.

No comments:

Post a Comment

"SEO" link builders: move on, your spam link will not get posted.

Recommended Products from Amazon