summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats/trs_cas.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/formats/trs_cas.cpp')
-rw-r--r--src/lib/formats/trs_cas.cpp219
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