summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/lib/formats/a26_cas.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/lib/formats/a26_cas.c')
-rw-r--r--trunk/src/lib/formats/a26_cas.c166
1 files changed, 166 insertions, 0 deletions
diff --git a/trunk/src/lib/formats/a26_cas.c b/trunk/src/lib/formats/a26_cas.c
new file mode 100644
index 00000000000..feceba102d9
--- /dev/null
+++ b/trunk/src/lib/formats/a26_cas.c
@@ -0,0 +1,166 @@
+/*
+
+Atari 2600 SuperCharger support
+
+*/
+
+
+#include "formats/a26_cas.h"
+
+
+#define A26_CAS_SIZE 8448
+#define A26_WAV_FREQUENCY 44100
+#define BIT_ZERO_LENGTH 10
+#define BIT_ONE_LENGTH 15
+#define ZEROS_ONES 2755
+
+static const UINT16 one_wave[BIT_ONE_LENGTH] = {
+ 0x2AE5, 0x4E60, 0x644E, 0x68E4, 0x5B56, 0x3DFE, 0x15ED, 0xEA13, 0xC202, 0xA4AA,
+ 0x971C, 0x9BB2, 0xB1A0, 0xD51B, 0x0000
+};
+
+static const UINT16 zero_wave[BIT_ZERO_LENGTH] = {
+ 0x3DFE, 0x644E, 0x644E, 0x3DFE, 0x0000, 0xC202, 0x9BB2, 0x9BB2, 0xC202, 0x0000
+};
+
+static int a26_cas_output_wave( INT16 **buffer, INT16 wave_data, int length ) {
+ int i;
+
+ if ( buffer ) {
+ for ( i = 0 ; i < length; i++ ) {
+ **buffer = wave_data;
+ *buffer = *buffer + 1;
+ }
+ }
+ return length;
+}
+
+static int a26_cas_output_bit( INT16 **buffer, int bit ) {
+ int size = 0;
+ int bit_size = bit ? BIT_ONE_LENGTH : BIT_ZERO_LENGTH;
+ const INT16 *p = bit ? (const INT16 *)one_wave : (const INT16 *)zero_wave;
+ int i;
+
+ for ( i = 0; i < bit_size; i++ ) {
+ size += a26_cas_output_wave( buffer, p[i], 1 );
+ }
+
+ return size;
+}
+
+static int a26_cas_output_byte( INT16 **buffer, UINT8 byte ) {
+ int size = 0;
+ int i;
+
+ for( i = 0; i < 8; i++, byte <<= 1 ) {
+ size += a26_cas_output_bit( buffer, ( ( byte & 0x80 ) ? 1 : 0 ) );
+ }
+ return size;
+}
+
+static int a26_cas_clearing_tone( INT16 **buffer ) {
+ int size = 0;
+
+ size += a26_cas_output_wave( buffer, 0, 44100 );
+ return size;
+}
+
+static int a26_cas_zeros_ones( INT16 **buffer ) {
+ int size = 0;
+ int i;
+
+ for ( i = 0; i < ZEROS_ONES; i++ ) {
+ size += a26_cas_output_bit( buffer, 0 );
+ size += a26_cas_output_bit( buffer, 1 );
+ }
+ size += a26_cas_output_bit( buffer, 0 );
+ size += a26_cas_output_bit( buffer, 0 );
+ return size;
+}
+
+static int a26_cas_output_contents( INT16 **buffer, const UINT8 *bytes ) {
+ int size = 0;
+ int i, pages, j;
+
+ /* There are 8 header bytes */
+ for( i = 0; i < 8; i++ ) {
+ size += a26_cas_output_byte( buffer, bytes[ 0x2000 + i ] );
+ }
+
+ pages = bytes[0x2003];
+
+ /* Output each page prefixed with a small page header */
+ for ( i = 0; i < pages; i++ ) {
+ size += a26_cas_output_byte( buffer, bytes[ 0x2010 + i ] );
+ size += a26_cas_output_byte( buffer, bytes[ 0x2040 + i ] );
+ for ( j = 0; j < 256; j++ ) {
+ size += a26_cas_output_byte( buffer, bytes[ i * 256 + j ] );
+ }
+ }
+
+ return size;
+}
+
+static int a26_cas_do_work( INT16 **buffer, const UINT8 *bytes ) {
+ int size = 0;
+
+ /* Output clearing tone */
+ size += a26_cas_clearing_tone( buffer );
+
+ /* Output header tone, alternating 1s and 0s for about a second ending with two 0s */
+ size += a26_cas_zeros_ones( buffer );
+
+ /* Output the actual contents of the tape */
+ size += a26_cas_output_contents( buffer, bytes );
+
+ /* Output footer tone, alternating 1s and 0s for about a second ending with two 0s */
+ size += a26_cas_zeros_ones( buffer );
+
+ return size;
+}
+
+static int a26_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) {
+ INT16 *p = buffer;
+
+ return a26_cas_do_work( &p, (const UINT8 *)bytes );
+}
+
+static int a26_cas_to_wav_size( const UINT8 *casdata, int caslen ) {
+ return a26_cas_do_work( NULL, casdata );
+}
+
+static const struct CassetteLegacyWaveFiller a26_legacy_fill_wave = {
+ a26_cas_fill_wave,
+ -1,
+ 0,
+ a26_cas_to_wav_size,
+ A26_WAV_FREQUENCY,
+ 0,
+ 0
+};
+
+static casserr_t a26_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) {
+ UINT64 size;
+
+ size = cassette_image_size( cassette );
+ if ( size == A26_CAS_SIZE ) {
+ return cassette_legacy_identify( cassette, opts, &a26_legacy_fill_wave );
+ }
+ return CASSETTE_ERROR_INVALIDIMAGE;
+}
+
+static casserr_t a26_cassette_load( cassette_image *cassette ) {
+ return cassette_legacy_construct( cassette, &a26_legacy_fill_wave );
+}
+
+static const struct CassetteFormat a26_cassette_format = {
+ "a26",
+ a26_cassette_identify,
+ a26_cassette_load,
+ NULL
+};
+
+CASSETTE_FORMATLIST_START(a26_cassette_formats)
+ CASSETTE_FORMAT(a26_cassette_format)
+CASSETTE_FORMATLIST_END
+