TAP
Example code to load a machine code program in TAP format
Table of Contents
A .TAP
file is a simple image format used to represent data saved to tape.
It's a simple image format which can be generated by some assemblers like
zasm
.
Now we have a simple BASIC loader which generates a .TAP
file.
This is effectively a simple BASIC program which when run loads the next file on the tape into memory at address
24000 0x5DC0 and then executes the machine code at that same address.
Hello World Example
For this example we will write a simple machine code program which writes Hello World to the screen.
1wget https://area51.dev/sinclair/asm/loaders/tap/loader.tap
2wget https://area51.dev/sinclair/asm/loaders/tap/helloworld.z80
3zasm helloworld.z80
4cat loader.tap helloworld.tap > tape.tap
The commands do the following:
-
Download loader.tap which is the precompiled loader.
The source is viewable here along with how to compile it yourself.
-
Download
helloworld.z80
which is the source shown below.
-
Compiles the source generating
helloworld.tap
.
-
Concatenates both tap files to generate our final
tape.tap
file.
Links to the required files are available in the Resources panel at the top right of this page including
a precompiled helloworld.tap
file.
Running the example
If no errors occurred you can run it with the fuse
emulator:
You should then see something like this screenshot.
The first line visible is from the boot loader as it loaded the file in helloworld.tap
.
The second line is the output of the source below.
helloworld.z80 source
; ***************************************************************************
; Hello world example showing how to use the TAP format and our simple
; BASIC boot loader that has been recompiled into TAP format.
;
; Author: Peter Mount, Area51.dev & Contributors
; URL: https://area51.dev/sinclair/asm/loaders/tap/
; ***************************************************************************
; for zasm we need to tell it to generate a tap file
#target tap
; code_start will be where our code will be compiled to
code_start equ 24000
; ***************************************************************************
; Header block containing the block name and the size of the machine code
; ***************************************************************************
#code CODE_HEADER,0,17,0
defb 3 ; Indicates binary data
defb "helloworld" ; the block name, 10 bytes long
defw code_end-code_start ; length of data block which follows
defw code_start ; default location for the data
defw 0 ; unused
; ***************************************************************************
; Data block containing a actual machine code program:
;
; Here we simply print the "Hello World!" message to the screen.
; ***************************************************************************
#code CODE_DATA, code_start,*,0xff
; This is the code_start address 24000 0x5DC0
ld a,2 ; set print channel to Screen:
call 0x1601
ld hl,msg ; message start address
loop: ld a,(hl) ; get next byte
and a ; check for null
ret z ; stop when we get a null
inc hl ; move to next character
rst 2 ; print the character
jr loop ; jump back to the loop
msg: dm 13, "Hello World!", 13, 0
; End of code marker needed for the CODE_HEADER
code_end:
; Anything after this point will not be included in the .tap file
1 - BASIC Loader
BASIC .TAP loader
This shows the source for the BASIC .TAP loader for use with the zasm
assembler.
To compile run the following commands in a Linux shell to download the sources and compile the loader:
1wget https://area51.dev/sinclair/spectrum/reference/include/zasm/headers.z80
2wget https://area51.dev/sinclair/asm/loaders/tap/loader.z80
3zasm loader.z80
The commands do the following:
-
Download headers.z80 which contains the
definitions for the Spectrum BASIC tokens we require.
-
Download
loader.z80
which is the source shown below.
-
Compiles our loader generating
loader.tap
.
The generated loader.tap
can now be used with your own .TAP
file as long as it loads at
address 24000 and its entry point is also at that same address.
All you need to do is concatenate both .TAP
files with loader.tap
first.
The Hello World example shows how this is done.
Links to the required files are shown in the resources panel at the top right of this page.
; ***************************************************************************
; Load a machine code program using the TAP format
;
; Author: Peter Mount, Area51.dev & Contributors
; URL: https://area51.dev/sinclair/asm/loaders/tap/
; ***************************************************************************
; fill byte is 0x00
; #code has an additional argument: the sync byte for the block.
; The assembler calculates and appends checksum byte to each segment.
;
; Note: If a segment is appended without an explicit address,
; then the sync byte and the checksum byte of the preceding segment are not
; counted when calculating the start address of this segment.
#target tap
; Include Spectrum headers from http://localhost:1313/sinclair/spectrum/reference/include/zasm/headers/
#include "headers.z80"
; ***************************************************************************
; Header block containing the block name and the size of the BASIC program
; ***************************************************************************
#code PROG_HEADER,0,17,0
defb 0 ; Indicates a Basic program
defb "mloader " ; the block name, 10 bytes long
defw variables_end-0 ; length of block = length of basic program plus variables
defw 10 ; line number for auto-start, 0x8000 if none
defw program_end-0 ; length of the basic program without variables
; ***************************************************************************
; Data block containing a simple BASIC program:
;
; 10 CLEAR 23999 ; Set end of Basic memory
; 20 LOAD "" CODE 24000 ; Load next file to the free memory
; 30 RANDOMIZE USR 24000 ; Execute the loaded code
;
; This will when run mark memory from 24000 (0x5DC0) to be unavailable to
; BASIC and everything above that point is then usable by the machine code
; program who's entry point is address 24000 (0x5DC0).
; ***************************************************************************
#code PROG_DATA,0,*,0xff
; 10 CLEAR 23999
defb 0,10 ; line number
defb end10-($+1) ; line length
defb 0 ; statement number
defb BAS_CLEAR ; token CLEAR
defm "23999",$0e0000bf5d00 ; number 23999, ascii & internal format
end10: defb $0d ; line end marker
; 20 LOAD "" CODE 24000
defb 0,20 ; line number
defb end20-($+1) ; line length
defb 0 ; statement number
defb BAS_LOAD,'"','"',BAS_CODE ; token LOAD, 2 quotes, token CODE
defm "24000",$0e0000c05d00 ; number 24000, ascii & internal format
end20: defb $0d ; line end marker
; 30 RANDOMIZE USR 24000
defb 0,30 ; line number
defb end30-($+1) ; line length
defb 0 ; statement number
defb BAS_RANDOMIZE, BAS_USR ; token RANDOMIZE, token USR
defm "24000",$0e0000c05d00 ; number 24000, ascii & internal format
end30: defb $0d ; line end marker
program_end:
; ZX Spectrum Basic variables
variables_end: