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