1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/*
Atari 2600 SuperCharger support
*/
#include <cassert>
#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
|