Relocatable Quick-Tape installer for the Sharp PC-1500

Saturday, 26th April 2025

After my previous journal post about faster saving and loading of Sharp PC-1500 programs to and from tape I discovered that I was using a somewhat out-of-date version of Sharp Pocket Tools. The later version I upgraded to adds support for the "SuperTape" and "Quick-Tape" formats, and also includes copies of the PC-1500 programs for those formats.

Both formats appear to support file names and verification of the recorded data (a minor gripe I had with the Fast Load was its lack of these features). SuperTape apparently manages even higher speeds, though at the cost of somewhat more RAM than Fast Load (711 bytes, or 761 for the full version with the ability to merge a program with the existing one in memory, instead of Fast Load's 540 bytes). Quick-Tape seems to be about the same speed as Fast Load, but actually consumes less memory overall (only 508 bytes). Quick-Tape also explicitly supports saving machine language programs by specifying a start and end address, something that I cannot see is supported with the SuperTape format, though it looks like it might support loading machine language programs if the correct load address is stored in the file.

Based on my experiences with Fast Load and the documentation that accompanies SuperTape and Quick-Tape I came up with a rough feature comparison table (I can't claim it's completely accurate, as it's based on what I've gleaned from the documentation):

Format Fast Load SuperTape Quick-Tape
Speed Fast Fastest Fast
Filenames
Verification
Machine language (Maybe?)
Variables
RAM usage (bytes) 540 711–761 508
Relocatable (Partial)

To get a rough idea of speed I tried converting the Globe application to a wave file. Both Fast Load and Quick-Tape produce a file that's 30 seconds long — a huge improvement over the seven minutes that the native cassette routines take. SuperTape still manages to blow both of these out of the water with a file that's only 13 seconds long! Only Fast Load seems to have the ability to save BASIC's variables to tape, though this is not a feature I personally have any use for.

The ability to relocate the machine language routines that make up the tape routines is a very important one, however! The available range of memory addresses in a PC-1500 computer will depend on which memory expansion module is installed and whether any other machine language routines are also loaded. Fast Load can be relocated to anywhere you want in memory via its BASIC installer program. SuperTape has a few different versions for different starting addresses based on certain memory modules as well as a BASIC installer, however as far as I can see it doesn't seem to allow being loaded at an arbitrary address which would make it trickier for it to coexist alongside other machine language programs on the computer. Quick-Tape doesn't have any relocation support at all, only working if loaded to address &00C5 and used with the CE-161 memory expansion module.

However, Quick-Tape seems like it would otherwise be ideal — it consumes the last RAM, it seems the most feature-complete and though it's not quite as fast as SuperTape it's still a massive improvement over the native cassette routines.

I therefore decided I'd try to make a relocatable version of Quick-Tape. To assist with this I first disassembled it using lhTools. It doesn't load any data nor jump to any absolute addresses within its own memory space, which is a good start — in fact, it only calls three subroutines inside itself: &01BE, &01BF and &01CF. You can spot these easily in the disassembled code as they are assigned named labels by lhTools instead of absolute addresses, for example in this snippet:

lbl_0_159:
	SJP	lbl_0_1be ; labelled address inside our memory
	ADR	Y
	LOP	lbl_0_159
	LDA	YH
	SJP	lbl_0_1bf ; labelled address inside our memory
	LDA	YL
	SJP	lbl_0_1bf ; labelled address inside our memory

...as opposed to:

lbl_0_117:
	SJP	BBD6 ; absolute address outside our memory
	VMJ	0A
	SJP	BBC0 ; another absolute address outside our memory

The version of lhTools I'm using is one I've modified to use the standard LH5801 instruction mnemonics instead of the Z80-inspired ones it normally uses; it also disassembles vectored subroutine calls into standard VMJ instructions instead of the lhTools pseudo-instructions, though as I haven't got it to re-assembled these instructions properly yet this is not something I'm able to share just yet. However, the important thing here is the subroutine called with the SJP instruction.

Now that we know where these subroutine calls are, a BASIC program can be written that patches the loaded machine language program with the correct target addresses for where it's been relocated to. Similarly, the reserve program (which contains BASIC CALL statements to call the entry points in the machine language program) needs to have its addresses patched to point at the relocated machine language program. The resulting installer looks like this:

    10 M=PEEK &7863*256+197:B=PEEK &7865*256+PEEK &7866
    20 IF B-M<508 PRINT "No room": END
    30 L=M: IF B-M>508 WAIT 0: PRINT "Load @";L;" ";: INPUT L
    40 CLS : WAIT : IF (L<M) OR (L+508>B) PRINT "Bad address": GOTO 10
    50 WAIT 0: PRINT "Play QUICK-TAPE...": CLOAD M"QUICK-TAPE";L
    60 FOR I=1 TO 3: READ A,C: FOR J=1 TO C
    70 READ O: POKE O+L,(A+L)/256,(A+L) AND 255
    80 NEXT J: NEXT I
    90 DATA 249,1,149,250,2,157,161,266,3,411,443,452
   100 M=M-197: PRINT "Play QUICK-RESERVE...": CLOAD M"QUICK-RESERVE";M
   110 FOR I=1 TO 6: READ A,O:A$=RIGHT$("00"+STR$ (A+L),5)
   120 FOR J=1 TO 5: POKE M+O+J,ASC MID$(A$,J,1): NEXT J: NEXT I
   130 DATA 4,89,6,101,5,113,2,130,1,153,0,165
   140 CLEAR :Z$="": WAIT : PRINT "QUICK-TAPE installed"

You can download the installer and reserve program as ready-to-go WAV files in Quick-Tape-Install.zip. You'll also need to create a WAV file for Quick-Tape itself, which can be downloaded as part of Pocket Tools. Instructions can be found inside the installer zip archive.

Faster tape loading on the Sharp PC-1500 with Fast Load

Sunday, 20th April 2025

After I've typed a program into my Sharp PC-1500, I tend to save it to my PC for long-term storage using the CE-150 cassette interface. I use the same cassette interface to download programs shared by others over the Internet, and have written a previous post about resampling those shared wave files if you're struggling to load them.

Loading a program this way is quicker than typing them in, but not by much! The Globe application is 5697 bytes in length but takes the best part of seven minutes to load from tape. I was therefore very intrigued by the Fast Load application, which "enables fast loading (4000 baud, 13 times the normal speed) of all Basic or machine language programs and variables".

I didn't have the easiest time using this program, so thought I'd put together this post in case it helps anyone else with similar struggles!

Fast Load's component parts

The included documentation is a little confusing. I assume it's been abbreviated from what would have been included with the original tape, but I'm really not sure. It mentions using CLOAD M to load FAST-LOAD, however if this is to be a relocatable module then how should it be relocated once loaded, and after that point how are you to invoke the saving and loading routines?

Included in the zip archive there are actually three different recordings for three different files, and after studying them a little they need to be loaded in the following order:

  • FAST-RELO: This is a BASIC program that loads the other two files and performs the relocation.
  • FAST-RESERVE: This is a reserve program that provides the key mappings to call the save/load routines.
  • FAST-LOAD: This is the machine language program that implements the save/load routines.

Understanding how all three parts operate will require a bit of knowledge of the PC-1500 and how memory is arranged.

PC-1500 memory layout for reserve program, machine language program and BASIC program

A reserve program occupies the first 197 bytes of RAM. This program can redefine the six keys positioned directly under the display to type in a sequence of characters when pressed. Three separate groups of key mappings are provided (the current group is indicated by the presence of an I, II or III icon on the display) which can be cycled through by pressing the select key. A string can also be stored for each group to act as a reminder of which key does what, and this can be displayed by pressing the RCL key. In the case of Fast Load, the reserve program sets up four keys (two in reserve group II, two in reserve group III) to execute the program/variable save/load routines which it does by typing in CALL <address> and pressing the Enter key. The appropriate routine address for each key is patched into the reserve program by the FAST-RELO BASIC program.

The routines themselves need to be loaded somewhere into memory. By default, the current BASIC program starts in memory immediately after the reserve program's fixed 197 bytes at the bottom of memory, however it is possible to move the start of the BASIC program by passing a numeric parameter to NEW. The numeric parameter defines the start of the BASIC program in memory, but it will not allow you to move it below the end of the reserve program and so a good way to reset the BASIC memory to its largest default is with NEW 0 – a command that you are prompted to enter whenever the device has been reset or a memory expansion module has been changed.

By moving the start of the BASIC program to a higher address in memory it leaves a gap between the end of the reserve program area and the BASIC program that is free for us to load persistent machine language programs.

Note that memory does not necessarily start at address 0, either! In its base configuration RAM starts at &4000, with a CE-155 (8KB) RAM expansion module RAM starts at &3800 and with a CE-161 (16KB) RAM expansion module RAM starts at &0000. Fortunately you can read the most significant byte of the RAM start address from address &7863, which makes it a bit easier to calculate sensible values to load to.

Loading Fast Load into memory using its loader

For simplicity, if we assume we're only going to load Fast Load (and don't need to worry about reserving any additional RAM) then the process should be:

  1. Move the start of the BASIC program up to start of RAM + 197 bytes for the reserve program + 540 bytes for Fast Load.
  2. Use CLOAD to load the FAST-RELO BASIC program.
  3. RUN the BASIC program, it should start trying to load the next part.
  4. Play FAST-RESERVE to load the reserve program into memory.
  5. Once this has loaded the computer will beep and prompt @: to ask where to load Fast Load to.
  6. Enter the start of RAM + 197 bytes for the reserve program, then when it starts loading again play FAST-LOAD.
  7. The reserve program and machine language program are then patched to their new addresses and you should be good to go – the save/load routines are now accessible using F1 (!) and F5 (%) when using reserve groups II or III.

