This page contains software for the ZX Spectrum Next, written and shared by former Crash magazine Tech Tipster Simon N Goodwin. Latest update: 9th June 2021, CopyDir, ListVars and NoGo% utilities added. All the programs have been updated and tested on NextZXOS 2.06A and Core 3.01.03 which are in the current official release.
The files on this page may be freely distributed on a non-commercial basis so long as the author's name Simon N Goodwin is acknowledged and a link to this page is included with any redistribution.
Please contact Simon if you have any comments or changes to these programs which you would like to share.
CopyDir.bas includes an eponymous PROC which takes two parameters - the name of a subdirectory to be created, and a value 0 or 1 to control the interactivity of the process. Pass 0 for everything to happen automatically, 1 if you want to be asked whether or not to copy each file in turn. Answer N to skip that file, Y to copy it, Q to quit immediately (leaving you in the new directory - CD .. to revert) or A to copy all the remaining files without further questions. Source files are taken from the current directory and written to the specified subdirectory therein.
Files of 10 million bytes or more will not be copied. This is because the ls dot command doesn't yet implement the documented switches to change the formatting of file-sizes, and files that large eat into the columns allocated for filename characters.
Very long filenames may cause problems. CopyDir has been tested with filenames of up to 170 characters, but the .ls command supports a maximum line length of 255 and reserves 20 of those for the file-size or type, so names more than 230 characters long are best avoided.
The directory listing is stored on the ram disk, drive M:, to avoid corrupting the directories being copied or deleted. There needs to be enough space there for the full directory listing as formatted by .ls.
Files with the extension .bak are NOT copied. These are backups of old versions of existing files automatically created by NextBASIC SAVE commands. This is deliberate, but if you want the backups copied too you can arrange that by removing the letter B from line 3150.
The interesting part of doing this from NextBASIC was minimising the use of main memory - the 40K or so specifically reserved for ZX BASIC - and coping with rare cases like very long names, a directory holding thousands of files, or very large files.
I started out assuming I'd read the file names with CAT (AKA DIR) to a file, read the file into a large string array (storing line lengths to ease trimming later), sort them with the BASIC Shell sort from my MGT disc reader, then COPY in that order to a fresh directory.
I had ruled out trying to do this in place by renaming - too much risk of things going wrong midway leaving a mess, and no shortage of space for 8-bit files on any modern SD card - preferring to leave the source files alone in case of accident.
Problems with long names and scrolling soon persuaded me to avoid CAT, so I looked in /dot for alternatives and found .ls 1.0 which, although currently incomplete and poorly documented, turned out to be more suitable, after I scanned through several of its C source files to work out which of the documented options was actually implemented, why it was truncating file names, how to detect directories and ignore format padding and file sizes (which can be turned on but not off!).
The good news is that .ls can sort directories of any size, internally using a sliding window of system memory pages. This removed any need for my BASIC sort, and the major memory, performance and capacity limitations of having to store names in fixed-sized array elements.
To avoid the need to write a temporary file on SD or in main memory I direct the output of .ls to a file on M: the RAMdisc. This takes no BASIC RAM but limits the total directory listing to around 60K (assuming M: is not already heavily used).
The remaining work is in a REPEAT loop which reads that file line by line, checking the file pointer against the file length to anticipate the end of the file, dropping short lines (file names start in column 20, shorter lines are unavoidable padding that must be skipped over), extracting the file name (up to the maximum line width set by -w 255) and copying if required.
Variable %Q controls interactivity. If zero all files (other than .BAK) are copied. 1 copies an individual file, 2 skips it. 3 copies all later files but not necessarily prior ones as it's set by the A option being used during the selection dialogue. Notice the use of PAUSE in PROC QUERY to clear the key buffer and reduce the risk of inadvertent multiple entries. The same PROC is used by EraseDir to make selective deletions.
In the course of writing CopyDir I created a lot of unwanted directories. EraseDir is a counterpart PROC, in the same source file, which deletes files, including backups, from a specified directory. If used in automatic mode with PROC EraseDir("subdir",0) it will then delete the directory itself; otherwise the interactive version PROC EraseDir("subdir",1) will ask about each file and leave you in the chosen directory once it's finished.
EraseDir.bas is a BASIC file containing just this PROC. Wildcard deletion is faster if you just want to blow away all the contents of an existing directory. Either way, be careful!
Source for CopyDir in text form is here.
One of the mixed blessings of NextBASIC updates was the change which makes the LINE (renumber) command insert % (per cent) signs in front of line numbers. This breaks backward compatibility with ZX BASIC and makes the listing harder to read.
It also marginally slows down references to most line numbers (but not single-digit ones). If your program is only intended to run on Next the % prefixes will make it five bytes shorter per line number thus decorated, which is a win for programmers pushing at the limits of 41K for variables and the main, editable, BASIC program. But not otherwise.
As I've been writing micro software since the 1970s and wish my work to be shared widely, compatibility is important to me and the %-prepending 'feature' is one I'd generally turn off, given the chance. But there's no option to disable it, other than reverting to an earlier NextBASIC, so I've written a ten-line program to strip out redundant % markers after GO TO, GO SUB, RESTORE and RUN tokens in the current program.
This builds upon a Z80 routine I published 36 years ago which is generally useful for automatic search-and-replace operations inside any conventionally-loaded BASIC program. The accompanying screenshots show a tiny new program which can be MERGEd with any you'd like to have purged of pesky per cents littering their line-number references. The only difference between the images is that the first was plastered with %s by the LINE 9900,5 command, while the second has been tidied by running itself. :-)
Lines from 9960 onwards are only needed for testing, in the absence of a main program earlier. They include GO, RUN and RESTORE statements which LINE can decorate and the earlier code is able to tidy up.
How it works
Lines 9910 to 9955 do the magic. The 225 byte CODE routine is loaded to the top of main memory, just below the Z80n's 64K limit. But the search-and-replace routine MultiSearch is relocatable and can be run from any address protected by CLEAR (or LAYER 0 screen RAM, if you must!) in main RAM. The original version could also run from the ZX BASIC printer buffer, but that's not an option on Next or other later Spectrums which re-assign that RAM to system bank paging routines.
The code works by scanning the entire editable BASIC source (ignoring any inaccessibly copied to BANKs) looking for the sequence of characters in the variable s$ and replacing them with the contents of the variable r$. So the four program lines after loading the CODE search for a per cent sign prefixed by the keywords which may precede a line number: RESTORE, GO TO, GO SUB and RUN - using their CHR$ codes from the 'Spectrum Character Set' chapter of the manual as (unlike 48K BASIC) they can't easily be entered into programs after THEN because of regressions in the 128 BASIC editor.
Once the variables are set, a USR call to the start of the loaded code performs the replacement. That in itself is not enough to fix the issue, because although the text of the number is now in standard form, the embedded binary form (which is what makes it faster in general) is not present. NextBASIC 2.06's LINE command strips that binary out while injecting the per cent signs.
To restore the hidden binary values we need to persuade NextBASIC to retokenise our program. The last four lines do this, by first saving the edited program to TEMP.BAS, then converting that to text (with nothing hidden) with the .bas2txt command, then converting that TEMP.TXT back to tokenised BASIC with .txt2bas, and finally loading the corrected TEMP.BAS. Your program is purged of per cent signs before line numbers, and the conversion routine can be removed with ERASE 9900,9999
Since the merged routine and 225 bytes of code temporarily increase the BASIC memory footprint, and retokenisation adds five bytes for each line number cited, if your program is pushing 40K already it might not fit. MultiSearch does as much as it can in the memory available and stops with 'out of memory' after that.
It may take a few seconds to adapt hundreds of line numbers as the rest of the program must be moved down as each number is edited. You can also stop it while it's working by pressing BREAK.
Incidentally there's no attempt to add or remove % signs in LINE commands, since NextBASIC prohibits use of that command in programs.
P.S. For proper backward compatibility I'd have included the standard LET statements before the "t= USR" lines. Whoops. But MultiSearch can fix those too. Try this:
s$= ":t=": r$=":"+ CHR$ 241+"t=":t= USR 65311
MultiSearch was first published in issue 12 of Your Spectrum magazine on pages 47 to 53 of the March 1985 edition. With my permission, the original article is republished in web form here.
My article includes diagrams and information about the internal storage of numbers which might be interesting to anyone wondering what's special about ZX BASIC's handling of numeric constants, as well as asm source for MultiSearch (since lost as I developed it on ZX Microdrive) and explanation of how the program works and can be used to replace any pattern of text, tokens or numbers in a ZX or NextBASIC program currently in main RAM. A Plus3DOS/NextZXOS-compatible CODE file containing MultiSearch can be downloaded from here. The program shown below, which depends upon that code, can be downloaded from here.
Streams.bas is a small NextBASIC program which can list the ZX input output channels - like K, F, P and S, find a free stream number for you, list open streams and their channel bindings. It also detects and illustrates the new M and V channels in use. The program listing is here.
PROC CHANS lists currently active channels while PROC STREAMS will list open streams and the channels they connect to. PROC TEST uses the V channel to capture output of dot commands in a string. The timers example uses an earlier version of this procedure to read the Real Time Clock. PROC FREESTREAM returns the lowest openable stream number 4..15 or 999 if all streams are already open. PROC PEEKSTRING and PROC POKESTRING use that to work with whatever streams are free, rather than limit the choices to programs that call them.
The fly in the ointment is that unlike original Spectrums which ran a steady PAL TV rate of 50 frames per second (Hertz) Next has to accommodate the weird collection of TVs and monitors we've semi-globalised since, including HDMI (which has to run a bit slow), authentic and US/Japan-friendly 60 Hertz frames, and seven flavo(u)rs of IBM-legacy VGA timings (cheap and sometimes works). The PROCs in Timers.bas include one which tells you how many real frames your '50' or '60' Hertz setting is really kicking out, which could make a crucial difference when timing and designing games and tools for all the thousands of Nexts in the wild. Next '50 Hertz' settings may vary in practice from 48 to 59 Hertz - clock speeds and frame rates vary accordingly. A nominal '100 second' pause might take 103s for 60 Hertz VGA 0 (PAUSE 6000) or 86 seconds in VGA 6 on the same system.
Similar but different things happen in 50 Hertz mode, or on HDMI. You'll need the real-time clock add-on to compare real times with frame counts. The real time clock counts whole seconds and rounds down; for consistency it waits for a tick before starting to time an interval. Here is the program listing, with explanatory comments before the key PROCs.The general principle is that you call a PROC like dT (for times up to 255 frames, about four seconds) or dF (for times up to 20 minutes or so, requiring NextZXOS 2.06 and later) a minimum of THREE times. Once to get things started, then AGAIN immediately before the operation you want to perform. As soon as that's done, call the third time. The interval between the second and third calls will be in t%. Integer variables %a, %b and %t will be used during the timing - alternate versions that use %c, %d and %f are included. If the code you are testing alters %a, or %c, these frame timers won't work.
PROC SECONDS requires real-time clock hardware. It returns the number of seconds the RTC has counted today. PROC REALTIME(30) times a '30 second' PAUSE and shows how many real seconds passed in that time. Run it on your Next, you may be surprised at the result!
Nextramon.bas This development and investigation tool reads ROM or RAM and decodes the Z80 assembly-language (mnemonic machine code), ZX 40-bit floating point language - used for much of the BASIC runtime code, and by native ZX compilers - ASCII text or numbers wherever you point it, to your screen or printer. It's a NextBASIC disassembler and memory monitor, capable of disassembling all the Z80N instructions including Nextras, Floating point calculator (FPC) language codes, System Variable mnemonics, Sinclair report and Interface 1, GDOS and Unidos hook codes. Memory areas can also be displayed as text, hex or decimal.
This is a major update and bugfix for Spectramon, a Z80 disassembler written in ZX BASIC first published in ZX Computing magazine. Simon wrote the original on his new Spectrum in 1982, in a three-day marathon attempt to get to grips with keyword entry and adapt from TRS-80 BASIC.
The original program was documented and listed in the April/May and June/July 1983 issues of ZX Computing, and later sold on cassette by Argus Press Software. Just for good measure, Argus also ran it, over nine pages, in the Spring 1984 issue of Personal Software magazine. An extended version for Sam Coupe appeared in issue 33 of the Sam Supplement disc magazine.
The 2020 Next versions are faster, though still not as fast as a disassembler written well in machine code - they select 28 MHz with RUN AT 3 from the start, to take full advantage of the Next. You still get nonsensical results, as from any dissembler, if you disassemble memory that does not contain valid Z80 code. You can avoid this by checking with the A or N options. if you're looking for ROM patches, these are a quicker way to find them than by paging through the disassembly.
In August 2020 the documentation for Nextramon was extended and converted to NextGuide format, a simple form of hypertext which can be viewed using Matt Davies's .GUIDE command. You can download Nextramon.gde here. It includes tips on disassembling to a file, references and extra information about the floating-point calculator instruction-set, reports and hook codes.
As the guide notes, all the extra features for decyphering ROM code mean that the full program is too large to allow easy disassembly of machine-code paged into the top 16K accessible to BASIC, so a cut-down version NextDis.bas has been added.
This is only 13K long (plus data for just the Z80n instructions) so it all fits below address 49152 in memory. Use CLEAR 49151 before loading, to protect that area, to protect room for 16K of code above it in memory. Any Next RAM page can be read from that address range, this allows any code in the system to be examined by setting NEXTREG 56 and 57 to remap the appropriate 8K page, numbered from 0 to 223, at address $C000 or $E000 respectively. To make it easier to keep track of RAM in use, NextDis.bas displays the RAMTOP address (above which code can safely be loaded or paged) and the amount of memory free to BASIC, in its main menu.
The April 2020 update of the full-fat Nextramon.bas knows about 182 of the System Variables stored in memory between 23552 TO 23733, and can use their symbolic names instead of numbers in the disassembly, making it easier to see what compiled code and ROM routines are doing. Since you may be dissassembling code that overwrites the system data or pages other memory to those addresses, there is a new option in the main menu - type S to toggle whether or not System variable names are shown in disassembly when corresponding addresses or offsets are encountered.
The above example, taken from the NextZXOS 2.06A implementation of the CLEAR and GO SUB commands, shows how System Variables are identified by Nextramon in two contexts. The Spectrum ROM expects the IY register to point into the system variables specifically at address 23610 (5C3AH) which holds ERRNR, the error report code. The interrupt routine which polls the keyboard at the start of each display field relies on this, which is why the Next manual says machine code called from BASIC must preserve that value. The same assumption is made in many places in the Spectrum ROM and system-friendly programs, so Nextramon checks the offsets associated with code references to the IY register and substitutes symbolic names if the offsets fall within the range of System Variables documented in all the Spectrum Manuals. Extras added in the ZX printer buffer (before the original system variables) are not detected as they fall out of the 8-bit signed range accessible via IY.
Often the ROM uses IY to access system variables, but sometimes it uses the 16-bit addresses directly, e.g. when transferring values between memory and the HL register. To cater for this, Nextramon checks 16-bit addresses used in load and store instructions and if they fall into the relevant range it substitutes names for numbers then as well, as long as the 'S' option is enabled in the menu. Since the symbolic values are offsets relative to address 23610, for compatibility with indexing IY, an extra symbolic constant SV is added to the absolute value; to reassemble code that uses either indexed or direct addressing, equate SV to 23610 in your assembler.
The mnemonic names are essentially those used in Spectrum manuals and Ian Logan's ROM disassembly, except that underscores are omitted as not all assemblers support them, and the names are unambiguous without them; when names differ between ZXOS versions the Next label is generally used. Next's STIMEOUT is an outlier (Sinclair labels were 6 bytes max) but it just fits the array. Many of the system variables occupy multiple bytes, so in the case of word pointers the label is used for the first (low order) byte and the same label with +1 appended appears if the second byte is accessed. KSTATE is similar so what Logan calls KSTATE4 is KSTATE+4 in the disassembly.
The large tables of stream pointers and calculator workspace are specially handled to show the internal structure; for instance the 30 byte workspace is shown as six five-byte memory areas, labelled MEM0 to MEM5 matching references to those in the Floating point calculator language. Those labels point to the exponent of each value. Suffixes +1 to +4 refer to the mantissa bytes, e.g. MEM5+4 is the label of the last byte of workspace. Streams use 38 bytes, with a pair for each channel number from #-3 to #15. For the negative streams I had to use M instead of the minus sign '-' as that's an operator in most assemblers. Streams accessible to BASIC use corresponding positive numbers in the internal labels, e.g. STRM15+1 for the last byte of the last stream pointer.
Nextramon automatically switches between Z80N and FPC machine language byte encodings after a RST 56 instruction which switches the Spectrum to expect FPC code, but sometimes FPC code is entered directly via a branch from other FPC code. The F option allows you to start disassembly in FPC mode, whereas D defaults to Z80N instructions. FPC code is extensively used in the original Spectrum ROM and generated code from ZX compilers that support Sinclair's five-byte floating point format. The rendering of embedded floating-point constants in FPC code has been improved, and the speed of hex-dumps increased, in the March 2020 update. Input addresses may now be prefixed with $ as an alternative to suffixing them with H, when it's not obvious from the characters that a value is hexadecimal rather than decimal.
Nextramon uses Timex 2068 hires mode for a 64-column display. It supports the Alphacom and ZX printers, and possibly other printers which are compatible with NextBASIC.
This program was written from scratch for Next, though informed by structured programs written for QL SuperBASIC, so it exercises many of the features that set NextBASIC apart from ZX BASIC. There are quite a few potentially useful and re-usable PROCs in the program, so here's the NextBASIC source text of the MGT reader in case you'd like to see inside and pick up ideas, even if you've not got any MGT disc images to recover.
The April 2020 update of this utility uses recent NextBASIC extensions like PEEK$ to speed things up, and restores a couple of nested REPEAT loops which upset NextZXOS releases before March.
The program starts by using the Next file browser to help you locate a disc image for unpacking. The filename should have the extension .DSK unless you modify line 1116 to let through a different file extension, such as .MGT if you have your disc images named that way. MGT disk images are usually 800K long and always a multiple of 200K, but .browse doesn't yet allow filtering of files by size though in this case it's a much stronger hint of the contents than however the filename ends. There are some test command to load disc images directly by name in the previous commented-out lines starting REM PROC ReadDir.
The first couple of PROCs are at the start of the program for speed, though they don't use any GO TO or GO SUB instructions. PROC CopyBlock calls the subsequent GetSector repeatedly to read each sector of a file inside the disc image into the 510 byte string G$ so it can be displayed (if it's ASCII - expect errors if it contains undisplayable characters interpreted by PRINT as ZX BASIC control codes) and copied to the output file (in any case). MGT sectors are the standard 512 bytes long but the last two characters are not file data but the track and sector number of the following chunk of file data. Track numbers over 127 refer to the second side of the disc (for disks of over 400K) and a pair of zeros mark the end of the file.
PROC UPPER$ has standard ZX BASIC internals and just converts its argument into a string with UPPER-CASE characters in place of any lower-case ones. To make a similar PROC LOWER$, substitute "Z" and "A" for "z" and "a" in line 2665 and add rather than subtract 32 in the next line.
Sinclair allocated space for details of a file, such as array names, code addresses or BASIC variable offsets, in the tape header or microdrive directory but Amstrad's CP/M adaptation for the Plus Three scrapped that approach and introduced a 128 byte data block at the start of every ZX-standard file - DATA arrays, programs and variables, CODE or SCREEN$. It's mostly empty but 128 is the original CP/M sector size so the minimum amount of data it's easy to skip over in a CP/M file. But this does mean there's an extra byte, at offset 13 in the data prefix, for files more than 64K long, which Sinclair didn't consider. The PROC SetHead creates an Amstrad-style header, also required for ZX files in ZXNextOS, and copies in the filetype and up to three words of ZX file details, as documented in the Sinclair manuals.
This PROC will be useful to anyone else bringing ZX files onto Next and expecting to load them later with the LOAD or MERGE commands. But it is not fully structured despite the parameters and LOCALs, as it has some external dependencies like the variables plus3header and headLen which tell it where to compile the header, and the flag file which is non-zero if the header is to be written to file stream #5 after compilation. It also clobbers two of Next's fast integer variables, c% and s%, which can't yet be safely declared LOCAL. Sundry cryptic messages appear if the expected setup is not found. One neat trick in this PROC is the way it uses BANK 2 ERASE to clear 120 bytes to zero in one statement, though this will need adjustment if plus3header is not in memory bank 2, 32768 to 49151 - hence the sanity checks.
For speed, PROC ReadDir uses the dot command .extract to read entire 5K tracks of disc data into memory in one statement. It ten calls PROC UnPack to decode each 256 byte disc directory entry, most of which is a bitmap identifying the sectors allocated to the file, though not their order. UnPack extracts vital parts of the directory data into seven BASIC arrays, with one entry per file in each array. PROC SortDir then sorts the names in f$ and corresponding elements in the arrays a(), b(), s(), t(), x() and z() into ascending alphabetical order, using a Shell sort, which is much faster than a slightly simpler bubblesort but doesn't require recursion, which is still problematic in Next BASIC. This nicely structured PROC (apart from the hard-wired array names it uses) fell foul of a new BASIC bug in the day 0 update so one of the REPEAT loops has had to be commented out and replaced with an olde-worlde IF test and pair of GO TOs; various workarounds are commented out. The same bug and workaround appears in PROC SelectFile.
PROC UnPack can identify and label 20 file types, including the four standard ZX ones and extensions added by SAM, UniDOS and BetaDOS. It strips off the 'hidden' and 'protected' flags in the MGT directory and sets limit to the number of files found, to speed up the sorting. Various diagnostic and test routines appear from line 3000 in the listing. These include StrTest for the string peeks and pokes, WordTest for the 16-bit ones, MDevTest for the memory channel and - probably the most useful - PROC GetHead which opens any file and looks for a Plus3DOS header at the start and extracts and displays key information from it, if found.