|
; ZX Spectrum tape file:
Assembler directives: #target Including C Source Files: #target#target tap
Assembler directives: #code Including C Source Files: #code#code <name>, <start>,<len>,<flag>
Assembler directives: #code Including C Source Files: #code#code <name>, <start>,<len>,flag=<flag>
Assembler directives: #code Including C Source Files: #code#code <name>, <start>,<len>,FLAG=<flag>
...
; Jupiter Ace tape file:
Assembler directives: #target Including C Source Files: #target#target tap
Assembler directives: #code Including C Source Files: #code#code <name>, <start>,<len>,flag=none
Assembler directives: #code Including C Source Files: #code#code <name>, <start>,<len>,none
This target defines a ZX Spectrum or a Jupiter Ace tape file. This file may and should contain several code segments. Normally each code segment defines one block on the tape. The blocks are written to the file in the uncompressed TAP format.
The tape file format represents programs and data saved to a music compact cassette by the original ZX Spectrum tape saving routines.
Unluckily the tape file formats for the ZX Spectrum and for the Jupiter Ace are only similar, not identical. zasm handles this difference for you, but it has to use a little heuristics to decide in which format to save.
Normally a ZX Spectrum file is written. Except Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif the following conditions are met, then a Jupiter Ace tape file is written:
Since version 4.2.0 zasm uses a different method to select Jupiter Ace .tap format:
Layout of a block in a ZX Spectrum tape file:
defw <length>
defb <flag>
defm <data>
defb <checksum>
Layout of a block in a Jupiter Ace tape file:
defw <length>
defm <data>
defb <checksum>
The flag argument defines the type of block and is Pseudo instructions: defl, set and '=' Labels: SETset from the Assembler directives: #code Including C Source Files: #code#code segment's #code: Flag Byte #code: Flag Byteflag byte.
Then the raw data follows which is taken from the Assembler directives: #code Including C Source Files: #code#code segment's data.
Finally a checksum follows which is calculated and appended by the assembler.
A tape file is a simple sequence of any number of these blocks.
Any kind of data is typically saved in two blocks: a header block, containing information about the following data, and a data block, containing data as described by the preceding header.
A complete game for the ZX Spectrum is typically saved in two parts: a basic loader, which consists of a header and a data block and the machine code part, which consists of a header and data block too. The basic part is typically saved with an auto start address which loads the following parts and starts the game.
A game for the Jupiter Ace may consist of only a single part (which consists of a header and data block). Machine code was not such a requirement because Forth is already pretty fast. The Jupiter Ace did not support auto starting and therefore the user had to type commands to load following blocks (Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif any) and to start and even restart a game.
The flag argument defines the type of block. On both the ZX Spectrum and the Jupiter Ace this is typically $00 for a header block and $FF for the following data block.
For an #insert: Examples: #assert: Example: incbin: Examples: #assert: Example:example see the .tap template file: template_tap.asm
It is possible to spread a tape block across multiple Assembler directives: #code Including C Source Files: #code#code segments. This is required Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif you want to include c source files. The first Assembler directives: #code Including C Source Files: #code#code segment must define an address and the block flag and the following Assembler directives: #code Including C Source Files: #code#code segments must have exactly matching addresses (you may put in '*' for the address) and no flag. All Assembler directives: #code Including C Source Files: #code#code segments which meet this requirement will be united with the first segment to one tape block.
New since version 4.0.24: Each code segment which has a #code: Flag Byte #code: Flag Byteflag byte starts a new tape block. All following segments which have no #code: Flag Byte #code: Flag Byteflag byte are appended to this tape block. A segment address defined for a following block does not create a 'gap' on the tape. The segment will be loaded where it is loaded and the program is assumed to move it to the defined location befor it is used. Actually this is true for the first block too: The tape loader defines where the block will be loaded, not the segment address stated in the Assembler directives: #code Including C Source Files: #code#code directive. This can be used Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif you want to move the entire program to a lower location in ram when it starts up.
Assembler directives: #target Including C Source Files: #target#target tap
;
headerflag: Pseudo instructions: equ Types of labels: Named values Labels: EQUequ 0
dataflag: Pseudo instructions: equ Types of labels: Named values Labels: EQUequ 0xff
;
; use printer buffer for variables:
;
Assembler directives: #data Including C Source Files: #data#data VARIABLES, 0x5B00, 0x100
;
; Basic loader, header:
;
Assembler directives: #code Including C Source Files: #code#code PROG_HEADER,0,17,headerflag
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 Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif none
defw program_end-0 ; length of the basic program without variables
;
; Tokenized Basic program:
;
Assembler directives: #code Including C Source Files: #code#code PROG_DATA,0,*,dataflag
;
; 10 CLEAR 23999
defb 0,10 ; line number
defb end10-($+1) ; line length
defb 0 ; statement number
defb tCLEAR ; token CLEAR
defm "23999",$0e0000bf5d00 ; number 23999, ascii & internal format
end10: defb $0d ; line Pseudo instructions: end, .end 8080 pseudo instructions: ENDend marker
;
; 20 LOAD "" CODE 24000
defb 0,20 ; line number
defb end20-($+1) ; line length
defb 0 ; statement number
defb tLOAD,'"','"',tCODE ; token LOAD, 2 quotes, token CODE
defm "24000",$0e0000c05d00 ; number 24000, ascii & internal format
end20: defb $0d ; line Pseudo instructions: end, .end 8080 pseudo instructions: ENDend marker
;
; 30 RANDOMIZE USR 24000
defb 0,30 ; line number
defb end30-($+1) ; line length
defb 0 ; statement number
defb tRANDOMIZE,tUSR ; token RANDOMIZE, token USR
defm "24000",$0e0000c05d00 ; number 24000, ascii & internal format
end30: defb $0d ; line Pseudo instructions: end, .end 8080 pseudo instructions: ENDend marker
;
program_end:
;
; <-- Basic variables -->
;
variables_end:
;
; Machine code block, header:
;
Assembler directives: #code Including C Source Files: #code#code CODE_HEADER,0,17,headerflag
defb 3 ; Indicates binary data
defb "mcode " ; 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
;
; Machine code block, data:
;
Assembler directives: #code Including C Source Files: #code#code CODE_DATA, code_start,*,dataflag
;
; <-- Command Line Options: --z80 Pseudo instructions: .z80, .z180 and .8080 Targets: #target Z80Z80 assembler code and data -->
;
code_end:
| |