diff options
Diffstat (limited to 'src/lib/formats/trs_cas.cpp')
-rw-r--r-- | src/lib/formats/trs_cas.cpp | 219 |
1 files changed, 176 insertions, 43 deletions
diff --git a/src/lib/formats/trs_cas.cpp b/src/lib/formats/trs_cas.cpp index a6701ed1263..2648c536320 100644 --- a/src/lib/formats/trs_cas.cpp +++ b/src/lib/formats/trs_cas.cpp @@ -3,79 +3,170 @@ /******************************************************************** Support for TRS80 .cas cassette images +Types handled: +- Model 1 Level I: 250 baud +- Model 1 Level II: 500 baud +- Model 3/4: 1500 baud. -********************************************************************/ +Level I and II tape format is completely identical, apart from the +baud rate. The contents are specific to each system though. +The Model 3 and 4 can load either Level II tapes (by answering L +to the Cass? prompt), or the fast format by hitting enter at Cass? -#include <assert.h> +********************************************************************/ #include "formats/trs_cas.h" + #define SILENCE 0 #define SMPLO -32768 #define SMPHI 32767 -static int cas_size; +static int cas_size = 0; // FIXME: global variable prevents multiple instances /******************************************************************* Generate one high-low cycle of sample data ********************************************************************/ -static inline int trs80l2_cas_cycle(int16_t *buffer, int sample_pos, int silence, int high, int low) +static inline int trs80m1_cas_cycle(int16_t *buffer, int sample_pos, bool bit) { - int i = 0; + uint8_t i; if ( buffer ) { - while( i < silence ) - { - buffer[ sample_pos + i ] = SILENCE; - i++; - } - while( i < silence + high) + for (i = 0; i < 32; i++) + buffer[ sample_pos++ ] = SILENCE; + for (i = 0; i < 6; i++) + buffer[ sample_pos++ ] = bit ? SMPHI : SILENCE; + for (i = 0; i < 6; i++) + buffer[ sample_pos++ ] = bit ? SMPLO : SILENCE; + } + return 44; +} + + +static int trs80m1_handle_cas(int16_t *buffer, const uint8_t *casdata) +{ + int data_pos = 0, sample_count = 0; + bool sw = false; + + // Make sure this is a trs80 tape + // Should have some zero bytes then one 0xA5 + while ((cas_size > data_pos) && (casdata[data_pos] == 0x00)) + data_pos++; + if (casdata[data_pos] != 0xA5) + return 0; + + data_pos = 0; + while( data_pos < cas_size ) + { + uint8_t data = casdata[data_pos]; + + for (uint8_t i = 0; i < 8; i++ ) { - buffer[ sample_pos + i ] = SMPHI; - i++; + /* Signal code */ + sample_count += trs80m1_cas_cycle( buffer, sample_count, true ); + + /* Bit code */ + sample_count += trs80m1_cas_cycle( buffer, sample_count, data >> 7 ); + + data <<= 1; } - while( i < silence + high + low ) + if (!sw && (casdata[data_pos] == 0xA5)) { - buffer[ sample_pos + i ] = SMPLO; - i++; + sw = true; + // Need 1ms silence here while rom is busy + sample_count += trs80m1_cas_cycle( buffer, sample_count, false ); } + + data_pos++; + } + + // Specification requires a short silence to indicate EOF + sample_count += trs80m1_cas_cycle( buffer, sample_count, false ); + sample_count += trs80m1_cas_cycle( buffer, sample_count, false ); + return sample_count; +} + +static inline int trs80m3_cas_cycle(int16_t *buffer, int sample_pos, bool bit) +{ + uint8_t i, counts = bit ? 8 : 16; + + if ( buffer ) + { + for (i = 0; i < counts; i++) + buffer[ sample_pos++ ] = SMPHI; + for (i = 0; i < counts; i++) + buffer[ sample_pos++ ] = SMPLO; } - return silence + high + low; + return counts*2; } -static int trs80l2_handle_cas(int16_t *buffer, const uint8_t *casdata) +static int trs80m3_handle_cas(int16_t *buffer, const uint8_t *casdata) { - int data_pos, sample_count; + int data_pos = 0, sample_count = 0; + uint8_t sw = 0, bitout = 0, byteout = 0; + + // Make sure this is a trs80m3 tape + // Should have ~256 0x55 then one 0x7f + // It's possible that 0x57 could be encountered instead, + // but no working tapes with it have been found. + // Other bit-shifted variants might exist too. + while ((cas_size > data_pos) && (casdata[data_pos] == 0x55)) + data_pos++; + if (casdata[data_pos] != 0x7f) + return 0; data_pos = 0; - sample_count = 0; - while( data_pos < cas_size ) { - uint8_t data = casdata[data_pos]; - int i; + uint8_t data = casdata[data_pos]; - for ( i = 0; i < 8; i++ ) + for (uint8_t i = 0; i < 8; i++ ) { - /* Signal code */ - sample_count += trs80l2_cas_cycle( buffer, sample_count, 33, 6, 6 ); - - /* Bit code */ - if ( data & 0x80 ) - sample_count += trs80l2_cas_cycle( buffer, sample_count, 33, 6, 6 ); - else - sample_count += trs80l2_cas_cycle( buffer, sample_count, 33 + 6 + 6, 0, 0 ); + sample_count += trs80m3_cas_cycle( buffer, sample_count, data >> 7 ); + + // This paragraph unscrambles and prints the SYSTEM name. + // If the first character is U, type SYSTEM, then the next 6 characters; otherwise use CLOAD. + if ((sw == 1) && buffer) + { + if (bitout) + { + byteout = (byteout << 1) | (data >> 7); + if (bitout == 8) + { + if (byteout == 0) + sw = 2; + printf("%c",byteout); + byteout = 0; + bitout = 0; + } + else + bitout++; + } + else bitout++; + } data <<= 1; } + if (!sw && (casdata[data_pos] != 0x55)) + { + sw = 1; + // This 1ms of silence isn't absolutely necessary, but the system + // writes it, so we may as well emulate it. + sample_count += trs80m1_cas_cycle( buffer, sample_count, false ); + } + data_pos++; } + + // Specification requires a short silence to indicate EOF + sample_count += trs80m1_cas_cycle( buffer, sample_count, false ); + sample_count += trs80m1_cas_cycle( buffer, sample_count, false ); return sample_count; } @@ -83,47 +174,85 @@ static int trs80l2_handle_cas(int16_t *buffer, const uint8_t *casdata) /******************************************************************* Generate samples for the tape image ********************************************************************/ -static int trs80l2_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes) +static int trs80_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int) { - return trs80l2_handle_cas( buffer, bytes ); + if (cas_size && (bytes[0] == 0x55)) + return trs80m3_handle_cas( buffer, bytes ); + else + return trs80m1_handle_cas( buffer, bytes ); } /******************************************************************* Calculate the number of samples needed for this tape image ********************************************************************/ -static int trs80l2_cas_to_wav_size(const uint8_t *casdata, int caslen) +static int trs80_cas_to_wav_size(const uint8_t *casdata, int caslen) { cas_size = caslen; - return trs80l2_handle_cas( nullptr, casdata ); + if (cas_size && (casdata[0] == 0x55)) + return trs80m3_handle_cas( nullptr, casdata ); + else + return trs80m1_handle_cas( nullptr, casdata ); +} + +static const cassette_image::LegacyWaveFiller trs80l1_cas_legacy_fill_wave = +{ + trs80_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + trs80_cas_to_wav_size, /* chunk_sample_calc */ + 22050, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + + +static cassette_image::error trs80l1_cas_identify(cassette_image *cassette, cassette_image::Options *opts) +{ + return cassette->legacy_identify(opts, &trs80l1_cas_legacy_fill_wave); +} + + +static cassette_image::error trs80l1_cas_load(cassette_image *cassette) +{ + return cassette->legacy_construct(&trs80l1_cas_legacy_fill_wave); } -static const struct CassetteLegacyWaveFiller trs80l2_cas_legacy_fill_wave = + +static const cassette_image::Format trs80l1_cas_format = +{ + "cas", + trs80l1_cas_identify, + trs80l1_cas_load, + nullptr +}; + +static const cassette_image::LegacyWaveFiller trs80l2_cas_legacy_fill_wave = { - trs80l2_cas_fill_wave, /* fill_wave */ + trs80_cas_fill_wave, /* fill_wave */ -1, /* chunk_size */ 0, /* chunk_samples */ - trs80l2_cas_to_wav_size, /* chunk_sample_calc */ + trs80_cas_to_wav_size, /* chunk_sample_calc */ 44100, /* sample_frequency */ 0, /* header_samples */ 0 /* trailer_samples */ }; -static cassette_image::error trs80l2_cas_identify(cassette_image *cassette, struct CassetteOptions *opts) +static cassette_image::error trs80l2_cas_identify(cassette_image *cassette, cassette_image::Options *opts) { - return cassette_legacy_identify(cassette, opts, &trs80l2_cas_legacy_fill_wave); + return cassette->legacy_identify(opts, &trs80l2_cas_legacy_fill_wave); } static cassette_image::error trs80l2_cas_load(cassette_image *cassette) { - return cassette_legacy_construct(cassette, &trs80l2_cas_legacy_fill_wave); + return cassette->legacy_construct(&trs80l2_cas_legacy_fill_wave); } -static const struct CassetteFormat trs80l2_cas_format = +static const cassette_image::Format trs80l2_cas_format = { "cas", trs80l2_cas_identify, @@ -132,6 +261,10 @@ static const struct CassetteFormat trs80l2_cas_format = }; +CASSETTE_FORMATLIST_START(trs80l1_cassette_formats) + CASSETTE_FORMAT(trs80l1_cas_format) +CASSETTE_FORMATLIST_END + CASSETTE_FORMATLIST_START(trs80l2_cassette_formats) CASSETTE_FORMAT(trs80l2_cas_format) CASSETTE_FORMATLIST_END |