As mentioned before, the most significant byte of the start of memory can be retrieved from &7863, and so the process would look like this on the computer's screen (using values from the readme included with Fast Load):

NEW PEEK &7863*256+736
CLOAD
RUN
@:PEEK &7863*256+197

Unfortunately, this didn't work; attempting to save seemed to do something sensible, but loads didn't work and after that everything seemed to completely haywire – garbled memory, weird crashes and lockups. What's wrong?

Off by one!

I earlier mentioned that the reserve program is 197 bytes and Fast Load is 540 bytes, so we need to set the start of the BASIC program to 197+540 bytes from the start of memory. 197+540 is 737, however the figure given in the readme is 736. This is one byte too short, and as a result the BASIC program ends up overwriting the last byte of the Fast Load routines. This is a RTN instruction and as a result is fairly important, as without it a subroutine will not return and instead end up executing whatever instructions the start of the BASIC program has put there instead. Not good! Fortunately, it's a very simple fix – just move the start of the BASIC program to the correct position (NEW PEEK &7863*256+737).

It's only a phase…

Once everything is loaded in again (and at the correct location) the routines should now work. I loaded the Globe application using a conventional CLOAD and then used Fast Load to save the program back to my PC. This only took around 30 seconds, roughly fourteen times faster than the native routines! Very impressive, but only useful if the programs could then be loaded back onto the computer. Unfortunately, attempting to load the program back didn't work – the recording would finish playing but the computer was still stuck in its "Busy" state. On a hunch, I tried inverting the wave in the recording software, as I know from previous experience that cassette recorders tend to invert the phase between recording and playback. Doing that did the trick, and I could load back a program in 30 seconds that previously took seven minutes. Brilliant!

