summaryrefslogblamecommitdiffstatshomepage
path: root/src/lib/formats/a26_cas.cpp
blob: 5030c2bfe0fdf51331336080b6ea2fea4835ee7b (plain) (tree)
1
2
3
4
5
6
7
8
9

                                





                               
                   



                            




                                     
 
                                                  



                                                                                       
                                                    


                                                                                      
                                                                                   










                                                 
                                                            
                     
                                                              
                                                                                        








                                                               
                                                                  

                         






                                                                                  
                                                      
                         




                                                        
                                                   

                         









                                                        
                                                                              

                            



















                                                                                    
                                                                      
                         















                                                                                             

                                                                             
 
                                                             

 
                                                                      
                                                   











                                                                     
                                                                                                              
                      




                                                                                         
                                                    

 
                                                                            






                                                                            
               




                                               
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/*

Atari 2600 SuperCharger support

*/

#include <assert.h>

#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_t one_wave[BIT_ONE_LENGTH] = {
	0x2AE5, 0x4E60, 0x644E, 0x68E4, 0x5B56, 0x3DFE, 0x15ED, 0xEA13, 0xC202, 0xA4AA,
	0x971C, 0x9BB2, 0xB1A0, 0xD51B, 0x0000
};

static const uint16_t zero_wave[BIT_ZERO_LENGTH] = {
	0x3DFE, 0x644E, 0x644E, 0x3DFE, 0x0000, 0xC202, 0x9BB2, 0x9BB2, 0xC202, 0x0000
};

static int a26_cas_output_wave( int16_t **buffer, int16_t 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_t **buffer, int bit ) {
	int size = 0;
	int bit_size = bit ? BIT_ONE_LENGTH : BIT_ZERO_LENGTH;
	const int16_t *p = bit ? (const int16_t *)one_wave : (const int16_t *)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_t **buffer, uint8_t 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_t **buffer ) {
	int     size = 0;

	size += a26_cas_output_wave( buffer, 0, 44100 );
	return size;
}

static int a26_cas_zeros_ones( int16_t **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_t **buffer, const uint8_t *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_t **buffer, const uint8_t *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_t *buffer, int length, uint8_t *bytes ) {
	int16_t   *p = buffer;

	return a26_cas_do_work( &p, (const uint8_t *)bytes );
}

static int a26_cas_to_wav_size( const uint8_t *casdata, int caslen ) {
	return a26_cas_do_work( nullptr, 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 cassette_image::error a26_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) {
	uint64_t size;

	size = cassette_image_size( cassette );
	if ( size == A26_CAS_SIZE ) {
		return cassette_legacy_identify( cassette, opts, &a26_legacy_fill_wave );
	}
	return cassette_image::error::INVALID_IMAGE;
}

static cassette_image::error 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,
	nullptr
};

CASSETTE_FORMATLIST_START(a26_cassette_formats)
	CASSETTE_FORMAT(a26_cassette_format)
CASSETTE_FORMATLIST_END