1 
2 /*
3  *      Sorcerer Exidy, MicroBee and Excalibur 64 audio cassette formats
4  *      Kansas City Standard (DGOS variants)
5  *
6  *      $Id: sorcerer.c $
7  */
8 
9 
10 #include "appmake.h"
11 
12 
13 
14 static char             *binname      = NULL;
15 static char             *crtfile      = NULL;
16 static char             *outfile      = NULL;
17 static char             *blockname    = NULL;
18 static int               origin       = -1;
19 static char              audio        = 0;
20 static char              fast         = 0;
21 static char              khz_22       = 0;
22 static char              bps300       = 0;
23 static char              bee          = 0;
24 static char              excalibur    = 0;
25 static char              bee1200      = 0;
26 static char              dumb         = 0;
27 static char              loud         = 0;
28 static char              help         = 0;
29 
30 static char              bit_state    = 0;
31 static uint8_t           h_lvl;
32 static uint8_t           l_lvl;
33 static uint8_t           sorcerer_h_lvl;
34 static uint8_t           sorcerer_l_lvl;
35 
36 static unsigned char     parity;
37 
38 
39 /* Options that are available for this module */
40 option_t sorcerer_options[] = {
41     { 'h', "help",      "Display this help",          OPT_BOOL,  &help},
42     { 'b', "binfile",   "Linked binary file",         OPT_STR,   &binname },
43     { 'c', "crt0file",  "crt0 file used in linking",  OPT_STR,   &crtfile },
44     { 'o', "output",    "Name of output file",        OPT_STR,   &outfile },
45     {  0,  "audio",     "Create also a WAV file",     OPT_BOOL,  &audio },
46     {  0,  "fast",      "Tweak the audio tones to run a bit faster",  OPT_BOOL,  &fast },
47     {  0,  "22",        "22050hz bitrate option",     OPT_BOOL,  &khz_22 },
48     {  0,  "300bps",    "300 baud mode instead than 1200",  OPT_BOOL,  &bps300 },
49     {  0,  "bee",       "MicroBee type header",       OPT_BOOL,  &bee },
50     {  0,  "excalibur", "Excalibur64 type header",    OPT_BOOL,  &excalibur },
51     {  0,  "dumb",      "Just convert to WAV a tape file",  OPT_BOOL,  &dumb },
52     {  0,  "loud",      "Louder audio volume",        OPT_BOOL,  &loud },
53     {  0 , "org",       "Origin of the binary",       OPT_INT,   &origin },
54     {  0 , "blockname", "Name of the code block in tap file", OPT_STR, &blockname},
55     {  0,  NULL,       NULL,                          OPT_NONE,  NULL }
56 };
57 
58 /* two fast cycles for '0', two slow cycles for '1' */
59 
sorcerer_bit(FILE * fpout,unsigned char bit)60 void sorcerer_bit(FILE* fpout, unsigned char bit)
61 {
62     int i, j, period0, period1;
63 
64     /* Most of the "fast" tricks wouldn't work on the Sorcerer (HW/clock driven tape interface) */
65     if (bps300 || bee) {
66         if (fast && bee) {
67             period1 = 7;
68             period0 = 16;
69         } else {
70             period1 = 9;
71             period0 = 18;
72         }
73     } else {
74         period1 = 18;
75         period0 = 37;
76     }
77 
78 	if (excalibur) {
79 		if (fast) {
80 			period1 = 7;
81 			period0 = 16;
82 		} else {
83 			period1 = 9;
84 			period0 = 18;
85 		}
86 	}
87 
88     /* phase inversion (Sorcerer only) */
89     if (bit_state) {
90         h_lvl = sorcerer_h_lvl;
91         l_lvl = sorcerer_l_lvl;
92     } else {
93         h_lvl = sorcerer_l_lvl;
94         l_lvl = sorcerer_h_lvl;
95     }
96 
97     if (bit) {
98         /* '1' */
99         if (bps300) {
100             for (j = 0; j < 8; j++) {
101                 for (i = 0; i < period1; i++)
102                     fputc(h_lvl, fpout);
103                 for (i = 0; i < period1; i++)
104                     fputc(l_lvl, fpout);
105             }
106         } else {
107             if (bee || excalibur) {
108                 for (j = 0; j < 2; j++) {
109                     for (i = 0; i < period1; i++)
110                         fputc(h_lvl, fpout);
111                     for (i = 0; i < period1; i++)
112                         fputc(l_lvl, fpout);
113                 }
114             } else {
115                 for (i = 0; i < period1; i++)
116                     fputc(h_lvl, fpout);
117                 for (i = 0; i < period1; i++)
118                     fputc(l_lvl, fpout);
119             }
120         }
121     } else {
122         /* '0' */
123         if (bps300) {
124             for (j = 0; j < 4; j++) {
125                 for (i = 0; i < period0; i++)
126                     fputc(h_lvl, fpout);
127                 for (i = 0; i < period0; i++)
128                     fputc(l_lvl, fpout);
129             }
130         } else {
131             if (bee || excalibur) {
132                 for (i = 0; i < period0; i++)
133                     fputc(h_lvl, fpout);
134                 for (i = 0; i < period0; i++)
135                     fputc(l_lvl, fpout);
136             } else {
137                 for (i = 0; i < (period0); i++)
138                     fputc(h_lvl, fpout);
139                 /* toggle phase */
140                 bit_state = !bit_state;
141             }
142         }
143     }
144 }
145 
sorcerer_rawout(FILE * fpout,unsigned char b)146 void sorcerer_rawout(FILE* fpout, unsigned char b)
147 {
148     /* bit order is reversed ! */
149     static unsigned char c[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
150     int i;
151 
152     /* Start bit */
153     sorcerer_bit(fpout, 0);
154 
155     /* byte */
156     for (i = 0; i < 8; i++)
157         sorcerer_bit(fpout, (b & c[i]));
158 
159     /* Stop bits */
160     sorcerer_bit(fpout, 1);
161     sorcerer_bit(fpout, 1);
162 }
163 
sorcerer_tone(FILE * fpout)164 void sorcerer_tone(FILE* fpout)
165 {
166     int i;
167 
168     if (fast) {
169         for (i = 0; (i < 1500); i++) /* pre-leader short extra delay */
170             sorcerer_bit(fpout, 1);
171     } else {
172         for (i = 0; (i < 3500); i++) /* pre-leader short extra delay */
173             sorcerer_bit(fpout, 1);
174     }
175 }
176 
177 /*
178  * Execution starts here
179  */
180 
sorcerer_exec(char * target)181 int sorcerer_exec(char* target)
182 {
183     char filename[FILENAME_MAX + 1];
184     char wavfile[FILENAME_MAX + 1];
185     char name[7];
186     FILE* fpin;
187     FILE* fpout;
188     int len;
189     long pos;
190     int c, i, j;
191     int leadinlength;
192 
193     if (help)
194         return -1;
195 
196     if (binname == NULL || (!dumb && (crtfile == NULL && origin == -1))) {
197         return -1;
198     }
199 
200     if (loud) {
201         sorcerer_h_lvl = 0xFF;
202         sorcerer_l_lvl = 0;
203     } else {
204         sorcerer_h_lvl = 0xe0;
205         sorcerer_l_lvl = 0x20;
206     }
207 
208     if (bee)
209         leadinlength = 64;
210     else
211         leadinlength = 100;
212 
213     if (dumb) {
214         strcpy(filename, binname);
215 
216     } else {
217         if (outfile == NULL) {
218             strcpy(filename, binname);
219             suffix_change(filename, ".srr");
220         } else {
221             strcpy(filename, outfile);
222         }
223 
224         if (blockname == NULL)
225             blockname = zbasename(binname);
226 
227         if (strcmp(binname, filename) == 0) {
228             exit_log(1, "Input and output file names must be different\n");
229         }
230 
231         if (origin != -1) {
232             pos = origin;
233         } else {
234             if ((pos = get_org_addr(crtfile)) == -1) {
235                 exit_log(1,"Could not find parameter ZORG (not z88dk compiled?)\n");
236             }
237         }
238 
239         if ((fpin = fopen_bin(binname, crtfile)) == NULL) {
240             exit_log(1, "Can't open input file %s\n", binname);
241         }
242 
243         if (fseek(fpin, 0, SEEK_END)) {
244             fclose(fpin);
245             exit_log(1,"Couldn't determine size of file\n");
246         }
247 
248         len = ftell(fpin);
249 
250         fseek(fpin, 0L, SEEK_SET);
251 
252         if ((fpout = fopen(filename, "wb")) == NULL) {
253             fclose(fpin);
254             exit_log(1,"Can't open output file\n");
255         }
256 
257         /* HEADER */
258 
259         /* Leader (DGOS' standard padding sequence) */
260         for (i = 0; (i < leadinlength); i++)
261             writebyte_pk(0, fpout, &parity);
262 
263 		/* leading SOH */
264         if (excalibur)
265 			writebyte_pk(0xa5, fpout, &parity);
266 		else
267 			writebyte_pk(1, fpout, &parity);
268 
269         parity = 0;
270 
271         /* Deal with the filename */
272         snprintf(name, sizeof(name), "%-*s", (int) sizeof(name)-1, blockname);
273 
274         if (excalibur) {
275 			writebyte_pk(0x2f, fpout, &parity); /* File type (0x2f for M/C block, 0xd3 for BASIC program) */
276 			writebyte_pk(0x2f, fpout, &parity); /* File type (0x2f for M/C block, 0xd3 for BASIC program) */
277 			writebyte_pk(0x2f, fpout, &parity); /* File type (0x2f for M/C block, 0xd3 for BASIC program) */
278 			/* File name */
279 			writebyte_pk(toupper(name[0]), fpout, &parity);
280 			writebyte_pk(toupper(name[1]), fpout, &parity);
281 			writebyte_pk(toupper(name[2]), fpout, &parity);
282 			writebyte_pk(0x5c, fpout, &parity);	/* Filename end marker */
283 			/* Program Location */
284 			writebyte_pk(pos/256, fpout, &parity);  /* MSB */
285 			writebyte_pk(pos%256, fpout, &parity);  /* LSB */
286 			/* Program Length */
287 			writebyte_pk(len/256, fpout, &parity);  /* MSB */
288 			writebyte_pk(len%256, fpout, &parity);  /* LSB */
289 		} else {
290 			if (bee) {
291 				for (i = 0; i <= 5; i++)
292 					writebyte_pk(name[i], fpout, &parity);
293 				writebyte_pk('M', fpout, &parity); /* File type (LOADM) */
294 			} else {
295 				for (i = 0; i <= 4; i++)
296 					writebyte_pk(name[i], fpout, &parity);
297 				writebyte_pk(0x55, fpout, &parity); /* File Header Identification */
298 				writebyte_pk(0, fpout, &parity); /* File type (bit 7 set = never autorun) */
299 			}
300 			writeword_pk(len, fpout, &parity); /* Program File Length */
301 			writeword_pk(pos, fpout, &parity); /* Program Location */
302 			writeword_pk(pos, fpout, &parity); /* GO Address: pos for autorun when LOADG (Sorcerer) / or LOADM (MicroBee w/FL_EXEC set to 0xff) */
303 
304 			if (bee) {
305 				if (!bps300) {
306 					bee1200 = 1; /* Speed (MicroBee at 1200 bps) */
307 					bps300 = 1; /* MicroBee HEADER is always at 300 bps */
308 				}
309 				writebyte_pk(bee1200, fpout, &parity);
310 				writebyte_pk(255, fpout, &parity); /* Autostart (FL_EXEC) */
311 			} else {
312 				writebyte_pk(0, fpout, &parity); /* Spare */
313 				writebyte_pk(0, fpout, &parity); /* ... */
314 			}
315 
316 			writebyte_pk(0, fpout, &parity); /* Spare */
317 
318 			writebyte_p(parity, fpout, &parity); /* Checksum for header and middle blocks*/
319 
320 			if (!bee) {
321 				/* (Sorcerer only) - Data block leader (DGOS' standard padding sequence) */
322 				for (j = 0; (j < leadinlength); j++)
323 					writebyte_pk(0, fpout, &parity);
324 				writebyte_pk(1, fpout, &parity); /* leading SOH */
325 				parity = 0;
326 			}
327 		}
328 
329 
330 
331         /* PROGRAM BLOCK */
332 
333         for (i = 0; i < len; i++) {
334             if (i > 0 && (i % 256 == 0))
335                 writebyte_p(parity, fpout, &parity); /* Checksum for header and middle blocks*/
336             c = getc(fpin);
337             writebyte_pk(c, fpout, &parity);
338         }
339 
340         if ((i - 1) % 256 != 0)
341             writebyte_p(parity, fpout, &parity); /* Checksum for header and middle blocks*/
342 
343         /* Trailing sequence (DGOS' standard padding sequence) */
344         for (j = 0; (j < leadinlength); j++)
345             writebyte_pk(0, fpout, &parity);
346         writebyte_pk(1, fpout, &parity); /* byte tail */
347 
348         fclose(fpin);
349         fclose(fpout);
350     }
351 
352     /* ***************************************** */
353     /*  Now, if requested, create the audio file */
354     /* ***************************************** */
355     if ((audio) || (fast) || (bps300) || (khz_22) || (loud)) {
356         if ((fpin = fopen(filename, "rb")) == NULL) {
357             exit_log(1,"Can't open file %s for wave conversion\n", filename);
358         }
359 
360         if (fseek(fpin, 0, SEEK_END)) {
361             fclose(fpin);
362             exit_log(1,"Couldn't determine size of file\n");
363         }
364         len = ftell(fpin);
365         fseek(fpin, 0, SEEK_SET);
366 
367         strcpy(wavfile, filename);
368 
369         suffix_change(wavfile, ".RAW");
370 
371         if ((fpout = fopen(wavfile, "wb")) == NULL) {
372             exit_log(1, "Can't open output raw audio file %s\n", wavfile);
373         }
374 
375         /* leading silence and tone*/
376         for (i = 0; i < 0x1000; i++)
377             fputc(0x80, fpout);
378         sorcerer_tone(fpout);
379 
380         /* Copy the header */
381         if (dumb)
382 			printf("\nInfo: Program Name found in header: ");
383 
384         j = 0;          // Prevent warning "may be used uninitialized"
385 
386 		for (i = 0; (i < (leadinlength + 18)); i++) {
387 			c = getc(fpin);
388 			if (dumb) {
389 				if (excalibur) {
390 					if (i > (leadinlength+3) && i < (leadinlength + 6))
391 						printf("%c", c);
392 					if ((i == (leadinlength + 7) || i == (leadinlength + 9)))
393 						j = c;
394 					if (i == (leadinlength + 8))
395 						printf("\nInfo: Start location $%x", c * 256 + j);
396 					if (i == (leadinlength + 10))
397 						printf("\nInfo: File Size $%x", c + j*256);
398 				} else {
399 					if (i > leadinlength && i < (leadinlength + 6))
400 						printf("%c", c);
401 					if (i == (leadinlength + 7))
402 						printf("\nInfo: File type $%x", c);
403 					if ((i == (leadinlength + 8) || i == (leadinlength + 10) || i == (leadinlength + 12)))
404 						j = c;
405 					if (i == (leadinlength + 9))
406 						printf("\nInfo: File Size $%x", c * 256 + j);
407 					if (i == (leadinlength + 11))
408 						printf("\nInfo: Start location $%x", c * 256 + j);
409 					if (i == (leadinlength + 13))
410 						printf("\nInfo: Go address $%x", c * 256 + j);
411 				}
412 			}
413 			sorcerer_rawout(fpout, c);
414 		}
415 
416 		len = len - 18 - leadinlength;
417 
418         if ((bee) && (bee1200))
419             bps300 = 0;
420 
421         /* program block */
422         if (len > 0) {
423             for (i = 0; i < len; i++) {
424                 if ((bee) && (bee1200) && (i == (len - leadinlength)))
425                     bps300 = 1;
426                 c = getc(fpin);
427                 sorcerer_rawout(fpout, c);
428             }
429         }
430 
431         /* trailing tone and silence (probably not necessary) */
432         /*
433         sorcerer_bit(fpout,0);
434         sorcerer_tone(fpout);
435         for (i=0; i < 0x1000; i++)
436             fputc(0x80, fpout);
437 */
438 
439         fclose(fpin);
440         fclose(fpout);
441 
442         /* Now complete with the WAV header */
443 		if (khz_22)
444 			raw2wav_22k(wavfile,2);
445 		else
446 			raw2wav(wavfile);
447 
448     } /* END of WAV CONVERSION BLOCK */
449 
450     return 0;
451 }
452