Unfortunately, Fast Load does seem to be fairly bare-bones. I haven't been able to find a verification routine similar to the native CLOAD ?, which compares the data on tape to the data in memory to allow you to verify that it was saved correctly, for example. Similarly I've only been able to find routines that save/load from the BASIC program area or the variable area, there doesn't seem to be a user-accessible way to save or load an arbitrary block of data based on its start and end addresses which would be required to save or load machine language programs. It doesn't even support filenames or file types to differentiate between programs and variable data! I have dug through a dissassembly of the Fast Load routines but couldn't spot anything, and the routines that are there do seem to go one byte beyond where they should to include a terminator byte as well. It's entirely possible that I missed something, but I'm still very happy about the speed improvements so don't mind the other limitations too much.

Digging into the protocol

Of course, once I'd got this working I wanted to dig into the cassette protocol used by Fast Load. Fortunately, it's very straightforward! There are two bit states, with the phase being 0°:

  • A "0" (zero) bit is represented by one cycle of a 2500Hz tone.
  • A "1" (one) bit is represented by two cycles of a 5000Hz tone.

Each byte is sent as follows:

  1. A "0" (zero) start bit.
  2. Eight data bits, least significant bit first.
  3. A "1" (one) stop bit.

There are two "1" bits between each byte, and the transfer starts with 3 seconds of 5000Hz pilot tone. The data format within the transfer is:

  1. Two byte data size (MSB first).
  2. Data bytes (including &FF program terminator).
  3. Three byte checksum (MSB first).

