TAPonDSK - ZX Spectrum Tape to +3 Disk Converter

There are a few utilities that transfer .TAP tape (not .TZX) contents to +3 disk but most, if not all, require manual intervention to change the loader and/or rename the files to disk compatible versions. I've taken a stab at creating a transfer utility which decodes the basic loader and attempts to re-write it for disk loading. It also renames the code files to be 3DOS friendly and for headerless Loaders the utility scans the BASIC and M/C for Tape ROM calls and replaces them with a very short Disk loader embedded in a REM statement. If all of this fails the utility has an option to ignore the BASIC altogether and create a custom loader. This is especially useful for some games which have machine code embedded in BASIC and other tricks which make it very hard to transfer.

The utility is complimentary to Z80onDSK but doesn't have as good a compatibility level. From testing I get around 60% to work (Z80onDSK is more like 98%) with the older titles more likely to work and the later ones less likely. It is also unlikely to work with 128k titles that use the extra memory due to incompatibilities with 3DOS.

In order to improve compatibility just after the last LOAD "" CODE the utility adds a switch to USR 0 with or without bank switching and also puts in a PAUSE to ensure the drive Motor is OFF. In addition to this I've added a few options to help improve the compatibility further. The most useful of these is probably the -o option which turns off bank switching which helps get the Ultimate titles to work. For all options see the Command Line Arguments section below.

It is worth noting that some titles loaders are just too complex and require deep diving into the machine code to figure out how it actually loads and things like load position or code run location. For those I'd recommend trying another version as a lot of .TAP files are actually conversions in the first place due to custom loaders (which are only preserved in TZX format). If that fails Z80onDSK works in nearly most cases.

The utility actually works for a lot more games than I was expecting. One of the reasons I created Z80onDSK instead of a tape version was I didn't think a tape version would work in most cases due to machine code loaders etc... anyway was pleasantly surprised.

Finally the utility will tell you if it can't transfer the tape and no it won't work for multi-loaders.

TAPonDSK v1.0e

Windows

Version History

v1.0e initial release

Worked Example

Quick example to show how to convert a .TAP tape

To convert Manic Miner type:

tapondsk "Manic Miner (1983)(Bug-Byte).tap"

This will create a DISK file with the following output:

+-------------------------------------------------------------------------------
| TAPonDSK v1.0e © 2020 Tom Dalby
+-------------------------------------------------------------------------------
| TAP File Scan
+-------------------------------------------------------------------------------
|  #  | Details
|-----+-------------------------------------------------------------------------
|   1 | Program: "ManicMiner" LINE 1 (L1:78,V:+78,C:0x14) (L2:80,C:0xc7)
|   2 | Code: "mm1       " 22528, 768 (C:0xc9) (L2:770,C:0x39)
|   3 | Code: "MM2       " 32768, 32768 (C:0x91) (L2:32770,C:0x71)
+-------------------------------------------------------------------------------
| Checking for LOAD redirection in BASIC
| - #2 Found LOAD CODE -> CODE location not specified so 22528 OK
| - #3 Found LOAD CODE -> CODE location not specified so 32768 OK
+-------------------------------------------------------------------------------
| Modifying Tape BASIC for Disk
| - Found LOAD, adding in new filename "MANICMIN.0"
| - Found LOAD, adding in new filename "MANICMIN.1"
| - L1:98, V:+98
| - Adding Disk Motor Stop PAUSE & USR 0 Mode Switch.
+-------------------------------------------------------------------------------
| Decoding Tape BASIC (L1:78, V:+78)
+-------------------------------------------------------------------------------
|  #  | LSZE | LNUM | BASIC
|-----+------+------+-----------------------------------------------------------
| 001 |   13 |   10 | CLEAR 30000
| 002 |   39 |   20 | BORDER 0: PAPER 0: INK 0: CLS : LOAD ""CODE : LOAD ""CODE 
| 003 |   14 |   30 | RANDOMIZE USR 33792
+-------------------------------------------------------------------------------
| Replaced 2 LOADs, Need 2.
| Bank Switching ON
| Decoding New Disk BASIC (L1:149)
+-------------------------------------------------------------------------------
|  #  | LSZE | LNUM | BASIC
|-----+------+------+-----------------------------------------------------------
| 001 |   13 |   10 | CLEAR 30000
| 002 |   76 |   20 | BORDER 0: PAPER 0: INK 0: CLS : LOAD "MANICMIN.0"CODE :
|     |      |      |  LOAD "MANICMIN.1"CODE : PAUSE CODE "d": RANDOMIZE USR 
|     |      |      | VAL "23875"
| 003 |   14 |   30 | RANDOMIZE USR 33792
| 004 |   37 | 9999 | REM  NEXT *=\#6.+6.+6.+6v+6.+6Q RANDOMIZE  CLEAR  THEN .
|     |      |      | INKEY$ CLS <> STEP  STEP  STEP  STEP  STEP  STEP  STEP 
+-------------------------------------------------------------------------------
| BASIC Top: 23903
| Checking for best CLEAR Value(s)
| - BASIC CLEAR is OK @ 30000
| - DISK Loader CLEAR OK @ 24575.
+-------------------------------------------------------------------------------
| DSK Name: MANICMIN 
| - Writing BASIC: "DISK     " [44]
| - Writing BASIC: "MANICMIN " [149 V:149]
| - Writing  CODE: "MANICMIN0" [768]
| - Writing  CODE: "MANICMIN1" [32768]
| Writing out Disk: Manic Miner (1983)(Bug-Byte).DSK
+-------------------------------------------------------------------------------

