Atari classic tape generator program. Version March 1999. Preface. This program is intended to be used with the wav2cas program that I wrote in may of 1997. A lot of the technical hoopla involved in storing information on cassettes is explained in the documentation that came with that program. If you are not familiar with it, I suggest you first read that stuff. If you do not have it, download it from the Umich archives, or send me an E-mail. Purpose of this project. The design goal of this project is to convert the digital tape format, introduced with the wav2cas project, back to a format that will enable us to re-create a cassette that can then be loaded on a regular Classic Atari equipped with any of the supported cassette units. The data is thus re-saved onto cassette tape. This can be used to clean up faulty tapes that contain dropouts, spikes, noise, or other problems. Data that is damaged can be restored, or repaired. The data on the new tape could be saved at a higher baud rate, thus slightly reducing the time required to load the tape. The quality of the cassette tape can be greatly improved in this way. The cassette data is encoded into a .wav file, which should then be played back through a .wav player program. The audio can then be recorded onto an audio cassette using a standard audio cassette recorder. Since the cassette data is recorded using only a sound card and some software, there is no need for an Atari cassette unit. Data can be retrieved from, and saved to cassette tapes even if your cassette unit is broken, unreliable, or otherwise not available. You will need a regular audio cassette recorder of course. If you want to load the cassette again, you will need an Atari and an Atari cassette unit, but these do not have to be connected to the PC. You could go to a friend with your tapes and process them on her/his PC if you do not have such a device yourself. Theory of operation. Data must be saved onto cassette tape. To do this, the data must be converted to some audio format. Converting the data to an audio format is accomplished by converting the digital data into a .wav file, which holds the audio information that results from encoding the data. Data is encoded on the cassette tape as a frequency shift keying audio signal, usually referred to as FSK. For the non-technical persons, this means that an audio tone is recorded on the tape that has either one frequency, referred to as the mark tone, or another frequency, called the space tone. Normally, the POKEY chip within the Classic Atari generates these tones, based on the bits of the data to be encoded, but this time we do it all in software. We add the start bit and the stop bit for each byte. All the bytes to be encoded are stored in the cassette records, including the marker bytes and the checksum. Data is normally encoded at 600 baud. The mark tone is usually 5327 Hertz. The space tone is usually 3995 Hertz. This, along with the information stored in the .cas file, enables us to create a .wav file that contains all the bits encoded in the FSK audio format. The bytes to be encoded are stored in the cassette records within the .cas file. These records also tell us the length of the IRG and the PRWT, and the length of the leader. For experimental purposes, we can attempt to increase reliability by tweaking these values, especially if we want to attempt to create a cassette that is encoded at a non-standard baud rate. Writing data to a cassette tape. Once a .wav file is created by the program, we simply play it back with a .wav player program, and record it onto the cassette tape. Never ever input this audio signal into the Atari computer DATA IN line directly, since this might damage your system. The audio signal from the cassette tape must be converted to digital data by the Atari cassette unit, thus we must first record it to a tape with our audio cassette recorder. Adjust the recording level as you would with any other sound. The data should be recorded on the right channel. If you want, you can record some music on the left channel, or whatever else you would like to hear while loading the tape. If you like to hear the FSK tones, you can record the data on both channels. The Classic Atari likes this 'music', but you might not particularly like it, or even call it noise, like your parents did with your music, so switch off your speakers, or turn the volume down. If you do not record anything on the left channel, set the recording level to zero for the left channel. You should take care that the length of the leader does not become excessive. Some leader is already included in the .wav file. If you do not start playing back the .wav file in time, the leader might become too long. Do not make it too short either though. If you start the player and the cassette deck simultaneously, you will be able to control the length of the leader by entering the proper value in the program. Just a few words about playing back a wave file are in place. I have tried a couple of programs to playback a .wav file. One of the things to watch is the fact that you will be processing a lot of data. If your hard drive is fragmented, the system will need more time to read the data. Since the sampling rate is high, this could cause your player program to skip a few samples while it is busy reading data from disk. I did not program that stuff, I would assume the program continues to play samples from memory, but somehow it looks like the O.S. disables the interrupts while doing I/O, so it will simply miss a few samples sometimes. This might also be caused by the fact that there are other tasks in the system that are allowed to use the system resources for a while. I have seen this happen with an Operating System that was released in 1998. You can check this by recording the audio file to a .wav file again, and then looking at the wave file with a wave editor. You should then be able to find the spot where the system could not keep up, especially if we are generating a very clean FSK signal. Since this problem occurs randomly, it is hard to do anything about it. You could simply create a 32 meg ram disk and run DOS, but not everybody is so fortunate to have memory in abundance. A fast hard drive is nice too, and it will do most of the time. If you cannot avoid this problem, simply record the file again, until you have a clean copy. It would be nice to have a utility that adds music to the left channel, so that we could listen and hear the bad spot on the tape. All we would need is a program that takes two mono .wav files and merge them into one stereo .wav file. Anyone interested in writing such a utility? Maybe such a utility already exists? Generating a .wav file. We want to generate a .wav file. A wave file starts with a header that specifies the sampling rate and other technical stuff. A wave file is a file in the standard RIFF file format, and documentation on this subject is available, so I will not go into detail here. The RIFF file header is written, then the wave chunk header is written to the file, and we write the sampling rate and all the other information about the format of the wave file to the header. Then we write the data header. The headers contain the length of the file, and the length of the chunk, so we have to memorize the file position where these numbers are supposed to be. When we are done writing the file, we must come back and update these values. After the headers, we can start the sample data for the audio level of the wave. According to the specifications, if the number of bytes in the chunk is odd, we will have to add a slack byte to the end. When we are done writing the sample data, we add this slack byte if it is required, and then we update the length of the file and the length of the chunk. The sample data is a very huge list of PCM values ranging from 0 to 255. We generate these samples based on the tone we want to represent, which is dictated by the values of the bits we want to encode. We know that we want to write either mark or space tones. Based on the frequency of these two tones, we generate a table for each tone. The table holds the PCM sample data for one second of that tone. The mark tone is usually 5327 Hertz. This means there are 5327 periods in one second. We want to make a sine wave, because we like pure tones, so we need to compute the amplitude of the signal as a function of the time since the start of the tone. We do this by computing the sine value of the time since zero. Now you cannot input time into a sine function, only degrees or radians, thus we have to convert the time. So let us choose radians, for no obvious reason. One period equals 2 PI radians. If we had chosen degrees, it would have been 360 degrees of course. Now we are generating one second of sine wave stuff, and over that second, we will generate the sample value for 44,100 samples. The frequency tells us how many periods there are within that second, so this means we have 5327 times 2 PI radians within one second. Our sample rate is the time value. When we have generated 44,100 samples, one second has passed. The sample number is our index into the table we wish to generate. Now we can compute the amplitude by multiplying our time value by the number of radians that one time element represents, and inputting that into the sine function. Thus we multiply the index into our table by the number of radians within one second, divided by the sampling rate. The output of the sine function is a value from -1 to 1, so we have to multiply this result by some value to increase the value to a level which we can use for the PCM sample values. Also, we have to add 128 in order to make it a positive number. If we multiply it by too much, the sound might become distorted a bit, so I choose a value of 64 for this. Since we have one second of samples in our table, we know that for any integer frequency value the end of our table will wrap nicely around to the start of the table again for a clean sine wave. The tables can be used to quickly write a large number of samples to the .wav file. We wanted the output to be a clean sine wave form. This can be accomplished with the following procedure. While writing data to the .wav file, the program keeps track of the value of the tone we were writing to the .wav file, either mark or space. If we need to write a mark tone, and the previous tone we wrote also happened to be a mark tone, the program continues to write the wave form from where it left off in the table the last time. The same is true for the space tone. Thus, if two consecutive bits have the same value, the wave continues smoothly on, as if nothing happened. If the tone is different, the last sample value we wrote to the file is noted. Then we determine whether the wave form was going up or down in amplitude. Then this last value is searched for in the table of the other tone. Of course the level must also match the fact whether it is rising or falling. When the match is found, we continue on with the other table from that point. This makes a smooth transition from one tone to another. At first I had it wait for the level to pass zero before changing over the tone, but this also works nice, and it results in a more exact bit length. Now why did we want a clean wave? Because it looks nice, or maybe because it sounds nice. But our only and most important goal should be, can we load the tape every time without a load error? Do not worry about what it sounds like, check the track record, does it load okay? This made me experiment more, and the result is another way of encoding data in waves. The key point seems to be to have the FSK decoder detect when the change in the tone occurs. We should therefore make sure that the wave starts and stops exactly where the bits start and stop, to make it easier for the FSK decoder. Unfortunately you cannot have it both ways, since if you make the wave start at a certain point, chances that the period ends when the bit is supposed to end are close to zero, unless you tamper with the frequency. The end of the space tone seemed to cause the most problems, so I choose to make the waves end when the space tone is supposed to end, and start when the mark bit is supposed to start. This makes a clean change from space to mark. The not so smooth change over at the change from mark to space seems to have little effect on the decoded bits, so this looks like it is the best encoding format. The way to accomplish this encoding scheme is to write the requested number of samples from the end of the table for the space tone, and the beginning of the table for the mark tone. These tables are sure to start and end in a way that makes a smooth change. The program calls this a transition at the zero level, even though only one of the two transitions actually occurs at the zero level. The baud rate can be selected. If the default baud rate is used, we find 600 bits per second on a tape. Since we need 44,100 samples for each second, a bit will last about 44,100 samples divided by 600 bits, which is 73.5 samples. Half a sample does not exist, so we would have to write 73 samples per bit. For each bit that we encode, we would write 73 samples in this case. This means that in effect, our bit rate would be slightly off from the selected value. This is not a problem for loading the tape, but we can do better. If we would process the bits in groups of a byte, we would have to compute how many samples are required for one byte, and then evenly distribute that over the 10 bits that make up a byte. This is only important if we want to generate a tape that exactly matches the baud rate entered. If we do not mind that the baud rate is slightly off, this is just extra complexity that is not needed. There is another reason why we would create the bits in groups of a byte though. I have noticed that if you have a test tape that alternates between mark and space tones, with bits of equal number of samples, that the space bits appear to be slightly shorter. It looks like the space tone detection takes a little while to kick in. For the mark tone, this is really not important, since in the absence of a space tone, a mark bit value is output by the cassette unit, even if there is no mark tone present, so the detection of the space tone appears to be the most crucial. The mark tone does prevent erroneous detection of the space tone, but the space tone is just more important. If we wanted to compensate for this, we could actually make the space tone bits on the tape start a little early. We cannot simply change the length of the space bit though, since the total length of the byte should remain the same. This means we have to steal a couple of samples from the mark tone following it. Of course it takes some time before the decoding circuit detects that the space tone is gone again. Again, we can compensate for this, by having the space tone stop a little early, and give these samples to the mark tone following it. However, stopping seems to be very simple. In fact it is so simple, that sometimes the last portion of the bit is not detected as a space tone, and the last few samples are treated as part of the mark tone following it. This makes the bit shorter so we would have to make it longer again. Confused? So was I! I have played around with this a great deal, and it all seems to be related to the way the two tones change over. Compensating for this does seem to improve reliability at the higher baud rates. The only problem is that I suspect that the compensation requirements of various cassette units might differ. If so, a tape that would load on one unit, might fail to load on another. Don't worry too much though, since these problems with loading should only occur when the baud rate is around 820 baud. The maximum baud rate that the standard O.S. will program the POKEY chip for when doing cassette reads is 820 baud. If we go beyond that, we are depending on the POKEY chip to cope with the deviation. It does seem to cope with some deviation. I tried to improve the signal such that we could even load a tape at 875 baud, a value that seems to be the limit if you use the CAS2SIO program. I have experimented a bit with adjusting the bit sizes and such, but in the end, what you really end up doing is simply reducing the size of the stop bit. The higher baud rate results in a smaller bit size. As long as the last data bit is presented at the time that it would be expected at 820 baud, POKEY seems to be able to decode the bit, and then the stop bit is just a lot shorter. The Atari has plenty of time to grab the byte from POKEY and store it somewhere, and then it can even compute the checksum. It does not need the time that the stop bit represents. POKEY also seems to not mind. So if this is all we wanted, it would be better to reduce the length of the stop bit, and encode the tape at 820 baud. I don't think this little extra speed is worth the loss of reliability. If we know how many samples to generate per bit, we can now write the record to the tape. We begin with the IRG/PRWT, which is specified as a number of milliseconds. We have to divide this by 1000 and multiply it by the sample rate to get the sample count. This number of samples is then written to the file, by writing the corresponding piece of tone data from the mark table. Then we start encoding the bytes of the record one by one. We write the data for the bits from the mark or space table based on the value of the bits. We start with the start bit, then the least significant bit of the data byte, followed by the other bits one by one, and finally the stop bit. Since we are processing bits by the byte, as discussed just now, we sum the length of the bits if they are alike, adjusting the bit length sometimes to evenly distribute the samples over the byte, and then compensate for the space tone delays. Tapes differ is length, depending on how much data is stored on them. Some tapes are 16K or less, other tapes are 48K or more. Most tapes require roughly 2.5 seconds per block of 128 bytes, which is 20 seconds per kilobyte. A 16K tape will take about 5 minutes, a 48K tape could easily take 15 minutes or more. At a sampling rate of 44,100 samples per second, we will have to write 44,100 bytes of data for each second of audio, or about 2.5 Megabytes per minute. A fifteen minute tape will easily consume over 30 Megabytes of disk space. If you do not have that much hard disk space available, you should backup some data and make space available, or simply buy a bigger hard disk, since hard disks are very cheap nowadays. If the program runs out of disk space, it reports a write error. Command line options. Running the CAS2WAV program is easy. You have two options. You can run it interactively, by simply starting it. It will ask for the filename. Once that is entered, it will ask you to enter whether or not you want the diagnostics to be printed. This program does not provide a lot of diagnostics, it will tell you at what offset in the .wav file a data record starts. The only time this appears to be useful is when you sample the created tape again, to see if the .wav file has been properly transferred to an audio tape. If you have a slow computer, this option also shows you that the system is still busy working, otherwise, you might think that the system crashed. It does take a little while sometimes, depending on the size of the file, and also on the option selected for the way the tone changes over. If you wish this diagnostic data to be printed, it will be printed to the screen. The output can be redirected to a file using the standard DOS redirection, but that would redirect the prompts to the redirected output too, so this is only recommended if you use the command line arguments. You are then asked to enter various information that is used for encoding the data on the tape. If you enter nothing, the default values for these settings are used. You only have to enter something if you want something non-standard. The wave format of the tones can be selected, either sine waves or block waves. There are two flavors of sine waves, one with an immediate change over, and one which only changes at the next zero crossing, which is a pure sine wave so to speak. If you want the zero crossing to occur at the time the bit is supposed to end, select the zero crossing option. This zero crossing works as described earlier. The baud rate should be something around 600. If you want to experiment with higher speeds, you can enter a higher value. Values up to 850 baud seem to work. Above that, load errors start to occur. If you enter a fixed value, the baud rate values stored in the cassette file will be ignored. The frequencies used for the mark and space tones can be altered too. If you think your cassette unit prefers some other frequency, you can try adjusting the frequencies a bit. The standard frequencies seem to work fine on my cassette units though. Finally, the length of the leader tone and the length of the Inter Record Gap can be set to a fixed value. If you enter a fixed value, the values from the cassette file will be ignored and overridden by the values entered. The leader value is used for the length of the beginning of the tape. The IRG value is used for any IRG on the tape that lasts less than 3 seconds. You can enter all this on the command line if you prefer. The file to process is the first argument on the command line. The printing of diagnostic data is an option switch, which can be selected by adding /d to the command. The wave form can be selected by using the /w switch, specifying sine waves, block waves, or pure waves. Add the letter s, b or p after the w. If you want to try the zero transition, add the /z switch to the command. The value of the baud rate can be selected by using the /b switch, specifying the desired baud rate. The frequency for the mark tone can be selected with the /m switch, for the space tone it is the /s switch. The length of the leader can be specified with the /l switch, and the IRG length can be set using the /i switch. These last five switches take a decimal number for setting the value, so setting the baud rate to 820 would require you to enter /b=820 on the command line. The equals sign is optional, but do not insert any spaces between the switch and the value. There is no check for weird values, so what you enter is what you get. If you enter very strange values, you might even be able to make the program crash, or hang, so if you do enter a value like that, don't do it again. I am not going to add checks for these values, in order for people to be able to experiment. If no command line arguments are given, the program will prompt for the file. The program allows the user to exit the interactive request for data by entering control Z. On most PC's, pressing control-BREAK will also terminate the program. I used the Symantec C++ compiler on the PC. If you press control-BREAK, it will terminate the program as soon as it tries to write to the screen. If the CAS2WAV program happens to be in the middle of writing some data, this may take a few moments though. There is one special command line option that cannot be selected in the interactive mode. The /t option generates a test tape for examining the quality of the FSK decoder inside the cassette unit. Any filename that is entered is ignored, since the filename for a test tape will always be "testtape.wav". The number after the /t specifies the length of the tape in milliseconds. The other options are all still valid, so you can experiment with various frequencies and baud rates. The test tape wave file will contain some form of alternating mark and space tones, each one bit long. This is intended for viewing the output of the FSK decoder on an oscilloscope. The signal should be a block form with all bits equal in length. In order to make the cassette unit play the tape, you will have to connect it to an Atari. You can either try to boot the tape, using the normal procedure, or you can enter the command from BASIC to start the cassette motor. If you enter POKE 54018,52 the motor will be turned on. Note that some cassette units draw their power from the computer via the SIO bus, so another reason to connect it to the computer. The cassette output on the DATA IN line is also designed such that you need to connect it to the computer if you want to measure the result with an oscilloscope. The oscilloscope should be connected to the DATA IN line to view the signal. If you recorded the audio to both channels, you can connect the AUDIO IN line to the second input if you have a dual beam oscilloscope. By experimenting with this, you can try to determine what frequency works best. Of course, it is advised to use the standard frequencies, but it is fun to experiment with this. Note that a test tape cannot actually be loaded, since it is not in the proper format. It can only be used to test the FSK decoder. You can modify the program to generate different test patterns. I have used this test tape feature a lot to investigate how the FSK decoder reacts to different ways of changing the tones from mark to space and back. At one point I even created a bi-tone signal, where the mark and the space tone were both present at the same time. I only made one of them louder than the other, depending on the bit to be encoded. Even then did the FSK decoder generate the correct bits. I thought that if the space tone is present all the time, the decoder might not have as much trouble to see the start of the space bit, but it looks like this does not make a difference, since the space bits were still shorter sometimes. Eventually I tried using a block wave, since at some point I doubted that the FSK decoder really likes sine waves. To my amazement, this resulted in a rock solid output. Using the diagnostic tape, the edges of the bits were stable, which indicates that the FSK decoder reacted in a stable way to changing over from mark to space and back. So I even wanted to make this the default wave form, but now it is up to you. Even though I have spent a considerable amount of time on research, this area could probably be explored more. After all, the tests I did were only with a few cassette units. If you feel like playing around with it yourself, let me know your results. Incidentally, when connecting the audio to the second channel of my oscilloscope, I could see that the signal that is output by the cassette unit is about three periods delayed from the actual change over in the audio signal. This might account for the rather unpredictable behavior that confused us. Fun stuff to research. All this weird behavior is probably caused by the fact that there is a slight difference in the output circuit of the filters for the mark and space tones, which is input into the comparator. There is a small capacitor, which is charged by the output of the filter. This charging takes some time, based on the value of the resistors in the circuit. Both filters have these resistors in their circuit, however one of these resistors differs slightly in value. This is probably because in the absence of an audio signal, the recorder should output a mark value. If there is no signal, both filters output the same value, but due to this difference in the resistor value, the mark tone voltage on the input of the comparator will be the highest. This design has one drawback. The charging time and discharging time of the capacitor is also influenced by this difference in resistor value. This causes the space bits to be shorter. I wonder if this problem could be solved by replacing the capacitor in the mark circuit by one of a lesser value. For now, we will fix this problem in software. I do not know whether all this is true for all the various types that Atari produced. Troubleshooting. If, after recording the .wav file to a cassette tape, the tape produces load errors or boot errors, obviously there must be something wrong. Check to see that the audio has been properly recorded. If you know where the boot error occurs, check that piece of the recording. You might want to turn off any special noise suppressors that might be on your cassette deck. For recording Atari stuff, I use regular tapes, because they are cheap, and because that is what these machines were designed for. You should also try some of the other wave formats to see if that helps. I had the best results with the zero transition option. Sometimes, the playback of .wav data can be suspended due to various O.S. related issues. If you feel that a data block has been hit by this, try recording the tape again. Most commercial tapes are recorded on both sides, so we might apply the same tactics here. If we record the .wav to both sides, we can try the other side, to see if the playback of the .wav file was affected by this. If it is the case, the bad side of the tape could simply be recorded over again. If a certain portion of a tape is damaged, or suffers from a dropout, then the tape is unreliable, and I suggest you do not use it for recording data. If you keep having trouble booting the cassette tape, first try booting the digital image with the CAS2SIO program and a SIO2PC cable. Like I stated in the WAV2CAS documentation, I have found that some of the load errors are not caused by the cassette unit at all. Boot errors that are caused by timing problems can sometimes be corrected by specifying a fixed IRG value. It is helpful to know whether the .cas file is okay to begin with. If you are still having troubles with loading a specific tape, make sure you know what procedure to follow in order to boot these tapes. The tape might not be compatible with the XL/XE O.S., or you might need a certain amount of memory. If you have a program that was written in BASIC, it will have been saved with the CSAVE command in BASIC, so use CLOAD instead on these tapes. Anyway, make sure you know the proper procedure for the tape. Another possibility is that there is simply a bug in my program. If a load error occurs consistently at the same spot on the tape, even if you record it again, this might be a problem inside the program. If this occurs, send me an E-mail. Epilog. Well, we have come to the end of this document, another project done. I hope this stuff helps some folks to learn to appreciate the cassette stuff a little more. After all, if it is more reliable, it is not half as bad. Booting a PC nowadays takes several minutes, so booting an Atari from tape might actually be faster, and lots more fun. The source for this program is included, so if you feel you want to experiment some more, you are welcome to try it out. Note that I did not put any of the PC's supposed to be 'C' crap in my source. I still cannot find the words FAR and NEAR and such in Kernighan and Ritchie, so I compile all my sources with some switch that makes the PC choose the correct pointer stuff. Select a proper memory model, whatever that may be, if you want to compile the stuff yourself. Allocating two times 44,100 bytes does not make default PC pointer stuff happy so it seems. Comments are welcome. I might ignore them, since I will spend my time on other things now, but it is always nice to hear somebody tried to use the stuff. The terms and conditions are the same as with the wav2cas stuff. So, use this stuff at your own risk. I am not responsible for any damage that might occur whatsoever. If you want to take portions of my program and improve on it, go ahead, provided that you comply with a few rules. You have to include in your documentation that your program was based on this work. On top of that, if you charge money for your product, you will have to inform people that my stuff is available free of charge. This includes shareware fees and stuff like that. I just want people to know that this thing is available for free. This means that I myself do not expect people to send me any money. So, you get what you pay for with my stuff. If you want to drop me a note, there are various options. For one thing, you can send me E-mail over the Internet. You can also send me a regular letter. Anyway, if you have questions, send them to me. Please do not send me .wav files to look at. It would be crazy to E-mail a file of several megabytes. You are welcome to send any comments. If you think some of my data or research is in error, let me know. The address is below. Ernest R. Schreurs. Kempenlandstraat 8 5211 VN Den Bosch The Netherlands E-mail: ernest@wxs.nl Keep those XL's/XE's humming. Or, I suppose we are talking to the real pioneers here: Keep those 400's/800's humming.