Spectrum ROM Load Routine

Please note this content is from the original WoS site, and may no longer be relevant. If you have any queries, please contact us.

The entire disassembly was taken from ‘The Complete Spectrum ROM Disassembly’ by Dr. Ian Logan and Dr. Frank O’Hara, as published by Melbourne House in 1983

This is the tape loading routine as in the Spectrum ROM. Note that all addresses and operands are in hexadecimal.
All timings are given in T states. 1 T state is 1 clock cycle = 1/3,500,000 seconds (on a 16K or 48K machine).

The routine starts at address 0556 and requires the following register settings:

IX Start of the block,
DE Length of the block,
A Flag byte (normally +00 for the header and +FF for a data block),
carry flag set for LOAD, reset for VERIFY.

It returns with the carry flag reset if an error occured. Possible errors are:

  • Flag byte incorrect
  • BREAK key pressed (D BREAK – CONT repeats)
  • Time-out (R Tape Loading Error)
  • Parity byte incorrect (R Tape Loading Error)

The following tape timing values are used by the ROM:

  • Each leader halfpulse takes 2,168 T
  • The first sync halfpulse takes 667 T
  • The second sync halfpulse takes 735 T
  • A ‘0’ bit takes two 855 T halfpulses
  • A ‘1’ bit takes two 1,710 T halfpulses (twice the length of a ‘0’ bit)

Notice that the data bits are built from 2 equal ‘edges’ (full pulses), the leader and syncs are made up from single ‘edges’ (halfpulses).


This subroutine is common to both SAVEing and LOADing.
The border is set to its original colour and the BREAK key tested for a last

053F SA/LD-RET  PUSH AF                 Save the carry flag (It is reset after
                                        a LOADing error)
                LD   A,(BORDCR)         Fetch the original border colour from
                AND  +38                its system variable
                RRCA                    Move the border colour to bits 2, 1 & 0
                OUT  (+FE),A            Set the border to its original colour
                LD   A,+7F              Read the BREAK key for a last time
                IN   A,(+FE)
                EI                      Enable the maskable interrupt
                JR   C,0554,SA/LD-END   Jump unless a break is to be made

Report D - BREAK-CONT repeats

0552 REPORT-D   RST  0008,ERROR-1       Call the error handling routine
                DEFB +0C

Continue here.

0554 SA/LD-END  POP  AF                 Retrieve the carry flag
                RET                     Return to the calling routine

This subroutine is called to LOAD the header information (from 076E) and later
LOAD, or VERIFY, an actual block of data (from 0802).

0556 LD-BYTES   INC  D                  This resets the zero flag (D cannot
                                        hold +FF)
                EX   AF,AF'             The A register holds +00 for a header
                                        and +FF for a block of data
                                        The carry flag is reset for VERIFYing
                                        and set for LOADing
                DEC  D                  Restore D to its original value
                DI                      The maskable interrupt is now disabled
                LD   A,+0F              The border is made WHITE
                OUT  (+FE),A
                LD   HL,+053F           Pre-load the machine stack with the
                PUSH HL                 address - SA/LD-RET
                IN   A,(+FE)            Make an initial read of port 254
                RRA                     Rotate the byte obtained
                AND  +20                but keep only the EAR bit
                OR   +02                Signal RED border
                LD   C,A                Store the value in the C register
                                        (+22 for 'off' and +02 for 'on' - the
                                        present EAR state)
                CP   A                  Set the zero flag

The first stage of reading a tape involves showing that a pulsing signal
actually exists. (i.e. 'On/off' or 'off/on' edges.)

056B LD-BREAK   RET  NZ                 Return if the BREAK key is being pressed
056C LD-START   CALL 05E7,LD-EDGE-1     Return with the carry flag reset if
                JR   NC,056B,LD-BREAK   there is no 'edge' within approx.
                                        14,000 T states. But if an 'edge' is
                                        found the border will go CYAN

The next stage involves waiting a while and then showing that the signal is
still pulsing.

                LD   HL,+0415           The length of this waiting period will
0574 LD-WAIT    DJNZ 0574,LD-WAIT       be almost one second in duration.
                DEC  HL
                LD   A,H
                OR   L
                JR   NZ,0574,LD-WAIT
                CALL 05E3,LD-EDGE-2     Continue only if two edges are found
                JR   NC,056B,LD-BREAK   within the allowed time period.

Now accept only a 'leader signal'.

0580 LD-LEADER  LD   B,+9C              The timing constant
                CALL 05E3,LD-EDGE-2     Continue only if two edges are found
                JR   NC,056B,LD-BREAK   within the allowed time period
                LD   A,+C6              However the edges must have been found
                CP   B                  within about 3,000 T states of each
                JR   NC,056C,LD-START   other
                INC  H                  Count the pair of edges in the H
                JR   NZ,0580,LD-LEADER  register until 256 pairs have been found

After the leader come the 'off' and 'on' parts of the sync pulse.

058F LD-SYNC    LD   B,+C9              The timing constant
                CALL 05E7,LD-EDGE-1     Every edge is considered until two edges
                JR   NC,056B,LD-BREAK   are found close together - these will be
                LD   A,B                the start and finishing edges of the
                CP   +D4                'off' sync pulse
                JR   NC,058F,LD-SYNC
                CALL 05E7,LD-EDGE-1     The finishing edge of the 'on' pulse
                RET  NC                 must exist
                                        (Return carry flag reset)

