asm10k.c revision 10913:1d1ed05d0838
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * Assembler for Emu10k1
29 */
30/*
31 * Copyright (C) 4Front Technologies 1996-2008.
32 */
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <unistd.h>
37#include <fcntl.h>
38#include <string.h>
39#include <stdarg.h>
40#include <ctype.h>
41#include <sys/param.h>
42
43#include <sys/soundcard.h>
44
45#define	MAX_GPR	256
46#define	MAX_GPR_PARMS	60
47#define	MAX_CONST_PARMS	128
48#define	GPR_NAME_SIZE 32
49
50typedef struct {
51	char name[GPR_NAME_SIZE];
52	unsigned int num;
53	int type;
54	int def;
55} gpr_t;
56
57typedef struct {
58	unsigned int gpr;
59	unsigned int value;
60} const_t;
61
62typedef struct {
63	unsigned int ngpr;
64
65	gpr_t gpr[MAX_GPR_PARMS];
66} gpr_info;
67
68typedef struct {
69	unsigned int nconst;
70
71	const_t consts[MAX_CONST_PARMS];
72} const_info;
73
74typedef struct {
75	unsigned int code[1024];
76	gpr_info parms;
77	const_info consts;
78	int	ninit;
79	struct {
80		uint32_t	gpr;
81		uint32_t	value;
82		char		name[GPR_NAME_SIZE];
83	} init[MAX_GPR];
84} emu10k1_file;
85
86#define	MAX_NAME	64
87#define	MAX_SYMBOLS	1024
88
89static int parms_only = 0;
90static int is_audigy = 0;
91static int verbose = 0;
92
93static int gpr_base = 0x100;
94static int input_base = 0x10;
95static int output_base = 0x20;
96
97static char *progname;
98
99typedef struct {
100	char name[MAX_NAME];
101	int type;
102#define	SY_DUMMY	0
103#define	SY_GPR		1
104#define	SY_INPUT	2
105#define	SY_OUTPUT	3
106#define	SY_CONST	4
107#define	SY_FX		5
108#define	SY_ACCUM	6
109#define	SY_PARM		7
110	int arg;
111} sym_t;
112
113typedef struct {
114	char *name;
115	int opcode;
116} instruction_t;
117
118static char remarks[2048] = "";
119static char *banner =
120	"/*\n"
121	" * Note: This file was automatically generated by %s\n"
122	" * on %s.\n"
123	" */\n";
124
125/*
126 * Instructions.  Each instruction takes 4 arguments, R, A, X, and Y.
127 */
128static instruction_t instructions[] = {
129	{ "MACS",	0x0},	/* R = A + (X * Y >> 31); saturation */
130	{ "MACS1",	0x1},	/* R = A + (-X * Y >> 31); saturation */
131	{ "MACW",	0x2},	/* R = A + (X * Y >> 31); wraparound */
132	{ "MACW1",	0x3},	/* R = A + (-X * Y >> 31); wraparound */
133	{ "MACINTS",	0x4},	/* R = A + (X * Y); saturation */
134	{ "MACINTW",	0x5},	/* R = A + (X * Y); wraparound */
135	{ "SUM",	0x6},	/* R = A + X + Y; saturation */
136	{ "ACC3",	0x6},	/* R = A + X + Y; saturation */
137	{ "MACMV",	0x7},	/* R = A, acc += X * Y >> 31 */
138	{ "ANDXOR",	0x8},	/* R = (A & X) ^ Y */
139	{ "TSTNEG",	0x9},	/* R = (A >= Y) ? X : ~X */
140	{ "LIMIT",	0xa},	/* R = (A >= Y) ? X : Y */
141	{ "LIMIT1",	0xb},	/* R = (A < Y) ? X : Y */
142	{ "LOG",	0xc},	/* R = ... (log?) */
143	{ "EXP",	0xd},	/* R = ... (exp?) */
144	{ "INTERP",	0xe},	/* R = A + (X * (Y - A) >> 31) */
145	{ "SKIP",	0xf},	/* R, CCR, CC_TEST, COUNT */
146	{ NULL, 0}
147};
148
149#define	CHECK_COUNT(tokens, cnt, mincnt, maxcnt)			\
150	if (cnt < mincnt) {						\
151		error("Too few parameters for '%s' (have %d, min %d)",	\
152		    tokens[0], cnt - 1, mincnt - 1);			\
153		return;							\
154	}								\
155	if (cnt > maxcnt) {						\
156		error("Too many parameters for '%s' (have %d, max %d)",	\
157		    tokens[0], cnt - 1, maxcnt - 1);			\
158		return;							\
159	}
160
161static sym_t symtab[MAX_SYMBOLS];
162static int nsyms = 0;
163
164static int lineno = 0, errors = 0;
165static emu10k1_file fle;
166static int pc;
167
168static int ngpr = 0;
169static char *infile;
170
171static int
172getline(FILE *input, char **tokens)
173{
174	char *s, *ls;
175	static char *stmt = NULL, *lasts = NULL;
176	static char line[4096];
177	int cnt, tokcnt;
178
179	for (;;) {
180
181		if (stmt == NULL) {
182			if (fgets(line, sizeof (line), input) == NULL)
183				return (-1);
184			lineno++;
185
186			/*
187			 * Special handling for .' comments.  We use
188			 * .' as a keyword to ensure that entire
189			 * comment makes it through the C preprocessor
190			 * unmolested.  We also need to make sure *we*
191			 * don't molest it either.  The comment will
192			 * be exported to any resulting header,
193			 * allowing us to pass through copyright and
194			 * other information from the source file to
195			 * the resulting header.
196			 */
197			s = line;
198			s += strspn(s, " \t");
199			if ((strncmp(s, ".'", 2) == 0) &&
200			    (strchr(" \t\n", s[2]) != NULL)) {
201				/* chop off trailing new line */
202				(void) strtok(line, "\n");
203				tokens[0] = s;
204				s += 2;
205				s += strspn(s, " \t");
206				if ((s[0] == '\'') &&
207				    (s[strlen(s) - 1] == '\'')) {
208					s[strlen(s) - 1] = 0;
209					s++;
210				}
211				tokens[1] = s;
212				tokens[0][2] = 0;
213				tokens[2] = NULL;
214				stmt = NULL;
215				return (strlen(tokens[1]) ? 2 : 1);
216			}
217
218			/* strip off any C++ style comments that CPP missed */
219			if ((s = strstr(line, "//")) != NULL) {
220				*s = NULL;
221			}
222			stmt = strtok_r(line, ";\n", &lasts);
223		} else {
224			stmt = strtok_r(NULL, ";\n", &lasts);
225		}
226
227		if (stmt != NULL) {
228			break;
229		}
230	}
231
232	/*
233	 * Ok, we have a statement, lets tokenize it.  For
234	 * simplicities sake we convert "OPCODE(arg1, arg2)" into
235	 * "OPCODE arg1 arg2".  This means that commas and parens are
236	 * treated as whitespace.  This can lead to some really messed
237	 * up syntaxes that get assembled properly (such as nested
238	 * calls, empty arguments, etc.)  Hopefully people don't abuse
239	 * this.
240	 */
241	ls = NULL;
242	s = strtok_r(stmt, " \t\n(),", &ls);
243	cnt = 0;
244	tokcnt = 0;
245	while (cnt < 10) {
246		tokens[cnt++] = s;
247		if (s != NULL) {
248			tokcnt++;
249			s = strtok_r(NULL, " \t\n(),", &ls);
250		}
251	}
252	return (tokcnt);
253}
254
255static void
256error(char *msg, ...)
257{
258	va_list va;
259	char msgbuf[1024];
260
261	va_start(va, msg);
262	(void) vsnprintf(msgbuf, sizeof (msgbuf), msg, va);
263	va_end(va);
264
265	(void) fprintf(stderr, "Error: %s on line %d of %s\n", msgbuf, lineno,
266	    infile);
267	errors++;
268}
269
270static sym_t *
271find_symbol(char *name)
272{
273	int i;
274
275	for (i = 0; i < nsyms; i++)
276		if (strcmp(symtab[i].name, name) == 0) {
277			return (&symtab[i]);
278		}
279
280	return (NULL);
281}
282
283static void
284add_symbol(char *name, int type, int arg)
285{
286	sym_t *sym;
287
288	if (nsyms >= MAX_SYMBOLS) {
289		error("Symbol table full");
290		exit(-1);
291	}
292
293	if (find_symbol(name) != NULL) {
294		error("Dublicate symbol '%s'", name);
295		return;
296	}
297
298	if (strlen(name) >= MAX_NAME) {
299		error("Symbol name '%s' too long", name);
300		exit(-1);
301	}
302
303	sym = &symtab[nsyms++];
304
305	(void) strcpy(sym->name, name);
306	sym->type = type;
307	sym->arg = arg;
308}
309
310static void
311add_init(uint32_t gpr, uint32_t val, const char *name)
312{
313	int	n;
314
315	n = fle.ninit;
316	if (n >= MAX_GPR) {
317		error("Too many GPRs");
318		return;
319	}
320	fle.init[n].gpr = gpr;
321	fle.init[n].value = val;
322	if (name)
323		(void) strlcpy(fle.init[n].name, name,
324		    sizeof (fle.init[n].name));
325	fle.ninit++;
326}
327
328static void
329compile_gpr(char **tokens, int cnt)
330{
331	CHECK_COUNT(tokens, cnt, 2, 2);
332
333	if (ngpr >= MAX_GPR)
334		error("Too many GPR variables");
335
336	add_symbol(tokens[1], SY_GPR, gpr_base + ngpr++);
337}
338
339static void
340compile_rem(char **tokens, int cnt)
341{
342	int i;
343
344	(void) strlcat(remarks, " *", sizeof (remarks));
345	for (i = 1; i < cnt; i++) {
346		(void) strlcat(remarks, " ", sizeof (remarks));
347		(void) strlcat(remarks, tokens[i], sizeof (remarks));
348	}
349	(void) strlcat(remarks, "\n", sizeof (remarks));
350}
351
352static void
353declare_const(unsigned int gpr, char *value)
354{
355	int n, intv;
356	float v;
357
358	n = fle.consts.nconst;
359
360	if (n >= MAX_CONST_PARMS) {
361		error("Too many constant parameters");
362		return;
363	}
364
365	if (*value == 'I') {
366		if (sscanf(&value[1], "%g", &v) != 1) {
367			error("Bad floating point value (%s)", value);
368			return;
369		}
370		intv = (int)v;
371	} else if (*value == '0' && value[1] == 'x') {
372		if (sscanf(&value[2], "%x", (unsigned *)&intv) != 1) {
373			error("Bad hexadecimal value (%s)", value);
374			return;
375		}
376	} else {
377		if (sscanf(value, "%g", &v) != 1) {
378			error("Bad floating point value (%s)", value);
379			return;
380		}
381		intv = (int)(v * 0x7fffffff);
382	}
383
384	fle.consts.consts[n].gpr = gpr;
385	fle.consts.consts[n].value = intv;
386	fle.consts.nconst = n + 1;
387
388	add_init(gpr, intv, NULL);
389}
390
391static void
392compile_const(char **tokens, int cnt)
393{
394	CHECK_COUNT(tokens, cnt, 2, 3);
395	char *name = tokens[1];
396	char *value = tokens[2] ? tokens[2] : tokens[1];
397
398	if (ngpr >= MAX_GPR)
399		error("Too many GPR variables");
400
401	declare_const(ngpr, value);
402
403	add_symbol(name, SY_GPR, gpr_base + ngpr++);
404}
405
406static void
407compile_bool(char **tokens, int cnt)
408{
409	char *parm, *def;
410	int n, num;
411
412	CHECK_COUNT(tokens, cnt, 3, 3);
413
414	parm = tokens[1];
415	def = tokens[2];
416
417	n = fle.parms.ngpr;
418	if (n >= MAX_GPR_PARMS) {
419		error("Too many GPR parameters");
420		return;
421	}
422
423	if (sscanf(def, "%d", &num) != 1) {
424		error("Bad integer value near '%s'", def);
425		return;
426	}
427
428	(void) strcpy(fle.parms.gpr[n].name, parm);
429	fle.parms.gpr[n].num = ngpr;
430	fle.parms.gpr[n].def = num;
431	fle.parms.ngpr = n + 1;
432
433	add_init(ngpr, num, parm);
434
435	add_symbol(parm, SY_PARM, gpr_base + ngpr++);
436}
437
438static void
439compile_mono(char **tokens, int cnt)
440{
441	char *parm, *def;
442	int n, num;
443	char tmp[128];
444
445	CHECK_COUNT(tokens, cnt, 3, 3);
446
447	parm = tokens[1];
448	def = tokens[2];
449
450	n = fle.parms.ngpr;
451	if (n >= MAX_GPR_PARMS) {
452		error("Too many GPR parameters");
453		return;
454	}
455
456	if (sscanf(def, "%d", &num) != 1) {
457		error("Bad integer value near '%s'", def);
458		return;
459	}
460
461	(void) strcpy(fle.parms.gpr[n].name, parm);
462	fle.parms.gpr[n].num = ngpr;
463	fle.parms.gpr[n].def = num;
464	fle.parms.ngpr = n + 1;
465
466	add_init(ngpr, num, parm);
467
468	add_symbol(parm, SY_PARM, gpr_base + ngpr++);
469}
470
471static void
472compile_stereo(char **tokens, int cnt)
473{
474	char *parm, *def;
475	int n, num;
476	char tmp[128];
477
478	CHECK_COUNT(tokens, cnt, 3, 3);
479
480	parm = tokens[1];
481	def = tokens[2];
482
483	n = fle.parms.ngpr;
484	if (n >= MAX_GPR_PARMS) {
485		error("Too many GPR parameters");
486		return;
487	}
488
489	if (sscanf(def, "%d", &num) != 1) {
490		error("Bad integer value near '%s'", def);
491		return;
492	}
493
494	(void) strcpy(fle.parms.gpr[n].name, parm);
495	fle.parms.gpr[n].num = ngpr;
496	fle.parms.gpr[n].def = num | (num << 8);
497	fle.parms.ngpr = n + 1;
498
499	add_init(ngpr, num, parm);
500	add_init(ngpr + 1, num, NULL);
501
502	(void) sprintf(tmp, "%s_L", parm);
503	add_symbol(tmp, SY_PARM, gpr_base + ngpr++);
504	(void) sprintf(tmp, "%s_R", parm);
505	add_symbol(tmp, SY_PARM, gpr_base + ngpr++);
506}
507
508static void
509compile_input(char **tokens, int cnt)
510{
511	int num;
512
513	CHECK_COUNT(tokens, cnt, 3, 3);
514
515	if (sscanf(tokens[2], "%d", &num) != 1) {
516		error("Bad integer value near '%s'", tokens[2]);
517		return;
518	}
519
520	add_symbol(tokens[1], SY_INPUT, input_base + num);
521}
522
523static void
524compile_send(char **tokens, int cnt)
525{
526	int num;
527
528	CHECK_COUNT(tokens, cnt, 3, 3);
529
530	if (sscanf(tokens[2], "%d", &num) != 1) {
531		error("Bad integer near '%s'", tokens[2]);
532		return;
533	}
534
535	add_symbol(tokens[1], SY_FX, num);
536}
537
538static void
539compile_output(char **tokens, int cnt)
540{
541	int num;
542
543	CHECK_COUNT(tokens, cnt, 3, 3);
544
545	if (sscanf(tokens[2], "%d", &num) != 1) {
546		error("Bad integer value near '%s'", tokens[2]);
547		return;
548	}
549
550	add_symbol(tokens[1], SY_OUTPUT, output_base + num);
551}
552
553static void
554compile_directive(char **tokens, int cnt)
555{
556	if (strcmp(tokens[0], ".gpr") == 0) {
557		compile_gpr(tokens, cnt);
558		return;
559	}
560
561	if (strcmp(tokens[0], ".const") == 0) {
562		compile_const(tokens, cnt);
563		return;
564	}
565
566	if (strcmp(tokens[0], ".stereo") == 0) {
567		compile_stereo(tokens, cnt);
568		return;
569	}
570
571	if (strcmp(tokens[0], ".mono") == 0) {
572		compile_mono(tokens, cnt);
573		return;
574	}
575
576	if (strcmp(tokens[0], ".bool") == 0) {
577		compile_bool(tokens, cnt);
578		return;
579	}
580
581	if (strcmp(tokens[0], ".input") == 0) {
582		compile_input(tokens, cnt);
583		return;
584	}
585
586	if (strcmp(tokens[0], ".send") == 0) {
587		compile_send(tokens, cnt);
588		return;
589	}
590
591	if (strcmp(tokens[0], ".output") == 0) {
592		compile_output(tokens, cnt);
593		return;
594	}
595
596	if (strcmp(tokens[0], ".rem") == 0) {
597		compile_rem(tokens, cnt);
598		return;
599	}
600	if (strcmp(tokens[0], ".'") == 0) {
601		compile_rem(tokens, cnt);
602		return;
603	}
604
605	error("Unknown directive '%s'", tokens[0]);
606}
607
608static void
609compile_asm(char **tokens, int cnt)
610{
611	char *parms[4];
612	sym_t *symbols[4];
613#define	EMIT(o, r, a, x, y) \
614	fle.code[pc*2] =  ((x) << 10) | (y);			\
615	fle.code[pc*2+1] = ((o) << 20) | ((r) << 10) | a; pc++
616#define	EMIT_AUDIGY(o, r, a, x, y) \
617	fle.code[pc*2] =  ((x) << 12) | (y);			\
618	fle.code[pc*2+1] = ((o) << 24) | ((r) << 12) | a; pc++
619
620	int i, n = 0, nerr = 0;
621	int ninputs = 0;
622
623	CHECK_COUNT(tokens, cnt, 5, 5);
624
625	for (i = 0; i < 4; i++) {
626		if ((symbols[i] = find_symbol(tokens[i+1])) == NULL) {
627			(void) fprintf(stderr, "%s\n", tokens[i+1]);
628			nerr++;
629			error("Undefined symbol '%s'", tokens[i + 1]);
630			continue;
631		}
632
633		if (symbols[i]->type == SY_INPUT)
634			ninputs++;
635
636		if (symbols[i]->type == SY_ACCUM && i != 1)
637			error("Bad usage of 'accum' operand.");
638	}
639
640	if (nerr > 0)
641		return;
642
643	if (ninputs > 1) {
644		error("Attempt to access more than one input "
645		    "GPRs by the same instruction");
646	}
647
648	for (i = 0; instructions[i].name != NULL; i++)
649		if (strcasecmp(tokens[0], instructions[i].name) == 0)  {
650
651			if (is_audigy) {
652				EMIT_AUDIGY(instructions[i].opcode,
653				    symbols[0]->arg,
654				    symbols[1]->arg,
655				    symbols[2]->arg,
656				    symbols[3]->arg);
657			} else {
658				EMIT(instructions[i].opcode,
659				    symbols[0]->arg,
660				    symbols[1]->arg,
661				    symbols[2]->arg,
662				    symbols[3]->arg);
663			}
664
665			return;
666		}
667
668	error("Unrecognized instruction '%s'", tokens[0]);
669}
670
671static void
672init_compiler(void)
673{
674	char tmp[100];
675	int i;
676
677	(void) memset(&fle, 0, sizeof (fle));
678	/*
679	 * Initialize few predefined GPR parameter registers. These
680	 * definitions have to be in sync with the GPR_* macros in
681	 * <sblive.h>.
682	 */
683
684	/*
685	 * Make sure we start at gpr id 2 for now; 0 and 1 may be used
686	 * differently.
687	 */
688	add_symbol("NULL", SY_DUMMY, gpr_base + ngpr++);
689	add_symbol("NULL_", SY_DUMMY, gpr_base + ngpr++);
690
691	pc = 0;
692
693	if (is_audigy) {
694		/* Initialize the code array with NOPs (AUDIGY) */
695		for (i = 0; i < 512; i++) {
696			fle.code[i * 2 + 0] = (0xc0 << 12) | 0xc0;
697			fle.code[i * 2 + 1] =
698			    (0x06 << 24) | (0xc0 << 12) | 0xc0;
699		}
700
701		for (i = 0; i < 32; i++) {
702			(void) sprintf(tmp, "fx%d", i);
703			add_symbol(tmp, SY_FX, i);
704		}
705	} else {
706		/* Initialize the code array with NOPs (LIVE) */
707		for (i = 0; i < 512; i++) {
708			fle.code[i * 2 + 0] = 0x10040;
709			fle.code[i * 2 + 1] = 0x610040;
710		}
711
712		for (i = 0; i < 16; i++) {
713			(void) sprintf(tmp, "fx%d", i);
714			add_symbol(tmp, SY_FX, i);
715		}
716	}
717
718	/*
719	 * Constants
720	 */
721
722	if (is_audigy) {
723		/* Audigy symbols */
724		add_symbol("0", SY_CONST, 0x0c0);
725		add_symbol("1", SY_CONST, 0x0c1);
726		add_symbol("2", SY_CONST, 0x0c2);
727		add_symbol("3", SY_CONST, 0x0c3);
728		add_symbol("4", SY_CONST, 0x0c4);
729		add_symbol("8", SY_CONST, 0x0c5);
730		add_symbol("16", SY_CONST, 0x0c6);
731		add_symbol("32", SY_CONST, 0x0c7);
732		add_symbol("256", SY_CONST, 0x0c8);
733		add_symbol("65536", SY_CONST, 0x0c9);
734
735		add_symbol("2048", SY_CONST, 0x0ca);
736		add_symbol("0x800", SY_CONST, 0x0ca);
737
738		add_symbol("2^28", SY_CONST, 0x0cb);
739		add_symbol("0x10000000", SY_CONST, 0x0cb);
740
741		add_symbol("2^29", SY_CONST, 0x0cc);
742		add_symbol("0x20000000", SY_CONST, 0x0cc);
743
744		add_symbol("2^30", SY_CONST, 0x0cd);
745		add_symbol("0x40000000", SY_CONST, 0x0cd);
746
747		add_symbol("2^31", SY_CONST, 0x0ce);
748		add_symbol("0x80000000", SY_CONST, 0x0ce);
749
750		add_symbol("0x7fffffff", SY_CONST, 0x0cf);
751
752		add_symbol("0xffffffff", SY_CONST, 0x0d0);
753		add_symbol("-1", SY_CONST, 0x0d0);
754
755		add_symbol("0xfffffffe", SY_CONST, 0x0d1);
756		add_symbol("-2", SY_CONST, 0x0d1);
757
758		add_symbol("0xc0000000", SY_CONST, 0x0d2);
759
760		add_symbol("0x4f1bbcdc", SY_CONST, 0x0d3);
761
762		add_symbol("0x5a7ef9db", SY_CONST, 0x0d4);
763
764		add_symbol("0x100000", SY_CONST, 0x0d5);
765		add_symbol("accum", SY_ACCUM, 0x0d6);
766		add_symbol("CCR", SY_CONST, 0x0d7);
767
768		add_symbol("noise_L", SY_CONST, 0x0d8);
769		add_symbol("noise_R", SY_CONST, 0x0d9);
770		add_symbol("IRQREQ", SY_CONST, 0x0da);
771	} else {
772		/* SB Live symbols */
773		add_symbol("0", SY_CONST, 0x040);
774		add_symbol("1", SY_CONST, 0x041);
775		add_symbol("2", SY_CONST, 0x042);
776		add_symbol("3", SY_CONST, 0x043);
777		add_symbol("4", SY_CONST, 0x044);
778		add_symbol("8", SY_CONST, 0x045);
779		add_symbol("16", SY_CONST, 0x046);
780		add_symbol("32", SY_CONST, 0x047);
781		add_symbol("256", SY_CONST, 0x048);
782		add_symbol("65536", SY_CONST, 0x049);
783
784		add_symbol("2^23", SY_CONST, 0x04a);
785		add_symbol("0x80000", SY_CONST, 0x04a);
786
787		add_symbol("2^28", SY_CONST, 0x04b);
788		add_symbol("0x10000000", SY_CONST, 0x04b);
789
790		add_symbol("2^29", SY_CONST, 0x04c);
791		add_symbol("0x20000000", SY_CONST, 0x04c);
792
793		add_symbol("2^30", SY_CONST, 0x04d);
794		add_symbol("0x40000000", SY_CONST, 0x04d);
795
796		add_symbol("2^31", SY_CONST, 0x04e);
797		add_symbol("0x80000000", SY_CONST, 0x04e);
798
799		add_symbol("0x7fffffff", SY_CONST, 0x04f);
800
801		add_symbol("0xffffffff", SY_CONST, 0x050);
802		add_symbol("-1", SY_CONST, 0x050);
803
804		add_symbol("0xfffffffe", SY_CONST, 0x051);
805		add_symbol("-2", SY_CONST, 0x051);
806
807		add_symbol("accum", SY_ACCUM, 0x056);
808		add_symbol("CCR", SY_CONST, 0x057);
809
810		add_symbol("noise_L", SY_CONST, 0x058);
811		add_symbol("noise_R", SY_CONST, 0x059);
812		add_symbol("IRQREQ", SY_CONST, 0x05a);
813	}
814}
815
816static void
817produce_map(char *name)
818{
819	char fname[1024];
820	int i;
821	FILE *f;
822
823	if ((f = fopen(name, "w")) == NULL) {
824		perror(name);
825		return;
826	}
827
828	(void) fprintf(f, "%d\n", pc);
829
830	for (i = 0; i < nsyms; i++) {
831		(void) fprintf(f, "%04x %x %s\n",
832		    symtab[i].arg, symtab[i].type, symtab[i].name);
833	}
834
835	(void) fclose(f);
836	if (verbose) {
837		(void) fprintf(stderr,
838		    "No errors detected - Map written to %s\n", name);
839	}
840}
841
842static void
843produce_output(char *fname)
844{
845	int fd;
846
847	if ((fd = creat(fname, 0644)) == -1) {
848		perror(fname);
849		exit(-1);
850	}
851
852	if (write(fd, &fle, sizeof (fle)) != sizeof (fle)) {
853		perror(fname);
854		exit(-1);
855	}
856
857	if (verbose) {
858		(void) fprintf(stderr,
859		    "No errors detected - Binary written to %s\n",
860		    fname);
861	}
862
863	(void) close(fd);
864}
865
866static void
867produce_header(char *fname, char *prefix)
868{
869	FILE *f;
870	char *s;
871	char sname[MAXPATHLEN + 1];
872	char dname[MAXPATHLEN + 1];
873	int i;
874	clock_t now;
875	char when[128];
876
877	/* get basename */
878	if (prefix == NULL) {
879		s = strrchr(fname, '/');
880		s = (s == NULL) ? fname : s + 1;
881	} else {
882		s = prefix;
883	}
884	(void) strlcpy(sname, s, sizeof (sname));
885
886	/* strip off any extension */
887	s = strchr(sname, '.');
888	if (s != NULL) {
889		*s = 0;
890	}
891	if ((f = fopen(fname, "w")) == NULL) {
892		perror(fname);
893		return;
894	}
895
896	if (remarks[0] != 0) {
897		(void) fprintf(f, "/*\n%s */\n", remarks);
898	}
899	now = time(NULL);
900	strftime(when, sizeof (when), "%c", localtime(&now));
901	(void) fprintf(f, banner, progname, when);
902
903	(void) strlcpy(dname, prefix ? prefix : sname, sizeof (dname));
904	for (i = 0; dname[i]; i++) {
905		dname[i] = toupper(dname[i]);
906		if (!isalnum(dname[i])) {
907			dname[i] = '_';
908		}
909	}
910
911	for (i = 0; i < fle.parms.ngpr; i++) {
912		(void) fprintf(f, "#define\t%s_%s\t\t%d\n",
913		    dname, fle.parms.gpr[i].name, fle.parms.gpr[i].num);
914	}
915
916	(void) fprintf(f, "\n");
917
918	if (parms_only)
919		goto done;
920
921	(void) fprintf(f, "uint32_t %s_code[] = {\n", sname);
922
923	for (i = 0; i < pc * 2; i++) {
924		if (i == 0) {
925			(void) fprintf(f, "\t0x%08xU", fle.code[i]);
926		} else if ((i % 4) == 0) {
927			(void) fprintf(f, ",\n\t0x%08xU", fle.code[i]);
928		} else {
929			(void) fprintf(f, ", 0x%08xU", fle.code[i]);
930		}
931	}
932	(void) fprintf(f, "\n};\n");
933
934	(void) fprintf(f, "uint32_t %s_ninit = %d;\n", sname, fle.ninit);
935	(void) fprintf(f, "uint32_t %s_init[] = {\n", sname);
936
937	for (i = 0; i < fle.ninit; i++) {
938		if (fle.init[i].name[0]) {
939			(void) fprintf(f, "\t%u, 0x%x%s,\t/* %s */\n",
940			    fle.init[i].gpr, fle.init[i].value,
941			    fle.init[i].value >= 0x80000000U ? "U" : "",
942			    fle.init[i].name);
943		} else {
944			(void) fprintf(f, "\t%u, 0x%x%s,\n",
945			    fle.init[i].gpr, fle.init[i].value,
946			    fle.init[i].value >= 0x80000000U ? "U" : "");
947		}
948	}
949	(void) fprintf(f, "};\n");
950
951done:
952	(void) fclose(f);
953	if (verbose) {
954		(void) fprintf(stderr,
955		    "No errors detected - Header written to %s\n",
956		    fname);
957	}
958}
959
960int
961main(int argc, char *argv[])
962{
963	char line[4096], *p, *s, *outfile;
964	char *iline;
965	int i;
966	FILE *input;
967	char *tokens[10];
968	int tokcnt;
969	char *mapfile = NULL;
970	char *header = NULL;
971	char *prefix = NULL;
972
973	outfile = NULL;
974	infile = NULL;
975	input = NULL;
976	progname = argv[0];
977
978	while ((i = getopt(argc, argv, "m:h:o:i:P:021v")) != EOF) {
979		switch (i) {
980		case 'o':
981			outfile = optarg;
982			break;
983		case 'i':
984			infile = strdup(optarg);
985			break;
986		case 'm':
987			mapfile = optarg;
988			break;
989		case 'P':
990			prefix = optarg;
991			break;
992		case 'h':
993			header = optarg;
994			break;
995		case '0':
996			parms_only = 1;
997			break;
998		case '2':
999			is_audigy = 1;
1000			break;
1001		case '1':
1002			is_audigy = 0;
1003			break;
1004		case 'v':
1005			verbose++;
1006			break;
1007		default:
1008			(void) fprintf(stderr,
1009			    "usage: %s [-m <map>] [-h <header>] "
1010			    "[-o <binary>] [-i <source>] [-2|-1]",
1011			    progname);
1012			exit(-1);
1013			break;
1014		}
1015	}
1016
1017	if ((outfile == NULL) && (mapfile == NULL) && (header == NULL)) {
1018		outfile = "dsp.bin";
1019	}
1020
1021	if (infile) {
1022		input = fopen(infile, "r");
1023		if (input == NULL) {
1024			perror(infile);
1025			exit(-1);
1026		}
1027	} else {
1028		infile = strdup("<stdin>");
1029		input = stdin;
1030	}
1031
1032	if (is_audigy) {
1033		gpr_base = 0x400;
1034		input_base = 0x40;
1035		output_base = 0x60;
1036		if (verbose)
1037			(void) fprintf(stderr, "Compiling for SB Audigy\n");
1038	} else {
1039		if (verbose)
1040			(void) fprintf(stderr, "Compiling for SB Live\n");
1041	}
1042
1043	init_compiler();
1044
1045	while ((tokcnt = getline(input, tokens)) != -1) {
1046		/* skip empty lines */
1047		if (tokcnt == 0) {
1048			continue;
1049		}
1050
1051		if (strcmp(tokens[0], "#") == 0) {
1052			int	num;
1053			if ((tokcnt >= 3) &&
1054			    (sscanf(tokens[1], "%d", &num) == 1)) {
1055				lineno = num;
1056				free(infile);
1057				infile = strdup(tokens[2]);
1058				/* we don't want to count the # directive */
1059				lineno--;
1060			}
1061
1062			/* unknown # directive? muddle on... */
1063			continue;
1064		}
1065		if (*tokens[0] == '.') {
1066			compile_directive(tokens, tokcnt);
1067		} else {
1068			compile_asm(tokens, tokcnt);
1069		}
1070	}
1071
1072	if (lineno < 1) {
1073		error("Empty input");
1074	}
1075
1076	if (errors == 0) {
1077		if (verbose) {
1078			(void) fprintf(stderr,
1079			    "%d instructions out of 512 assembled\n", pc);
1080		}
1081
1082		if (outfile)
1083			produce_output(outfile);
1084		if (mapfile)
1085			produce_map(mapfile);
1086		if (header)
1087			produce_header(header, prefix);
1088	}
1089
1090	if (errors > 0) {
1091		(void) fprintf(stderr, "%d errors - compile failed\n", errors);
1092		exit(-1);
1093	}
1094
1095	return (0);
1096}
1097