This is probably the easiest type of tape to convert as it is very straight forward with no funky BASIC. Note the added REM statement at line 9999 is the USR 0 switch and also notice the PAUSE added to switch off the disk Motor.

Now for a more complex title, let us try BMX Simulator. Again type:

tapondsk "BMX Simulator (1987)(Code Masters)(48K-128K).tap"

Now this title has 2 Headerless CODE blocks so the utility scans the BASIC and M/C before these blocks to find Tape Load ROM calls. As you can see from the output it successfully finds these in the 1st CODE block loaded and replaces the CALLs with a custom DISK Loader. I've done it this way to keep the loader in tact so any copying or setting up remains as is to improve compatibility.

+-------------------------------------------------------------------------------
| TAPonDSK v1.0e © 2020 Tom Dalby
+-------------------------------------------------------------------------------
| TAP File Scan
+-------------------------------------------------------------------------------
| TAP File Scan
+-------------------------------------------------------------------------------
|  #  | Details
|-----+-------------------------------------------------------------------------
|   1 | Program: "BMX       " LINE 0 (L1:44,V:+44,C:0x77) (L2:46,C:0x7f)
|   2 | Code: "          " 65500, 34 (C:0x82) (L2:36,C:0x80)
|   3 | HL Data Block: ?????, 6912 (L2:6914,C:0x74)
|   4 | HL Data Block: ?????, 40600 (L2:40602,C:0x43)
+-------------------------------------------------------------------------------
| Checking for LOAD redirection in BASIC
| - #2 Found LOAD CODE -> CODE location not specified so 65500 OK
+-------------------------------------------------------------------------------
| Headerless CODE blocks found. Scanning BASIC & M/C to determine Start Pos.
| - Found 0 Start(s) in BASIC
|   - JP Not Found & SP Not Found
| - Found 2 Start(s) in CODE Block File 2
|   - Found JP 47500 & SP Not Found
+-------------------------------------------------------------------------------
| HL# | Updated Details
|-----+-------------------------------------------------------------------------
|   2 | CODE: "" 16384,6912
|   3 | CODE: "" 24900,40600
+-------------------------------------------------------------------------------
| Modifying Tape BASIC for Disk
| - Found LOAD, adding in new filename "BMX.0"
| - L1:49, V:+49
| Decoding Tape BASIC (L1:44, V:+44)
+-------------------------------------------------------------------------------
|  #  | LSZE | LNUM | BASIC
|-----+------+------+-----------------------------------------------------------
| 001 |   13 |   10 | CLEAR 24900
| 002 |    5 |   20 | LOAD ""CODE 
| 003 |   14 |   30 | RANDOMIZE USR 65500
+-------------------------------------------------------------------------------
+-------------------------------------------------------------------------------
| 2 Headerless Blocks Found, scanning previous m/c for loader...
| - Redirecting Headerless Tape Calls to 23809.
| - Headerless File 1 -> F:2 @11 -> 0xcd 0x0556 to 0xcd 0x5d01
| - Headerless File 2 -> F:2 @26 -> 0xcd 0x0556 to 0xcd 0x5d01
+-------------------------------------------------------------------------------
| Replaced 1 LOADs, Need 1.
| Decoding New Disk BASIC (L1:136)
+-------------------------------------------------------------------------------
|  #  | LSZE | LNUM | BASIC
|-----+------+------+-----------------------------------------------------------
| 001 |   13 |   10 | CLEAR 24900
| 002 |   10 |   20 | LOAD "BMX.0"CODE 
| 003 |   14 |   30 | RANDOMIZE USR 65500
| 004 |   83 | 9999 | REM  NEXT . CLEAR ¸>. GO SUB y2\[ CLS  INVERSE  RESTORE 
|     |      |      |  MERGE  STEP ..CODE W_go STEP ?....PY!G] STEP ..!P]4...
|     |      |      |  MOVE  LLIST  STEP .... STEP .. STEP .. NEXT . CLEAR ¸>.
|     |      |      |  GO SUB y2\[ CLS <>BMX     .1 COPY .
+-------------------------------------------------------------------------------
| BASIC Top: 23890
| Checking for best CLEAR Value(s)
| - BASIC CLEAR is OK @ 24900
| - DISK Loader CLEAR OK @ 24575.
+-------------------------------------------------------------------------------
| DSK Name: BMX      
| - Writing BASIC: "DISK     " [44]
| - Writing BASIC: "BMX      " [136 V:136]
| - Writing  CODE: "BMX     0" [34]
| - Writing  CODE: "BMX     1" [6912]
| - Writing  CODE: "BMX     2" [40600]
| Writing out Disk: BMX Simulator (1987)(Code Masters)(48K-128K).DSK
+-------------------------------------------------------------------------------

Now the REM statement at line 9999 is the custom disk loader and not the USR 0 switch which is now not possible. You are also unable to turn bank switching off as the loader needs switching to remain on to work.

Ok so let us get a little more complex. For a Spectrum +3 memory area 0x5B00 to 0x5C00 is used for storing 3DOS variables. As such you cannot overwrite it during the load and unfortunately a lot of tape loaders do exactly this including the Ultimate Play the Game titles. If the utility encounters one of these it abandons the original BASIC loader and builds a custom version which copies this memory area at the end of load once 3DOS has finished.

Let us try Atic Atac. Again type:

tapondsk "Atic Atac (1983)(Ultimate Play The Game).tap"

This will start as normal but after the initial scan will determine that the 3DOS area is being overwritten. It immediately flips into building the custom loader. One thing to note is that the custom loader automatically turns off the bank switching, the -o option now actually turns it back on if required.

+-------------------------------------------------------------------------------
| TAPonDSK v1.0e © 2020 Tom Dalby
+-------------------------------------------------------------------------------
| TAP File Scan
+-------------------------------------------------------------------------------
|  #  | Details
|-----+-------------------------------------------------------------------------
|   1 | Program: "ATIC      " LINE 0 (L1:388,V:+388,C:0x1f) (L2:390,C:0x84)
|   2 | Code: "ATSP      " 16384, 6912 (*P2:388,C:0xcb) (L2:6914,C:0x90)
|   3 | Code: "0         " 24575, 30209 (*P2:388,C:0x41) (L2:30211,C:0xae)
|   4 | Code: "1         " 23424, 18 *3DOS (*P2:388,C:0x5e) (L2:20,C:0x98)
|   5 | Code: "2         " 23728, 1 (*P2:388,C:0x79) (L2:3,C:0x16)
|   6 | Code: "3         " 23672, 2 (*P2:388,C:0xb3) (L2:4,C:0xee)
+-------------------------------------------------------------------------------
| Checking for LOAD redirection in BASIC
| - #2 Found LOAD SCREEN$ -> 16384 Compared to 16384
| - #3 Found LOAD CODE -> CODE location not specified so 24575 OK
| - #4 Found LOAD CODE -> CODE location not specified so 23424 OK
| - #5 Found LOAD CODE -> CODE location not specified so 23728 OK
| - #6 Found LOAD CODE -> CODE location not specified so 23672 OK
+-------------------------------------------------------------------------------
| CODE block in File 4 overwrites the 3DOS buffers @ 0x5B00 to 0x5BFF. This will
| cause the disk load to fail. It might be possible to build a Custom Loader...
+-------------------------------------------------------------------------------
|  #  | LSZE | LNUM | BASIC
|-----+------+------+-----------------------------------------------------------
| 001 |  384 |    0 | CLEAR 24574: BEEP .1,1: BEEP .1,2: BEEP .1,3: BEEP .1,4:
|     |      |      |  BEEP .1,5: PAPER 0: BORDER 0: INK 7: BRIGHT 1: CLS :
|     |      |      |  PRINT  BRIGHT 1; INK 7;AT 9,6;"ATIC ATAC IS LOADING";AT 1
|     |      |      | 2,10;"[IV1]PLEASE WAIT"[IV0]: PRINT AT 0,0: LOAD ""
|     |      |      | SCREEN$ : INK 0: PAPER 0: PRINT AT 6,0: LOAD ""CODE :
|     |      |      |  PRINT AT 6,0: LOAD ""CODE : PRINT AT 6,0: LOAD ""CODE :
|     |      |      |  PRINT AT 6,0: LOAD ""CODE : PRINT USR 23424
+-------------------------------------------------------------------------------
| Scanning BASIC for CLEAR and USR
| - Found CLEAR: 24574 (24574)
| - Found USR: 23424 (23424
+-------------------------------------------------------------------------------
| + F:2 16384, 6912
| + F:4 23424,   18
| + F:6 23672,    2
| + F:5 23728,    1
| + F:3 24575,30209
| #1 (1) 16384 -> (16384,6912) -> 6912 (P:0) (O:0)
| #2 (4) 23296 -> (23424,18)(23672,2)(23728,1)(24575,30209) -> 1280 (P:1258) (O:30208)
| #3 (0) 24576 -> 30208 (P:0) -> 54784
| Custom loader:
| - CODE 16384,38400 + SP 24574 + JP 23424
| - Bank Switching OFF
+-------------------------------------------------------------------------------
| DSK Name: ATIC     
| - Writing BASIC: "DISK     " [44]
| - Writing BASIC: "ATIC     " [145]
| - Writing  CODE: "ATIC    0" [38400]
| Writing out Disk: Atic Atac (1983)(Ultimate Play The Game).DSK
+-------------------------------------------------------------------------------

There are some other options which can help with things like removing files in the tape which are not needed and cause the conversion to fail, and removing lines from the BASIC loader which are again not required and may cause issues. I got Warlock of Firetop Mountain to work by removing a lot of the BASIC loader before conversion.

Command Line Arguments

+-------------------------------------------------------------------------------
| TAPonDSK v1.0e © 2020 Tom Dalby
+-------------------------------------------------------------------------------
| Usage: TAPonDSK tapfile 
|        disk filename taken from tapfile
|   options:
|     -d supress adding a basic loader called DISK which autoruns from menu
|     -D instead of using additional file just rename the basic loader to DISK
|     -f NUM - skip initial program(s) before starting transfer, useful if the
|        tape has startup intros or similar. Only works if the files after the
|        skipped ones is the BASIC loader with the rest of code following.
|     -o switch off bank switching, useful for some older games. Note if the
|        utility creates a custom loader bank switching is already OFF,
|        specify this option for custom will turn ON bank switching instead
|     -r LINE(s) - BASIC lines to remove from the loader, useful for some games
|        with unused or surpless BASIC which clashes with disk loading but can
|        be safely removed. Specify lines as comma separated: 1,5,7,8
|     -m NUM - show machine code dump for the FILE number specified.
|     -b NUM - just decode the basic FILE number specified
|     -l instead of hacking the current BASIC/LOADER force creation of the
|        custom Disk Loader even if deemed not required
|     -i NUM - change colour of Loader PAPER & BORDER. Note only effects the
|        basic loader so if arguments -d or -D used this does nothing.
|     -n do not add either the disk motor stop pause & usr 0 mode switch
|     -p just add disk motor stop PAUSE & not USR 0 mode switch
+-------------------------------------------------------------------------------