The bytes of the header or the program/data block can now be LOADed or VERIFied.
But the first byte is the flag byte.

                LD   A,C                The border colours from now on will be
                XOR  +03                BLUE & YELLOW
                LD   C,A
                LD   H,+00              Initialize the 'parity matching' byte
                                        to zero
                LD   B,+B0              Set the timing constant for the flag
                JR   05C8,LD-MARKER     Jump forward into the byte LOADing loop

The byte LOADing loop is used to fetch the bytes one at a time. The flag byte is
first. This is followed by the data bytes and the last byte is the 'parity'

05A9 LD-LOOP    EX   AF,AF'             Fetch the flags
                JR   NZ,05B3,LD-FLAG    Jump forward only when handling the
                                        first byte
                JR   NC,05BD,LD-VERIFY  Jump forward is VERIFYing a tape
                LD   (IX+00),L          Make the actual LOAD when required
                JR   05C2,LD-NEXT       Jump forward to LOAD the next byte
05B3 LD-FLAG    RL   C                  Keep the carry flag in a safe place
                XOR  L                  Return now if the flag byte does not
                RET  NZ                 match the first byte on the tape
                                        (Carry flag reset)
                LD   A,C                Restore the carry flag now
                LD   C,A
                INC  DE                 Increase the counter to compensate for
                JR   05C4,LD-DEC        its decrease after the jump

If a data block is being verified then the freshly loaded byte is tested against
the original byte.

05BD LD-VERIFY  LD   A,(IX+00)          Fetch the original byte
                XOR  L                  Match it against the new byte
                RET  NZ                 Return if 'no match' (Carry flag reset)

A new byte can now be collected from the tape.

05C2 LD-NEXT    INC  IX                 Increase the 'destination'
05C4 LD-DEC     DEC  DE                 Decrease the 'counter'
                EX   AF,AF'             Save the flags
                LD   B,+B2              Set the timing constant
05C8 LD-MARKER  LD   L,+01              Clear the 'object' register apart from
                                        a 'marker' bit

The 'LD-8-BITS' loop is used to build up a byte in the L register.

05CA LD-8-BITS  CALL 05E3,LD-EDGE-2     Find the length of the 'off' and 'on'
                                        pulses of the next bit
                RET  NC                 Return if the time period is exceeded
                                        (Carry flag reset)
                LD   A,+CB              Compare the length against approx.
                CP   B                  2,400 T states; resetting the carry flag
                                        for a '0' and setting it for a '1'
                RL   L                  Include the new bit in the L register
                LD   B,+B0              Set the timing constant for the next bit
                JP   NC,05CA,LD-8-BITS  Jump back whilst there are still bits to
                                        be fetched

The 'parity matching' byte has to be updated with each new byte.

                LD   A,H                Fetch the 'parity matching' byte and
                XOR  L                  include the new byte
                LD   H,A                Save it once again

Passes round the loop are made until the 'counter' reaches zero. At that point
the 'parity matching' byte should be holding zero.

                LD   A,D                Make a furter pass if the DE register
                OR   E                  pair does not hold zero
                JR   NZ,05A9,LD-LOOP
                LD   A,H                Fetch the 'parity matching' byte
                CP   +01                Return with the carry flag set if the
                RET                     value is zero (Carry flag reset if in

These two subroutines form the most important part of the LOAD/VERIFY operation.
The subroutines are entered with a timing constant in the B register, and the
previous border colour and 'edge-type' in the C register.
The subroutines return with the carry flag set if the required number of 'edges'
have been found in the time allowed; and the change to the value in the B
register shows just how long it took to find the 'edge(s)'.
The carry flag will be reset if there is an error. The zero flag then signals
'BREAK pressed' by being reset, or 'time-up' by being set.
The entry point LD-EDGE-2 is used when the length of a complete pulse is
required and LD-EDGE-1 is used to find the time before the next 'edge'.

05E3 LD-EDGE-2  CALL 05E7,LD-EDGE-1     In effect call LD-EDGE-1 twice;
                RET  NC                 returning in between in there is an
05E7 LD-EDGE-1  LD   A,+16              Wait 358 T states before entering the
05E9 LD-DELAY   DEC  A                  sampling loop
                JR   NZ,05E9,LD-DELAY
                AND  A

The sampling loop is now entered. The value in the B register is incremented for
each pass; 'time-up' is given when B reaches zero.

05ED LD-SAMPLE  INC  B                  Count each pass
                RET  Z                  Return carry reset & zero set if
                LD   A,+7F              Read from port +7FFE
                IN   A,(+FE)            i.e. BREAK and EAR
                RRA                     Shift the byte
                RET  NC                 Return carry reset & zero reset if BREAK
                                        was pressed
                XOR  C                  Now test the byte against the 'last
                AND  +20                edge-type'
                JR   Z,05ED,LD-SAMPLE   Jump back unless it has changed

A new 'edge' has been found within the time period allowed for the search.
So change the border colour and set the carry flag.

                LD   A,C                Change the 'last edge-type' and border
                CPL                     colour
                LD   C,A
                AND  +07                Keep only the border colour
                OR   +08                Signal 'MIC off'
                OUT  (+FE),A            Change the border colour (RED/CYAN or
                SCF                     Signal the successful search before
                RET                     returning

Note: The LD-EDGE-1 subroutine takes 464 T states, plus an additional 59 T
states for each unsuccessful pass around the sampling loop.
For example, therefore, when awaiting the sync pulse (see LD-SYNC at 058F)
allowance is made for ten additional passes through the sampling loop.
The search is thereby for the next edge to be found within, roughly, 1,100 T
states (464 + 10 * 59 overhead).
This will prove successful for the sync 'off' pulse that comes after the long
'leader pulses'.