Interrupt driven MSX tape read

danjovicdanjovic wrote 12/13/2021 at 23:02 • 3 min read • Like

I've been following athread on to add recording functionality to Casduino. So far the tape reading is being done using sampling / signal processing but the real bottleneck seems to be the latency of the SD card writing.

Worth to notice that the same issue happens on the native playback function and such issue was worked around using an interrupt based playback engine.

Then I though why not use the same approach for the recording ?

I have started to work in the problem, and after reading the code on the BIOS listing altogether with the explanations on the Red Book, I came to an architecture using a state machine to mimic the behavior of some of the cassette routines.

TAPION:  Find a header in the cassette and measure the baud rate.

TAPIN: Read a byte from the cassette. Tape is read continuously until a start bit is found. Then read the next eight bits.

The state machine is defined as

typedef enum  { _INTERRUPT,      // 0
                _HEADER_SYNC,    // 1
                _HEADER_AVERAGE, // 2
                _DATA_STARTBIT,  // 3
                _DATA_DATABITS   // 4
              } t_rxMachineState; 

The resources used are one interrupt pin, programmed to trigger an interrupt at every pin change and the Timer 2 which runs free with a pre-scale set to advance one count at every 8us.

The _HEADER_SYNC state counts pairs of transitions until the difference between 1111 sequential pairs is within 32us (4 counts).

The _HEADER_AVERAGE state counts 512 single transitions to determine the average length of  a full bit. The parameters LOWLIMIT and MAXBITLEN are defined as 25% around the average (MEDIA)

The  _DATA_STARTBIT state add the time taken by sequential transitions until the sum is larger than LOWLIMT, which marks a transition from the last quarter cycle of a "1" bit to the first half of the "0" bit (the start bit). After that the second half of the Start Bit is read.

The _DATA_DATABITS was a tricky one to write (but not as tricky to make it work as the previous state). On the MSX the Z80 counts how many  transitions occurred have passed within a given window of time to determine the state of the bit, whilst using interrupts, it is necessary to compare the time taken after you have 2 transitions. If it takes more than a LOWLIMIT, we have a bit 0 and we're happy. Otherwise it is necessary to wait to more cycles and we have a bit 1. After 8 bits, the data is pushed to the stack and the state return to _DATA_STARTBIT. As in real MSX the stop bits are ignored as they are there just to provide some time to process the data read.

Worth to mention that none of the interrupt routines do rely upon the phase of the cassete signal, but soleley on the timing between the transitions.

The _INTERRUPT state is there to take care of unforeseen conditions during the tape reading.