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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/*
Tape support for Glaksija GTP format
Miodrag Milanovic
*/
#include <assert.h>
#include "gtp_cas.h"
#define GTP_WAV_FREQUENCY 44100
#define WAVE_LOW -0x5a9e
#define WAVE_HIGH 0x5a9e
#define WAVE_NULL 0
#define GTP_BLOCK_STANDARD 0x00
#define GTP_BLOCK_TURBO 0x01
#define GTP_BLOCK_NAME 0x10
static INT16 wave_data;
static INT16 len;
#define PULSE_WIDTH 30
#define PERIOD_BASE 150
#define PERIOD_1 75
#define PERIOD_0 150
#define INTERBYTE_PAUSE 225
#define INTERBLOCK_PAUSE 100000
static void gtp_output_wave( INT16 **buffer, int length ) {
if ( buffer == nullptr ) {
return;
}
for( ; length > 0; length-- ) {
**buffer = wave_data;
*buffer = *buffer + 1;
}
}
static int gtp_mod_1( INT16 **buffer )
{
wave_data = WAVE_LOW;
gtp_output_wave(buffer,PULSE_WIDTH);
wave_data = WAVE_HIGH;
gtp_output_wave(buffer,PULSE_WIDTH);
wave_data = WAVE_NULL;
gtp_output_wave(buffer,PERIOD_1 - 2 * PULSE_WIDTH);
wave_data = WAVE_LOW;
gtp_output_wave(buffer,PULSE_WIDTH);
wave_data = WAVE_HIGH;
gtp_output_wave(buffer,PULSE_WIDTH);
wave_data = WAVE_NULL;
gtp_output_wave(buffer,PERIOD_1 - 2 * PULSE_WIDTH);
return PERIOD_1 * 2;
}
static int gtp_mod_0( INT16 **buffer )
{
wave_data = WAVE_LOW;
gtp_output_wave(buffer,PULSE_WIDTH);
wave_data = WAVE_HIGH;
gtp_output_wave(buffer,PULSE_WIDTH);
wave_data = WAVE_NULL;
gtp_output_wave(buffer,PERIOD_0 - 2 * PULSE_WIDTH);
return PERIOD_0;
}
static int gtp_byte( INT16 **buffer, UINT8 val )
{
UINT8 b;
int j,size = 0;
for (j=0;j<8;j++) {
b = (val >> j) & 1;
if (b==0) {
size += gtp_mod_0(buffer);
} else {
size += gtp_mod_1(buffer);
}
}
return size;
}
static int gtp_sync( INT16 **buffer )
{
int i;
int size = 0;
for(i=0;i<100;i++) {
if (i!=0) {
// Interbyte pause
wave_data = WAVE_NULL;
gtp_output_wave(buffer,INTERBYTE_PAUSE);
size += INTERBYTE_PAUSE;
}
size += gtp_byte(buffer,0);
}
return size;
}
static int gtp_cas_to_wav_size( const UINT8 *casdata, int caslen ) {
int size,n;
size = 0;
n = 0;
if (casdata == nullptr) return -1;
while(n<caslen) {
int block_type = casdata[n];
int block_size = casdata[n+2]*256 + casdata[n+1];
n+=5;
if (block_type==GTP_BLOCK_STANDARD) {
// Interblock pause
size += INTERBLOCK_PAUSE;
size += 100 * (PERIOD_0 * 8 + INTERBYTE_PAUSE) - INTERBYTE_PAUSE;
size += (PERIOD_0 * 8 + INTERBYTE_PAUSE) * block_size;
}
n += block_size;
}
len = caslen;
return size;
}
static int gtp_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) {
int i,size,n;
size = 0;
n = 0;
if (bytes == nullptr) return -1;
while(n<len)
{
int block_type = bytes[n];
int block_size = bytes[n+2]*256 + bytes[n+1];
n+=5;
if (block_type==GTP_BLOCK_STANDARD) {
// Interblock pause
wave_data = WAVE_NULL;
gtp_output_wave(&buffer,INTERBLOCK_PAUSE);
size += INTERBLOCK_PAUSE;
size += gtp_sync(&buffer);
for (i=0;i<block_size;i++) {
// Interbyte pause
wave_data = WAVE_NULL;
gtp_output_wave(&buffer,INTERBYTE_PAUSE);
size += INTERBYTE_PAUSE;
size += gtp_byte(&buffer,bytes[n]);
n++;
}
} else {
n += block_size;
}
}
return size;
}
static const struct CassetteLegacyWaveFiller gtp_legacy_fill_wave = {
gtp_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
0, /* chunk_samples */
gtp_cas_to_wav_size, /* chunk_sample_calc */
GTP_WAV_FREQUENCY, /* sample_frequency */
0, /* header_samples */
0 /* trailer_samples */
};
static cassette_image::error gtp_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) {
return cassette_legacy_identify( cassette, opts, >p_legacy_fill_wave );
}
static cassette_image::error gtp_cassette_load( cassette_image *cassette ) {
return cassette_legacy_construct( cassette, >p_legacy_fill_wave );
}
static const struct CassetteFormat gtp_cassette_format = {
"gtp",
gtp_cassette_identify,
gtp_cassette_load,
nullptr
};
CASSETTE_FORMATLIST_START(gtp_cassette_formats)
CASSETTE_FORMAT(gtp_cassette_format)
CASSETTE_FORMATLIST_END
|