The checksum is calculated by adding up all of the bytes in the data section (including the &FF terminator).

All of this puts the "4000 baud" claim somewhat in question. Each bit takes the same amount of time – two 5000Hz cycles take as much time as one 2500Hz cycle, so the bit rate would appear to be 2500 baud, not 4000. In our "Globe" example, a 5697 byte program is saved in around 30 seconds. 5697 bytes becomes 5703 when we add the two bytes for the size prefix, the one byte terminator and three byte checksum. Each transmitted byte takes up eight bits for the data bits, two bits for the start and stop and two bits between each byte, so that comes to 5703×12=68436 bits in total. Divided by our 2500 baud rate gives us 27.37 seconds, plus the three seconds for the pilot tone comes to 30.37 seconds, matching our recording – so I think it's fair to say the baud rate is 2500.

Buiding some tools to work with recordings

There is a suite of tools called Pocket Tools that allows you to work with recordings of programs in the native PC-1500 format. You can extract binary data from a wave file recorded from the PC-1500, or detokenise a text BASIC program listing from one, or convert binary data or a text BASIC program listing into a wave file that can be played into the PC-1500. I've written a pair of tools, fwav2bin and fbin2wav, that perform a similar function for Fast Tools. These programs only implement saving and loading raw binary data – if you want to convert to or from BASIC program listings you'll need to first convert to or from a binary image (e.g. using Pocket Tools or lhTools).

Overall I've been very impressed with the speed boost you can achieve with Fast Load, once I managed to get it working.

Resampling Sharp PC-1500 tape recordings

Wednesday, 16th October 2024

This is a quick post about problems I'd been having loading tape cassette recordings from my PC to a Sharp PC-1500 Pocket Computer along with a potential solution for anyone having similar issues.

Photo of Sharp PC-1500 Pocket Computer connected to the CE-150 Printer and Cassette Interface
Sharp PC-1500 Pocket Computer connected to the CE-150 Printer and Cassette Interface

The Sharp PC-1500 is a small computer from the early 1980s that can run programs primarily written in BASIC. Programs can be saved to and loaded back from cassette tape but to do this requires it to be connected to the CE-150, a cassette interface that also includes a four-colour plotter. It's the plotter that really attracted me to the computer in the first place, but being able to load programs from cassette rather than having to type them in by hand is definitely a very handy feature!

Unfortunately this CE-150 interface contains an internal battery pack of five Ni-Cd cells. A certain amount of battery leakage is a risk in any old piece of electronics hardware, but having said battery pack soldered directly in with no easy way for the user to remove it for long-term storage makes it more of a certainty than a risk. My CE-150 had not escaped, with heavy corrosion visible on the metal plate under the computer…

Photo of corroded metal plate on CE-150        Photo of corroded metal plate on CE-150

The inside of the PC-1500 was a bit better and not quite as musty-smelling, but circuit board traces had definitely been eaten away in places and some of the internal metal structure was looking a bit crusty.

Photo of corroded circuit traces inside the PC-1500        Photo of corroded metal bracket inside the PC-1500

I cleaned up the rusty metal using some appliance descaler (and recorded a video of the process, which can be found on YouTube) then got to work with a scalpel and some fine enamelled copper wire to repair the damaged traces.

Photo of repaired circuit traces inside the PC-1500        Photo of repaired traces inside the PC-1500

Thankfully when I put it all back together again I was greeted by the NEW0? :CHECK prompt, so I connected the computer to the CE-150 and confirmed I could save and load back programs using my PC in place of a cassette recorder. Hooray!

Photo of Sharp PC-1500 Pocket Computer showing the 'NEW0? :CHECK' prompt

When looking to see what other people had managed to achieve with the computer I found the Sharp PC-1500 (TRS-80 PC-2) resource page which hosts several games and applications which can be downloaded in WAV format. I downloaded a few, but when playing them back on my PC the PC-1500 would refuse to load them. It could load from my own recordings, why not these?

The odd thing is that my own recordings started with a few seconds of constant pilot tone followed by the varying pulses of the program data I was trying to load. When playing back the downloaded recordings they'd start with silence and only make sound when they got to the actual data – and that data sounded quite unlike what my own recordings sounded like. What was going on? I opened a recording in Audacity to see what it looked like, and zoomed in:

Screenshot of Audacity showing the pilot tone of the recorded file

