1 /*
2  *	Spectravideo SVI Cassette file
3  *
4  *	BLOAD "CAS:",R
5  *
6  *	By Stefano Bodrato
7  *
8  *	$Id: svi.c $
9  */
10 
11 #include "appmake.h"
12 
13 
14 static char             *binname      = NULL;
15 static char             *crtfile      = NULL;
16 static char             *outfile      = NULL;
17 static int               origin       = -1;
18 static char              audio        = 0;
19 static char              c_disk       = 0;
20 static char              fast         = 0;
21 static char              khz_22       = 0;
22 static char              dumb         = 0;
23 static char              loud         = 0;
24 static char              help         = 0;
25 
26 static unsigned char              h_lvl;
27 static unsigned char              l_lvl;
28 
29 
30 static int	create_disk();
31 
32 /* Options that are available for this module */
33 option_t svi_options[] = {
34     { 'h', "help",     "Display this help",          OPT_BOOL,  &help},
35     { 'b', "binfile",  "Linked binary file",         OPT_STR,   &binname },
36     { 'c', "crt0file", "crt0 file used in linking",  OPT_STR,   &crtfile },
37     { 'o', "output",   "Name of output file",        OPT_STR,   &outfile },
38     {  0 , "org",      "Origin of the binary",       OPT_INT,   &origin },
39     {  0,  "audio",    "Create also a WAV file",     OPT_BOOL,  &audio },
px_exec(char * target)40     {  0,  "fast",     "Tweak the audio tones to run a bit faster",  OPT_BOOL,  &fast },
41     {  0,  "22",       "22050hz bitrate option",     OPT_BOOL,  &khz_22 },
42     {  0,  "dumb",     "Just convert to WAV a tape file",  OPT_BOOL,  &dumb },
43     {  0,  "loud",     "Louder audio volume",        OPT_BOOL,  &loud },
44     {  0,  "disk",     "Create a .dsk image",        OPT_BOOL,  &c_disk },
45     {  0,  NULL,       NULL,                         OPT_NONE,  NULL }
46 };
47 
48 /* two fast cycles for '0', two slow cycles for '1' */
49 
50 void sv_bit(FILE* fpout, int bit, char tweak)
51 {
52     int i, period0, period1, period1lo;
53 
54     if (fast) {
55         period1 = 14;
56         period0 = 9;
57     } else {
58         period1 = 18;
59         period0 = 9;
60     }
61 
62     if (fast)
63         period1lo = period1 + 3;
64     else
65         period1lo = period1 + 1;
66 
67     if (tweak)
68         period1 += 3;
69 
70     if (bit) {
71         /* '1' */
72         for (i = 0; i < period0; i++)
73             fputc(h_lvl, fpout);
74         for (i = 0; i < period0; i++)
75             fputc(l_lvl, fpout);
76     } else {
77         /* '0' */
78         for (i = 0; i < period1; i++)
79             fputc(h_lvl, fpout);
80         for (i = 0; i < period1lo; i++)
81             fputc(l_lvl, fpout);
82     }
83 }
84 
85 void sv_rawout(FILE* fpout, unsigned char b)
86 {
87     static unsigned char c[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
88     int i;
89 
90     /* Start bit */
91     sv_bit(fpout, 1, 1);
92 
93     /* byte */
94     for (i = 0; i < 8; i++)
95         sv_bit(fpout, (b & c[i]), 0);
96 }
97 
98 void sv_tone(FILE* fpout)
99 {
100     int i;
101 
102     for (i = 0; (i < 1600); i++)
103         /* workaround (64 bit gcc bug ?) */
104         sv_bit(fpout, 0, (i & 4) ? 1 : 0);
105     sv_bit(fpout, 1, 1);
106 }
107 
108 /* 10101....0101010101111111 */
109 void headtune(FILE* fp)
110 {
111     int i;
112 
113     for (i = 0; i < 16; i++)
114         writebyte(85, fp);
115     writebyte(127, fp);
116 }
117 
118 int svi_exec(char* target)
119 {
120     char filename[FILENAME_MAX + 1];
121     char wavfile[FILENAME_MAX + 1];
122     char name[11];
123     FILE *fpin, *fpout;
124     int c;
125     int i;
126     int pos;
127     int len;
128 
129     if (help)
130         return -1;
131 
132     if (binname == NULL || (!dumb && (crtfile == NULL && origin == -1))) {
133         return -1;
134     }
135 
136     if ( c_disk ) {
137         return create_disk();
138     }
139 
140     if (origin != -1) {
141         pos = origin;
142     } else {
143         if ((pos = get_org_addr(crtfile)) == -1) {
144             exit_log(1,"Could not find parameter ZORG (not z88dk compiled?)\n");
145         }
146     }
147 
148     if (loud) {
149         h_lvl = 0xFF;
150         l_lvl = 0;
151     } else {
152         h_lvl = 0xe0;
153         l_lvl = 0x20;
154     }
155 
156     if (dumb) {
157         strcpy(filename, binname);
158     } else {
159 
160         if (outfile == NULL) {
161             strcpy(filename, binname);
162             suffix_change(filename, ".cas");
163         } else {
164             strcpy(filename, outfile);
165         }
166 
167         if (strcmp(binname, filename) == 0) {
168             exit_log(1, "Input and output file names must be different\n");
169         }
170 
171         if ((fpin = fopen_bin(binname, crtfile)) == NULL) {
172             exit_log(1,"Can't open input file\n");
173         }
174 
175         /*
176 	 *	Now we try to determine the size of the file
177 	 *	to be converted
178 	 */
179         if (fseek(fpin, 0, SEEK_END)) {
180             fclose(fpin);
181             exit_log(1,"Couldn't determine size of file\n");
182         }
183 
184         len = ftell(fpin);
185 
186         fseek(fpin, 0L, SEEK_SET);
187 
188         if ((fpout = fopen(filename, "wb")) == NULL) {
189             printf("Can't open output file\n");
190             exit(1);
191         }
192 
193         /* Write out the header file */
194         headtune(fpout);
195         for (i = 0; i < 10; i++)
196             writebyte(208, fpout);
197 
198         /* Deal with the filename */
199         snprintf(name, sizeof(name), "%-*s", (int) sizeof(name)-1, binname);
200 
201         for (i = 0; i < 6; i++)
202             writebyte(name[i], fpout);
203 
204         writeword(0, fpout);
205 
206         /* Now, the body */
207         headtune(fpout);
208         writeword(pos, fpout); /* Start Address */
209         writeword(pos + len + 1, fpout); /* End Address */
210         writeword(pos, fpout); /* Call Address */
211 
212         /* (58 bytes written so far...) */
213 
214         /* We append the binary file */
215 
216         for (i = 0; i < len; i++) {
217             c = getc(fpin);
218             writebyte(c, fpout);
219         }
220 
221         /* Append some zeros, just to be sure not to get an error*/
222 
223         for (i = 0; i < 16384; i++)
224             writebyte(0, fpout);
225 
226         fclose(fpin);
227         fclose(fpout);
228     }
229 
230     /* ***************************************** */
231     /*  Now, if requested, create the audio file */
232     /* ***************************************** */
233     if ((audio) || (fast) || (khz_22) || (loud)) {
234         if ((fpin = fopen(filename, "rb")) == NULL) {
235             exit_log(1, "Can't open file %s for wave conversion\n", filename);
236         }
237 
238         if (fseek(fpin, 0, SEEK_END)) {
239             fclose(fpin);
240             exit_log(1,"Couldn't determine size of file\n");
241         }
242         len = ftell(fpin);
243         fseek(fpin, 0, SEEK_SET);
244 
245         strcpy(wavfile, filename);
246 
247         suffix_change(wavfile, ".RAW");
248 
249         if ((fpout = fopen(wavfile, "wb")) == NULL) {
250             exit_log(1, "Can't open output raw audio file %s\n", wavfile);
251         }
252 
253         /* leading silence and tone*/
254         for (i = 0; i < 0x3000; i++)
255             fputc(0x80, fpout);
256         sv_tone(fpout);
257 
258         /* Write $7f */
259         sv_bit(fpout, 0, 1);
260         for (i = 0; i < 7; i++)
261             sv_bit(fpout, 1, 1);
262 
263         /* Skip the tone leader bytes */
264         for (i = 0; (i < 17); i++)
265             c = getc(fpin);
266         len -= 17;
267 
268         /* Copy the header */
269         if (dumb)
270             printf("\nInfo: Program Name found in header: ");
271         for (i = 0; (i < 18); i++) {
272             c = getc(fpin);
273             if (dumb && i > 10 && i < 17)
274                 printf("%c", c);
275             sv_rawout(fpout, c);
276         }
277 
278         len -= 18;
279 
280         /* leading silence and tone*/
281         for (i = 0; i < 0x8000; i++)
282             fputc(h_lvl, fpout);
283         sv_tone(fpout);
284 
285         /* Write $7f */
286         sv_bit(fpout, 0, 1);
287         for (i = 0; i < 7; i++)
288             sv_bit(fpout, 1, 1);
289 
290         /* Skip the tone leader bytes */
291         for (i = 0; (i < 17); i++)
292             c = getc(fpin);
293         len -= 17;
294 
295         /* program block */
296         if (len > 0) {
297             for (i = 0; i < len; i++) {
298                 c = getc(fpin);
299                 sv_rawout(fpout, c);
300             }
301         }
302 
303         fclose(fpin);
304         fclose(fpout);
305 
306         /* Now complete with the WAV header */
307 		if (khz_22)
308 			raw2wav_22k(wavfile,2);
309 		else
310 			raw2wav(wavfile);
311 
312     } /* END of WAV CONVERSION BLOCK */
313 
314     return 0;
315 }
316 
317 static uint8_t    sectorbuf[256];
318 
319 static int create_disk()
320 {
321     char    disc_name[FILENAME_MAX+1];
322     char    bootname[FILENAME_MAX+1];
323     FILE   *fpin,*fpout;
324     int track, sector;
325     size_t binlen;
326 
327     if ( outfile == NULL ) {
328         strcpy(disc_name,binname);
329         suffix_change(disc_name,".svi");
330     } else {
331         strcpy(disc_name,outfile);
332     }
333 
334 
335     strcpy(bootname, binname);
336     suffix_change(bootname, "_BOOTSTRAP.bin");
337 
338     if ( (fpin=fopen_bin(bootname, crtfile) ) == NULL ) {
339         exit_log(1,"Can't open input file %s\n",bootname);
340     }
341     if ( fseek(fpin,0,SEEK_END) ) {
342         fclose(fpin);
343         exit_log(1,"Couldn't determine size of file\n");
344     }
345     binlen = ftell(fpin);
346     fseek(fpin,0L,SEEK_SET);
347 
348     if ( binlen > 128 ) {
349         exit_log(1, "Bootstrap has length %d > 128", binlen);
350     }
351     memset(sectorbuf, 0, sizeof(sectorbuf));
352     if (128 != fread(sectorbuf, 1, 128, fpin)) { fclose(fpin); exit_log(1, "Could not read required data from <%s>\n",bootname); }
353     fclose(fpin);
354 
355     if ( (fpout = fopen(disc_name, "wb")) == NULL ) {
356         exit_log(1,"Can't open output file %s\n",disc_name);
357     }
358 
359     if ((fpin = fopen_bin(binname, crtfile)) == NULL) {
360         exit_log(1,"Can't open input file %s\n",bootname);
361     }
362 
363     // First track has 18 sectors of 128 bytes
364     fwrite(sectorbuf, 1, 128, fpout);	// Track 0, sector 0
365 
366     // And now we're on track 1, we have 17 sectors of 256 bytes
367     for ( track = 0; track < 40; track++ ) {
368         for ( sector = (track == 0 ? 1 : 0); sector < (track == 0 ? 18 : 17); sector++ ) {
369             int size = track == 0 ? 128 : 256;
370             memset(sectorbuf, 0, sizeof(sectorbuf));
371             if ( !feof(fpin) ) {
372                 if (size != fread(sectorbuf, 1, size, fpin)) { fclose(fpin); exit_log(1, "Could not read required data from <%s>\n",binname); }
373             }
374             fwrite(sectorbuf, 1, size, fpout);
375         }
376     }
377     fclose(fpout);
378     fclose(fpin);
379 
380     return 0;
381 
382 }
383