only write extrn symbol if it's actually used
[xorcyst.git] / xlnk.c
blob79f9164d49536b2a1162a78eb234e3806e58af6e
1 /*
2 * $Id: xlnk.c,v 1.20 2007/11/11 22:35:22 khansen Exp $
3 * $Log: xlnk.c,v $
4 * Revision 1.20 2007/11/11 22:35:22 khansen
5 * compile on mac
7 * Revision 1.19 2007/08/12 19:01:11 khansen
8 * xorcyst 1.5.0
10 * Revision 1.18 2007/08/07 22:43:01 khansen
11 * version
13 * Revision 1.17 2007/07/22 13:33:26 khansen
14 * convert tabs to whitespaces
16 * Revision 1.16 2005/01/09 11:19:41 kenth
17 * xorcyst 1.4.5
18 * prints code/data addresses of public symbols when --verbose
20 * Revision 1.15 2005/01/05 09:33:37 kenth
21 * xorcyst 1.4.4
22 * fixed RAM allocator bug
23 * print RAM statistics when --verbose
25 * Revision 1.14 2005/01/05 01:52:19 kenth
26 * xorcyst 1.4.3
28 * Revision 1.13 2005/01/04 21:35:58 kenth
29 * return error code from main() when error count > 0
31 * Revision 1.12 2004/12/29 21:43:55 kenth
32 * xorcyst 1.4.2
34 * Revision 1.11 2004/12/27 06:42:05 kenth
35 * fixed bug in alloc_ram()
37 * Revision 1.10 2004/12/25 02:23:28 kenth
38 * xorcyst 1.4.1
40 * Revision 1.9 2004/12/19 19:58:54 kenth
41 * xorcyst 1.4.0
43 * Revision 1.8 2004/12/18 19:10:39 kenth
44 * relocation improved (I hope)
46 * Revision 1.7 2004/12/18 17:02:00 kenth
47 * CMD_LINE*, CMD_FILE handling, error location printing
48 * CMD_DSW, CMD_DSD gone
50 * Revision 1.6 2004/12/16 13:20:41 kenth
51 * xorcyst 1.3.5
53 * Revision 1.5 2004/12/14 01:50:21 kenth
54 * xorcyst 1.3.0
56 * Revision 1.4 2004/12/11 02:06:18 kenth
57 * xorcyst 1.2.0
59 * Revision 1.3 2004/12/06 04:53:18 kenth
60 * xorcyst 1.1.0
62 * Revision 1.2 2004/06/30 23:38:22 kenth
63 * replaced argp with something else
65 * Revision 1.1 2004/06/30 07:56:04 kenth
66 * Initial revision
68 * Revision 1.1 2004/06/30 07:42:03 kenth
69 * Initial revision
73 /**
74 * (C) 2004 Kent Hansen
76 * The XORcyst is free software; you can redistribute it and/or modify
77 * it under the terms of the GNU General Public License as published by
78 * the Free Software Foundation; either version 2 of the License, or
79 * (at your option) any later version.
81 * The XORcyst is distributed in the hope that it will be useful,
82 * but WITHOUT ANY WARRANTY; without even the implied warranty of
83 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
84 * GNU General Public License for more details.
86 * You should have received a copy of the GNU General Public License
87 * along with The XORcyst; if not, write to the Free Software
88 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
91 /**
92 * This is the 6502 linker program. It takes one or more object files generated
93 * by the 6502 assembler program, and then (rough list)
94 * - maps all data labels to physical 6502 RAM
95 * - relocates code to 6502 address space
96 * - resolves external references
97 * - writes everything to a final binary
99 * The input to the linker is a script file which describes the layout and
100 * contents of the final binary.
103 #include <stdio.h>
104 #include <stdlib.h>
105 #include <stdarg.h>
106 #include <string.h>
107 #include <assert.h>
108 #include "getopt.h"
109 #include "objdef.h"
110 #include "opcode.h"
111 #include "script.h"
112 #include "unit.h"
113 #include "hashtab.h"
115 #define SAFE_FREE(m) if ((m) != NULL) { free(m); m = NULL; }
118 * Parses a string to an integer.
119 * @param s String
120 * @return Integer
122 static int str_to_int(const char *s)
124 if (s[0] == '$') {
125 return strtol(&s[1], NULL, 16);
127 else if (s[0] == '%') {
128 return strtol(&s[1], NULL, 2);
130 return strtol(s, NULL, 0);
133 /*--------------------------------------------------------------------------*/
134 /* Argument parsing stuff. */
136 static char program_version[] = "xlnk 1.5.2";
138 struct tag_arguments {
139 char *input_file;
140 int silent;
141 int verbose;
144 typedef struct tag_arguments arguments;
146 /* Argument variables set by arg parser. */
147 static arguments program_args;
149 /* Long options for getopt_long(). */
150 static struct option long_options[] = {
151 { "quiet", no_argument, 0, 'q' },
152 { "silent", no_argument, 0, 's' },
153 { "verbose", no_argument, 0, 'v' },
154 { "help", no_argument, 0, 0 },
155 { "usage", no_argument, 0, 0 },
156 { "version", no_argument, 0, 'V' },
157 { 0 }
160 /* Prints usage message and exits. */
161 static void usage()
163 printf("\
164 Usage: xlnk [-qsvV] [--quiet] [--silent] [--verbose] [--help] [--usage]\n\
165 [--version] FILE\n\
167 exit(0);
170 /* Prints help message and exits. */
171 static void help()
173 printf("\
174 Usage: xlnk [OPTION...] FILE\n\
175 The XORcyst Linker -- it creates quite a stir\n\
177 -q, -s, --quiet, --silent Don't produce any output\n\
178 -v, --verbose Produce verbose output\n\
179 --help Give this help list\n\
180 --usage Give a short usage message\n\
181 -V, --version Print program version\n\
183 Report bugs to <kentmhan@gmail.com>.\n\
185 exit(0);
188 /* Prints version and exits. */
189 static void version()
191 printf("%s\n", program_version);
192 exit(0);
195 /* Parses program arguments. */
196 static void
197 parse_arguments (int argc, char **argv)
199 int key;
200 /* getopt_long stores the option index here. */
201 int index = 0;
203 /* Set default values. */
204 program_args.silent = 0;
205 program_args.verbose = 0;
206 program_args.input_file = NULL;
208 while ((key = getopt_long(argc, argv, "qsvV", long_options, &index)) != -1) {
209 switch (key) {
210 case 'q': case 's':
211 program_args.silent = 1;
212 break;
214 case 'v':
215 ++program_args.verbose;
216 break;
218 case 0:
219 /* Use index to differentiate between options */
220 if (strcmp(long_options[index].name, "usage") == 0) {
221 usage();
223 else if (strcmp(long_options[index].name, "help") == 0) {
224 help();
226 break;
228 case 'V':
229 version();
230 break;
232 case '?':
233 /* Error message has been printed by getopt_long */
234 exit(1);
235 break;
237 default:
238 /* Forgot to handle a short option, most likely */
239 exit(1);
240 break;
244 /* Must be one additional argument, which is the input file. */
245 if (argc-1 != optind) {
246 printf("Usage: xlnk [OPTION...] FILE\nTry `xlnk --help' or `xlnk --usage' for more information.\n");
247 exit(1);
249 else {
250 program_args.input_file = argv[optind];
254 /*--------------------------------------------------------------------------*/
255 /* Data structures. */
257 /* Describes a local label in the unit. */
258 struct tag_local
260 char *name; /* NULL if not exported */
261 int resolved; /* 0 initially, set to 1 when phys_addr has been assigned */
262 int virt_addr;
263 int phys_addr;
264 int ref_count;
265 int size;
266 struct tag_xunit *owner;
267 unsigned short align;
268 unsigned char flags;
271 typedef struct tag_local local;
273 /* Describes an array of local labels. */
274 struct tag_local_array
276 local *entries;
277 int size;
280 typedef struct tag_local_array local_array;
283 * eXtended unit, has extra info built from basic unit ++
285 struct tag_xunit
287 xasm_unit _unit_; /* NB!!! "Superclass", must be first field for casting to work */
288 local_array data_locals;
289 local_array code_locals;
290 int bank_id;
291 int data_size;
292 int code_origin;
293 int code_size;
294 int loaded;
297 typedef struct tag_xunit xunit;
300 * Describes a 6502 RAM block available for allocation.
302 struct tag_avail_ram_block
304 int start; /* Start address in 6502 space */
305 int end; /* End address in 6502 space (not inclusive) */
306 struct tag_avail_ram_block *next;
309 typedef struct tag_avail_ram_block avail_ram_block;
311 /** */
312 struct tag_calc_address_args
314 xunit *xu;
315 int index;
318 typedef struct tag_calc_address_args calc_address_args;
320 /** */
321 struct tag_write_binary_args
323 xunit *xu;
324 FILE *fp;
327 typedef struct tag_write_binary_args write_binary_args;
329 /*--------------------------------------------------------------------------*/
331 /** Array containing the units to link. */
332 static xunit *units;
333 /* Number of units in above array. */
334 static int unit_count;
336 /** Holds the current memory address. */
337 static int pc;
339 /** Hash tables used to lookup symbols. */
340 static hashtab *label_hash;
341 static hashtab *constant_hash;
342 static hashtab *unit_hash;
344 /** Number of errors and warnings during linking */
345 static int err_count;
346 static int warn_count;
348 static int suppress;
350 /* Head of the list of available 6502 RAM blocks (for data allocation). */
351 static avail_ram_block *ram_block_head = NULL;
353 /* Total amount of 6502 RAM (bytes) that was registered */
354 static int total_ram = 0;
356 /* Bank info */
357 static int bank_offset;
358 static int bank_size;
359 static int bank_origin;
360 static int bank_id;
362 /* Debug info */
363 static const unsigned char *unit_file = NULL; /* length byte followed by chars */
364 static int unit_line = -1;
366 /* Turn on to produce flat (dis)assembly code. The resulting code can be
367 assembled by xasm using the --pure-binary switch.
368 It's useful for checking that the linker doesn't do anything stupid
369 in binary output mode.
371 static const int generate_assembly = 0;
373 /*--------------------------------------------------------------------------*/
376 * If the object file contains FILE and LINE bytecodes (assembled with
377 * --debug switch), unit_file and unit_line will contain the current
378 * source location. In that case, this function prints the location.
380 static void maybe_print_location()
382 char *str;
383 int len;
384 if (unit_file != NULL) {
385 len = unit_file[0] + 1;
386 str = (char *)malloc(len + 1);
387 strncpy(str, (char *)&unit_file[1], len);
388 str[len] = '\0';
389 fprintf(stderr, "%s:%d: ", str, unit_line);
390 free(str);
395 * If the object doesn't contain FILE and LINE bytecodes,
396 * unit_file will be <code>NULL</code>. In that case, this
397 * function prints a tip about reassembling with --debug switch.
399 static void maybe_print_debug_tip()
401 if (unit_file == NULL) {
402 fprintf(stderr, "\treassemble with --debug switch to obtain source location\n");
407 * Issues an error.
408 * @param fmt format string for printf
410 static void err(const char *fmt, ...)
412 va_list ap;
413 va_start(ap, fmt);
414 if (!suppress) {
415 maybe_print_location();
416 fprintf(stderr, "error: ");
417 vfprintf(stderr, fmt, ap);
418 fprintf(stderr, "\n");
419 maybe_print_debug_tip();
420 err_count++;
422 va_end(ap);
426 * Issues a warning.
427 * @param fmt format string for printf
429 static void warn(const char *fmt, ...)
431 va_list ap;
432 va_start(ap, fmt);
433 if (!suppress) {
434 maybe_print_location();
435 fprintf(stderr, "warning: ");
436 vfprintf(stderr, fmt, ap);
437 fprintf(stderr, "\n");
438 maybe_print_debug_tip();
439 warn_count++;
441 va_end(ap);
445 * Prints a message if --verbose switch was given.
446 * @param level verbosity level
447 * @param fmt format string for printf
449 static void verbose(int level, const char *fmt, ...)
451 va_list ap;
452 va_start(ap, fmt);
453 if (!suppress && program_args.verbose >= level) {
454 vfprintf(stdout, fmt, ap);
455 fprintf(stdout, "\n");
457 va_end(ap);
460 /*--------------------------------------------------------------------------*/
461 /* Functions to manage 6502 RAM blocks. */
462 /* The RAM allocator maintains a list of these blocks that are used to
463 map the contents of the units' data segments to memory.
467 * Calculates number of bytes of 6502 RAM left for allocation.
469 static int ram_left()
471 int sum;
472 avail_ram_block *b;
473 for (sum = 0, b = ram_block_head; b != NULL; b = b->next) {
474 sum += b->end - b->start;
476 return sum;
480 * Adds a block of 6502 memory to the list of available memory regions.
481 * When adding multiple blocks they should be added in prioritized order.
482 * @param start Start address of the block
483 * @param end End address of the block (non-inclusive!)
485 static void add_ram_block(int start, int end)
487 avail_ram_block *b;
488 avail_ram_block *new_block = (avail_ram_block *)malloc( sizeof(avail_ram_block) );
489 if (new_block != NULL) {
490 new_block->start = start;
491 new_block->end = end;
492 new_block->next = NULL;
493 if (ram_block_head == NULL) {
494 /* Start the list */
495 ram_block_head = new_block;
496 } else {
497 /* Add to end */
498 for (b = ram_block_head; b->next != NULL; b = b->next) ;
499 b->next = new_block;
501 verbose(1, " added RAM block: %.4X-%.4X", new_block->start, new_block->end);
506 * Allocates a chunk of 6502 RAM to a local.
507 * @param l Local
508 * @return 0 if there isn't enough RAM to satisfy the request (fail), 1 otherwise (success)
510 static int alloc_ram(local *l)
512 /* Try the available blocks in order. */
513 /* Use the first one that's sufficient. */
514 avail_ram_block *b;
515 avail_ram_block *p = NULL;
516 for (b = ram_block_head; b != NULL; p = b, b = b->next) {
517 int left;
518 int pad;
519 avail_ram_block *n;
520 if (l->flags & XASM_LABEL_FLAG_ZEROPAGE) {
521 if (b->start >= 0x100) {
522 continue; /* This block is no good */
525 left = b->end - b->start;
526 if (left < l->size) {
527 continue; /* Not enough, sorry */
529 if (l->flags & XASM_LABEL_FLAG_ALIGN) {
530 pad = b->start & ((1 << l->align) - 1);
531 if (pad != 0) {
532 /* This block doesn't match the alignment */
533 /* Break it into two blocks if possible */
534 pad = (1 << l->align) - pad;
535 pad = (left < pad) ? left : pad;
536 if (pad < left) {
537 n = (avail_ram_block *)malloc(sizeof(avail_ram_block));
538 n->start = b->start;
539 n->end = n->start + pad;
540 b->start += pad;
541 n->next = b;
542 if (b == ram_block_head) {
543 ram_block_head = n; /* New head */
545 b = n;
547 continue;
550 /* Pick this one. */
551 l->phys_addr = b->start;
552 /* Decrease block size by moving start address ahead */
553 b->start += l->size;
554 /* If there's no more space left in this block, discard it */
555 if (left == l->size) {
556 /* Remove from linked list */
557 if (p == NULL) {
558 /* Set successor block as new head */
559 ram_block_head = b->next;
561 else {
562 /* Squeeze out */
563 p->next = b->next;
565 SAFE_FREE(b);
567 return 1;
569 return 0;
573 * Frees up memory associated with list of RAM blocks.
575 static void finalize_ram_blocks()
577 avail_ram_block *b;
578 avail_ram_block *t;
579 for (b = ram_block_head; b != NULL; b = t) {
580 t = b->next;
581 SAFE_FREE(b);
585 /*--------------------------------------------------------------------------*/
586 /* Functions to get big-endian values from byte buffer. */
588 /* Gets single byte from buffer and increments index. */
589 static unsigned char get_1(const unsigned char *b, int *i)
591 return b[(*i)++];
593 /* Gets big-endian short from buffer and increments index. */
594 static unsigned short get_2(const unsigned char *b, int *i)
596 unsigned short result = get_1(b, i) << 8;
597 result |= get_1(b, i);
598 return result;
600 /* Gets big-endian 24-bit integer from buffer and increments index. */
601 static unsigned int get_3(const unsigned char *b, int *i)
603 unsigned int result = get_2(b, i) << 8;
604 result |= get_1(b, i);
605 return result;
607 /* Gets big-endian int from buffer and increments index. */
608 /*static unsigned int get_4(unsigned char *b, int *i)
610 unsigned int result = get_2(b, i) << 16;
611 result |= get_2(b, i);
612 return result;
615 /*--------------------------------------------------------------------------*/
618 * Calculates the storage occupied by a CMD_LABEL bytecode's arguments.
620 static int label_cmd_args_size(const unsigned char *bytes)
622 int size = 1; /* Smallest possible: flag byte */
623 int flags = bytes[0];
624 if (flags & XASM_LABEL_FLAG_EXPORT) { size += bytes[1] + 1 + 1; } /* Length byte + string */
625 if (flags & XASM_LABEL_FLAG_ALIGN) { size += 1; } /* Alignment */
626 if (flags & XASM_LABEL_FLAG_ADDR) { size += 2; } /* Address */
627 return size;
631 * Walks an array of bytecodes, calling corresponding bytecode handlers
632 * along the way.
633 * @param bytes Array of bytecodes, terminated by CMD_END
634 * @param handlers Array of bytecode handlers (entries can be NULL)
635 * @param arg Argument passed to bytecode handler, can be anything
637 static void bytecode_walk(const unsigned char *bytes, xasm_bytecodeproc *handlers, void *arg)
639 int i;
640 unsigned char cmd;
641 unit_file = NULL;
642 unit_line = -1;
643 if (bytes == NULL) { return; }
644 i = 0;
645 do {
646 cmd = get_1(bytes, &i);
648 /* Check if debug command */
649 if (cmd < XASM_CMD_END) {
650 switch (cmd) {
651 case XASM_CMD_FILE:
652 unit_file = &bytes[i];
653 i += get_1(bytes, &i) + 1; /* Skip count and array of bytes */
654 break;
655 case XASM_CMD_LINE8: unit_line = get_1(bytes, &i); break;
656 case XASM_CMD_LINE16: unit_line = get_2(bytes, &i); break;
657 case XASM_CMD_LINE24: unit_line = get_3(bytes, &i); break;
658 case XASM_CMD_LINE_INC: unit_line++; break;
660 continue;
663 if (handlers[cmd-XASM_CMD_END] != NULL) {
664 handlers[cmd-XASM_CMD_END](&bytes[i-1], arg);
666 /* Skip any bytecode arguments */
667 switch (cmd) {
668 case XASM_CMD_END: break;
669 case XASM_CMD_BIN8: i += get_1(bytes, &i) + 1; break; /* Skip count and array of bytes */
670 case XASM_CMD_BIN16: i += get_2(bytes, &i) + 1; break; /* Skip count and array of bytes */
671 case XASM_CMD_LABEL: i += label_cmd_args_size(&bytes[i]); break; /* Skip flag byte and possibly name and alignment */
672 case XASM_CMD_INSTR: i += 3; break; /* Skip 6502 opcode and 16-bit expr id */
673 case XASM_CMD_DB: i += 2; break; /* Skip 16-bit expr id */
674 case XASM_CMD_DW: i += 2; break; /* Skip 16-bit expr id */
675 case XASM_CMD_DD: i += 2; break; /* Skip 16-bit expr id */
676 case XASM_CMD_DSI8: i += 1; break; /* Skip 8-bit count */
677 case XASM_CMD_DSI16: i += 2; break; /* Skip 16-bit count */
678 case XASM_CMD_DSB: i += 2; break; /* Skip 16-bit expr id */
680 default:
681 err("invalid bytecode");
682 break;
684 } while (cmd != XASM_CMD_END);
687 /*--------------------------------------------------------------------------*/
688 /* Functions for expression evaluation. */
691 * Finalizes a constant.
692 * @param c Constant to finalize
694 /* ### Merge with finalize_constant() in unit.c? */
695 static void finalize_constant(xasm_constant *c)
697 if (c->type == XASM_STRING_CONSTANT) {
698 SAFE_FREE(c->string);
703 * Evaluates an expression recursively.
704 * The result will either be a integer or string literal, indicating successful
705 * evaluation; or an invalid type indicating that a symbol could not be translated
706 * to a constant (in other words, it could not be resolved). In this case,
707 * result->string contains the name of the symbol which couldn't be evaluated.
708 * @param u The unit where the expression is contained
709 * @param e The expression to evaluate
710 * @param result Pointer to resulting value
712 static void eval_recursive(xunit *u, xasm_expression *e, xasm_constant *result)
714 char *s;
715 local *l;
716 xasm_constant *c;
717 xasm_constant lhs_result, rhs_result;
718 switch (e->type) {
719 case XASM_OPERATOR_EXPRESSION:
720 switch (e->op_expr.operator) {
721 /* Binary operators */
722 case XASM_OP_PLUS:
723 case XASM_OP_MINUS:
724 case XASM_OP_MUL:
725 case XASM_OP_DIV:
726 case XASM_OP_MOD:
727 case XASM_OP_SHL:
728 case XASM_OP_SHR:
729 case XASM_OP_AND:
730 case XASM_OP_OR:
731 case XASM_OP_XOR:
732 case XASM_OP_EQ:
733 case XASM_OP_NE:
734 case XASM_OP_LT:
735 case XASM_OP_GT:
736 case XASM_OP_LE:
737 case XASM_OP_GE:
738 /* Evaluate both sides */
739 eval_recursive(u, e->op_expr.lhs, &lhs_result);
740 eval_recursive(u, e->op_expr.rhs, &rhs_result);
741 /* If either side is unresolved, then result is unresolved. */
742 if ((lhs_result.type == -1) || (rhs_result.type == -1)) {
743 result->type = -1;
745 /* If both sides are integer, then result is integer. */
746 else if ((lhs_result.type == XASM_INTEGER_CONSTANT) &&
747 (rhs_result.type == XASM_INTEGER_CONSTANT)) {
748 result->type = XASM_INTEGER_CONSTANT;
749 /* Perform the proper operation to obtain result. */
750 switch (e->op_expr.operator) {
751 case XASM_OP_PLUS: result->integer = lhs_result.integer + rhs_result.integer; break;
752 case XASM_OP_MINUS: result->integer = lhs_result.integer - rhs_result.integer; break;
753 case XASM_OP_MUL: result->integer = lhs_result.integer * rhs_result.integer; break;
754 case XASM_OP_DIV: result->integer = lhs_result.integer / rhs_result.integer; break;
755 case XASM_OP_MOD: result->integer = lhs_result.integer % rhs_result.integer; break;
756 case XASM_OP_SHL: result->integer = lhs_result.integer << rhs_result.integer; break;
757 case XASM_OP_SHR: result->integer = lhs_result.integer >> rhs_result.integer; break;
758 case XASM_OP_AND: result->integer = lhs_result.integer & rhs_result.integer; break;
759 case XASM_OP_OR: result->integer = lhs_result.integer | rhs_result.integer; break;
760 case XASM_OP_XOR: result->integer = lhs_result.integer ^ rhs_result.integer; break;
761 case XASM_OP_EQ: result->integer = lhs_result.integer == rhs_result.integer; break;
762 case XASM_OP_NE: result->integer = lhs_result.integer != rhs_result.integer; break;
763 case XASM_OP_LT: result->integer = lhs_result.integer < rhs_result.integer; break;
764 case XASM_OP_GT: result->integer = lhs_result.integer > rhs_result.integer; break;
765 case XASM_OP_LE: result->integer = lhs_result.integer <= rhs_result.integer; break;
766 case XASM_OP_GE: result->integer = lhs_result.integer >= rhs_result.integer; break;
769 /* If both sides are string... */
770 else if ((lhs_result.type == XASM_STRING_CONSTANT) &&
771 (rhs_result.type == XASM_STRING_CONSTANT)) {
772 switch (e->op_expr.operator) {
773 case XASM_OP_PLUS:
774 /* Concatenate */
775 result->string = (char *)malloc(strlen(lhs_result.string)+strlen(rhs_result.string)+1);
776 if (result->string != NULL) {
777 strcpy(result->string, lhs_result.string);
778 strcat(result->string, rhs_result.string);
779 result->type = XASM_STRING_CONSTANT;
781 break;
783 /* String comparison: using strcmp() */
784 case XASM_OP_EQ: result->integer = strcmp(lhs_result.string, rhs_result.string) == 0; break;
785 case XASM_OP_NE: result->integer = strcmp(lhs_result.string, rhs_result.string) != 0; break;
786 case XASM_OP_LT: result->integer = strcmp(lhs_result.string, rhs_result.string) < 0; break;
787 case XASM_OP_GT: result->integer = strcmp(lhs_result.string, rhs_result.string) > 0; break;
788 case XASM_OP_LE: result->integer = strcmp(lhs_result.string, rhs_result.string) <= 0; break;
789 case XASM_OP_GE: result->integer = strcmp(lhs_result.string, rhs_result.string) >= 0; break;
791 default:
792 /* Not defined operator for string operation... */
793 assert(0);
794 break;
797 else {
798 result->type = -1;
799 err("incompatible operands to `%s' in expression", xasm_operator_to_string(e->op_expr.operator) );
801 /* Discard the operands */
802 finalize_constant(&lhs_result);
803 finalize_constant(&rhs_result);
804 break; /* Binary operator */
806 /* Unary operators */
807 case XASM_OP_NOT:
808 case XASM_OP_NEG:
809 case XASM_OP_LO:
810 case XASM_OP_HI:
811 case XASM_OP_UMINUS:
812 /* Evaluate the single operand */
813 eval_recursive(u, e->op_expr.lhs, &lhs_result);
814 /* If operand is unresolved then result is unresolved. */
815 if (lhs_result.type == -1) {
816 result->type = -1;
818 /* If operand is integer then result is integer. */
819 else if (lhs_result.type == XASM_INTEGER_CONSTANT) {
820 result->type = XASM_INTEGER_CONSTANT;
821 /* Perform the proper operation to obtain result. */
822 switch (e->op_expr.operator) {
823 case XASM_OP_NOT: result->integer = !lhs_result.integer; break;
824 case XASM_OP_NEG: result->integer = ~lhs_result.integer; break;
825 case XASM_OP_LO: result->integer = lhs_result.integer & 0xFF; break;
826 case XASM_OP_HI: result->integer = (lhs_result.integer >> 8) & 0xFF; break;
827 case XASM_OP_UMINUS: result->integer = -lhs_result.integer; break;
830 else {
831 /* Error, invalid operand */
832 err("incompatible operand to `%s' in expression", xasm_operator_to_string(e->op_expr.operator) );
833 result->type = -1;
835 /* Discard the operand */
836 finalize_constant(&lhs_result);
837 break; /* Unary operator */
839 case XASM_OP_BANK:
840 switch (e->op_expr.lhs->type) {
841 case XASM_LOCAL_EXPRESSION:
842 /* Simple, it must be in the same (current) bank */
843 result->integer = bank_id;
844 result->type = XASM_INTEGER_CONSTANT;
845 break;
847 case XASM_EXTERNAL_EXPRESSION:
848 s = u->_unit_.externals[e->op_expr.lhs->extrn_id].name;
849 if ((l = (local *)hashtab_get(label_hash, s)) != NULL) {
850 /* It's a label */
851 result->integer = l->owner->bank_id;
852 result->type = XASM_INTEGER_CONSTANT;
854 else if ((c = (xasm_constant *)hashtab_get(constant_hash, s)) != NULL) {
855 /* It's a constant */
856 result->integer = ((xunit *)c->unit)->bank_id;
857 result->type = XASM_INTEGER_CONSTANT;
859 else {
860 result->type = -1;
862 break;
864 default:
865 result->type = -1;
866 break;
868 break;
870 break;
872 case XASM_INTEGER_EXPRESSION:
873 result->type = XASM_INTEGER_CONSTANT;
874 result->integer = e->integer;
875 break;
877 case XASM_STRING_EXPRESSION:
878 result->string = (char *)malloc(strlen(e->string) + 1);
879 if (result->string != NULL) {
880 strcpy(result->string, e->string);
881 result->type = XASM_STRING_CONSTANT;
883 break;
885 case XASM_LOCAL_EXPRESSION:
886 if (e->local_id >= u->data_locals.size) {
887 /* It's a code local */
888 l = &u->code_locals.entries[e->local_id - u->data_locals.size];
890 else {
891 /* It's a data local */
892 l = &u->data_locals.entries[e->local_id];
894 if (l->resolved) {
895 result->type = XASM_INTEGER_CONSTANT;
896 result->integer = l->phys_addr;
898 else {
899 /* Not resolved (yet, at least) */
900 result->type = -1;
902 break;
904 case XASM_EXTERNAL_EXPRESSION:
905 s = u->_unit_.externals[e->extrn_id].name;
906 if ((l = (local *)hashtab_get(label_hash, s)) != NULL) {
907 /* It's a label */
908 if (l->resolved) {
909 result->type = XASM_INTEGER_CONSTANT;
910 result->integer = l->phys_addr;
912 else {
913 /* Not resolved (yet) */
914 result->type = -1;
917 else if ((c = (xasm_constant *)hashtab_get(constant_hash, s)) != NULL) {
918 switch (c->type) {
919 case XASM_INTEGER_CONSTANT:
920 result->type = XASM_INTEGER_CONSTANT;
921 result->integer = c->integer;
922 break;
924 case XASM_STRING_CONSTANT:
925 result->string = (char *)malloc(strlen(c->string) + 1);
926 if (result->string != NULL) {
927 strcpy(result->string, c->string);
928 result->type = XASM_STRING_CONSTANT;
930 break;
933 else {
934 result->type = -1;
935 err("unknown symbol `%s' referenced from %s", s, u->_unit_.name);
937 break;
939 case XASM_PC_EXPRESSION:
940 result->type = XASM_INTEGER_CONSTANT;
941 result->integer = pc;
942 break;
947 * Evaluates an expression.
948 * @param u The unit where the expression is contained
949 * @param exid The unique ID of the expression
950 * @param result Where to store the result of the evaluation
952 static void eval_expression(xunit *u, int exid, xasm_constant *result)
954 xasm_expression *exp = u->_unit_.expressions[exid];
955 eval_recursive(u, exp, result);
958 /*--------------------------------------------------------------------------*/
959 /* Functions for incrementing PC, with error handling for wraparound. */
962 * Increases PC by amount.
963 * Issues error if the PC wraps around.
965 static void inc_pc(int amount, void *arg)
967 calc_address_args *aargs;
968 if ((pc <= 0x10000) && ((pc+amount) > 0x10000)) {
969 aargs = (calc_address_args *)arg;
970 err("PC went beyond 64K when linking `%s'", aargs->xu->_unit_.name);
972 pc += amount;
976 * Increases PC by 8-bit value immediately following bytecode command.
978 static void inc_pc_count8(const unsigned char *b, void *arg)
980 int i = 1;
981 inc_pc( get_1(b, &i) + 1, arg );
985 * Increases PC by 16-bit value immediately following bytecode command.
987 static void inc_pc_count16(const unsigned char *b, void *arg)
989 int i = 1;
990 inc_pc( get_2(b, &i) + 1, arg );
994 * Increases PC by 1.
996 static void inc_pc_1(const unsigned char *b, void *arg)
998 inc_pc( 1, arg );
1002 * Increases PC by 2.
1004 static void inc_pc_2(const unsigned char *b, void *arg)
1006 inc_pc( 2, arg );
1010 * Increases PC by 4.
1012 static void inc_pc_4(const unsigned char *b, void *arg)
1014 inc_pc( 4, arg );
1018 * Increases PC according to size of define data command.
1020 static void inc_pc_dsb(const unsigned char *b, void *arg)
1022 xasm_constant c;
1023 int exid;
1024 calc_address_args *args = (calc_address_args *)arg;
1025 int i = 1;
1026 /* Get expression ID */
1027 exid = get_2(b, &i);
1028 /* Evaluate expression */
1029 eval_expression(args->xu, exid, &c);
1030 /* Handle the result */
1031 if (c.type == XASM_INTEGER_CONSTANT) {
1032 /* An array of bytes will be located here */
1033 /* Advance PC appropriately */
1034 inc_pc( c.integer, arg );
1036 else if (c.type == XASM_STRING_CONSTANT) {
1037 err("unexpected string operand (`%s') to storage directive", c.string);
1039 else {
1040 //err("unresolved symbol");
1041 assert(0);
1044 finalize_constant(&c);
1048 * Increments PC according to the length of this instruction.
1050 static void inc_pc_instr(const unsigned char *b, void *arg)
1052 xasm_constant c;
1053 unsigned char op, t;
1054 int exid;
1055 calc_address_args *args = (calc_address_args *)arg;
1056 /* Get opcode */
1057 int i = 1;
1058 op = get_1(b, &i);
1059 /* Get expression ID */
1060 exid = get_2(b, &i);
1061 /* Evaluate it */
1062 eval_expression(args->xu, exid, &c);
1063 /* Handle the result */
1064 if (c.type == XASM_INTEGER_CONSTANT) {
1065 /* See if it can be reduced to ZP instruction */
1066 if ((c.integer < 0x100) &&
1067 ((t = opcode_zp_equiv(op)) != 0xFF)) {
1068 /* replace op by ZP-version */
1069 op = t;
1070 ((unsigned char*)b)[1] = t;
1073 else if (c.type == XASM_STRING_CONSTANT) {
1074 err("invalid instruction operand (string)");
1076 else {
1077 /* Address not available yet (forward reference). */
1078 //err("unresolved symbol");
1080 /* Advance PC */
1081 inc_pc( opcode_length(op), arg );
1084 /*--------------------------------------------------------------------------*/
1085 /* Functions for writing pure 6502 binary from bytecodes. */
1088 * Writes an array of bytes.
1090 static void write_bin8(const unsigned char *b, void *arg)
1092 int count;
1093 int i;
1094 write_binary_args *args = (write_binary_args *)arg;
1095 i = 1;
1096 count = get_1(b, &i) + 1;
1097 fwrite(&b[i], 1, count, args->fp);
1098 inc_pc( count, arg );
1102 * Writes an array of bytes.
1104 static void write_bin16(const unsigned char *b, void *arg)
1106 int count;
1107 int i;
1108 write_binary_args *args = (write_binary_args *)arg;
1109 i = 1;
1110 count = get_2(b, &i) + 1;
1111 fwrite(&b[i], 1, count, args->fp);
1112 inc_pc( count, arg );
1116 * Writes an instruction.
1118 static void write_instr(const unsigned char *b, void *arg)
1120 xasm_constant c;
1121 unsigned char op;
1122 int i;
1123 int exid;
1124 write_binary_args *args = (write_binary_args *)arg;
1125 /* Get opcode */
1126 i = 1;
1127 op = get_1(b, &i);
1128 assert(opcode_length(op) > 1);
1129 /* Get expression ID */
1130 exid = get_2(b, &i);
1131 /* Evaluate expression */
1132 eval_expression(args->xu, exid, &c);
1133 assert(c.type == XASM_INTEGER_CONSTANT);
1134 /* Write the opcode */
1135 fputc(op, args->fp);
1136 if (opcode_length(op) == 2) {
1137 /* Operand must fit in 1 byte */
1138 /* Check if it's a relative jump */
1139 switch (op) {
1140 case 0x10:
1141 case 0x30:
1142 case 0x50:
1143 case 0x70:
1144 case 0x90:
1145 case 0xB0:
1146 case 0xD0:
1147 case 0xF0:
1148 /* Calculate difference between target and address of next instruction */
1149 c.integer = c.integer - (pc + 2);
1150 /* Make sure jump is in range */
1151 if ( (c.integer < -128) || (c.integer > 127) ) {
1152 err("branch out of range");
1154 /* Make it a byte value */
1155 c.integer &= 0xFF;
1156 break;
1158 if (c.integer >= 0x100) {
1159 err("instruction operand doesn't fit in 1 byte");
1161 else {
1162 /* Write it */
1163 fputc(c.integer, args->fp);
1165 } else {
1166 assert(opcode_length(op) == 3);
1167 /* Operand must fit in 2 bytes */
1168 if (c.integer >= 0x10000) {
1169 err("instruction operand doesn't fit in 2 bytes");
1171 else {
1172 /* Write it, low byte first */
1173 fputc(c.integer, args->fp);
1174 fputc(c.integer >> 8, args->fp);
1177 inc_pc( opcode_length(op), arg );
1181 * Writes a byte, word or dword.
1183 static void write_dx(const unsigned char *b, void *arg)
1185 xasm_constant c;
1186 int i;
1187 int exid;
1188 write_binary_args *args = (write_binary_args *)arg;
1189 /* Get expression ID */
1190 i = 1;
1191 exid = get_2(b, &i);
1192 /* Evaluate expression */
1193 eval_expression(args->xu, exid, &c);
1195 if (c.type == XASM_INTEGER_CONSTANT) {
1196 /* Write low byte */
1197 fputc(c.integer, args->fp);
1198 /* If 2+ bytes, write high ones */
1199 switch (b[0]) {
1200 case XASM_CMD_DB:
1201 if (c.integer > 0xFF) {
1202 warn("`.DB' operand $%X out of range; truncated", c.integer);
1204 break;
1206 case XASM_CMD_DW:
1207 fputc(c.integer >> 8, args->fp);
1208 if (c.integer > 0xFFFF) {
1209 warn("`.DW' operand $%X out of range; truncated", c.integer);
1211 break;
1213 case XASM_CMD_DD:
1214 fputc(c.integer >> 8, args->fp);
1215 fputc(c.integer >> 16, args->fp);
1216 fputc(c.integer >> 24, args->fp);
1217 break;
1219 /* Advance PC */
1220 switch (b[0]) {
1221 case XASM_CMD_DB: inc_pc( 1, arg ); break;
1222 case XASM_CMD_DW: inc_pc( 2, arg ); break;
1223 case XASM_CMD_DD: inc_pc( 4, arg ); break;
1226 else if (c.type == XASM_STRING_CONSTANT) {
1227 for (i=0; i<strlen(c.string); i++) {
1228 /* Write low byte */
1229 fputc(c.string[i], args->fp);
1230 /* If 2+ bytes, write high ones */
1231 switch (b[0]) {
1232 case XASM_CMD_DW:
1233 fputc(0, args->fp);
1234 break;
1236 case XASM_CMD_DD:
1237 fputc(0, args->fp);
1238 fputc(0, args->fp);
1239 fputc(0, args->fp);
1240 break;
1242 /* Advance PC */
1243 switch (b[0]) {
1244 case XASM_CMD_DB: inc_pc( 1, arg ); break;
1245 case XASM_CMD_DW: inc_pc( 2, arg ); break;
1246 case XASM_CMD_DD: inc_pc( 4, arg ); break;
1249 } else {
1250 assert(0);
1253 finalize_constant(&c);
1257 * Writes a series of zeroes.
1259 static void write_dsi8(const unsigned char *b, void *arg)
1261 int count;
1262 int i;
1263 write_binary_args *args = (write_binary_args *)arg;
1264 i = 1;
1265 count = get_1(b, &i) + 1;
1266 for (i=0; i<count; i++) {
1267 fputc(0, args->fp);
1269 inc_pc( count, arg );
1273 * Writes a series of zeroes.
1275 static void write_dsi16(const unsigned char *b, void *arg)
1277 int count;
1278 int i;
1279 write_binary_args *args = (write_binary_args *)arg;
1280 i = 1;
1281 count = get_2(b, &i) + 1;
1282 for (i=0; i<count; i++) {
1283 fputc(0, args->fp);
1285 inc_pc( count, arg );
1289 * Writes a series of zeroes.
1291 static void write_dsb(const unsigned char *b, void *arg)
1293 xasm_constant c;
1294 int i;
1295 int exid;
1296 write_binary_args *args = (write_binary_args *)arg;
1297 /* Get expression ID */
1298 i = 1;
1299 exid = get_2(b, &i);
1300 /* Evaluate expression */
1301 eval_expression(args->xu, exid, &c);
1302 assert(c.type == XASM_INTEGER_CONSTANT);
1303 if (c.integer < 0) {
1304 err("negative count");
1305 } else if (c.integer > 0) {
1306 for (i=0; i<c.integer; i++) {
1307 fputc(0, args->fp);
1309 inc_pc( c.integer, arg );
1314 * Writes a code segment as fully native 6502 code.
1315 * @param fp File handle
1316 * @param u Unit whose code to write
1318 static void write_as_binary(FILE *fp, xunit *u)
1320 write_binary_args args;
1321 /* Table of callback functions for our purpose. */
1322 static xasm_bytecodeproc handlers[] =
1324 NULL, /* CMD_END */
1325 write_bin8, /* CMD_BIN8 */
1326 write_bin16, /* CMD_BIN16 */
1327 NULL, /* CMD_LABEL */
1328 write_instr, /* CMD_INSTR */
1329 write_dx, /* CMD_DB */
1330 write_dx, /* CMD_DW */
1331 write_dx, /* CMD_DD */
1332 write_dsi8, /* CMD_DSI8 */
1333 write_dsi16, /* CMD_DSI16 */
1334 write_dsb /* CMD_DSB */
1336 /* Fill in args */
1337 args.xu = u;
1338 args.fp = fp;
1339 /* Reset PC */
1340 pc = u->code_origin;
1341 /* Do the walk */
1342 bytecode_walk(u->_unit_.codeseg.bytes, handlers, (void *)&args);
1345 /*--------------------------------------------------------------------------*/
1346 /* Functions for writing 6502 assembly from bytecodes. */
1349 Prints \a size bytes of data defined by \a buf to \a out.
1351 static void print_chunk(FILE *out, const char *label,
1352 const unsigned char *buf, int size, int cols)
1354 int i, j, m;
1355 int pos = 0;
1356 if (label)
1357 fprintf(out, "%s:\n", label);
1358 for (i = 0; i < size / cols; ++i) {
1359 fprintf(out, ".DB ");
1360 for (j = 0; j < cols-1; ++j)
1361 fprintf(out, "$%.2X,", buf[pos++]);
1362 fprintf(out, "$%.2X\n", buf[pos++]);
1364 m = size % cols;
1365 if (m > 0) {
1366 fprintf(out, ".DB ");
1367 for (j = 0; j < m-1; ++j)
1368 fprintf(out, "$%.2X,", buf[pos++]);
1369 fprintf(out, "$%.2X\n", buf[pos++]);
1374 * Writes an array of bytes.
1376 static void asm_write_bin8(const unsigned char *b, void *arg)
1378 int count;
1379 int i;
1380 write_binary_args *args = (write_binary_args *)arg;
1381 i = 1;
1382 count = get_1(b, &i) + 1;
1383 // fprintf(args->fp, "; %d byte(s)\n", count);
1384 print_chunk(args->fp, /*label=*/0, &b[i], count, /*cols=*/16);
1385 inc_pc( count, arg );
1389 * Writes an array of bytes.
1391 static void asm_write_bin16(const unsigned char *b, void *arg)
1393 int count;
1394 int i;
1395 write_binary_args *args = (write_binary_args *)arg;
1396 i = 1;
1397 count = get_2(b, &i) + 1;
1398 // fprintf(args->fp, "; %d byte(s)\n", count);
1399 print_chunk(args->fp, /*label=*/0, &b[i], count, /*cols=*/16);
1400 inc_pc( count, arg );
1404 * Writes a label.
1406 static void asm_write_label(const unsigned char *b, void *arg)
1408 unsigned char flags;
1409 int i= 1;
1410 write_binary_args *args = (write_binary_args *)arg;
1411 fprintf(args->fp, "; label");
1412 flags = get_1(b, &i);
1413 if (flags & XASM_LABEL_FLAG_EXPORT) {
1414 char *name;
1415 int len = get_1(b, &i) + 1;
1416 name = (char *)malloc( len + 1 );
1417 assert(name != 0);
1418 memcpy(name, &b[i], len);
1419 name[len] = '\0';
1420 i += len;
1421 fprintf(args->fp, " %s (PC=$%.4X)", name, pc);
1422 free(name);
1423 } else {
1424 fprintf(args->fp, " PC=$%.4X", pc);
1426 fprintf(args->fp, "\n");
1430 * Writes an instruction.
1432 static void asm_write_instr(const unsigned char *b, void *arg)
1434 xasm_constant c;
1435 unsigned char op;
1436 addressing_mode mode;
1437 int i;
1438 int exid;
1439 write_binary_args *args = (write_binary_args *)arg;
1440 /* Get opcode */
1441 i = 1;
1442 op = get_1(b, &i);
1443 assert(opcode_length(op) > 1);
1444 mode = opcode_addressing_mode(op);
1445 assert(mode != INVALID_MODE);
1446 /* Get expression ID */
1447 exid = get_2(b, &i);
1448 /* Evaluate expression */
1449 eval_expression(args->xu, exid, &c);
1450 assert(c.type == XASM_INTEGER_CONSTANT);
1451 /* Write the opcode */
1452 fprintf(args->fp, "%s", opcode_to_string(op));
1453 switch (mode) {
1454 case IMPLIED_MODE:
1455 case ACCUMULATOR_MODE:
1456 break;
1457 case IMMEDIATE_MODE:
1458 fprintf(args->fp, " #$");
1459 break;
1460 case ZEROPAGE_MODE:
1461 case ZEROPAGE_X_MODE:
1462 case ZEROPAGE_Y_MODE:
1463 case ABSOLUTE_MODE:
1464 case ABSOLUTE_X_MODE:
1465 case ABSOLUTE_Y_MODE:
1466 fprintf(args->fp, " $");
1467 break;
1468 case PREINDEXED_INDIRECT_MODE:
1469 case POSTINDEXED_INDIRECT_MODE:
1470 case INDIRECT_MODE:
1471 fprintf(args->fp, " [$");
1472 break;
1473 case RELATIVE_MODE:
1474 fprintf(args->fp, " $");
1475 break;
1476 case INVALID_MODE:
1477 break;
1479 /* Write the operand */
1480 fprintf(args->fp, "%.4X", (unsigned)c.integer);
1481 switch (mode) {
1482 case IMPLIED_MODE:
1483 case ACCUMULATOR_MODE:
1484 case IMMEDIATE_MODE:
1485 case ZEROPAGE_MODE:
1486 break;
1487 case ZEROPAGE_X_MODE:
1488 fprintf(args->fp, ",X");
1489 break;
1490 case ZEROPAGE_Y_MODE:
1491 fprintf(args->fp, ",Y");
1492 break;
1493 case ABSOLUTE_MODE:
1494 break;
1495 case ABSOLUTE_X_MODE:
1496 fprintf(args->fp, ",X");
1497 break;
1498 case ABSOLUTE_Y_MODE:
1499 fprintf(args->fp, ",Y");
1500 break;
1501 case PREINDEXED_INDIRECT_MODE:
1502 fprintf(args->fp, ",X]");
1503 break;
1504 case POSTINDEXED_INDIRECT_MODE:
1505 fprintf(args->fp, "],Y");
1506 break;
1507 case INDIRECT_MODE:
1508 fprintf(args->fp, "]");
1509 break;
1510 case RELATIVE_MODE:
1511 break;
1512 case INVALID_MODE:
1513 break;
1515 fprintf(args->fp, "\n");
1516 inc_pc( opcode_length(op), arg );
1520 * Writes a byte, word or dword.
1522 static void asm_write_dx(const unsigned char *b, void *arg)
1524 xasm_constant c;
1525 int i;
1526 int exid;
1527 write_binary_args *args = (write_binary_args *)arg;
1528 /* Get expression ID */
1529 i = 1;
1530 exid = get_2(b, &i);
1531 /* Evaluate expression */
1532 eval_expression(args->xu, exid, &c);
1533 if (c.type == XASM_INTEGER_CONSTANT) {
1534 switch (b[0]) {
1535 case XASM_CMD_DB:
1536 fprintf(args->fp, ".DB $%.2X", (unsigned)c.integer);
1537 break;
1538 case XASM_CMD_DW:
1539 fprintf(args->fp, ".DW $%.4X", (unsigned)c.integer);
1540 break;
1541 case XASM_CMD_DD:
1542 fprintf(args->fp, ".DD $%.8X", (unsigned)c.integer);
1543 break;
1545 /* Advance PC */
1546 switch (b[0]) {
1547 case XASM_CMD_DB: inc_pc( 1, arg ); break;
1548 case XASM_CMD_DW: inc_pc( 2, arg ); break;
1549 case XASM_CMD_DD: inc_pc( 4, arg ); break;
1551 } else if (c.type == XASM_STRING_CONSTANT) {
1552 int count = strlen(c.string);
1553 switch (b[0]) {
1554 case XASM_CMD_DB:
1555 fprintf(args->fp, ".DB");
1556 break;
1557 case XASM_CMD_DW:
1558 fprintf(args->fp, ".DW");
1559 break;
1560 case XASM_CMD_DD:
1561 fprintf(args->fp, ".DD");
1562 break;
1564 fprintf(args->fp, " \"%s\"", c.string);
1565 /* Advance PC */
1566 switch (b[0]) {
1567 case XASM_CMD_DB: inc_pc( count * 1, arg ); break;
1568 case XASM_CMD_DW: inc_pc( count * 2, arg ); break;
1569 case XASM_CMD_DD: inc_pc( count * 4, arg ); break;
1571 } else {
1572 assert(0);
1574 fprintf(args->fp, "\n");
1575 finalize_constant(&c);
1579 * Writes a series of zeroes.
1581 static void asm_write_dsi8(const unsigned char *b, void *arg)
1583 int count;
1584 int i;
1585 write_binary_args *args = (write_binary_args *)arg;
1586 i = 1;
1587 count = get_1(b, &i) + 1;
1588 fprintf(args->fp, ".DSB $%X\n", count);
1589 inc_pc( count, arg );
1593 * Writes a series of zeroes.
1595 static void asm_write_dsi16(const unsigned char *b, void *arg)
1597 int count;
1598 int i;
1599 write_binary_args *args = (write_binary_args *)arg;
1600 i = 1;
1601 count = get_2(b, &i) + 1;
1602 fprintf(args->fp, ".DSB $%X\n", count);
1603 inc_pc( count, arg );
1607 * Writes a series of zeroes.
1609 static void asm_write_dsb(const unsigned char *b, void *arg)
1611 xasm_constant c;
1612 int i;
1613 int exid;
1614 write_binary_args *args = (write_binary_args *)arg;
1615 /* Get expression ID */
1616 i = 1;
1617 exid = get_2(b, &i);
1618 /* Evaluate expression */
1619 eval_expression(args->xu, exid, &c);
1620 assert(c.type == XASM_INTEGER_CONSTANT);
1621 if (c.integer < 0) {
1622 err("negative count");
1624 else if (c.integer > 0) {
1625 fprintf(args->fp, ".DSB $%X\n", (unsigned)c.integer);
1626 inc_pc( c.integer, arg );
1631 * Writes a code segment as fully native 6502 code.
1632 * @param fp File handle
1633 * @param u Unit whose code to write
1635 static void write_as_assembly(FILE *fp, xunit *u)
1637 write_binary_args args;
1638 /* Table of callback functions for our purpose. */
1639 static xasm_bytecodeproc handlers[] =
1641 NULL, /* CMD_END */
1642 asm_write_bin8, /* CMD_BIN8 */
1643 asm_write_bin16, /* CMD_BIN16 */
1644 asm_write_label, /* CMD_LABEL */
1645 asm_write_instr, /* CMD_INSTR */
1646 asm_write_dx, /* CMD_DB */
1647 asm_write_dx, /* CMD_DW */
1648 asm_write_dx, /* CMD_DD */
1649 asm_write_dsi8, /* CMD_DSI8 */
1650 asm_write_dsi16, /* CMD_DSI16 */
1651 asm_write_dsb /* CMD_DSB */
1653 /* Fill in args */
1654 args.xu = u;
1655 args.fp = fp;
1656 /* Reset PC */
1657 pc = u->code_origin;
1658 fprintf(fp, "; ***************************************\n");
1659 fprintf(fp, "; * %s, PC=$%.4X\n", u->_unit_.name, pc);
1660 fprintf(fp, "; ***************************************\n");
1661 /* Do the walk */
1662 bytecode_walk(u->_unit_.codeseg.bytes, handlers, (void *)&args);
1665 #define XLNK_NO_DEBUG
1666 #ifndef XLNK_NO_DEBUG
1668 /*--------------------------------------------------------------------------*/
1669 /* Functions for debugging bytecodes. */
1672 * Gets string representation of bytecode command.
1673 * @param cmd CMD_*
1674 * @return String representation ("CMD_*")
1676 static const char *bytecode_to_string(unsigned char cmd)
1678 switch (cmd) {
1679 case XASM_CMD_FILE: return "CMD_FILE";
1680 case XASM_CMD_LINE8: return "CMD_LINE8";
1681 case XASM_CMD_LINE16:return "CMD_LINE16";
1682 case XASM_CMD_LINE24:return "CMD_LINE24";
1683 case XASM_CMD_LINE_INC: return "CMD_LINE_INC";
1684 case XASM_CMD_END: return "CMD_END";
1685 case XASM_CMD_BIN8: return "CMD_BIN8";
1686 case XASM_CMD_BIN16: return "CMD_BIN16";
1687 case XASM_CMD_LABEL: return "CMD_LABEL";
1688 case XASM_CMD_INSTR: return "CMD_INSTR";
1689 case XASM_CMD_DB: return "CMD_DB";
1690 case XASM_CMD_DW: return "CMD_DW";
1691 case XASM_CMD_DD: return "CMD_DD";
1692 case XASM_CMD_DSI8: return "CMD_DSI8";
1693 case XASM_CMD_DSI16: return "CMD_DSI16";
1694 case XASM_CMD_DSB: return "CMD_DSB";
1696 return "bytecode_to_string: invalid bytecode";
1700 * Print a bytecode.
1701 * @param b Bytecodes
1702 * @param arg Not used
1704 static void print_it(const unsigned char *b, void *arg)
1706 printf("%s\n", bytecode_to_string(b[0]) );
1710 * Prints bytecodes.
1711 * @param bytes Bytecodes
1713 static void print_bytecodes(const unsigned char *bytes)
1715 static xasm_bytecodeproc handlers[] =
1717 print_it,print_it,print_it,print_it,print_it,
1718 print_it,print_it,print_it,print_it,print_it,
1719 print_it,print_it,print_it
1721 bytecode_walk(bytes, handlers, NULL);
1725 * Prints a unit.
1726 * @param u Unit
1728 static void print_unit(xasm_unit *u)
1730 print_bytecodes(u->dataseg.bytes);
1731 print_bytecodes(u->codeseg.bytes);
1734 #endif /* !XLNK_NO_DEBUG */
1736 /*--------------------------------------------------------------------------*/
1737 /* Functions for managing arrays of unit locals. */
1740 * Creates array of locals.
1741 * @param size Number of locals
1742 * @param la Local array
1744 static void create_local_array(int size, local_array *la)
1746 la->size = size;
1747 if (size > 0) {
1748 la->entries = (local *)malloc(sizeof(local) * size);
1750 else {
1751 la->entries = NULL;
1756 * Finalizes array of locals.
1758 static void finalize_local_array(local_array *la)
1760 int i;
1761 for (i=0; i<la->size; i++) {
1762 SAFE_FREE(la->entries[i].name);
1764 SAFE_FREE(la->entries);
1767 /*--------------------------------------------------------------------------*/
1768 /* Functions for counting and registering locals in a unit. */
1769 /* In bytecode expressions, locals are referred to by their index.
1770 In order to not have to go through the bytecodes every time to
1771 find a label definition, the following functions build an array
1772 of structures that can be indexed by the local ID to obtain its
1773 information.
1777 * Counts this local.
1779 static void count_one_local(const unsigned char *b, void *arg)
1781 /* Argument points to the counter */
1782 int *count = (int *)arg;
1783 (*count)++;
1787 * Counts the number of locals (labels) in an array of bytecodes.
1788 * @param b Bytecodes, terminated by CMD_END
1789 * @return Number of locals counted
1791 static int count_locals(const unsigned char *b)
1793 int count;
1794 /* Table of callback functions for our purpose. */
1795 static xasm_bytecodeproc handlers[] =
1797 NULL, /* CMD_END */
1798 NULL, /* CMD_BIN8 */
1799 NULL, /* CMD_BIN16 */
1800 count_one_local, /* CMD_LABEL */
1801 NULL, /* CMD_INSTR */
1802 NULL, /* CMD_DB */
1803 NULL, /* CMD_DW */
1804 NULL, /* CMD_DD */
1805 NULL, /* CMD_DSI8 */
1806 NULL, /* CMD_DSI16 */
1807 NULL /* CMD_DSB */
1809 count = 0;
1810 bytecode_walk(b, handlers, (void *)&count);
1811 return count;
1815 * Variable that points to the unit that locals are being registered for.
1817 static xunit *reg_unit = NULL;
1820 * Puts this local into array of locals for current unit.
1822 static void register_one_local(const unsigned char *b, void *arg)
1824 int len;
1825 int i= 1;
1826 /* Argument points to a pointer which points to the local struct to fill in */
1827 local **lpptr = (local **)arg;
1828 local *lptr = *lpptr;
1829 /* Initialize some fields */
1830 lptr->resolved = 0;
1831 lptr->ref_count = 0;
1832 lptr->name = NULL;
1833 lptr->align = 1;
1834 lptr->owner = reg_unit;
1835 /* Get flag byte */
1836 lptr->flags = get_1(b, &i);
1837 /* Test export flag */
1838 if (lptr->flags & XASM_LABEL_FLAG_EXPORT) {
1839 /* Get the length of the name */
1840 len = get_1(b, &i) + 1;
1841 /* Allocate space for name */
1842 lptr->name = (char *)malloc( len + 1 );
1843 if (lptr->name != NULL) {
1844 /* Copy name from bytecodes */
1845 memcpy(lptr->name, &b[i], len);
1846 /* Zero-terminate string */
1847 lptr->name[len] = '\0';
1849 i += len;
1851 if (lptr->flags & XASM_LABEL_FLAG_ALIGN) {
1852 lptr->align = get_1(b, &i);
1854 if (lptr->flags & XASM_LABEL_FLAG_ADDR) {
1855 lptr->phys_addr = get_2(b, &i);
1856 lptr->resolved = 1;
1858 #if 0
1859 if (program_args.verbose) {
1860 verbose(1, " %s align=%d resolved=%d",
1861 lptr->name ? lptr->name : "(anonymous)",
1862 lptr->align, lptr->resolved);
1864 #endif
1865 /* Point to next local in array */
1866 *lpptr += 1;
1870 * Puts all locals found in the array of bytecodes into array.
1871 * @param b Bytecodes, terminated by CMD_END
1872 * @param la Pointer to array to receive locals
1873 * @param xu Owner unit
1875 static void register_locals(const unsigned char *b, local_array *la, xunit *xu)
1877 local *lptr;
1878 local **lpptr;
1879 /* Table of callback functions for our purpose. */
1880 static xasm_bytecodeproc handlers[] =
1882 NULL, /* CMD_END */
1883 NULL, /* CMD_BIN8 */
1884 NULL, /* CMD_BIN16 */
1885 register_one_local, /* CMD_LABEL */
1886 NULL, /* CMD_INSTR */
1887 NULL, /* CMD_DB */
1888 NULL, /* CMD_DW */
1889 NULL, /* CMD_DD */
1890 NULL, /* CMD_DSI8 */
1891 NULL, /* CMD_DSI16 */
1892 NULL /* CMD_DSB */
1894 /* Create array of locals */
1895 create_local_array(count_locals(b), la);
1896 /* Prepare args */
1897 lptr = la->entries;
1898 lpptr = &lptr;
1899 reg_unit = xu;
1900 /* Go! */
1901 bytecode_walk(b, handlers, (void *)lpptr);
1904 /*--------------------------------------------------------------------------*/
1905 /* Functions for entering exported symbols into proper hash table. */
1908 * Enters an exported symbol into a hash table.
1909 * @param tab Hash table to enter it into
1910 * @param key Key
1911 * @param data Data
1912 * @param u Owner unit
1914 static void enter_exported_symbol(hashtab *tab, void *key, void *data, xasm_unit *u)
1916 if ((hashtab_get(label_hash, key) != NULL)
1917 || (hashtab_get(constant_hash, key) != NULL) ) {
1918 err("duplicate symbol `%s' exported from unit `%s'", (char *)key, u->name);
1920 else {
1921 verbose(1, " %s", (char*)key);
1922 hashtab_put(tab, key, data);
1927 * Enters all constants in a unit into the proper hash table.
1928 * @param u Unit whose constants to enter
1930 static void enter_exported_constants(xasm_unit *u)
1932 int i;
1933 xasm_constant *c;
1934 for (i=0; i<u->const_count; i++) {
1935 c = &u->constants[i];
1936 enter_exported_symbol(constant_hash, (void *)c->name, (void *)c, u);
1941 * Enters locals which should be globally visible into the proper hash table.
1942 * @param la Array of locals
1943 * @param u Owner unit
1945 static void enter_exported_locals(local_array *la, xasm_unit *u)
1947 int i;
1948 local *l;
1949 for (i=0; i<la->size; i++) {
1950 l = &la->entries[i];
1951 /* If it has a name, it is exported */
1952 if (l->name != NULL) {
1953 enter_exported_symbol(label_hash, (void *)l->name, (void *)l, u);
1958 /*--------------------------------------------------------------------------*/
1959 /* Functions for calculating addresses of data labels in a unit. */
1962 * Sets the virtual address of this local to current PC value.
1964 static void set_data_address(const unsigned char *b, void *arg)
1966 calc_address_args *args = (calc_address_args *)arg;
1967 local *l = &args->xu->data_locals.entries[args->index];
1968 if (!l->resolved) {
1969 l->virt_addr = pc;
1970 verbose(2, " %.4X %s", l->virt_addr, l->name ? l->name : "");
1972 /* Increase label index */
1973 args->index++;
1977 * Calculates addresses of labels in a data segment relative to 0.
1978 * Only a small set of bytecode commands are allowed in a data segment:
1979 * - label (which we want to assign a virtual address)
1980 * - storage (constant or variable)
1982 static void calc_data_addresses(xunit *u)
1984 calc_address_args args;
1985 /* Table of callback functions for our purpose. */
1986 static xasm_bytecodeproc handlers[] =
1988 NULL, /* CMD_END */
1989 NULL, /* CMD_BIN8 */
1990 NULL, /* CMD_BIN16 */
1991 set_data_address, /* CMD_LABEL */
1992 NULL, /* CMD_INSTR */
1993 NULL, /* CMD_DB */
1994 NULL, /* CMD_DW */
1995 NULL, /* CMD_DD */
1996 inc_pc_count8, /* CMD_DSI8 */
1997 inc_pc_count16, /* CMD_DSI16 */
1998 inc_pc_dsb /* CMD_DSB */
2000 /* Fill in args */
2001 args.xu = u;
2002 args.index = 0;
2003 /* Reset PC */
2004 pc = 0;
2005 verbose(1, " %s", u->_unit_.name);
2006 /* Map away! */
2007 bytecode_walk(u->_unit_.dataseg.bytes, handlers, (void *)&args);
2008 /* Store the end address, which is the total size of data */
2009 u->data_size = pc;
2012 /*--------------------------------------------------------------------------*/
2014 /* Constructs 32-bit sort key for local. */
2015 #define SORT_KEY(l) (unsigned long)((((l)->flags & XASM_LABEL_FLAG_ZEROPAGE) << 30) | ((l)->align << 24) | (0x10000-(l)->size))
2018 * Array is sorted from high to low value.
2020 static int label_partition(local **a, int p, int r)
2022 int x;
2023 int i;
2024 int j;
2025 x = SORT_KEY(a[r]);
2026 i = p - 1;
2027 local *temp;
2028 for (j=p; j<r; j++) {
2029 if (SORT_KEY(a[j]) >= x) {
2030 i = i + 1;
2031 temp = a[i];
2032 a[i] = a[j];
2033 a[j] = temp;
2036 temp = a[i+1];
2037 a[i+1] = a[r];
2038 a[r] = temp;
2039 return i + 1;
2043 * Quicksort implementation used to sort array of pointers to locals.
2045 static void label_qsort(local **a, int p, int r)
2047 int q;
2048 if (p < r) {
2049 q = label_partition(a, p, r);
2050 label_qsort(a, p, q-1);
2051 label_qsort(a, q+1, r);
2056 * Maps all data labels to 6502 RAM locations.
2057 * This is a very important function. It takes all the data labels from all
2058 * the loaded units and attempts to assign them unique physical addresses.
2059 * The list of target RAM blocks given in the linker script is the premise.
2061 static void map_data_to_ram()
2063 int i, k;
2064 local **total_order;
2065 local *l;
2066 int count;
2067 /* Use a bit array to keep track of allocations,
2068 to ensure that there is no overlap */
2069 unsigned char *allocated;
2070 int ram_base, ram_end;
2071 if (ram_block_head == NULL)
2072 return; /* Nothing to do. */
2074 avail_ram_block *b;
2075 ram_base = 10000000;
2076 ram_end = -10000000;
2077 for (b = ram_block_head; b != NULL; b = b->next) {
2078 if (b->start < ram_base)
2079 ram_base = b->start;
2080 if (b->end > ram_end)
2081 ram_end = b->end;
2084 allocated = (unsigned char *)malloc(((ram_end - ram_base) + 7) / 8);
2085 memset(allocated, 0, ((ram_end - ram_base) + 7) / 8);
2086 /* Calculate total number of labels to map */
2087 count = 0;
2088 for (i=0; i<unit_count; i++) {
2089 count += units[i].data_locals.size;
2091 /* Put pointers to all data labels in one big array */
2092 total_order = (local **)malloc( count * sizeof(local *) );
2093 for (i=0, k=0; i<unit_count; i++) {
2094 int j;
2095 local_array *la;
2096 la = &units[i].data_locals;
2097 for (j=0; j<la->size; j++) {
2098 int size;
2099 /* Use virtual addresses to calculate size from this label to next */
2100 if (j == la->size-1) {
2101 size = units[i].data_size;
2103 else {
2104 size = la->entries[j+1].virt_addr;
2106 la->entries[j].size = size - la->entries[j].virt_addr;
2107 /* Put pointer in array */
2108 total_order[k++] = &la->entries[j];
2111 /* Sort them */
2112 label_qsort(total_order, 0, count-1);
2113 /* Map them */
2114 for (i=0; i<count; i++) {
2115 l = total_order[i];
2116 /* Try to allocate it */
2117 if (alloc_ram(l) == 1) {
2118 /* Good, label mapped successfully */
2119 l->resolved = 1;
2120 verbose(1, " %.4X-%.4X %s (%s)", l->phys_addr,
2121 l->phys_addr + l->size-1, l->name ? l->name : "",
2122 l->owner->_unit_.name);
2124 /* Verify that there's no overlap with other variable */
2125 int a;
2126 for (a = l->phys_addr; a < l->phys_addr + l->size; ++a) {
2127 assert((allocated[(a - ram_base) / 8] & (1 << (a & 7))) == 0);
2128 allocated[(a - ram_base) / 8] |= 1 << (a & 7);
2132 else {
2133 err("out of 6502 RAM while allocating unit `%s'", l->owner->_unit_.name);
2134 return;
2137 free(total_order);
2138 free(allocated);
2141 /*--------------------------------------------------------------------------*/
2142 /* Functions for calculating offsets of code labels in a unit. */
2145 * Sets the address of this code label to current PC.
2147 static void set_code_address(const unsigned char *b, void *arg)
2149 calc_address_args *args = (calc_address_args *)arg;
2150 local *l = &args->xu->code_locals.entries[args->index];
2151 if (!l->resolved) {
2152 l->phys_addr = pc;
2153 l->resolved = 1;
2154 if (program_args.verbose) {
2155 fprintf(stdout, " %.4X %s (%s)\n", l->phys_addr,
2156 l->name ? l->name : "", l->owner->_unit_.name);
2159 /* Increase label index */
2160 args->index++;
2164 * Calculates addresses of code labels in a segment.
2165 * NOTE: Only the virtual addresses (relative to 0) are calculated.
2166 * The labels then need to be relocated to obtain the physical address (see below).
2167 * @param u Unit
2169 static void calc_code_addresses(xunit *u)
2171 calc_address_args args;
2172 /* Table of callback functions for our purpose. */
2173 static xasm_bytecodeproc handlers[] =
2175 NULL, /* CMD_END */
2176 inc_pc_count8, /* CMD_BIN8 */
2177 inc_pc_count16, /* CMD_BIN16 */
2178 set_code_address, /* CMD_LABEL */
2179 inc_pc_instr, /* CMD_INSTR */
2180 inc_pc_1, /* CMD_DB -- TODO, error if string */
2181 inc_pc_2, /* CMD_DW */
2182 inc_pc_4, /* CMD_DD */
2183 inc_pc_count8, /* CMD_DSI8 */
2184 inc_pc_count16, /* CMD_DSI16 */
2185 inc_pc_dsb /* CMD_DSB */
2187 /* Fill in args */
2188 args.xu = u;
2189 args.index = 0;
2190 /* Do the walk */
2191 bytecode_walk(u->_unit_.codeseg.bytes, handlers, (void *)&args);
2192 /* Store the total size of code */
2193 u->code_size = pc - u->code_origin;
2196 /*--------------------------------------------------------------------------*/
2199 * Issues a script error.
2201 static void scripterr(xlnk_script *s, xlnk_script_command *c, const char *fmt, ...)
2203 va_list ap;
2204 va_start(ap, fmt);
2206 if (!suppress) {
2207 fprintf(stderr, "error: %s:%d: `%s': ", s->name, c->line, xlnk_script_command_type_to_string(c->type) );
2208 vfprintf(stderr, fmt, ap);
2209 fprintf(stderr, "\n");
2210 err_count++;
2212 va_end(ap);
2215 #define require_arg(s, c, a, d) { \
2216 d = xlnk_script_get_command_arg(c, a); \
2217 if (d == NULL) { \
2218 scripterr(s, c, "missing argument `%s'", a); \
2219 return; \
2223 #define require_arg_in_range(s, c, a, v, l, h) { \
2224 if (((v) < (l)) || ((v) > (h))) { \
2225 scripterr(s, c, "value of argument `%s' is out of range", a); \
2226 return; \
2230 /*--------------------------------------------------------------------------*/
2231 /* Functions for registering RAM blocks in script. */
2234 * Registers one RAM block based on 'ram' script command.
2235 * @param s Linker script
2236 * @param c Command of type RAM_COMMAND
2237 * @param arg Not used
2239 static void register_one_ram_block(xlnk_script *s, xlnk_script_command *c, void *arg)
2241 int start;
2242 int end;
2243 const char *start_str;
2244 const char *end_str;
2245 require_arg(s, c, "start", start_str);
2246 require_arg(s, c, "end", end_str);
2247 start = str_to_int(start_str);
2248 end = str_to_int(end_str);
2249 require_arg_in_range(s, c, "start", start, 0x0000, 0xFFFF);
2250 require_arg_in_range(s, c, "end", end, 0x0000, 0xFFFF);
2251 if (end <= start) {
2252 scripterr(s, c, "`end' is smaller than `start'");
2254 add_ram_block(start, end);
2258 * Registers RAM blocks based on 'ram' commands in a script.
2259 * @param sc Linker script
2261 static void register_ram_blocks(xlnk_script *sc)
2263 /* Table of mappings for our purpose */
2264 static xlnk_script_commandprocmap map[] = {
2265 { XLNK_RAM_COMMAND, register_one_ram_block },
2266 { XLNK_BAD_COMMAND, NULL }
2268 /* Do the walk */
2269 xlnk_script_walk(sc, map, NULL);
2270 /* Calculate total RAM size */
2271 total_ram = ram_left();
2274 /*--------------------------------------------------------------------------*/
2275 /* Functions for loading and initial processing of units in script. */
2278 * Registers (parses etc.) one unit based on 'link' script command.
2279 * @param s Linker script
2280 * @param c Command of type LINK_COMMAND
2281 * @param arg Pointer to unit index
2283 static void register_one_unit(xlnk_script *s, xlnk_script_command *c, void *arg)
2285 const char *file;
2286 int *i;
2287 xunit *xu;
2288 require_arg(s, c, "file", file);
2289 /* arg is pointer to unit index */
2290 i = (int *)arg;
2291 /* Get pointer to xunit to fill in */
2292 xu = &units[*i];
2293 /* Read basic unit from file */
2294 if (xasm_unit_read(file, &xu->_unit_) == 0) {
2295 scripterr(s, c, "failed to load unit `%s'", file);
2296 xu->loaded = 0;
2297 return;
2299 xu->loaded = 1;
2300 verbose(1, " unit `%s' loaded", file);
2302 verbose(1, " registering local symbols...");
2303 register_locals(xu->_unit_.dataseg.bytes, &xu->data_locals, xu);
2304 register_locals(xu->_unit_.codeseg.bytes, &xu->code_locals, xu);
2306 verbose(1, " registering public symbols...");
2307 enter_exported_constants(&xu->_unit_);
2308 enter_exported_locals(&xu->data_locals, &xu->_unit_);
2309 enter_exported_locals(&xu->code_locals, &xu->_unit_);
2311 hashtab_put(unit_hash, (void*)file, xu);
2312 /* Increment unit index */
2313 (*i)++;
2317 * Registers units based on 'link' commands in script.
2318 * @param sc Linker script
2320 static void register_units(xlnk_script *sc)
2322 /* Table of mappings for our purpose */
2323 static xlnk_script_commandprocmap map[] = {
2324 { XLNK_LINK_COMMAND, register_one_unit },
2325 { XLNK_BAD_COMMAND, NULL }
2327 int i = 0;
2328 /* Do the walk */
2329 xlnk_script_walk(sc, map, (void *)&i);
2332 /*--------------------------------------------------------------------------*/
2333 /* Functions for composing a binary file based on a sequential list of
2334 script commands. */
2337 * Sets the output file according to 'output' script command.
2338 * @param s Linker script
2339 * @param c Command of type OUTPUT_COMMAND
2340 * @param arg Pointer to file handle
2342 static void set_output(xlnk_script *s, xlnk_script_command *c, void *arg)
2344 const char *file;
2345 FILE **fpp;
2346 require_arg(s, c, "file", file);
2347 /* Arg is pointer to file handle pointer */
2348 fpp = (FILE **)arg;
2349 if (*fpp != NULL) {
2350 fclose(*fpp);
2352 *fpp = fopen(file, "wb");
2353 if (*fpp == NULL) {
2354 scripterr(s, c, "could not open `%s' for writing", file);
2356 else {
2357 verbose(1, " output goes to `%s'", file);
2362 * Copies a file to output according to 'copy' script command.
2363 * @param s Linker script
2364 * @param c Command of type COPY_COMMAND
2365 * @param arg Pointer to file handle
2367 static void copy_to_output(xlnk_script *s, xlnk_script_command *c, void *arg)
2369 const char *file;
2370 FILE **fpp;
2371 FILE *cf;
2372 unsigned char k;
2373 /* Arg is pointer to file handle pointer */
2374 fpp = (FILE **)arg;
2375 if (*fpp == NULL) {
2376 scripterr(s, c, "no output open");
2378 else {
2379 require_arg(s, c, "file", file);
2380 cf = fopen(file, "rb");
2381 if (cf == NULL) {
2382 scripterr(s, c, "could not open `%s' for reading", file);
2384 else {
2385 verbose(1, " copying `%s' to output at position %ld...", file, ftell(*fpp) );
2386 for (k = fgetc(cf); !feof(cf); k = fgetc(cf) ) {
2387 fputc(k, *fpp);
2389 bank_offset += ftell(cf);
2390 pc += ftell(cf);
2391 fclose(cf);
2392 if (bank_offset > bank_size) {
2393 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2400 * Starts a new bank according to 'bank' script command.
2401 * @param s Linker script
2402 * @param c Command of type BANK_COMMAND
2403 * @param arg Pointer to file handle
2405 static void start_bank(xlnk_script *s, xlnk_script_command *c, void *arg)
2407 const char *size_str;
2408 const char *origin_str;
2409 size_str = xlnk_script_get_command_arg(c, "size");
2410 if (size_str != NULL) {
2411 bank_size = str_to_int(size_str);
2412 if (bank_size <= 0) {
2413 scripterr(s, c, "invalid size");
2416 else {
2417 /* Use bank size of previous bank if there was one */
2418 if (bank_size == 0x7FFFFFFF) {
2419 scripterr(s, c, "no bank size set");
2422 origin_str = xlnk_script_get_command_arg(c, "origin");
2423 if (origin_str != NULL) {
2424 bank_origin = str_to_int(origin_str);
2425 require_arg_in_range(s, c, "origin", bank_origin, 0x0000, 0xFFFF);
2427 else {
2428 /* Use old bank origin */
2430 bank_id++;
2431 bank_offset = 0;
2432 pc = bank_origin;
2436 * Writes unit according to 'link' script command.
2437 * @param s Linker script
2438 * @param c Command of type LINK_COMMAND
2439 * @param arg Pointer to file handle
2441 static void write_unit(xlnk_script *s, xlnk_script_command *c, void *arg)
2443 FILE **fpp;
2444 xunit *xu;
2445 const char *file;
2446 /* Arg is pointer to file handle pointer */
2447 fpp = (FILE **)arg;
2448 if (*fpp == NULL) {
2449 scripterr(s, c, "no output open");
2451 else {
2452 require_arg(s, c, "file", file);
2453 xu = (xunit *)hashtab_get(unit_hash, (void*)file);
2454 verbose(1, " appending unit `%s' to output at position %ld...", file, ftell(*fpp));
2455 write_as_binary(*fpp, xu);
2456 bank_offset += xu->code_size;
2457 if (bank_offset > bank_size) {
2458 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2464 * Pads output file according to 'pad' script command.
2465 * @param s Linker script
2466 * @param c Command of type PAD_COMMAND
2467 * @param arg Pointer to file handle
2469 static void write_pad(xlnk_script *s, xlnk_script_command *c, void *arg)
2471 FILE **fpp;
2472 int i;
2473 int count;
2474 int offset;
2475 int origin;
2476 const char *offset_str;
2477 const char *origin_str;
2478 const char *size_str;
2479 /* Arg is pointer to file handle pointer */
2480 fpp = (FILE **)arg;
2481 if (*fpp == NULL) {
2482 scripterr(s, c, "no output open");
2484 else {
2485 if ((offset_str = xlnk_script_get_command_arg(c, "offset")) != NULL) {
2486 offset = str_to_int(offset_str);
2487 count = offset - bank_offset;
2489 else if ((origin_str = xlnk_script_get_command_arg(c, "origin")) != NULL) {
2490 origin = str_to_int(origin_str);
2491 count = origin - pc;
2493 else if ((size_str = xlnk_script_get_command_arg(c, "size")) != NULL) {
2494 count = str_to_int(size_str);
2496 else {
2497 scripterr(s, c, "missing argument");
2498 count = 0;
2500 if (count < 0) {
2501 scripterr(s, c, "cannot pad backwards");
2502 count = 0;
2504 else if (count > 0) {
2505 verbose(1, " padding %d bytes...", count);
2507 for (i=0; i<count; i++) {
2508 fputc(0, *fpp);
2510 bank_offset += count;
2511 pc += count;
2512 if (bank_offset > bank_size) {
2513 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2519 * Pads to end of bank in file if bank size not reached.
2520 * @param s Linker script
2521 * @param c Command of type BANK_COMMAND
2522 * @param fp File handle
2524 static void maybe_pad_bank(xlnk_script *s, xlnk_script_command *c, FILE *fp)
2526 int i;
2527 if ( (bank_size != 0x7FFFFFFF) && (bank_offset < bank_size) ) {
2528 if (fp == NULL) {
2529 scripterr(s, c, "no output open");
2531 else {
2532 for (i=bank_offset; i<bank_size; i++) {
2533 fputc(0, fp);
2540 * Finishes old bank in output and starts new bank.
2541 * @param s Linker script
2542 * @param c Command of type BANK_COMMAND
2543 * @param arg Pointer to file handle
2545 static void write_bank(xlnk_script *s, xlnk_script_command *c, void *arg)
2547 FILE **fpp;
2548 /* Arg is pointer to file handle pointer */
2549 fpp = (FILE **)arg;
2550 maybe_pad_bank(s, c, *fpp);
2551 start_bank(s, c, arg);
2555 * Generates the final binary output from the linker.
2556 * @param sc Linker script
2558 static void generate_binary_output(xlnk_script *sc)
2560 FILE *fp = NULL;
2561 /* Table of mappings for our purpose */
2562 static xlnk_script_commandprocmap map[] = {
2563 { XLNK_OUTPUT_COMMAND, set_output },
2564 { XLNK_COPY_COMMAND, copy_to_output },
2565 { XLNK_BANK_COMMAND, write_bank },
2566 { XLNK_LINK_COMMAND, write_unit },
2567 { XLNK_PAD_COMMAND, write_pad },
2568 { XLNK_BAD_COMMAND, NULL }
2570 /* Reset offsets */
2571 bank_size = 0x7FFFFFFF;
2572 bank_offset = 0;
2573 bank_origin = 0;
2574 bank_id = -1;
2575 pc = 0;
2576 /* Do the walk */
2577 xlnk_script_walk(sc, map, (void *)&fp);
2578 /* Pad last bank if necessary */
2579 maybe_pad_bank(sc, sc->first_command, fp);
2582 /*--------------------------------------------------------------------------*/
2583 /* Functions for producing assembly code based on a sequential list of
2584 script commands. */
2587 * Sets the output file according to 'output' script command.
2588 * @param s Linker script
2589 * @param c Command of type OUTPUT_COMMAND
2590 * @param arg Pointer to file handle
2592 static void asm_set_output(xlnk_script *s, xlnk_script_command *c, void *arg)
2594 /* No-op when generating assembly. */
2598 * Copies a file to output according to 'copy' script command.
2599 * @param s Linker script
2600 * @param c Command of type COPY_COMMAND
2601 * @param arg Pointer to file handle
2603 static void asm_copy_to_output(xlnk_script *s, xlnk_script_command *c, void *arg)
2605 const char *file;
2606 FILE **fpp;
2607 FILE *cf;
2608 /* Arg is pointer to file handle pointer */
2609 fpp = (FILE **)arg;
2610 require_arg(s, c, "file", file);
2611 cf = fopen(file, "rb");
2612 if (cf == NULL) {
2613 scripterr(s, c, "could not open `%s' for reading", file);
2614 } else {
2615 unsigned char buf[1024];
2616 int count = fread(buf, 1, 1024, cf);
2617 fprintf(*fpp, "; begin %s\n", file);
2618 while (count > 0) {
2619 print_chunk(*fpp, /*label=*/0, buf, count, /*cols=*/16);
2620 count = fread(buf, 1, 1024, cf);
2622 fprintf(*fpp, "; end %s\n", file);
2623 bank_offset += ftell(cf);
2624 pc += ftell(cf);
2625 fclose(cf);
2626 if (bank_offset > bank_size) {
2627 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2633 * Starts a new bank according to 'bank' script command.
2634 * @param s Linker script
2635 * @param c Command of type BANK_COMMAND
2636 * @param arg Pointer to file handle
2638 static void asm_start_bank(xlnk_script *s, xlnk_script_command *c, void *arg)
2640 FILE *fp = *(FILE**)arg;
2641 start_bank(s, c, arg);
2642 fprintf(fp, ".ORG $%.4X\n", pc);
2646 * Writes unit according to 'link' script command.
2647 * @param s Linker script
2648 * @param c Command of type LINK_COMMAND
2649 * @param arg Pointer to file handle
2651 static void asm_write_unit(xlnk_script *s, xlnk_script_command *c, void *arg)
2653 FILE **fpp;
2654 xunit *xu;
2655 const char *file;
2656 /* Arg is pointer to file handle pointer */
2657 fpp = (FILE **)arg;
2658 require_arg(s, c, "file", file);
2659 xu = (xunit *)hashtab_get(unit_hash, (void*)file);
2660 verbose(1, " appending unit `%s' to output at position %ld...", file, ftell(*fpp));
2661 write_as_assembly(*fpp, xu);
2662 bank_offset += xu->code_size;
2663 if (bank_offset > bank_size) {
2664 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2669 * Pads output file according to 'pad' script command.
2670 * @param s Linker script
2671 * @param c Command of type PAD_COMMAND
2672 * @param arg Pointer to file handle
2674 static void asm_write_pad(xlnk_script *s, xlnk_script_command *c, void *arg)
2676 FILE **fpp;
2677 int count;
2678 int offset;
2679 int origin;
2680 const char *offset_str;
2681 const char *origin_str;
2682 const char *size_str;
2683 /* Arg is pointer to file handle pointer */
2684 fpp = (FILE **)arg;
2685 if ((offset_str = xlnk_script_get_command_arg(c, "offset")) != NULL) {
2686 offset = str_to_int(offset_str);
2687 count = offset - bank_offset;
2688 } else if ((origin_str = xlnk_script_get_command_arg(c, "origin")) != NULL) {
2689 origin = str_to_int(origin_str);
2690 count = origin - pc;
2691 } else if ((size_str = xlnk_script_get_command_arg(c, "size")) != NULL) {
2692 count = str_to_int(size_str);
2693 } else {
2694 scripterr(s, c, "missing argument");
2695 count = 0;
2697 if (count < 0) {
2698 scripterr(s, c, "cannot pad backwards");
2699 count = 0;
2700 } else if (count > 0) {
2701 verbose(1, " padding %d bytes...", count);
2703 fprintf(*fpp, ".DSB $%X\n", count);
2704 bank_offset += count;
2705 pc += count;
2706 if (bank_offset > bank_size) {
2707 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2712 * Pads to end of bank in file if bank size not reached.
2713 * @param s Linker script
2714 * @param c Command of type BANK_COMMAND
2715 * @param fp File handle
2717 static void asm_maybe_pad_bank(xlnk_script *s, xlnk_script_command *c, FILE *fp)
2719 if ( (bank_size != 0x7FFFFFFF) && (bank_offset < bank_size) ) {
2720 fprintf(fp, ".DSB $%X\n", bank_size - bank_offset);
2725 * Finishes old bank in output and starts new bank.
2726 * @param s Linker script
2727 * @param c Command of type BANK_COMMAND
2728 * @param arg Pointer to file handle
2730 static void asm_write_bank(xlnk_script *s, xlnk_script_command *c, void *arg)
2732 FILE **fpp = (FILE **)arg;
2733 asm_maybe_pad_bank(s, c, *fpp);
2734 asm_start_bank(s, c, arg);
2737 static void generate_assembly_output(xlnk_script *sc, FILE *fp)
2739 /* Table of mappings for our purpose */
2740 static xlnk_script_commandprocmap map[] = {
2741 { XLNK_OUTPUT_COMMAND, asm_set_output },
2742 { XLNK_COPY_COMMAND, asm_copy_to_output },
2743 { XLNK_BANK_COMMAND, asm_write_bank },
2744 { XLNK_LINK_COMMAND, asm_write_unit },
2745 { XLNK_PAD_COMMAND, asm_write_pad },
2746 { XLNK_BAD_COMMAND, NULL }
2748 /* Reset offsets */
2749 bank_size = 0x7FFFFFFF;
2750 bank_offset = 0;
2751 bank_origin = 0;
2752 bank_id = -1;
2753 pc = 0;
2754 fprintf(fp, ".CODESEG\n");
2755 /* Do the walk */
2756 xlnk_script_walk(sc, map, (void *)&fp);
2757 /* Pad last bank if necessary */
2758 asm_maybe_pad_bank(sc, sc->first_command, fp);
2759 fprintf(fp, ".END\n");
2762 /*--------------------------------------------------------------------------*/
2765 * Increases bank offset and PC according to size of the file specified by
2766 * 'copy' script command.
2767 * @param s Linker script
2768 * @param c Command of type COPY_COMMAND
2769 * @param arg Not used
2771 static void inc_offset_copy(xlnk_script *s, xlnk_script_command *c, void *arg)
2773 const char *file;
2774 FILE *fp;
2775 require_arg(s, c, "file", file);
2776 fp = fopen(file, "rb");
2777 if (fp == NULL) {
2778 scripterr(s, c, "could not open `%s' for reading", file);
2780 else {
2781 fseek(fp, 0, SEEK_END);
2782 bank_offset += ftell(fp);
2783 pc += ftell(fp);
2784 fclose(fp);
2785 if (bank_offset > bank_size) {
2786 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2792 * Checks that all external symbols can be resolved.
2794 static void check_externals(xasm_unit *u)
2796 int i;
2797 for (i = 0; i < u->ext_count; ++i) {
2798 const char *s = u->externals[i].name;
2799 if (!hashtab_get(label_hash, (void*)s)
2800 && !hashtab_get(constant_hash, (void*)s)) {
2801 err("unknown symbol `%s' referenced from %s", s, u->name);
2807 * Sets the origin of a unit and relocates its code to this location.
2808 * @param s Linker script
2809 * @param c Command of type LINK_COMMAND
2810 * @param arg Not used
2812 static void set_unit_origin(xlnk_script *s, xlnk_script_command *c, void *arg)
2814 xunit *xu;
2815 const char *file;
2816 const char *origin_str;
2817 int origin;
2818 require_arg(s, c, "file", file);
2819 xu = (xunit *)hashtab_get(unit_hash, (void*)file);
2820 origin_str = xlnk_script_get_command_arg(c, "origin");
2821 if (origin_str != NULL) {
2822 origin = str_to_int(origin_str);
2823 require_arg_in_range(s, c, "origin", origin, 0x0000, 0xFFFF);
2824 xu->code_origin = origin;
2825 pc = origin;
2827 else {
2828 /* No origin specified. Set to PC. */
2829 xu->code_origin = pc;
2831 xu->bank_id = bank_id;
2832 /* Now we can calculate the physical code addresses of the unit. */
2833 calc_code_addresses(xu);
2834 verbose(1, " unit `%s' relocated to %.4X", xu->_unit_.name, xu->code_origin);
2835 bank_offset += xu->code_size;
2836 check_externals(&xu->_unit_);
2840 * Increases bank offset and PC according to 'pad' script command.
2841 * @param s Linker script
2842 * @param c Command of type PAD_COMMAND
2843 * @param arg Not used
2845 static void inc_offset_pad(xlnk_script *s, xlnk_script_command *c, void *arg)
2847 int count;
2848 int offset;
2849 int origin;
2850 const char *offset_str;
2851 const char *origin_str;
2852 const char *size_str;
2853 if ((offset_str = xlnk_script_get_command_arg(c, "offset")) != NULL) {
2854 offset = str_to_int(offset_str);
2855 count = offset - bank_offset;
2857 else if ((origin_str = xlnk_script_get_command_arg(c, "origin")) != NULL) {
2858 origin = str_to_int(origin_str);
2859 count = origin - pc;
2861 else if ((size_str = xlnk_script_get_command_arg(c, "size")) != NULL) {
2862 count = str_to_int(size_str);
2864 else {
2865 scripterr(s, c, "missing argument");
2866 count = 0;
2868 if (count < 0) {
2869 scripterr(s, c, "cannot pad %d bytes backwards", -count);
2870 count = 0;
2872 bank_offset += count;
2873 pc += count;
2874 if (bank_offset > bank_size) {
2875 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2880 * Relocates code of all units according to script commands and/or their position
2881 * in the final binary.
2882 * @param sc Linker script
2884 static void relocate_units(xlnk_script *sc)
2886 /* Table of mappings for our purpose */
2887 static xlnk_script_commandprocmap map[] = {
2888 { XLNK_COPY_COMMAND, inc_offset_copy },
2889 { XLNK_BANK_COMMAND, start_bank },
2890 { XLNK_LINK_COMMAND, set_unit_origin },
2891 { XLNK_PAD_COMMAND, inc_offset_pad },
2892 { XLNK_BAD_COMMAND, NULL }
2894 /* Reset offsets */
2895 bank_size = 0x7FFFFFFF;
2896 bank_offset = 0;
2897 bank_origin = 0;
2898 bank_id = -1;
2899 pc = 0;
2900 /* Do the walk */
2901 xlnk_script_walk(sc, map, NULL);
2907 static void maybe_print_ram_statistics()
2909 int used;
2910 int left;
2911 if (total_ram > 0) {
2912 left = ram_left();
2913 used = total_ram - left;
2914 verbose(1, " total RAM: %d bytes", total_ram);
2915 verbose(1, " RAM used: %d bytes (%d%%)", used, (int)(((float)used / (float)total_ram)*100.0f) );
2916 verbose(1, " RAM left: %d bytes (%d%%)", left, (int)(((float)left / (float)total_ram)*100.0f) );
2920 /*--------------------------------------------------------------------------*/
2923 * Program entrypoint.
2925 int main(int argc, char **argv)
2927 int i;
2928 xlnk_script sc;
2930 parse_arguments(argc, argv);
2932 suppress = 0;
2933 err_count = 0;
2934 warn_count = 0;
2936 verbose(1, "parsing linker script...");
2937 if (xlnk_script_parse(program_args.input_file, &sc) == 0) {
2938 /* Something bad happened when parsing script, halt */
2939 return(1);
2942 verbose(1, "registering RAM blocks...");
2943 register_ram_blocks(&sc);
2945 constant_hash = hashtab_create(23, HASHTAB_STRKEYHSH, HASHTAB_STRKEYCMP);
2946 label_hash = hashtab_create(23, HASHTAB_STRKEYHSH, HASHTAB_STRKEYCMP);
2947 unit_hash = hashtab_create(11, HASHTAB_STRKEYHSH, HASHTAB_STRKEYCMP);
2949 unit_count = xlnk_script_count_command_type(&sc, XLNK_LINK_COMMAND);
2950 if (unit_count > 0) {
2951 units = (xunit *)malloc( sizeof(xunit) * unit_count );
2952 memset(units, 0, sizeof(xunit) * unit_count);
2954 else {
2955 units = NULL;
2957 verbose(1, "loading units...");
2958 register_units(&sc);
2960 /* Only continue with processing if no unresolved symbols */
2961 if (err_count == 0) {
2962 verbose(1, "calculating data addresses...");
2963 for (i=0; i<unit_count; i++) {
2964 calc_data_addresses(&units[i]);
2967 /* TODO: Count references: go through all instructions, find EXTRN and LOCAL operands in expressions */
2968 /* TODO: Find modes of access for each DATA label (i.e. label MUST be allocated in zero page) */
2970 verbose(1, "mapping data to RAM...");
2971 map_data_to_ram();
2972 maybe_print_ram_statistics();
2974 if (err_count == 0) {
2975 verbose(1, "relocating code...");
2976 suppress = 1;
2977 relocate_units(&sc);
2978 suppress = 0;
2979 relocate_units(&sc);
2981 if (err_count == 0) {
2982 verbose(1, "generating output...");
2983 generate_binary_output(&sc);
2984 if (generate_assembly)
2985 generate_assembly_output(&sc, stdout);
2990 verbose(1, "cleaning up...");
2992 for (i=0; i<unit_count; i++) {
2993 if (units[i].loaded) {
2994 finalize_local_array( &units[i].data_locals );
2995 finalize_local_array( &units[i].code_locals );
2996 xasm_unit_finalize( &units[i]._unit_ );
2999 hashtab_finalize(label_hash);
3000 hashtab_finalize(constant_hash);
3001 hashtab_finalize(unit_hash);
3002 finalize_ram_blocks();
3003 xlnk_script_finalize(&sc);
3005 return (err_count == 0) ? 0 : 1;