This note is about the cassette interface built into the Apple II and Apple II+, subroutines. An assumption made here is that the cassette recorder is in the proper mode, play or record, when the read and write routines are executed. Note also that the timing is approximate and may vary from one Apple to another. A record is a block of binary data. This data may be a BASIC or APPLESOFT program, a machine language program, or just binary data. Records representing BASIC or APPLESOFT programs are really two records, the length of the program and the actual program. A record consists of a header, synchronous bit, the actual data, and a checksum byte for error detection. Monitor record format +--------+-+-----------------------+-+ | HEADER |S| DATA |C| +--------+-+-----------------------+-+ BASIC program record format +--------+-+----+-+--------+-+----------------+-+ | HEADER |S| LB |C| HEADER |S| PROGRAM |C| +--------+-+----+-+--------+-+----------------+-+ Key: S = SYNC bit C = CHECKSUM byte LB = BASIC program length The header consists of 10 seconds of 770 Hz tone, (1 cycle equals 1300 microseconds). This gives enough time for the cassette motor to attain speed and the plastic tape leader to go by. A subroutine called HEADR generates a shortened header between the BASIC length bytes and the BASIC program itself. The length of the header tone is controlled by the value of the accumulator on entry to the subroutine. This can vary from 0.2 seconds to 40 seconds. On entry the X register should be 0 and the carry flag should be set. HEADR also generates a synchronous bit at the end of the tone. HEADR resides at hexadecimal address $FCC9, or decimal address -882. The last cycle of header tone and SYNC bit ---+ +-------------+ +-----+ | | | | | +-------------+ +----+ | | 1300 microseconds | 200| 250 | header tone | synchronous bit | The synchronous bit, generated by HEADR, is one half cycle of 2500 Hz, (200 microseconds) and one half cycle of 2000 Hz, (250 microseconds). It is used to signal the end of the header tone and the start of the data. The data is recorded on the tape with a low starting address and a high ending address. Each byte of data is shifted out most significant bit first, least significant bit last. A zero bit is made up of one cycle of 2 kHz, (250 microseconds per half cycle) and a one bit is one cycle of 1 kHz, (500 microseconds per half cycle). This works out to 2000 baud for zeros only and 1000 baud for ones, or an average of 1500 baud. A zero bit and a one bit +-----+ +----------+ + | | | | | + +-----+ +----------+ | 500 usec | 1000 usec | The checksum byte is written on the tape at the end of the data block. All during reading or writing each data byte is EXCLUSIVE OR-ed with the checksum byte. If the checksum computed during a read agrees with the checksum that was written out, then the data is probably good. This method will detect an odd number of errors for any of the eight bits of the byte. In writing data, the cassette output uses quite simple circuitry, a flip-flop connected through a voltage divider to the jack on the back panel of the Apple. Any time the address $C020 is accessed this flip-flop changes state. Accessing the flip-flop once every 500 microseconds generates a 1000 Hz tone. For reading data, the cassette recorder uses a more complicated input circuit consisting of a 741 operational amplifier configured as a zero crossing detector. Zero crossing detection means that whenever the voltage at the input jack goes from positive to negative (or negative to positive) the output of the amplifier switches from a 1 to a 0 (or 0 to 1). The detector is accessed by any read to address $C060. The sign bit (most significant bit) of the byte read reflects the detector status. The read routines continually EXCLUSIVE ORs this bit with the value most recently read to detect a change in state. The amount of time required to change state indicates the incoming frequency which then is used to determine if a one or a zero has been received. After detecting the first zero crossing at the start of the header, the read routine uses HEADR to generate a 3.5 delay, and then the read routine waits for the sync bit. After HEADR generates the synchronous bit, the read routine reads the data and puts it in the specified memory range. In using the cassette interface to either read or write, all you need do is specify an address range and execute the read or write subroutine. The address range is stored in four bytes, two for the first address to be saved and two for the last to be saved. In both cases the least significant byte is first. Commanding the cassette interface: 1. from the monitor: If the start is $800 and the end is $9FF, then 800.9FFW will write the data to the cassette and 800.9FFR will retrieve it. 2. from machine language: Again, if the start is $800 and the end is $9FF then store the address range, LDA #$00 STA $3C starting address low LDA #$08 STA $3D starting address high LDA #$FF STA $3E ending address low LDA #$09 STA $3F ending address high JSR $FEDC write to block to tape The JSR $FEDC will write to the cassette; JSR $FEFD will read from the cassette. 3. from BASIC: First set up the address range. If S = the start and E = the end then from integer BASIC, POKE 60,S MOD 256 POKE 61,S / 256 POKE 62,E MOD 256 POKE 63,E / 256 4. from APPLESOFT, POKE 60,S - INT(S / 256) * 256 POKE 61,S / 256 POKE 62,E - INT(E / 256) * 256 POKE 63,E / 256 Then, to write out to cassette, use CALL -307; to read in from the cassette, use CALL -259.