xref: /freebsd/sys/dev/aic7xxx/aicasm/aicasm.c (revision 8e6b01171e30297084bb0b4457c4183c2746aacc)
1 /*+M*************************************************************************
2  * Adaptec AIC7770/AIC7870 sequencer code assembler.
3  *
4  * Copyright (c) 1994 John Aycock
5  *   The University of Calgary Department of Computer Science.
6  *   All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions, and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of Calgary
19  *      Department of Computer Science and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * Comments are started by `#' and continue to the end of the line; lines
37  * may be of the form:
38  *      <label>*
39  *      <label>*  <undef-sym> = <value>
40  *      <label>*  <opcode> <operand>*
41  *
42  * A <label> is an <undef-sym> ending in a colon.  Spaces, tabs, and commas
43  * are token separators.
44  *
45  *-M*************************************************************************/
46 static char id[] = "$Id: aic7xxx_asm.c,v 1.8 1995/04/15 21:45:56 gibbs Exp $";
47 #include <ctype.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 
53 #define MEMORY		448
54 #define MAXLINE		1024
55 #define MAXTOKEN	32
56 #define ADOTOUT		"a.out"
57 #define NOVALUE		-1
58 
59 /*
60  * AIC-7770/AIC-7870 register definitions
61  */
62 #define R_SINDEX	0x65
63 #define R_ALLONES	0x69
64 #define R_ALLZEROS	0x6a
65 #define R_NONE		0x6a
66 
67 int debug;
68 int lineno, LC;
69 char *filename;
70 FILE *ifp, *ofp;
71 unsigned char M[MEMORY][4];
72 
73 void
74 error(char *s)
75 {
76 	fprintf(stderr, "%s: %s at line %d\n", filename, s, lineno);
77 	exit(EXIT_FAILURE);
78 }
79 
80 void *
81 Malloc(size_t size)
82 {
83 	void *p = malloc(size);
84 	if (!p)
85 		error("out of memory");
86 	return(p);
87 }
88 
89 void *
90 Realloc(void *ptr, size_t size)
91 {
92 	void *p = realloc(ptr, size);
93 	if (!p)
94 		error("out of memory");
95 	return(p);
96 }
97 
98 char *
99 Strdup(char *s)
100 {
101 	char *p = (char *)Malloc(strlen(s) + 1);
102 	strcpy(p, s);
103 	return(p);
104 }
105 
106 typedef struct sym_t {
107 	struct sym_t	*next;		/* MUST BE FIRST */
108 	char		*name;
109 	int		value;
110 	int		npatch;
111 	int		*patch;
112 } sym_t;
113 
114 sym_t *head;
115 
116 void
117 define(char *name, int value)
118 {
119 	sym_t *p, *q;
120 
121 	for (p = head, q = (sym_t *)&head; p; p = p->next) {
122 		if (!strcmp(p->name, name))
123 			error("redefined symbol");
124 		q = p;
125 	}
126 
127 	p = q->next = (sym_t *)Malloc(sizeof(sym_t));
128 	p->next = NULL;
129 	p->name = Strdup(name);
130 	p->value = value;
131 	p->npatch = 0;
132 	p->patch = NULL;
133 
134 	if (debug) {
135 		fprintf(stderr, "\"%s\" ", p->name);
136 		if (p->value != NOVALUE)
137 			fprintf(stderr, "defined as 0x%x\n", p->value);
138 		else
139 			fprintf(stderr, "undefined\n");
140 	}
141 }
142 
143 sym_t *
144 lookup(char *name)
145 {
146 	sym_t *p;
147 
148 	for (p = head; p; p = p->next)
149 		if (!strcmp(p->name, name))
150 			return(p);
151 	return(NULL);
152 }
153 
154 void
155 patch(sym_t *p, int location)
156 {
157 	p->npatch += 1;
158 	p->patch = (int *)Realloc(p->patch, p->npatch * sizeof(int *));
159 
160 	p->patch[p->npatch - 1] = location;
161 }
162 
163 void backpatch(void)
164 {
165 	int i;
166 	sym_t *p;
167 
168 	for (p = head; p; p = p->next) {
169 
170 		if (p->value == NOVALUE) {
171 			fprintf(stderr,
172 				"%s: undefined symbol \"%s\"\n",
173 				filename, p->name);
174 			exit(EXIT_FAILURE);
175 		}
176 
177 		if (p->npatch) {
178 			if (debug)
179 				fprintf(stderr,
180 					"\"%s\" (0x%x) patched at",
181 					p->name, p->value);
182 
183 			for (i = 0; i < p->npatch; i++) {
184 				M[p->patch[i]][0] &= ~1;
185 				M[p->patch[i]][0] |= ((p->value >> 8) & 1);
186 				M[p->patch[i]][1] = p->value & 0xff;
187 
188 				if (debug)
189 					fprintf(stderr, " 0x%x", p->patch[i]);
190 			}
191 
192 			if (debug)
193 				fputc('\n', stderr);
194 		}
195 	}
196 }
197 
198 /*
199  *  Output words in byte-reversed order (least significant first)
200  *  since the sequencer RAM is loaded that way.
201  */
202 void
203 output(FILE *fp)
204 {
205 	int i;
206 
207 	for (i = 0; i < LC; i++)
208 		fprintf(fp, "\t0x%02x, 0x%02x, 0x%02x, 0x%02x,\n",
209 			M[i][3],
210 			M[i][2],
211 			M[i][1],
212 			M[i][0]);
213 	printf("%d out of %d instructions used.\n", LC, MEMORY);
214 }
215 
216 char **
217 getl(int *n)
218 {
219 	int i;
220 	char *p, *quote;
221 	static char buf[MAXLINE];
222 	static char *a[MAXTOKEN];
223 
224 	i = 0;
225 
226 	while (fgets(buf, sizeof(buf), ifp)) {
227 
228 		lineno += 1;
229 
230 		if (buf[strlen(buf)-1] != '\n')
231 			error("line too long");
232 
233 		p = strchr(buf, '#');
234 		if (p)
235 			*p = '\0';
236 		p = buf;
237 rescan:
238 		quote = strchr(p, '\"');
239 		if (quote)
240 			*quote = '\0';
241 		for (p = strtok(p, ", \t\n"); p; p = strtok(NULL, ", \t\n"))
242 			if (i < MAXTOKEN-1)
243 				a[i++] = p;
244 			else
245 				error("too many tokens");
246 		if (quote) {
247 			quote++;
248 			p = strchr(quote, '\"');
249 			if (!p)
250 				error("unterminated string constant");
251 			else if (i < MAXTOKEN-1) {
252 				a[i++] = quote;
253 				*p = '\0';
254 				p++;
255 			}
256 			else
257 				error("too many tokens");
258 			goto rescan;
259 		}
260 		if (i) {
261 			*n = i;
262 			return(a);
263 		}
264 	}
265 	return(NULL);
266 }
267 
268 #define A	0x8000		/* `A'ccumulator ok */
269 #define I	0x4000		/* use as immediate value */
270 #define SL	0x2000		/* shift left */
271 #define SR	0x1000		/* shift right */
272 #define RL	0x0800		/* rotate left */
273 #define RR	0x0400		/* rotate right */
274 #define LO	0x8000		/* lookup: ori-{jmp,jc,jnc,call} */
275 #define LA	0x4000		/* lookup: and-{jz,jnz} */
276 #define LX	0x2000		/* lookup: xor-{je,jne} */
277 #define NA	-1		/* not applicable */
278 
279 struct {
280 	char *name;
281 	int n;			/* number of operands, including opcode */
282 	unsigned int op;	/* immediate or L?|pos_from_0 */
283 	unsigned int dest;	/* NA, pos_from_0, or I|immediate */
284 	unsigned int src;	/* NA, pos_from_0, or I|immediate */
285 	unsigned int imm;	/* pos_from_0, A|pos_from_0, or I|immediate */
286 	unsigned int addr;	/* NA or pos_from_0 */
287 	int fmt;		/* instruction format - 1, 2, or 3 */
288 } instr[] = {
289 /*
290  *		  N  OP    DEST		SRC		IMM	ADDR	FMT
291  */
292 	{ "mov",  3, 1,	   1,		2,		I|0xff,	NA,	1 },
293 	{ "mov",  4, LO|2, NA,		1,		I|0,	3,	3 },
294 	{ "mvi",  3, 0,	   1,		I|R_ALLZEROS,	A|2,	NA,	1 },
295 	{ "mvi",  4, LO|2, NA,		I|R_ALLZEROS,	1,	3,	3 },
296 	{ "not",  2, 2,	   1,		1,		I|0xff,	NA,	1 },
297 	{ "and",  3, 1,	   1,		1,		A|2,	NA,	1 },
298 	{ "and",  4, 1,	   1,		3,		A|2,	NA,	1 },
299 	{ "or",	  3, 0,	   1,		1,		A|2,	NA,	1 },
300 	{ "or",	  4, 0,	   1,		3,		A|2,	NA,	1 },
301 	{ "or",   5, LO|3, NA,		1,		2,	4,	3 },
302 	{ "xor",  3, 2,	   1,		1,		A|2,	NA,	1 },
303 	{ "xor",  4, 2,	   1,		3,		A|2,	NA,	1 },
304 	{ "nop",  1, 1,	   I|R_NONE,	I|R_ALLZEROS,	I|0xff,	NA,	1 },
305 	{ "inc",  2, 3,	   1,		1,		I|1,	NA,	1 },
306 	{ "inc",  3, 3,	   1,		2,		I|1,	NA,	1 },
307 	{ "dec",  2, 3,	   1,		1,		I|0xff,	NA,	1 },
308 	{ "dec",  3, 3,	   1,		2,		I|0xff,	NA,	1 },
309 	{ "jmp",  2, LO|0,   NA,	I|R_SINDEX,	I|0,	1,	3 },
310 	{ "jc",   2, LO|0,   NA,	I|R_SINDEX,	I|0,	1,	3 },
311 	{ "jnc",  2, LO|0,   NA,	I|R_SINDEX,	I|0,	1,	3 },
312 	{ "call", 2, LO|0,   NA,	I|R_SINDEX,	I|0,	1,	3 },
313 	{ "test", 5, LA|3,   NA,	1,		A|2,	4,	3 },
314 	{ "cmp",  5, LX|3,   NA,	1,		A|2,	4,	3 },
315 	{ "ret",  1, 1,	 I|R_NONE,	I|R_ALLZEROS,	I|0xff,	NA,	1 },
316 	{ "ret",  1, 1,	 I|R_NONE,	I|R_ALLZEROS,	I|0xff,	NA,	1 },
317 	{ "clc",  1, 3,	 I|R_NONE,	I|R_ALLZEROS,	I|1,	NA,	1 },
318 	{ "clc",  4, 3,	 2,		I|R_ALLZEROS,	A|3,	NA,	1 },
319 	{ "stc",  2, 3,	 1,		I|R_ALLONES,	I|1,	NA,	1 },
320 	{ "add",  3, 3,	 1,		1,		A|2,	NA,	1 },
321 	{ "add",  4, 3,	 1,		3,		A|2,	NA,	1 },
322 	{ "adc",  3, 4,	 1,		1,		A|2,	NA,	1 },
323 	{ "adc",  4, 4,	 1,		3,		A|2,	NA,	1 },
324 	{ "shl",  3, 5,	 1,		1,		SL|2,	NA,	2 },
325 	{ "shl",  4, 5,	 1,		2,		SL|3,	NA,	2 },
326 	{ "shr",  3, 5,	 1,		1,		SR|2,	NA,	2 },
327 	{ "shr",  4, 5,	 1,		2,		SR|3,	NA,	2 },
328 	{ "rol",  3, 5,	 1,		1,		RL|2,	NA,	2 },
329 	{ "rol",  4, 5,	 1,		2,		RL|3,	NA,	2 },
330 	{ "ror",  3, 5,	 1,		1,		RR|2,	NA,	2 },
331 	{ "ror",  4, 5,	 1,		2,		RR|3,	NA,	2 },
332 	/*
333 	 *  Extensions (note also that mvi allows A)
334 	 */
335  	{ "clr",  2, 1,	 1,	I|R_ALLZEROS,		I|0xff,	NA,	1 },
336 	{ 0,      0, 0,  0, 	0,			0,	0,	0 }
337 };
338 
339 int
340 eval_operand(char **a, int spec)
341 {
342 	int i;
343 	unsigned int want = spec & (LO|LA|LX);
344 
345 	static struct {
346 		unsigned int what;
347 		char *name;
348 		int value;
349 	} jmptab[] = {
350 		{ LO,	"jmp",		8  },
351 		{ LO,	"jc",		9  },
352 		{ LO,	"jnc",		10 },
353 		{ LO,	"call",		11 },
354 		{ LA,	"jz",		15 },
355 		{ LA,	"jnz",		13 },
356 		{ LX,	"je",		14 },
357 		{ LX,	"jne",		12 },
358 	};
359 
360 	spec &= ~(LO|LA|LX);
361 
362 	for (i = 0; i < sizeof(jmptab)/sizeof(jmptab[0]); i++)
363 		if (jmptab[i].what == want &&
364 		    !strcmp(jmptab[i].name, a[spec]))
365 		{
366 			return(jmptab[i].value);
367 		}
368 
369 	if (want)
370 		error("invalid jump");
371 
372 	return(spec);		/* "case 0" - no flags set */
373 }
374 
375 int
376 eval_sdi(char **a, int spec)
377 {
378 	sym_t *p;
379 	unsigned val;
380 
381 	if (spec == NA)
382 		return(NA);
383 
384 	switch (spec & (A|I|SL|SR|RL|RR)) {
385 	    case SL:
386 	    case SR:
387 	    case RL:
388 	    case RR:
389 		if (isdigit(*a[spec &~ (SL|SR|RL|RR)]))
390 			val = strtol(a[spec &~ (SL|SR|RL|RR)], NULL, 0);
391 		else {
392 			p = lookup(a[spec &~ (SL|SR|RL|RR)]);
393 			if (!p)
394 				error("undefined symbol used");
395 			val = p->value;
396 		}
397 
398 		switch (spec & (SL|SR|RL|RR)) {		/* blech */
399 		    case SL:
400 			if (val > 7)
401 				return(0xf0);
402 			return(((val % 8) << 4) |
403 			       (val % 8));
404 		    case SR:
405 			if (val > 7)
406 				return(0xf0);
407 			return(((val % 8) << 4) |
408 			       (1 << 3) |
409 			       ((8 - (val % 8)) % 8));
410 		    case RL:
411 			return(val % 8);
412 		    case RR:
413 			return((8 - (val % 8)) % 8);
414 		}
415 	    case I:
416 		return(spec &~ I);
417 	    case A:
418 		/*
419 		 *  An immediate field of zero selects
420 		 *  the accumulator.  Vigorously object
421 		 *  if zero is given otherwise - it's
422 		 *  most likely an error.
423 		 */
424 		spec &= ~A;
425 		if (!strcmp("A", a[spec]))
426 			return(0);
427 		if (isdigit(*a[spec]) &&
428 		    strtol(a[spec], NULL, 0) == 0)
429 		{
430 			error("immediate value of zero selects accumulator");
431 		}
432 		/* falls through */
433 	    case 0:
434 		if (isdigit(*a[spec]))
435 			return(strtol(a[spec], NULL, 0));
436 		p = lookup(a[spec]);
437 		if (p)
438 			return(p->value);
439 		error("undefined symbol used");
440 	}
441 
442 	return(NA);		/* shut the compiler up */
443 }
444 
445 int
446 eval_addr(char **a, int spec)
447 {
448 	sym_t *p;
449 
450 	if (spec == NA)
451 		return(NA);
452 	if (isdigit(*a[spec]))
453 		return(strtol(a[spec], NULL, 0));
454 
455 	p = lookup(a[spec]);
456 
457 	if (p) {
458 		if (p->value != NOVALUE)
459 			return(p->value);
460 		patch(p, LC);
461 	} else {
462 		define(a[spec], NOVALUE);
463 		p = lookup(a[spec]);
464 		patch(p, LC);
465 	}
466 
467 	return(NA);		/* will be patched in later */
468 }
469 
470 int
471 crack(char **a, int n)
472 {
473 	int i;
474 	int I_imm, I_addr;
475 	int I_op, I_dest, I_src, I_ret;
476 
477 	/*
478 	 *  Check for "ret" at the end of the line; remove
479 	 *  it unless it's "ret" alone - we still want to
480 	 *  look it up in the table.
481 	 */
482 	I_ret = (strcmp(a[n-1], "ret") ? 0 : !0);
483 	if (I_ret && n > 1)
484 		n -= 1;
485 
486 	for (i = 0; instr[i].name; i++) {
487 		/*
488 		 *  Look for match in table given constraints,
489 		 *  currently just the name and the number of
490 		 *  operands.
491 		 */
492 		if (!strcmp(instr[i].name, *a) && instr[i].n == n)
493 			break;
494 	}
495 	if (!instr[i].name)
496 		error("unknown opcode or wrong number of operands");
497 
498 	I_op	= eval_operand(a, instr[i].op);
499 	I_src	= eval_sdi(a, instr[i].src);
500 	I_imm	= eval_sdi(a, instr[i].imm);
501 	I_dest	= eval_sdi(a, instr[i].dest);
502 	I_addr	= eval_addr(a, instr[i].addr);
503 
504 	if( LC >= MEMORY )
505 		error("Memory exhausted!\n");
506 
507 	switch (instr[i].fmt) {
508 	    case 1:
509 	    case 2:
510 		M[LC][0] = (I_op << 1) | I_ret;
511 		M[LC][1] = I_dest;
512 		M[LC][2] = I_src;
513 		M[LC][3] = I_imm;
514 		break;
515 	    case 3:
516 		if (I_ret)
517 			error("illegal use of \"ret\"");
518 		M[LC][0] = (I_op << 1) | ((I_addr >> 8) & 1);
519 		M[LC][1] = I_addr & 0xff;
520 		M[LC][2] = I_src;
521 		M[LC][3] = I_imm;
522 		break;
523 	}
524 
525 	return (1);		/* no two-byte instructions yet */
526 }
527 
528 #undef SL
529 #undef SR
530 #undef RL
531 #undef RR
532 #undef LX
533 #undef LA
534 #undef LO
535 #undef I
536 #undef A
537 
538 void
539 assemble(void)
540 {
541 	int n;
542 	char **a;
543 	sym_t *p;
544 
545 	while ((a = getl(&n))) {
546 
547 		while (a[0][strlen(*a)-1] == ':') {
548 			a[0][strlen(*a)-1] = '\0';
549 			p = lookup(*a);
550 			if (p)
551 				p->value = LC;
552 			else
553 				define(*a, LC);
554 			a += 1;
555 			n -= 1;
556 		}
557 
558 		if (!n)			/* line was all labels */
559 			continue;
560 
561 		if (n == 3 && !strcmp("VERSION", *a))
562 			fprintf(ofp, "#define %s \"%s\"\n", a[1], a[2]);
563 		else {
564 			if (n == 3 && !strcmp("=", a[1]))
565 				define(*a, strtol(a[2], NULL, 0));
566 			else
567 				LC += crack(a, n);
568 		}
569 	}
570 
571 	backpatch();
572 	output(ofp);
573 
574 	if (debug)
575 		output(stderr);
576 }
577 
578 int
579 main(int argc, char **argv)
580 {
581 	int c;
582 
583 	while ((c = getopt(argc, argv, "dho:vD")) != EOF) {
584 		switch (c) {
585 		    case 'd':
586 			debug = !0;
587 			break;
588 		    case 'D':
589 		    {
590 			char *p;
591 			if ((p = strchr(optarg, '=')) != NULL) {
592 				*p = '\0';
593 				define(optarg, strtol(p + 1, NULL, 0));
594 			}
595 			else
596 				define(optarg, 1);
597 			break;
598 		    }
599 		    case 'o':
600 		        ofp = fopen(optarg, "w");
601 			if (!ofp) {
602 				perror(optarg);
603 				exit(EXIT_FAILURE);
604 			}
605 			break;
606 		    case 'h':
607 			printf("usage: %s [-d] [-Dname] [-ooutput] input\n",
608 				*argv);
609 			exit(EXIT_SUCCESS);
610 			break;
611 		    case 'v':
612 			printf("%s\n", id);
613 			exit(EXIT_SUCCESS);
614 			break;
615 		    default:
616 			exit(EXIT_FAILURE);
617 			break;
618 		}
619 	}
620 
621 	if (argc - optind != 1) {
622 		fprintf(stderr, "%s: must have one input file\n", *argv);
623 		exit(EXIT_FAILURE);
624 	}
625 	filename = argv[optind];
626 
627 	ifp = fopen(filename, "r");
628 	if (!ifp) {
629 		perror(filename);
630 		exit(EXIT_FAILURE);
631 	}
632 
633 	if (!ofp) {
634 		ofp = fopen(ADOTOUT, "w");
635 		if (!ofp) {
636 			perror(ADOTOUT);
637 			exit(EXIT_FAILURE);
638 		}
639 	}
640 
641 	assemble();
642 	exit(EXIT_SUCCESS);
643 }
644