1 
2 /*
3  *        BIN to .cas program file (NEC PC computers)
4  *
5  *        Original code name: hex2cas,(c) 2003-2007
6  *        by Takahide Matsutsuka.
7  *
8  *        MC loader by Stefano Bodrato, (c) 2013
9  *
10  *        $Id: nec.c,v 1.9 2016-06-26 00:46:55 aralbrec Exp $
11  */
12 
13 #include "appmake.h"
14 
15 static char* binname = NULL;
16 static char* crtfile = NULL;
17 static char* outfile = NULL;
18 static int origin = -1;
19 static int mode = -1;
20 static char help = 0;
21 static char audio = 0;
22 
23 char nec_fast = 0;
24 char nec_22 = 0;
25 //static char              dumb         = 0;
26 
27 /* Options that are available for this module */
28 option_t nec_options[] = {
29     { 'h', "help", "Display this help", OPT_BOOL, &help },
msxrom_exec(char * target)30     { 'b', "binfile", "Linked binary file", OPT_STR, &binname },
31     { 'c', "crt0file", "crt0 file used in linking", OPT_STR, &crtfile },
32     { 'o', "output", "Name of output file", OPT_STR, &outfile },
33     { 0, "audio", "Create also a WAV file", OPT_BOOL, &audio },
34     { 0, "fast", "Create a fast loading WAV", OPT_BOOL, &nec_fast },
35     { 0, "22", "22050hz bitrate option", OPT_BOOL, &nec_22 },
36     //    {  0,  "dumb",     "Just convert to WAV a tape file",  OPT_BOOL,  &dumb },
37     { 0, "org", "Origin of the binary", OPT_INT, &origin },
38     { 0, "mode", "0..5: 16k,32k,mk2,n,ROM,n88", OPT_INT, &mode },
39     { 0, NULL, NULL, OPT_NONE, NULL }
40 };
41 
42 /* modes */
43 enum {
44     MODE1,
45     MODE2,
46     MODE5,
47     MODEN,
48     MODEROM,
49     MODEN88,
50     MODE_QUIT
51 };
52 
53 /* A default architecture-depend file name. */
54 #define DEFAULT_ARCH_FILENAME "noname"
55 
56 /* Definitions of code segment for each mode. */
57 //#define MODE1_ADDRESS_CODESEG 0xc40f
58 //#define MODE2_ADDRESS_CODESEG 0x840f
59 //#define MODE5_ADDRESS_CODESEG 0x800f
60 #define MODE1_ADDRESS_CODESEG 0xc437
61 #define MODE2_ADDRESS_CODESEG 0x8437
62 #define MODE5_ADDRESS_CODESEG 0x8037
63 #define MODEN88_ADDRESS_CODESEG 0x0100
64 #define MODEROM_ADDRESS_CODESEG 0x4004
65 
66 /* It is a sort of a fast mode KansasCity format,          */
67 /* very close to the SC-3000                               */
68 /* four fast cycles for '1', two slow cycles for '0'       */
69 /* but the samples I've seen seem to have also an aplitude */
70 /* variation, so here it is !                              */
71 
72 extern void nec_bit(FILE* fpout, unsigned char bit)
73 {
74     int i, period0, period1;
75 
76     if (nec_fast) {
77         period1 = 8;
78         period0 = 14;
79     } else {
80         period1 = 9;
81         period0 = 21;
82     }
83 
84     if (bit) {
85         /* '1' */
86         for (i = 0; i < period1; i++)
87             fputc(0x30, fpout);
88         for (i = 0; i < period1; i++)
89             fputc(0xd0, fpout);
90         for (i = 0; i < period1; i++)
91             fputc(0x30, fpout);
92         for (i = 0; i < period1; i++)
93             fputc(0xd0, fpout);
94     } else {
95         /* '0' */
96         for (i = 0; i < (period0); i++)
97             fputc(0x10, fpout);
98         for (i = 0; i < (period0); i++)
99             fputc(0xf0, fpout);
100     }
101 }
102 
103 extern void nec_rawout(FILE* fpout, unsigned char b)
104 {
105     /* bit order is reversed ! */
106     static unsigned char c[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
107     int i;
108 
109     /* 1 start bit */
110     nec_bit(fpout, 0);
111 
112     /* byte */
113     for (i = 0; i < 8; i++)
114         nec_bit(fpout, (b & c[i]));
115 
116     /* 2 stop bits */
117     nec_bit(fpout, 1);
118     nec_bit(fpout, 1);
119 }
120 
121 // output file structure
122 // 1/ prefix (prefix_length bytes)
123 // 2/ startup (STARTUP_LENGTH bytes)
124 // 3/ a little patch for rst8 (3 bytes)
125 // 4/ codeseg
126 
127 static unsigned char prefix_p6[] = {
128     0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,
129     0xd3, 0xd3, 0x47, 0x6f, 0x00, 0x00, 0x00, 0x00,
130     0x0d, 0x84, 0x0a, 0x00, 0xa5, '&', 'H', '8',
131     '4', '0', 'F', 0x00, 0x00, 0x00, // 30 bytes so far
132 
133     // LOADER:  Here we're at pos $840f or $c40f
134     // puts the code just after itself and falls in it when finished
135     205, 0x9a, 0x25, //	call	$259a		; start tape
136     6, 5, //	ld		b,5
137     205, 0x70, 0x1a, //	call	$1a70		; read a single byte
138     254, 0xaf, //	cp		$af			; leader tone (af,af,---)
139     32, 0xf7, //	jr		nz,$f7  ; -8
140     16, 0xf7, //	djnz	$f7     ; -8
141 
142     // pos [45] and [46]
143     33, 0x37, 0x84, //	ld		hl,loc
144     // pos [48] and [49]
145     1, 0, 0, //	ld		bc,len
146     0xe5, //	push	hl			; keep entry location
147 
148     0x1e, 0, //	ld		e,0
149     205, 0x70, 0x1a, //	call	$1a70		; read a single byte
150 
151     0x57, //	ld		d,a
152     0x83, //	add		a,e
153     0x5f, //	ld		e,a
154     0x72, //	ld		(hl),d
155     0x23, //	inc		hl
156     0x0b, //	dec		bc
157     0x78, //	ld		a,b
158     0xb1, //	or		c
159     0x20, 0xf3, //	jr		nz,$f3		;-12
160 
161     205, 0xaa, 0x1a, //	call	$1aaa	; stop tape
162 
163     0xe1, //	pop		hl		; restore program entry loc
164 
165     // loader size: 40 bytes
166 
167     0, 0, 0, 0, 0, 0, 0, 0,
168     0, 0, 0, 0, 0, 0, 0, 0,
169     0, 0, 0,
170     0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, // lead for next block
171     0xAF, 0xAF, 0xAF, 0xAF, 0xAF
172 };
173 
174 static int prefix_length_p6 = 100;
175 
176 static unsigned char prefix_p88[] = {
177     0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0x64, 0x65, 0x66, 0x61, 0x6c, 0x74,
178     0x0d, 0x00, 0x0a, 0x00, 0x97, 0x20, 0xe0, 0xf1, 0x0c, 0x00, 0x01, 0x00, 0x18, 0x00, 0x14, 0x00,
179     0x41, 0xf1, 0xe0, 0x28, 0x11, 0x29, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
180     0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
181     0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
182     0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
183     0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
184     0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
185     0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
186     0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
187     0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
188     0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
189     0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
190     0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
191     0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
192     0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
193     0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
194 };
195 static int prefix_length_p88 = 0x10e;
196 
197 //  char prefix_p88[] = { 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,
198 //  		      0xd3, 0xd3, 0x47, 0x6f, 0x00, 0x00, 0x00, 0x00,
199 //        /* 0x0001 */    0x0d, 0x00, 0x0a, 0x00, 0x97, 0x20, 0xe0, 0xf1,
200 //  		      0x0c, 0x1a, 0x00, 0x00, 0x18, 0x00, 0x14, 0x00,
201 //  		      //    ~~~~~~~~~~ start address
202 //        /* 0x0011 */    0x41, 0xf1, 0xe0, 0x28, 0x11, 0x29, 0x00, 0x00,
203 //                        0x00 };  /* followed by 12 bytes long startup code */
204 //  //                       8     9    10    11    12  (here)
205 
206 unsigned char prefix_rom[] = { 'A', 'B', 0x04, 0x40 };
207 int prefix_length_rom = 0x4;
208 
209 int nec_exec(char* target)
210 {
211     char filename[FILENAME_MAX + 1];
212     char wavfile[FILENAME_MAX + 1];
213     FILE *fpin, *fpout;
214     int c, i, j;
215     int len, stage;
216     int zerocount = 0;
217 
218     unsigned char* prefix;
219     int prefix_length;
220     // int codeseg = 0;
221 
222     if (binname == NULL) {
223         return -1;
224     }
225 
226     //	if (dumb) {
227     //		strcpy(filename,binname);
228     //	} else {
229 
230     if (origin == -1) {
231         if ((origin = get_org_addr(crtfile)) == -1) {
232             fprintf(stderr, "Warning: could not get the code ORG, ORG defaults to MODE1 ($C437)\n");
233             origin = 0xC437;
234         }
235     }
236 
237     if (mode == -1) {
238         switch (origin) {
239         case MODE1_ADDRESS_CODESEG:
240             mode = MODE1;
241             break;
242         case MODE2_ADDRESS_CODESEG:
243             mode = MODE2;
244             break;
245         case MODE5_ADDRESS_CODESEG:
246             mode = MODE5;
247             break;
248         case MODEROM_ADDRESS_CODESEG:
249             mode = MODEROM;
250             break;
251         case MODEN88_ADDRESS_CODESEG:
252             mode = MODEN88;
253             break;
254         default:
255             fprintf(stderr, "Unhandled value for ORG: %i\n", origin);
256             return -1;
257         }
258     }
259     //fprintf(stderr,"Mode: %i\n",mode);
260 
261     if (strcmp(binname, filename) == 0) {
262         exit_log(1, "Input and output file names must be different\n");
263     }
264 
265     if ((fpin = fopen_bin(binname, crtfile)) == NULL) {
266         exit_log(1,"Can't open input file %s\n", binname);
267     }
268 
269     /*
270 	 *        Now we try to determine the size of the file
271 	 *        to be converted
272 	 */
273     if (fseek(fpin, 0, SEEK_END)) {
274         fclose(fpin);
275         exit_log(1,"Couldn't determine size of file\n");
276     }
277 
278     len = ftell(fpin);
279 
280     fseek(fpin, 0L, SEEK_SET);
281 
282     if (outfile == NULL) {
283         strcpy(filename, binname);
284         suffix_change(filename, ".cas");
285     } else {
286         strcpy(filename, outfile);
287     }
288 
289     if ((fpout = fopen(filename, "wb")) == NULL) {
290         fclose(fpin);
291         exit_log(1, "Can't open output file %s\n", filename);
292     }
293 
294     // MSB of BASIC RAM start address
295     switch (mode) {
296     case MODE1:
297         prefix = prefix_p6;
298         prefix_length = prefix_length_p6;
299         prefix[17] = 0xc4;
300         prefix[46] = 0xc4;
301         prefix[23] = 'C';
302         prefix[24] = '4';
303         prefix[48] = len % 256;
304         prefix[49] = len / 256;
305         // codeseg = MODE1_ADDRESS_CODESEG;
306         break;
307     case MODE5:
308         prefix = prefix_p6;
309         prefix_length = prefix_length_p6;
310         prefix[17] = 0x80;
311         prefix[46] = 0x80;
312         prefix[23] = '8';
313         prefix[24] = '0';
314         prefix[48] = len % 256;
315         prefix[49] = len / 256;
316         // codeseg = MODE5_ADDRESS_CODESEG;
317         break;
318     case MODEN88:
319         prefix = prefix_p88;
320         prefix_length = prefix_length_p88;
321         // codeseg = MODEN88_ADDRESS_CODESEG;
322         break;
323     case MODEROM:
324         prefix = prefix_rom;
325         prefix_length = prefix_length_rom;
326         // codeseg = MODEROM_ADDRESS_CODESEG;
327         break;
328     case MODE2:
329     default:
330         prefix = prefix_p6;
331         prefix_length = prefix_length_p6;
332         //prefix[23] = '8';
333         //prefix[24] = '4';
334         prefix[48] = len % 256;
335         prefix[49] = len / 256;
336         // codeseg = MODE2_ADDRESS_CODESEG;
337         break;
338     }
339     // set p6 file name
340     for (i = 0; i < 6; i++) {
341         prefix[i + 0x0a] = filename[i];
342     }
343 
344     // write prefix
345     for (i = 0; i < prefix_length; i++) {
346         fputc(prefix[i], fpout);
347     }
348 
349     /* ... M/C ...*/
350     for (i = 0; i < len; i++) {
351         c = getc(fpin);
352         fputc(c, fpout);
353     }
354 
355     if (mode != MODEROM) {
356         // write suffix
357         for (i = 0; i < 12; i++) {
358             fputc(0, fpout);
359         }
360     } else {
361         // write trailing bytes
362         for (; i < (0x8000 - MODEROM_ADDRESS_CODESEG); i++) {
363             //putc(memory[i], out);
364             fputc(0, fpout);
365         }
366     }
367 
368     fclose(fpin);
369     fclose(fpout);
370     //	}
371 
372     /* ***************************************** */
373     /*  Now, if requested, create the audio file */
374     /* ***************************************** */
375     if ((audio) || (nec_fast) || (nec_22)) {
376         if ((fpin = fopen(filename, "rb")) == NULL) {
377             exit_log(1, "Can't open file %s for wave conversion\n", filename);
378         }
379 
380         if (fseek(fpin, 0, SEEK_END)) {
381             fclose(fpin);
382             exit_log(1,"Couldn't determine size of file\n");
383         }
384         len = ftell(fpin);
385         fseek(fpin, 0, SEEK_SET);
386 
387         strcpy(wavfile, filename);
388 
389         suffix_change(wavfile, ".RAW");
390 
391         if ((fpout = fopen(wavfile, "wb")) == NULL) {
392             exit_log(1, "Can't open output raw audio file %s\n", wavfile);
393         }
394 
395         /* leading silence */
396         for (i = 0; i < 0x3000; i++)
397             fputc(0x20, fpout);
398 
399         /* leading tone */
400         for (i = 0; i < 3600; i++)
401             nec_bit(fpout, 1);
402 
403         stage = 0;
404 
405         for (i = 0; (i < len); i++) {
406             c = getc(fpin);
407             switch (stage) {
408             // BASIC Header: seek for the filename termination
409             case 0:
410                 if (c == 0) {
411                     nec_rawout(fpout, 0);
412                     stage++;
413                     for (j = 0; j < 600; j++)
414                         nec_bit(fpout, 1);
415                     c = getc(fpin);
416                 }
417                 break;
418             // BASIC Block + MC stub: seek for more than 3 zeroes in sequence
419             case 1:
420                 if ((c == 0) && (zerocount == 3)) {
421                     stage++;
422                     while ((c = getc(fpin)) == 0)
423                         zerocount++;
424                     for (j = 3; j <= zerocount; j++)
425                         nec_rawout(fpout, 0);
426                     zerocount = 0; // Not necessary
427                     for (j = 0; j < 600; j++)
428                         nec_bit(fpout, 1);
429                 }
430                 break;
431             default:
432                 break;
433                 //fprintf(stderr,"Problems",origin);
434                 //return -1;
435             }
436             nec_rawout(fpout, c);
437             if (c)
438                 zerocount = 0;
439             else
440                 zerocount++;
441         }
442 
443         // tail
444         for (j = 0; j < 200; j++)
445             nec_bit(fpout, 1);
446 
447         /* Data blocks */
448         //while (ftell(fpin) < len) {
449         /* leader tone (3600 records of bit 1) */
450         //	for (i=0; i < 3600; i++)
451         //		nec_bit (fpout,1);
452         /* data block */
453         //	blocklen = (getc(fpin) + 256 * getc(fpin));
454         //	for (i=0; (i < blocklen); i++) {
455         //		c=getc(fpin);
456         //		nec_rawout(fpout,c);
457         //	}
458         //}
459 
460         /* trailing silence */
461         for (i = 0; i < 0x10000; i++)
462             fputc(0x20, fpout);
463 
464         fclose(fpin);
465         fclose(fpout);
466 
467         /* Now complete with the WAV header */
468 		if (nec_22)
469 			raw2wav_22k(wavfile,2);
470 		else
471 			raw2wav(wavfile);
472     }
473 
474     exit(0);
475 }
476