I've been playing with Spectrum emulators, their file formats and some reverse engineering tools recently.

Thanks to this forum, I was able to parse the TR-DOS disk image format and find the bytes of the code files with the address fields of where they should be loaded.

But when I try to load the code into the given address ranges into Ghidra (a reverse-engineering tool), addresses don't seem to match up.

The Spectrum and its clones used the Z80, which is 8-bit with 16-bit addresses and can access 64 KB of memory. But later Spectrums and clones added paged memory circuitry and could access 128 KB or 256 KB.

Perhaps I'm missing something about which bank(s) the code files are loaded into. TR-DOS only gives one address field, so I'm assuming it's both the load address and the execution address. But perhaps one of those assumptions is wrong and instead one of those addresses is a standard that's not in the file metadata.

I've Googled but only found high-level TR-DOS manuals. I've found the TR-DOS ROM disassembly but not the relevant code in it. I'm looking for a technical description of how this works.

Maybe such documentation is around but in Russian or Czech. Perhaps we have people who hacked on those systems back in the day, or worked on emulators for them more recently.

  • 2
    I'm about 30% sure that TR-DOS occupies the lower 16k, and applications are "somewhere" above that. There's some kind of mechanism that allows TR-DOS to call the BASIC ROM.
    – OmarL
    Apr 23 '20 at 8:25

How it [likely] works

  1. When ZX-Spectrum with Beta interface (re)starts, Beta Disk Interface sets ROM_CS flag to disable its builtin ROM and connects TR-DOS ROM to initialize the interface, dirves, and to try to boot from floppy. This behavior can be different from one computer to another, for example, in my personal 48K Speccy with Beta-128 interface it happened seldomly (I likely had a kind of bogus Beta), and I used to invoke TR-DOS prompt as RANDOMIZE USR 15616 from Basic.
  2. Z80 CPU has M1 leg, which can be used to detect if CPU currentry reads a code (not a data or stack) from memory.
  3. TR-DOS entry points are located in addresses 15616–15871 (#3D00#3DFF). Native Speccy ROM stores the font here, so there is no reason to execute it as a code. Thus, when CPU tries to execute a code here, Beta Interface detects memory read from this address + M1 leg signal, which indicates the intention to invoke some TR-DOS entry point, e.g. RANDOMIZE USR 15619: REM: RUN "SAB248K" from Basic or CALL 15635 from Assembly.
  4. According to this, when address bus points to #3D00#3DFF and control bus indicates reading and M1 flags, Beta Interface switches off builtin Speccy ROM and plugs its own ROM to the buses. Now Z80 executes TR-DOS code.
  5. TR-DOS does its work. So the answer for the quiestion: in this state, TR-DOS can execute any code from TR-DOS ROM and access Basic and native code and data in the RAM.
  6. ROM switches back to Speccy's one by Beta Interface when code from 16384–65535 (#4000–#FFFF) is executed. Not sure what should be placed here to execute correctly, but likely a few bytes of code in TR-DOS system memory area, which is reserved on top of Interface 1 system memory area. This code should likely perform some cpleanup and then return using CPU stack (see USR or CALL above).


  1. Z80 CPU User Manual. Zilog, Inc, 2016.
  2. А. Ларченко, Н. Родионов. ZX-Spectrum и TR-DOS для пользователей и программистов, [in Russian, A. Larchenko, N. Rodionov. ZX-Spectrum & TR-DOS for users and coders]. Питер, 1994.

Some exotics =)

Here are two relevant pages from [2].

Parts of the text related to items (4) and (6) of above list are highlighted with cyan and green respectively. My above text is not a translation of those scans, just a brief retelling with some comments.

How ROMs are switched

And this is an overview figure of Russian Beta Disk Interface clone using some Soviet chips.

Beta Disk Interface

  • 1
    Also note the difference: Apple delivered M1 in 2020 while Zilog uses its M1 since 1976 %)))
    – dluciv
    Jan 16 at 18:10
  • This only explains how the hardware works -- not what the disk format is and not how the loading process is performed.
    – lvd
    Jan 16 at 21:12

This is only a partial answer so far.

TR-DOS was first part of the Beta 128 disk interface, which was cloned along with the Spectrum itself in Russia and Eastern Europe.

According to this Beta 128 manual I found online, by default code does both load and start at the same address. Loading such code files manually is known as "auto-run". But you can also load code as part of a BASIC program and you can use this method to either load code at its default address, or a specified address, followed by another instruction to begin execution at another specific address:

For the machine code programs, they can be loaded to the same address from which they were saved, eg:


or loaded to a different address by specifying in the loading instructions, eg:

 LOAD "margin"CODE 51000

When running a machine code program the auto-run address should be the same as the starting address of the program, eg:

 RUN "Discound"CODE 47800

provided the code is meant to auto-run from 47800 on loading all will be well.

The alternative is to use a two line loader, eg, SAVE the following program on disk as say "DCTLOAD"LINE 10

 10 RANDOMIZE USR 15619:REM:LOAD "Dsicount"CODE 47800

To run the program you now enter RUN "DCTLOAD" which will load the machine code and the "run it" fom 47838.

A small number of the disk image files I downloaded seem to have code files with offsets and lengths that would not fit in a single RAM page, or would overlap the end of a RAM page. I have no idea what happens in such cases. Some of the files do seem to include various tricks, copy protection, and/or obfuscation though, which makes it harder to learn by exploring.


The usual way of such HW peripherials was switching the original ZX ROM (0-16384) chip for its own memory space where was usually another ROM or even RAM on the runtime. It was done by the /ROMCS pin on the extention bus/connector.

I am not famillair with TR-DOS as I was heavily using MDOS instead. The MDOS was an ZX OS extention for Didaktik D40/D80 Floppy disc drives. It has a nice interaction with original ZX BASIC making able to use BASIC programs to use the flopies. For example:

LOAD * "some_file.P"

loads a file from floppy. It worked in a way that when you run this program original BASIC throw an exception like Syntax error or Non sense ion BASIC and D40/D80 hardware detects that by scanning Z80 pins. Then it switch to its own ROM and instead of throwing exception it executes the extended BASIC commands. After that original ROM switches back and BASIC program execution is restored.

If you want more details just study the D40/D80 or any other similar HW circuitry and its commented disassembly.

The usual detection was done by simple logic circuit detecting hardcoded address bus state along with M1,MREQ,RD signals... IIRC D40/D80 was sensitive to RST 0 and RST 8 instructions.

Now the bank switching for stuff above original 48K was done by the programs itself. Each bank switching able computer (like ZX128) had a special I/O mapped register that controls the actual configuration of memory map. That is why in file systems is usually only 16 bit target address.

Now I expect that TR-DOS was working in similar manner

  • 2
    This answer seems to be about MDOS, not TR-DOS
    – OmarL
    Apr 23 '20 at 12:58
  • @Wilson yes and its also stated ... but the mechanisms are most likely the same or at least similar
    – Spektre
    Apr 23 '20 at 13:00
  • 1
    Some of it will be similar. Probably the hardware will be similar; I don't know. One big difference is that TR-DOS does not trap BASIC errors but instead has its own prompt; (but still uses the BASIC keywords like RUN and LOAD for some reason).
    – OmarL
    Apr 23 '20 at 13:06

TR-DOS programs are usually started as BASIC ones, by loading *.B file a number of bytes given in the file descriptor. No sector size is honored this time and loading address is essentially ignored (but set to be equal to byte size most of times). After the basic program is run, the things might go mad in any possible way.

The most simple programs would do RANDOMIZE USR 15619 : REM : LOAD "filename" CODE, either giving specific load address or counting on the load address stored in the file descriptor. This way *.C files are loaded and usually their sector sizes correspond to their byte sizes (both from file descriptor).

However, common practice has become the following:

  1. Once loaded, basic program runs machine code stored in REM operator (instead of human-readable comments).
  2. The machine code then takes current sector and track numbers from somewhere of TR-DOS variables and uses CALL #3D13 with specific contents of registers to execute raw sectors read procedures to arbitrary addresses.

This way, after the basic program loads the number of bytes specified in its file descriptor (and not the number of sectors specified there!), loads continues immediately from the following sectors to any addresses and in any number of chunks. However, to keep consistency and to be able to copy such programs as files, the sector size of such program (that looks like single *.B file) is bigger and accomodates for all the raw sector loads it executes.

For very big programs it is common to have several 255-sectored files with an implication that those files are always in the same order and placed without holes between them on a disk.

Regarding the documentation for TR-DOS, I would recommend these ones:

The second one is essentially a thoroughfully commented disassembler of the TR-DOS rom, much like the famous complete Spectrum ROM disassembly book and could be treated as an ultimate guide to the TR-DOS.

Of course, only in Russian, as TR-DOS haven't got any spread elsewhere.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.