That looks like the pilot tone, in that it alternates between high and low at a fixed frequency, though it's only one sample high then one sample low so is a square wave rather than the smooth sine wave I was expecting. The cassette format uses frequency-shift keying so I would expect to see data further on in the recording at half the frequency, or twice the period, i.e. two samples high and then two samples low. Scrolling along, I certainly find that:

Screenshot of Audacity showing the frequency-shift keyed data in the recorded file

Why does it look so spiky, though? It would only need to look like that if it had been sampled at an extremely low rate, and indeed that's what I found – these files are recorded at a 5kHz sample rate:

Screenshot of MediaInfo showing the 5kHz sample rate of the recording

5kHz is a pretty unusual (and very low!) sample rate that I suspected my sound card was not handling particularly gracefully. Typically audio would need to be resampled to 44.1kHz or 48kHz before playing back, so I thought I'd try resampling in Audacity. This produced this weirdness:

Screenshot of Audacity showing the signal being distorted when being resampled.

Instead of a sine wave that alternates between two frequencies, I ended up with something more akin to an amplitude-modulated signal! The high-frequency pilot also disappears entirely, apart from what looks to be some initial ringing:

Screenshot of Audacity showing the pilot signal disappearing when being resampled.

Now, this is a complete guess, but I suspect that what is happening is an attempt to reduce unpleasant harmonics in the resampled audio. A square wave (such as the one we're resampling) produces a lot of harmonics at higher frequencies. As the original audio was sampled at 5kHz, the highest frequency that this could represent was 2.5kHz (alternating high and low each sample, as per the pilot tone). A low-pass filter at 2.5kHz could therefore reduce the harmonics, though I'd still expect some of it to get through (rather than it being apparently being attenuated to zero in this case!) In any case, the signal is being destroyed by the resampling process, so it's no wonder the computer can't load the data.

So, what's the solution? A simpler resampling algorithm that just duplicates samples (a sort of "nearest neighbour") should give us an acceptable square wave when processing, however I could not find any way to achieve this natively in Audacity. Fortunately, though, there's a Nyquist Plugin script that can be used to repeat all samples (install via Tools→Nyquish Plugin Installer, then check it's listed in Tools→Plugin Manager – it will appear as Effect→Repeat Samples). As we want to end up around 44.1kHz the plugin can be used to repeat each sample eight times, which would take our 5kHz sample rate to 40kHz:

Screenshot of Repeat Samples plugin.

Screenshot of Audacity showing the pilot signal having each sample repeated eight times.

When played back the pilot tone can finally be heard, but it's at much too low a pitch! This is because the samples have been repeated eight times, but the track's sample rate hasn't been increased to match. This can be done by right-clicking the track and selecting Rate→Other and entering 40kHz:

Screenshot of Set Rate dialog showing new 40kHz value.

Now when the clip is played back it sounds as it should. As a further experiment, the sample rate could be changed from its somewhat odd 40kHz to a more conventional 44.1kHz via Tracks→Resample. When this is done, the nice clean square wave loses definition again:

Screenshot of Audacity showing distortion on the square wave after resampling to 44.1kHz.

As I mentioned earlier, square waves have a lot of harmonics, and the resampling process makes these harmonics extremely visible riding on top and bottom of each cycle of the square wave. There is one way to clean this up quite nicely, however – as we want to cull high-frequency harmonics, and as the original recording could only represent frequencies up to 2.5kHz we can apply a low-pass filter at 2.5kHz from the Effects menu:

Screenshot of Low-Pass Filter plugin.

Screenshot of Audacity showing the low-pass filtered signal.

This resulting file sounds much better and loads into the computer as it should. It's a bit of a convoluted process, but it does at least bypass the resampling weirdness that distorts the original files in such a way that they can't be loaded. The only additional step I might recommend is to more neatly balance the signal around zero by normalising it as the first step:

Screenshot of Normalize plugin.

In summary, the steps are as follows:

  1. Normalise the file (Effect→Normalize)
  2. Repeat all samples by 8× (Effect→Repeat Samples)
  3. Set track sample rate to 8× what it was (right-click, Rate→Other, multiply the value by 8)
  4. Resample to 44.1kHz (Tracks→Resample)
  5. Low-pass filter at 2.5kHz (Effect→Low-Pass Filter)

Happy tape loading!

Subscribe to an RSS feed that only contains items with the Sharp Pocket Computer tag.