xref: /freebsd/sys/dev/aic7xxx/aicasm/aicasm.c (revision df7f5d4de4592a8948a25ce01e5bddfbb7ce39dc)
1 /*
2  * Aic7xxx SCSI host adapter firmware asssembler
3  *
4  * Copyright (c) 1997 Justin T. Gibbs.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice immediately at the beginning of the file, without modification,
12  *    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. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  *      $Id: aic7xxx_asm.c,v 1.15 1997/03/16 07:08:15 gibbs Exp $
32  */
33 #include <sys/types.h>
34 #include <sys/mman.h>
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sysexits.h>
40 #include <unistd.h>
41 
42 #include "aic7xxx_asm.h"
43 #include "symbol.h"
44 #include "sequencer.h"
45 
46 static void usage __P((void));
47 static void back_patch __P((void));
48 static void output_code __P((FILE *ofile));
49 static void output_listing __P((FILE *listfile, char *ifilename,
50 				char *options));
51 static struct patch *next_patch __P((struct patch *cur_patch, int options,
52 				     int instrptr));
53 
54 struct path_list search_path;
55 int includes_search_curdir;
56 char *appname;
57 FILE *ofile;
58 char *ofilename;
59 
60 static STAILQ_HEAD(,instruction) seq_program;
61 static STAILQ_HEAD(, patch) patch_list;
62 symlist_t patch_options;
63 
64 #if DEBUG
65 extern int yy_flex_debug;
66 extern int yydebug;
67 #endif
68 extern FILE *yyin;
69 extern int yyparse __P((void));
70 
71 int
72 main(argc, argv)
73 	int argc;
74 	char *argv[];
75 {
76 	extern char *optarg;
77 	extern int optind;
78 	int ch;
79 	int  retval;
80 	char *inputfilename;
81 	char *regfilename;
82 	FILE *regfile;
83 	char *listfilename;
84 	FILE *listfile;
85 	char *options;
86 
87 	SLIST_INIT(&search_path);
88 	STAILQ_INIT(&seq_program);
89 	STAILQ_INIT(&patch_list);
90 	SLIST_INIT(&patch_options);
91 	includes_search_curdir = 1;
92 	appname = *argv;
93 	regfile = NULL;
94 	listfile = NULL;
95 	options = NULL;
96 #if DEBUG
97 	yy_flex_debug = 0;
98 #endif
99 	while ((ch = getopt(argc, argv, "d:l:n:o:r:I:O:")) != EOF) {
100 		switch(ch) {
101 		case 'd':
102 #if DEBUG
103 			if (strcmp(optarg, "s") == 0)
104 				yy_flex_debug = 1;
105 			else if (strcmp(optarg, "p") == 0)
106 				yydebug = 1;
107 #else
108 			stop("-d: Assembler not built with debugging "
109 			     "information", EX_SOFTWARE);
110 #endif
111 			break;
112 		case 'l':
113 			/* Create a program listing */
114 			if ((listfile = fopen(optarg, "w")) == NULL) {
115 				perror(optarg);
116 				stop(NULL, EX_CANTCREAT);
117 			}
118 			listfilename = optarg;
119 			break;
120 		case 'n':
121 			/* Don't complain about the -nostdinc directrive */
122 			if (strcmp(optarg, "ostdinc")) {
123 				fprintf(stderr, "%s: Unknown option -%c%s\n",
124 					appname, ch, optarg);
125 				usage();
126 				/* NOTREACHED */
127 			}
128 			break;
129 		case 'o':
130 			if ((ofile = fopen(optarg, "w")) == NULL) {
131 				perror(optarg);
132 				stop(NULL, EX_CANTCREAT);
133 			}
134 			ofilename = optarg;
135 			break;
136 		case 'O':
137 			/* Patches to include in the listing */
138 			options = optarg;
139 			break;
140 		case 'r':
141 			if ((regfile = fopen(optarg, "w")) == NULL) {
142 				perror(optarg);
143 				stop(NULL, EX_CANTCREAT);
144 			}
145 			regfilename = optarg;
146 			break;
147 		case 'I':
148 		{
149 			path_entry_t include_dir;
150 
151 			if (strcmp(optarg, "-") == 0) {
152 				if (includes_search_curdir == 0) {
153 					fprintf(stderr, "%s: Warning - '-I-' "
154 							"specified multiple "
155 							"times\n", appname);
156 				}
157 				includes_search_curdir = 0;
158 				for (include_dir = search_path.slh_first;
159 				     include_dir != NULL;
160 				     include_dir = include_dir->links.sle_next)
161 					/*
162 					 * All entries before a '-I-' only
163 					 * apply to includes specified with
164 					 * quotes instead of "<>".
165 					 */
166 					include_dir->quoted_includes_only = 1;
167 			} else {
168 				include_dir =
169 				    (path_entry_t)malloc(sizeof(*include_dir));
170 				if (include_dir == NULL) {
171 					perror(optarg);
172 					stop(NULL, EX_OSERR);
173 				}
174 				include_dir->directory = strdup(optarg);
175 				if (include_dir->directory == NULL) {
176 					perror(optarg);
177 					stop(NULL, EX_OSERR);
178 				}
179 				include_dir->quoted_includes_only = 0;
180 				SLIST_INSERT_HEAD(&search_path, include_dir,
181 						  links);
182 			}
183 			break;
184 		}
185 		case '?':
186 		default:
187 			usage();
188 			/* NOTREACHED */
189 		}
190 	}
191 	argc -= optind;
192 	argv += optind;
193 
194 	if (argc != 1) {
195 		fprintf(stderr, "%s: No input file specifiled\n", appname);
196 		usage();
197 		/* NOTREACHED */
198 	}
199 
200 	symtable_open();
201 	inputfilename = *argv;
202 	include_file(*argv, SOURCE_FILE);
203 	retval = yyparse();
204 	if (retval == 0) {
205 		back_patch();
206 		if (ofile != NULL)
207 			output_code(ofile);
208 		if (regfile != NULL)
209 			symtable_dump(regfile);
210 		if (listfile != NULL)
211 			output_listing(listfile, inputfilename, options);
212 	}
213 
214 	stop(NULL, 0);
215 	/* NOTREACHED */
216 	return (0);
217 }
218 
219 static void
220 usage()
221 {
222 
223 	(void)fprintf(stderr,
224 "usage: %-16s [-nostdinc] [-I-] [-I directory] [-o output_file]
225 			[-r register_output_file] [-l program_list_file]
226 			[-O option_name[|options_name2]] input_file\n",
227 			appname);
228 	exit(EX_USAGE);
229 }
230 
231 static void
232 back_patch()
233 {
234 	struct instruction *cur_instr;
235 
236 	for(cur_instr = seq_program.stqh_first;
237 	    cur_instr != NULL;
238 	    cur_instr = cur_instr->links.stqe_next) {
239 		if (cur_instr->patch_label != NULL) {
240 			struct ins_format3 *f3_instr;
241 			u_int address;
242 
243 			if (cur_instr->patch_label->type != LABEL) {
244 				char buf[255];
245 
246 				snprintf(buf, sizeof(buf),
247 					 "Undefined label %s",
248 					 cur_instr->patch_label->name);
249 				stop(buf, EX_DATAERR);
250 				/* NOTREACHED */
251 			}
252 			f3_instr = &cur_instr->format.format3;
253 			address = ((f3_instr->opcode_addr & ADDR_HIGH_BIT) << 8)
254 				| f3_instr->address;
255 			address += cur_instr->patch_label->info.linfo->address;
256 			f3_instr->opcode_addr &= ~ADDR_HIGH_BIT;
257 			f3_instr->opcode_addr |= (address >> 8) & ADDR_HIGH_BIT;
258 			f3_instr->address = address & 0xFF;
259 		}
260 	}
261 }
262 
263 static void
264 output_code(ofile)
265 	FILE *ofile;
266 {
267 	struct instruction *cur_instr;
268 	patch_t *cur_patch;
269 	symbol_node_t *cur_node;
270 	int instrcount;
271 
272 	instrcount = 0;
273 	fprintf(ofile,
274 "/*
275   * DO NOT EDIT - This file is automatically generated.
276   */\n");
277 
278 	fprintf(ofile, "static u_int8_t seqprog[] = {\n");
279 	for(cur_instr = seq_program.stqh_first;
280 	    cur_instr != NULL;
281 	    cur_instr = cur_instr->links.stqe_next) {
282 		fprintf(ofile, "\t0x%02x, 0x%02x, 0x%02x, 0x%02x,\n",
283 			cur_instr->format.bytes[0],
284 			cur_instr->format.bytes[1],
285 			cur_instr->format.bytes[2],
286 			cur_instr->format.bytes[3]);
287 		instrcount++;
288 	}
289 	fprintf(ofile, "};\n");
290 
291 	/*
292 	 *  Output the patch list, option definitions first.
293 	 */
294 	for(cur_node = patch_options.slh_first;
295 	    cur_node != NULL;
296 	    cur_node = cur_node->links.sle_next) {
297 		fprintf(ofile, "#define\t%-16s\t0x%x\n", cur_node->symbol->name,
298 			cur_node->symbol->info.condinfo->value);
299 	}
300 
301 	fprintf(ofile,
302 "struct patch {
303 	int	options;
304 	int	negative;
305 	int	begin;
306 	int	end;
307 } patches[] = {\n");
308 
309 	for(cur_patch = patch_list.stqh_first;
310 	    cur_patch != NULL;
311 	    cur_patch = cur_patch->links.stqe_next)
312 
313 		fprintf(ofile, "\t{ 0x%08x, %d, 0x%03x, 0x%03x },\n",
314 			cur_patch->options, cur_patch->negative, cur_patch->begin,
315 			cur_patch->end);
316 
317 	fprintf(ofile, "\t{ 0x%08x, %d, 0x%03x, 0x%03x }\n};\n",
318 		0, 0, 0, 0);
319 
320 	fprintf(stderr, "%s: %d instructions used\n", appname, instrcount);
321 }
322 
323 void
324 output_listing(listfile, ifilename, patches)
325 	FILE *listfile;
326 	char *ifilename;
327 	char *patches;
328 {
329 	FILE *ifile;
330 	int line;
331 	struct instruction *cur_instr;
332 	int instrcount;
333 	int instrptr;
334 	char buf[1024];
335 	patch_t *cur_patch;
336 	char *option_spec;
337 	int options;
338 
339 	instrcount = 0;
340 	instrptr = 0;
341 	line = 1;
342 	options = 1; /* All code outside of patch blocks */
343 	if ((ifile = fopen(ifilename, "r")) == NULL) {
344 		perror(ifilename);
345 		stop(NULL, EX_DATAERR);
346 	}
347 
348 	/*
349 	 * Determine which options to apply to this listing.
350 	 */
351 	while ((option_spec = strsep(&patches, "|")) != NULL) {
352 		symbol_t *symbol;
353 
354 		symbol = symtable_get(option_spec);
355 		if (symbol->type != CONDITIONAL) {
356 			stop("Invalid option specified in patch list for "
357 			     "program listing", EX_USAGE);
358 			/* NOTREACHED */
359 		}
360 		options |= symbol->info.condinfo->value;
361 	}
362 
363 	cur_patch = patch_list.stqh_first;
364 	for(cur_instr = seq_program.stqh_first;
365 	    cur_instr != NULL;
366 	    cur_instr = cur_instr->links.stqe_next,instrcount++) {
367 
368 		cur_patch = next_patch(cur_patch, options, instrcount);
369 		if (cur_patch
370 		 && cur_patch->begin <= instrcount
371 		 && cur_patch->end > instrcount)
372 			/* Don't count this instruction as it is in a patch
373 			 * that was removed.
374 			 */
375                         continue;
376 
377 		while (line < cur_instr->srcline) {
378 			fgets(buf, sizeof(buf), ifile);
379 				fprintf(listfile, "\t\t%s", buf);
380 				line++;
381 		}
382 		fprintf(listfile, "%03x %02x%02x%02x%02x", instrptr,
383 			cur_instr->format.bytes[0],
384 			cur_instr->format.bytes[1],
385 			cur_instr->format.bytes[2],
386 			cur_instr->format.bytes[3]);
387 		fgets(buf, sizeof(buf), ifile);
388 		fprintf(listfile, "\t%s", buf);
389 		line++;
390 		instrptr++;
391 	}
392 	/* Dump the remainder of the file */
393 	while(fgets(buf, sizeof(buf), ifile) != NULL)
394 		fprintf(listfile, "\t\t%s", buf);
395 
396 	fclose(ifile);
397 }
398 
399 static struct patch *
400 next_patch(cur_patch, options, instrptr)
401 	struct patch *cur_patch;
402 	int	options;
403 	int	instrptr;
404 {
405 	while(cur_patch != NULL) {
406 		if (((cur_patch->options & options) != 0
407 		   && cur_patch->negative == FALSE)
408 		 || ((cur_patch->options & options) == 0
409 		   && cur_patch->negative == TRUE)
410 		 || (instrptr >= cur_patch->end)) {
411 			/*
412 			 * Either we want to keep this section of code,
413 			 * or we have consumed this patch. Skip to the
414 			 * next patch.
415 			 */
416 			cur_patch = cur_patch->links.stqe_next;
417 		} else
418 			/* Found an okay patch */
419 			break;
420 	}
421 	return (cur_patch);
422 }
423 
424 /*
425  * Print out error information if appropriate, and clean up before
426  * terminating the program.
427  */
428 void
429 stop(string, err_code)
430 	const char *string;
431 	int  err_code;
432 {
433 	if (string != NULL) {
434 		fprintf(stderr, "%s: ", appname);
435 		if (yyfilename != NULL) {
436 			fprintf(stderr, "Stopped at file %s, line %d - ",
437 				yyfilename, yylineno);
438 		}
439 		fprintf(stderr, "%s\n", string);
440 	}
441 
442 	if (ofile != NULL) {
443 		fclose(ofile);
444 		if (err_code != 0) {
445 			fprintf(stderr, "%s: Removing %s due to error\n",
446 				appname, ofilename);
447 			unlink(ofilename);
448 		}
449 	}
450 
451 	symlist_free(&patch_options);
452 	symtable_close();
453 
454 	exit(err_code);
455 }
456 
457 struct instruction *
458 seq_alloc()
459 {
460 	struct instruction *new_instr;
461 
462 	new_instr = (struct instruction *)malloc(sizeof(struct instruction));
463 	if (new_instr == NULL)
464 		stop("Unable to malloc instruction object", EX_SOFTWARE);
465 	memset(new_instr, 0, sizeof(*new_instr));
466 	STAILQ_INSERT_TAIL(&seq_program, new_instr, links);
467 	new_instr->srcline = yylineno;
468 	return new_instr;
469 }
470 
471 patch_t *
472 patch_alloc()
473 {
474 	patch_t *new_patch;
475 
476 	new_patch = (patch_t *)malloc(sizeof(patch_t));
477 	if (new_patch == NULL)
478 		stop("Unable to malloc patch object", EX_SOFTWARE);
479 	memset(new_patch, 0, sizeof(*new_patch));
480 	STAILQ_INSERT_TAIL(&patch_list, new_patch, links);
481 	return new_patch;
482 }
483