These couple of articles are intended as a reference for anyone wishing to access cassette or disk files from machine code.
Part 1 : The Cassette System
Why would you want to use the cassette system from machine code? One of the reasons is the ability to deal with corrupted tapes. Suppose you have this rather long BASIC program saved on tape, and half way through it returns an IO error. There may be only 1 bit corrupted yet the whole file is deemed unusable. If you wrote your own routines, you could ignore the error and carry on loading the file, giving you a chance to try & spot the error & fix it.
The Dragon's cassette system uses the internal DAC to create two distinct sounds, one representing binary bit 0 & the other bit 1. Each byte is then turned into its appropriate bits & sent to the tape. The cassette can then be read in by detecting the different frequencies.
However, you don't need to know how it works in order to use the system. Rather than just dumping the raw data to tape, the Dragon has it's own tape file format and it is probably easier to use this format rather than adopt your own.
A Dragon cassette file consists of the following:
1. A leader block of $55 multiplied by the 16 bit number in location $90:91 (default 128). 2. A namefile block. 3. A blank section of tape for processing of the namefile block. 4. Another leader block of $90:91 bytes of $55 5. One or more data blocks. 6. An end-of-file block.
A header block, data block or EOF file block consists of:
1. A leader byte - $55 2. A sync byte - $3C 3. A block type byte: 00=namefile block 01=data block FF=end-of-file block 4. A block length byte (0-255) 5. 0-255 bytes of data. For a namefile block this consists of: 5.1 An 8 byte program name 5.2 A file ID byte where: 00=BASIC program 01=Data file 03=Binary file 5.3 An ASCII flag where: 00=Binary file FF=ASCII file 5.4 A gap flag to indicate whether the data stream is continuous (00) as in binary or BASIC files, or in blocks where the tape keeps stopping (FF) as in data files. 5.5 Two bytes for the default EXEC address of a binary file. 5.6 Two bytes for the default load address of a binary file.
For a data block, this consists of the actual data to load/save and there is no data associated with an EOF block.
6. A checksum byte which is: sum of all the data bytes + block type + block length. 7. A trailer byte - $55
The namefile data listed under 5.1-5.6 is stored in locations 474-488 when a cassette file is read by the Dragon's BASIC commands.
There are 3 ROM calls necessary for writing tape files:
$801B - write the tape leader [$A008] - write a data block $8018 - turn the motor off
Most of the work is handled by these routines, and the user has to do very little. For example, in order to write a binary file.
HEADER - this defines the header of the tape FCC \EXAMPLE1\ - the filename FCB $02 - a binary type FCB $00 - binary data FCB $00 - continuous data FDB $89B4 - EXEC address, in this example I've pointed it to the ?SN ERROR routine FDB $0500 - load address, in this example it will load on the text screen DATA - this defines the data to write FCC \A TAPE SAVE DEMO\
Having set up the data, the file can now be written:
TAPE JSR $801B - write the leader LDX #HEADER - point to the header STX $7E - $7E:$7F is used to point to the data to write CLRA - code for namefile block STA $7C - $7C is used for the block type LDA #15 - 15 bytes to write STA $7D - $7D is used for the length JSR [$A008] - write the header block JSR $801B - write the next leader LDX #DATA - point to the data STX $7E LDA #01 - data block STA $7C LDA #16 - 16 bytes of data STA $7D JSR [$A008] - write the data block LDA #$FF - end of file block STA $7C CLR $7D - no data bytes JSR [$A008] - write the EOF block JSR $8018 - turn the cassette motor off RTS
Running this program will write the tape file. You should now be able to CLOADM the file, which when loaded displays the text 'A TAPE SAVE DEMO' in the middle of the screen (all be it the spaces have turned black), and by typing EXEC should display ?SN ERROR.
The first article explained the theory of tape handling by the Dragon but probably of more use is the ability to read Dragon tape files. Once again using existing ROM calls makes life significantly easier. For loading tapes, the relevent calls are:
$8021 - turn cassette on for reading. [$A006] - read a tape block $8018 - turn cassette motor off
Reading a file is therefore straightforward. Call $8021 to open the file, following by [$A006] repeatedly until an end of file block is detected, then $8018 to switch the tape off.
As an example, to read the file written by the program in the last article:
BUFFER RMB 15 - buffer for header info LOADER JSR $8021 - turn tape on for read LDX #BUFFER - point tape buffer STX $7E - to our buffer (7E:7F) NXTBLK JSR [$A006] - read a block into buffer TST $81 - error indicator BNE ERROR - non-zero if an error LDA $7C - block type BEQ HEADER - 00 = header to process CMPA #$FF - FF = EOF block BEQ FINISH - close & quit BRA NXTBLK - must be data, loop back for next. HEADER LDX BUFFER+11 - get the EXEC address STX 157 - store in the EXEC vector LDX BUFFER+13 - get the load address STX $7E - store for the next block BRA NXTBLK - get the next data block FINISH JSR $8018 - switch tape off RTS ERROR JSR $8018 - switch tape off LDB #$2A - code for IO error JMP $8344 - call the error routine
This routine starts by calling $8021 to switch the cassette on. It then points the cassette buffer ($7E:7F) to a temporary buffer in order to load the header block (details of the structure of this block in last article). The header block is handled by the HEADER routine which extracts the only 2 pieces of information relevent to this program: where the program is to load & what EXEC address to default to. Any other data for the header can be extracted at this time (such as ID type), but if you are going to do a lot of processing, then you should switch the tape off/on as follows:
JSR $8018 - tape off - process header - JSR $8021 - tape back on for reading.
Between reading the header block & the first data block is the only time you can switch the tape on/off unless you are reading a gapped file (ie. a data file) which has gaps of silence interspurcing the data blocks.
The program load address is then stored in the cassette buffer pointer in order that data can then be loaded directly to that address. The routine then fetches the next block.
When data blocks are read in, the cassette buffer pointer ($7E:7F) is updated automatically so you can call the Block In routine without any further processing. Block In also returns the block length just read in location $7C.
If an error occurs (as detected by testing location $81) the tape is switched off and the BASIC error handler called to generate an IO error.
When an EOF block is detected, the tape is switched off, and the program finishes.