[img] Devices in Filenames
 MDFS::Info.Comp.Spectrum.ProgTips.device/htm Search  

Programs such as Tasword and HiSoft Devpac allow you to save and load data to and and from tape and microdrive.
They allow you to specify what device to use in the filename by using a colon as the second character. So, 1:DATA
specifies the file DATA on microdrive 1 and T:DATA specifies a file on tape. A filename without a device specifier uses
a previously set default device.

I found this such a useful concept that I built it into many of the Spectrum programs that I wrote. In fact, it is such a
sensible concept I wish it had been implemented natively in the Interface 1, but that is another story.

The program fragments here provide the code to load, save and catalogue with microdrives 1 to 8, tape or a remote
server connected by the serial port. The code is shown on lines 9000 onwards, but it does not reference any line
numbers itself and can be renumbered to anywhere convenient.

This code will also work with any device that uses the Interface 1 microdrive syntax. Unless the same Interface 1
variables are used at 23734 and 23766 the initialisation will default to drive "T" for Tape.

You can download the routines as plain text in device.txt (2K) and Spectrum Tapefile in device.tap (2K).

Initialise file interface

The following code should be called at the beginning of the program. It sets int1 to non-zero if the Interface 1 is
present, otherwise int1 is set to zero. If the Interface 1 is present, then drive is set to the character code of the
most recently used microdrive, CODE"1" to CODE"8". Otherwise, drive is set to CODE"T" to indicate tape. If the
Interface 1 is present, then stream 9 is opened to the serial port for serial access to a remote file server.
9000 REM Initialise file interface
9010 REM Sets drive=current device, int1=Interface 1 present
9020 LET drive=CODE"0"+PEEK 23766:CLOSE#0:LET int1=(PEEK 23734<>244)
9030 IF drive<CODE"1" OR drive>CODE"8" OR int1=0 THEN LET drive=CODE"T"
9040 IF int1 THEN IF PEEK 23769=66 THEN LET drive=CODE"S"
9050 IF int1 THEN CLEAR#:OPEN#9,"b":POKE 23769,drive-17:POKE 23766,drive-CODE"0"

Saving data

Calling the save code saves data to the current device specified in drive. or to the device specified in the filename in
f$. The data is saved from start with length length. On return a$="" or holds an error message.
9100 REM Save data
9110 REM drive=default drive, f$=filename, start+length=>data, int1=Interface 1 present
9120 LET a$="":IF f$="" THEN RETURN
9130 IF f$(2)<>":" THEN LET f$=CHR$drive+":"+f$
9140 IF f$(1)>"0" AND f$(1)<"9" THEN SAVE*"m";VALf$(1);f$(3 TO)CODE start,length:RETURN
9150 IF f$(1)<>"S" THEN SAVE f$(3 TO)CODE start,length:RETURN
9160 IF int1=0 THEN RETURN
9170 INPUT#9;"S";(f$(3 TO))'a$;#2;(a$(2 TO))':IFa$<>""THEN RETURN
9180 SAVE*"b"CODE start,length:RETURN

Loading data

Calling this code loads data from the current device specified in drive. or from the device specified in the filename in
f$. If start is negative, then the data is loaded to its own load address. If start is positive then the code is loaded
to the address it specifies. On return a$="" or holds an error message.
9200 REM Load data
9210 REM drive=default drive, f$=filename, start=-1 or load address, int1=Interface 1 present
9220 LET a$="":IF f$="" THEN RETURN
9230 IF f$(2)<>":" THEN LET f$=CHR$drive+":"+f$
9240 IF start<0 THEN IF f$(1)>"0" AND f$(1)<"9" THEN LOAD*"m";VALf$(1);f$(3 TO)CODE:RETURN
9250 IF f$(1)>"0" AND f$(1)<"9" THEN LOAD*"m";VALf$(1);f$(3 TO)CODE start:RETURN
9260 IF start<0 THEN IF f$(1)<>"S" THEN LOAD f$(3 TO)CODE:RETURN
9270 IF f$(1)<>"S" THEN LOAD f$(3 TO)CODE start:RETURN
9280 IF int1=0 THEN RETURN
9285 INPUT#9;"L";(f$(3 TO))'a$;#2;(a$(2 TO))':IFa$<>"" THEN RETURN
9290 IF start<0 THEN LOAD*"b"CODE:RETURN
9295 LOAD*"b"CODE start:RETURN


The code will catalogue the current device specified in drive. There is no native tape catalogue function on the
Spectrum, so if Tape is specified, the routine just exits. If your program provides a tape catalogue routine you would
call it here. On return a$="" or holds an error message.
9300 REM Catalogue
9310 REM drive=default drive
9320 LET a$="":IF CHR$drive>"0" AND CHR$drive<"9" THEN CAT VALCHR$drive:RETURN
9330 IF CHR$drive="S" THEN IF int1=0 THEN RETURN
9340 IF CHR$drive="S" THEN INPUT#9;"C"'a$;#2;(a$(2 TO)):RETURN
9350 REM If a tape catalogue routine available, call it here
9360 REM Eg: LET bc=USR cat

Set Drive

Call the following routine with f$ set to the device to default to in future if a filename does not specify a drive
9400 REM Set default drive
9410 REM f$=drive character
9420 IF f$<>"" THEN LET drive=CODE f$(1)

Other notes

In typing up these documents I noticed that my code checked location 23754 after waking up the Interface 1 with the
following code:
LET int1=0:CLOSE#0:IF PEEK 23754<>128 THEN LET int1=1
On an unexpanded Spectrum this would contain 128 as the end-marker of the CHANS area. With the Interface 1
variables present, this location does not contain the end marker, and in most cases it is not 128. With the Interface 1
woken up location 23754 contains the low byte of CHADD_. If the address of the colon after the CLOSE#0 happens to
happens to be at location &xx80 in memory, then location 23754 will contain 128 when the CLOSE#0 is executed.

A better solution is to check location 23734. This will either be the first byte of the CHANS area and contain &F4 or
will be FLAGS3 and will never contain &F4 while BASIC is operating correctly.

Best viewed with Any Browser Valid HTML 4.0! Authored by J.G.Harston
Last update: 17-Oct-2004