allow extrn symbols to be defined by the unit itself (part II)
[xorcyst.git] / xlnk.c
blobfdaaa25b5dad3c8e45fcf18857419be276f29771
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_xlnk_arguments {
139 const char *input_file;
140 const char *output_file;
141 int silent;
142 int verbose;
145 typedef struct tag_xlnk_arguments xlnk_arguments;
147 /* Argument variables set by arg parser. */
148 static xlnk_arguments program_args;
150 /* Long options for getopt_long(). */
151 static struct option long_options[] = {
152 { "output", required_argument, 0, 'o' },
153 { "quiet", no_argument, 0, 'q' },
154 { "silent", no_argument, 0, 's' },
155 { "verbose", no_argument, 0, 'v' },
156 { "help", no_argument, 0, 0 },
157 { "usage", no_argument, 0, 0 },
158 { "version", no_argument, 0, 'V' },
159 { 0 }
162 /* Prints usage message and exits. */
163 static void usage()
165 printf("\
166 Usage: xlnk [-qsvV] [--quiet] [--silent] [--verbose] [--help] [--usage]\n\
167 [-o FILE] [--output=FILE] [--version] FILE\n\
169 exit(0);
172 /* Prints help message and exits. */
173 static void help()
175 printf("\
176 Usage: xlnk [OPTION...] FILE\n\
177 The XORcyst Linker -- it creates quite a stir\n\
179 -o, --output=FILE Output to FILE\n\
180 -q, -s, --quiet, --silent Don't produce any output\n\
181 -v, --verbose Produce verbose output\n\
182 --help Give this help list\n\
183 --usage Give a short usage message\n\
184 -V, --version Print program version\n\
186 Mandatory or optional arguments to long options are also mandatory or optional\n\
187 for any corresponding short options.\n\
189 Report bugs to <kentmhan@gmail.com>.\n\
191 exit(0);
194 /* Prints version and exits. */
195 static void version()
197 printf("%s\n", program_version);
198 exit(0);
201 /* Parses program arguments. */
202 static void
203 parse_arguments (int argc, char **argv)
205 int key;
206 /* getopt_long stores the option index here. */
207 int index = 0;
209 /* Set default values. */
210 program_args.silent = 0;
211 program_args.verbose = 0;
212 program_args.input_file = NULL;
213 program_args.output_file = NULL;
215 while ((key = getopt_long(argc, argv, "o:qsvV", long_options, &index)) != -1) {
216 switch (key) {
217 case 'q': case 's':
218 program_args.silent = 1;
219 break;
221 case 'v':
222 ++program_args.verbose;
223 break;
225 case 'o':
226 program_args.output_file = optarg;
227 break;
229 case 0:
230 /* Use index to differentiate between options */
231 if (strcmp(long_options[index].name, "usage") == 0) {
232 usage();
234 else if (strcmp(long_options[index].name, "help") == 0) {
235 help();
237 break;
239 case 'V':
240 version();
241 break;
243 case '?':
244 /* Error message has been printed by getopt_long */
245 exit(1);
246 break;
248 default:
249 /* Forgot to handle a short option, most likely */
250 exit(1);
251 break;
255 /* Must be one additional argument, which is the input file. */
256 if (argc-1 != optind) {
257 printf("Usage: xlnk [OPTION...] FILE\nTry `xlnk --help' or `xlnk --usage' for more information.\n");
258 exit(1);
260 else {
261 program_args.input_file = argv[optind];
265 /*--------------------------------------------------------------------------*/
266 /* Data structures. */
268 /* Describes a local label in the unit. */
269 struct tag_local
271 char *name; /* NULL if not exported */
272 int resolved; /* 0 initially, set to 1 when phys_addr has been assigned */
273 int virt_addr;
274 int phys_addr;
275 int ref_count;
276 int size;
277 struct tag_xunit *owner;
278 unsigned short align;
279 unsigned char flags;
282 typedef struct tag_local local;
284 /* Describes an array of local labels. */
285 struct tag_local_array
287 local *entries;
288 int size;
291 typedef struct tag_local_array local_array;
294 * eXtended unit, has extra info built from basic unit ++
296 struct tag_xunit
298 xasm_unit _unit_; /* NB!!! "Superclass", must be first field for casting to work */
299 local_array data_locals;
300 local_array code_locals;
301 int bank_id;
302 int data_size;
303 int code_origin;
304 int code_size;
305 int loaded;
308 typedef struct tag_xunit xunit;
311 * Describes a 6502 RAM block available for allocation.
313 struct tag_avail_ram_block
315 int start; /* Start address in 6502 space */
316 int end; /* End address in 6502 space (not inclusive) */
317 struct tag_avail_ram_block *next;
320 typedef struct tag_avail_ram_block avail_ram_block;
322 /** */
323 struct tag_calc_address_args
325 xunit *xu;
326 int index;
329 typedef struct tag_calc_address_args calc_address_args;
331 /** */
332 struct tag_write_binary_args
334 xunit *xu;
335 FILE *fp;
338 typedef struct tag_write_binary_args write_binary_args;
340 /*--------------------------------------------------------------------------*/
342 /** Array containing the units to link. */
343 static xunit *units;
344 /* Number of units in above array. */
345 static int unit_count;
347 /** Holds the current memory address. */
348 static int pc;
350 /** Hash tables used to lookup symbols. */
351 static hashtab *label_hash;
352 static hashtab *constant_hash;
353 static hashtab *unit_hash;
355 /** Number of errors and warnings during linking */
356 static int err_count;
357 static int warn_count;
359 static int suppress;
361 /* Head of the list of available 6502 RAM blocks (for data allocation). */
362 static avail_ram_block *ram_block_head = NULL;
364 /* Total amount of 6502 RAM (bytes) that was registered */
365 static int total_ram = 0;
367 /* Bank info */
368 static int bank_offset;
369 static int bank_size;
370 static int bank_origin;
371 static int bank_id;
373 /* Debug info */
374 static const unsigned char *unit_file = NULL; /* length byte followed by chars */
375 static int unit_line = -1;
377 /* Turn on to produce flat (dis)assembly code. The resulting code can be
378 assembled by xasm using the --pure-binary switch.
379 It's useful for checking that the linker doesn't do anything stupid
380 in binary output mode.
382 static const int generate_assembly = 0;
384 /*--------------------------------------------------------------------------*/
387 * If the object file contains FILE and LINE bytecodes (assembled with
388 * --debug switch), unit_file and unit_line will contain the current
389 * source location. In that case, this function prints the location.
391 static void maybe_print_location()
393 char *str;
394 int len;
395 if (unit_file != NULL) {
396 len = unit_file[0] + 1;
397 str = (char *)malloc(len + 1);
398 strncpy(str, (char *)&unit_file[1], len);
399 str[len] = '\0';
400 fprintf(stderr, "%s:%d: ", str, unit_line);
401 free(str);
406 * If the object doesn't contain FILE and LINE bytecodes,
407 * unit_file will be <code>NULL</code>. In that case, this
408 * function prints a tip about reassembling with --debug switch.
410 static void maybe_print_debug_tip()
412 if (unit_file == NULL) {
413 fprintf(stderr, "\treassemble with --debug switch to obtain source location\n");
418 * Issues an error.
419 * @param fmt format string for printf
421 static void err(const char *fmt, ...)
423 va_list ap;
424 va_start(ap, fmt);
425 if (!suppress) {
426 maybe_print_location();
427 fprintf(stderr, "error: ");
428 vfprintf(stderr, fmt, ap);
429 fprintf(stderr, "\n");
430 maybe_print_debug_tip();
431 err_count++;
433 va_end(ap);
437 * Issues a warning.
438 * @param fmt format string for printf
440 static void warn(const char *fmt, ...)
442 va_list ap;
443 va_start(ap, fmt);
444 if (!suppress) {
445 maybe_print_location();
446 fprintf(stderr, "warning: ");
447 vfprintf(stderr, fmt, ap);
448 fprintf(stderr, "\n");
449 maybe_print_debug_tip();
450 warn_count++;
452 va_end(ap);
456 * Prints a message if --verbose switch was given.
457 * @param level verbosity level
458 * @param fmt format string for printf
460 static void verbose(int level, const char *fmt, ...)
462 va_list ap;
463 va_start(ap, fmt);
464 if (!suppress && program_args.verbose >= level) {
465 vfprintf(stdout, fmt, ap);
466 fprintf(stdout, "\n");
468 va_end(ap);
471 /*--------------------------------------------------------------------------*/
472 /* Functions to manage 6502 RAM blocks. */
473 /* The RAM allocator maintains a list of these blocks that are used to
474 map the contents of the units' data segments to memory.
478 * Calculates number of bytes of 6502 RAM left for allocation.
480 static int ram_left()
482 int sum;
483 avail_ram_block *b;
484 for (sum = 0, b = ram_block_head; b != NULL; b = b->next) {
485 sum += b->end - b->start;
487 return sum;
491 * Adds a block of 6502 memory to the list of available memory regions.
492 * When adding multiple blocks they should be added in prioritized order.
493 * @param start Start address of the block
494 * @param end End address of the block (non-inclusive!)
496 static void add_ram_block(int start, int end)
498 avail_ram_block *b;
499 avail_ram_block *new_block = (avail_ram_block *)malloc( sizeof(avail_ram_block) );
500 if (new_block != NULL) {
501 new_block->start = start;
502 new_block->end = end;
503 new_block->next = NULL;
504 if (ram_block_head == NULL) {
505 /* Start the list */
506 ram_block_head = new_block;
507 } else {
508 /* Add to end */
509 for (b = ram_block_head; b->next != NULL; b = b->next) ;
510 b->next = new_block;
512 verbose(1, " added RAM block: %.4X-%.4X", new_block->start, new_block->end);
517 * Allocates a chunk of 6502 RAM to a local.
518 * @param l Local
519 * @return 0 if there isn't enough RAM to satisfy the request (fail), 1 otherwise (success)
521 static int alloc_ram(local *l)
523 /* Try the available blocks in order. */
524 /* Use the first one that's sufficient. */
525 avail_ram_block *b;
526 avail_ram_block *p = NULL;
527 for (b = ram_block_head; b != NULL; p = b, b = b->next) {
528 int left;
529 int pad;
530 avail_ram_block *n;
531 if (l->flags & XASM_LABEL_FLAG_ZEROPAGE) {
532 if (b->start >= 0x100) {
533 continue; /* This block is no good */
536 left = b->end - b->start;
537 if (left < l->size) {
538 continue; /* Not enough, sorry */
540 if (l->flags & XASM_LABEL_FLAG_ALIGN) {
541 pad = b->start & ((1 << l->align) - 1);
542 if (pad != 0) {
543 /* This block doesn't match the alignment */
544 /* Break it into two blocks if possible */
545 pad = (1 << l->align) - pad;
546 pad = (left < pad) ? left : pad;
547 if (pad < left) {
548 n = (avail_ram_block *)malloc(sizeof(avail_ram_block));
549 n->start = b->start;
550 n->end = n->start + pad;
551 b->start += pad;
552 n->next = b;
553 if (b == ram_block_head) {
554 ram_block_head = n; /* New head */
556 b = n;
558 continue;
561 /* Pick this one. */
562 l->phys_addr = b->start;
563 /* Decrease block size by moving start address ahead */
564 b->start += l->size;
565 /* If there's no more space left in this block, discard it */
566 if (left == l->size) {
567 /* Remove from linked list */
568 if (p == NULL) {
569 /* Set successor block as new head */
570 ram_block_head = b->next;
572 else {
573 /* Squeeze out */
574 p->next = b->next;
576 SAFE_FREE(b);
578 return 1;
580 return 0;
584 * Frees up memory associated with list of RAM blocks.
586 static void finalize_ram_blocks()
588 avail_ram_block *b;
589 avail_ram_block *t;
590 for (b = ram_block_head; b != NULL; b = t) {
591 t = b->next;
592 SAFE_FREE(b);
596 /*--------------------------------------------------------------------------*/
597 /* Functions to get big-endian values from byte buffer. */
599 /* Gets single byte from buffer and increments index. */
600 static unsigned char get_1(const unsigned char *b, int *i)
602 return b[(*i)++];
604 /* Gets big-endian short from buffer and increments index. */
605 static unsigned short get_2(const unsigned char *b, int *i)
607 unsigned short result = get_1(b, i) << 8;
608 result |= get_1(b, i);
609 return result;
611 /* Gets big-endian 24-bit integer from buffer and increments index. */
612 static unsigned int get_3(const unsigned char *b, int *i)
614 unsigned int result = get_2(b, i) << 8;
615 result |= get_1(b, i);
616 return result;
618 /* Gets big-endian int from buffer and increments index. */
619 /*static unsigned int get_4(unsigned char *b, int *i)
621 unsigned int result = get_2(b, i) << 16;
622 result |= get_2(b, i);
623 return result;
626 /*--------------------------------------------------------------------------*/
629 * Calculates the storage occupied by a CMD_LABEL bytecode's arguments.
631 static int label_cmd_args_size(const unsigned char *bytes)
633 int size = 1; /* Smallest possible: flag byte */
634 int flags = bytes[0];
635 if (flags & XASM_LABEL_FLAG_EXPORT) { size += bytes[1] + 1 + 1; } /* Length byte + string */
636 if (flags & XASM_LABEL_FLAG_ALIGN) { size += 1; } /* Alignment */
637 if (flags & XASM_LABEL_FLAG_ADDR) { size += 2; } /* Address */
638 return size;
642 * Walks an array of bytecodes, calling corresponding bytecode handlers
643 * along the way.
644 * @param bytes Array of bytecodes, terminated by CMD_END
645 * @param handlers Array of bytecode handlers (entries can be NULL)
646 * @param arg Argument passed to bytecode handler, can be anything
648 static void bytecode_walk(const unsigned char *bytes, xasm_bytecodeproc *handlers, void *arg)
650 int i;
651 unsigned char cmd;
652 unit_file = NULL;
653 unit_line = -1;
654 if (bytes == NULL) { return; }
655 i = 0;
656 do {
657 cmd = get_1(bytes, &i);
659 /* Check if debug command */
660 if (cmd < XASM_CMD_END) {
661 switch (cmd) {
662 case XASM_CMD_FILE:
663 unit_file = &bytes[i];
664 i += get_1(bytes, &i) + 1; /* Skip count and array of bytes */
665 break;
666 case XASM_CMD_LINE8: unit_line = get_1(bytes, &i); break;
667 case XASM_CMD_LINE16: unit_line = get_2(bytes, &i); break;
668 case XASM_CMD_LINE24: unit_line = get_3(bytes, &i); break;
669 case XASM_CMD_LINE_INC: unit_line++; break;
671 continue;
674 if (handlers[cmd-XASM_CMD_END] != NULL) {
675 handlers[cmd-XASM_CMD_END](&bytes[i-1], arg);
677 /* Skip any bytecode arguments */
678 switch (cmd) {
679 case XASM_CMD_END: break;
680 case XASM_CMD_BIN8: i += get_1(bytes, &i) + 1; break; /* Skip count and array of bytes */
681 case XASM_CMD_BIN16: i += get_2(bytes, &i) + 1; break; /* Skip count and array of bytes */
682 case XASM_CMD_LABEL: i += label_cmd_args_size(&bytes[i]); break; /* Skip flag byte and possibly name and alignment */
683 case XASM_CMD_INSTR: i += 3; break; /* Skip 6502 opcode and 16-bit expr id */
684 case XASM_CMD_DB: i += 2; break; /* Skip 16-bit expr id */
685 case XASM_CMD_DW: i += 2; break; /* Skip 16-bit expr id */
686 case XASM_CMD_DD: i += 2; break; /* Skip 16-bit expr id */
687 case XASM_CMD_DSI8: i += 1; break; /* Skip 8-bit count */
688 case XASM_CMD_DSI16: i += 2; break; /* Skip 16-bit count */
689 case XASM_CMD_DSB: i += 2; break; /* Skip 16-bit expr id */
691 default:
692 err("invalid bytecode");
693 break;
695 } while (cmd != XASM_CMD_END);
698 /*--------------------------------------------------------------------------*/
699 /* Functions for expression evaluation. */
702 * Finalizes a constant.
703 * @param c Constant to finalize
705 /* ### Merge with finalize_constant() in unit.c? */
706 static void finalize_constant(xasm_constant *c)
708 if (c->type == XASM_STRING_CONSTANT) {
709 SAFE_FREE(c->string);
714 * Evaluates an expression recursively.
715 * The result will either be a integer or string literal, indicating successful
716 * evaluation; or an invalid type indicating that a symbol could not be translated
717 * to a constant (in other words, it could not be resolved). In this case,
718 * result->string contains the name of the symbol which couldn't be evaluated.
719 * @param u The unit where the expression is contained
720 * @param e The expression to evaluate
721 * @param result Pointer to resulting value
723 static void eval_recursive(xunit *u, xasm_expression *e, xasm_constant *result)
725 char *s;
726 local *l;
727 xasm_constant *c;
728 xasm_constant lhs_result, rhs_result;
729 switch (e->type) {
730 case XASM_OPERATOR_EXPRESSION:
731 switch (e->op_expr.operator) {
732 /* Binary operators */
733 case XASM_OP_PLUS:
734 case XASM_OP_MINUS:
735 case XASM_OP_MUL:
736 case XASM_OP_DIV:
737 case XASM_OP_MOD:
738 case XASM_OP_SHL:
739 case XASM_OP_SHR:
740 case XASM_OP_AND:
741 case XASM_OP_OR:
742 case XASM_OP_XOR:
743 case XASM_OP_EQ:
744 case XASM_OP_NE:
745 case XASM_OP_LT:
746 case XASM_OP_GT:
747 case XASM_OP_LE:
748 case XASM_OP_GE:
749 /* Evaluate both sides */
750 eval_recursive(u, e->op_expr.lhs, &lhs_result);
751 eval_recursive(u, e->op_expr.rhs, &rhs_result);
752 /* If either side is unresolved, then result is unresolved. */
753 if ((lhs_result.type == -1) || (rhs_result.type == -1)) {
754 result->type = -1;
756 /* If both sides are integer, then result is integer. */
757 else if ((lhs_result.type == XASM_INTEGER_CONSTANT) &&
758 (rhs_result.type == XASM_INTEGER_CONSTANT)) {
759 result->type = XASM_INTEGER_CONSTANT;
760 /* Perform the proper operation to obtain result. */
761 switch (e->op_expr.operator) {
762 case XASM_OP_PLUS: result->integer = lhs_result.integer + rhs_result.integer; break;
763 case XASM_OP_MINUS: result->integer = lhs_result.integer - rhs_result.integer; break;
764 case XASM_OP_MUL: result->integer = lhs_result.integer * rhs_result.integer; break;
765 case XASM_OP_DIV: result->integer = lhs_result.integer / rhs_result.integer; break;
766 case XASM_OP_MOD: result->integer = lhs_result.integer % rhs_result.integer; break;
767 case XASM_OP_SHL: result->integer = lhs_result.integer << rhs_result.integer; break;
768 case XASM_OP_SHR: result->integer = lhs_result.integer >> rhs_result.integer; break;
769 case XASM_OP_AND: result->integer = lhs_result.integer & rhs_result.integer; break;
770 case XASM_OP_OR: result->integer = lhs_result.integer | rhs_result.integer; break;
771 case XASM_OP_XOR: result->integer = lhs_result.integer ^ rhs_result.integer; break;
772 case XASM_OP_EQ: result->integer = lhs_result.integer == rhs_result.integer; break;
773 case XASM_OP_NE: result->integer = lhs_result.integer != rhs_result.integer; break;
774 case XASM_OP_LT: result->integer = lhs_result.integer < rhs_result.integer; break;
775 case XASM_OP_GT: result->integer = lhs_result.integer > rhs_result.integer; break;
776 case XASM_OP_LE: result->integer = lhs_result.integer <= rhs_result.integer; break;
777 case XASM_OP_GE: result->integer = lhs_result.integer >= rhs_result.integer; break;
780 /* If both sides are string... */
781 else if ((lhs_result.type == XASM_STRING_CONSTANT) &&
782 (rhs_result.type == XASM_STRING_CONSTANT)) {
783 switch (e->op_expr.operator) {
784 case XASM_OP_PLUS:
785 /* Concatenate */
786 result->string = (char *)malloc(strlen(lhs_result.string)+strlen(rhs_result.string)+1);
787 if (result->string != NULL) {
788 strcpy(result->string, lhs_result.string);
789 strcat(result->string, rhs_result.string);
790 result->type = XASM_STRING_CONSTANT;
792 break;
794 /* String comparison: using strcmp() */
795 case XASM_OP_EQ: result->integer = strcmp(lhs_result.string, rhs_result.string) == 0; break;
796 case XASM_OP_NE: result->integer = strcmp(lhs_result.string, rhs_result.string) != 0; break;
797 case XASM_OP_LT: result->integer = strcmp(lhs_result.string, rhs_result.string) < 0; break;
798 case XASM_OP_GT: result->integer = strcmp(lhs_result.string, rhs_result.string) > 0; break;
799 case XASM_OP_LE: result->integer = strcmp(lhs_result.string, rhs_result.string) <= 0; break;
800 case XASM_OP_GE: result->integer = strcmp(lhs_result.string, rhs_result.string) >= 0; break;
802 default:
803 /* Not defined operator for string operation... */
804 assert(0);
805 break;
808 else {
809 result->type = -1;
810 err("incompatible operands to `%s' in expression", xasm_operator_to_string(e->op_expr.operator) );
812 /* Discard the operands */
813 finalize_constant(&lhs_result);
814 finalize_constant(&rhs_result);
815 break; /* Binary operator */
817 /* Unary operators */
818 case XASM_OP_NOT:
819 case XASM_OP_NEG:
820 case XASM_OP_LO:
821 case XASM_OP_HI:
822 case XASM_OP_UMINUS:
823 /* Evaluate the single operand */
824 eval_recursive(u, e->op_expr.lhs, &lhs_result);
825 /* If operand is unresolved then result is unresolved. */
826 if (lhs_result.type == -1) {
827 result->type = -1;
829 /* If operand is integer then result is integer. */
830 else if (lhs_result.type == XASM_INTEGER_CONSTANT) {
831 result->type = XASM_INTEGER_CONSTANT;
832 /* Perform the proper operation to obtain result. */
833 switch (e->op_expr.operator) {
834 case XASM_OP_NOT: result->integer = !lhs_result.integer; break;
835 case XASM_OP_NEG: result->integer = ~lhs_result.integer; break;
836 case XASM_OP_LO: result->integer = lhs_result.integer & 0xFF; break;
837 case XASM_OP_HI: result->integer = (lhs_result.integer >> 8) & 0xFF; break;
838 case XASM_OP_UMINUS: result->integer = -lhs_result.integer; break;
841 else {
842 /* Error, invalid operand */
843 err("incompatible operand to `%s' in expression", xasm_operator_to_string(e->op_expr.operator) );
844 result->type = -1;
846 /* Discard the operand */
847 finalize_constant(&lhs_result);
848 break; /* Unary operator */
850 case XASM_OP_BANK:
851 switch (e->op_expr.lhs->type) {
852 case XASM_LOCAL_EXPRESSION:
853 /* Simple, it must be in the same (current) bank */
854 result->integer = bank_id;
855 result->type = XASM_INTEGER_CONSTANT;
856 break;
858 case XASM_EXTERNAL_EXPRESSION:
859 s = u->_unit_.externals[e->op_expr.lhs->extrn_id].name;
860 if ((l = (local *)hashtab_get(label_hash, s)) != NULL) {
861 /* It's a label */
862 result->integer = l->owner->bank_id;
863 result->type = XASM_INTEGER_CONSTANT;
865 else if ((c = (xasm_constant *)hashtab_get(constant_hash, s)) != NULL) {
866 /* It's a constant */
867 result->integer = ((xunit *)c->unit)->bank_id;
868 result->type = XASM_INTEGER_CONSTANT;
870 else {
871 result->type = -1;
873 break;
875 default:
876 result->type = -1;
877 break;
879 break;
881 break;
883 case XASM_INTEGER_EXPRESSION:
884 result->type = XASM_INTEGER_CONSTANT;
885 result->integer = e->integer;
886 break;
888 case XASM_STRING_EXPRESSION:
889 result->string = (char *)malloc(strlen(e->string) + 1);
890 if (result->string != NULL) {
891 strcpy(result->string, e->string);
892 result->type = XASM_STRING_CONSTANT;
894 break;
896 case XASM_LOCAL_EXPRESSION:
897 if (e->local_id >= u->data_locals.size) {
898 /* It's a code local */
899 l = &u->code_locals.entries[e->local_id - u->data_locals.size];
901 else {
902 /* It's a data local */
903 l = &u->data_locals.entries[e->local_id];
905 if (l->resolved) {
906 result->type = XASM_INTEGER_CONSTANT;
907 result->integer = l->phys_addr;
909 else {
910 /* Not resolved (yet, at least) */
911 result->type = -1;
913 break;
915 case XASM_EXTERNAL_EXPRESSION:
916 s = u->_unit_.externals[e->extrn_id].name;
917 if ((l = (local *)hashtab_get(label_hash, s)) != NULL) {
918 /* It's a label */
919 if (l->resolved) {
920 result->type = XASM_INTEGER_CONSTANT;
921 result->integer = l->phys_addr;
923 else {
924 /* Not resolved (yet) */
925 result->type = -1;
928 else if ((c = (xasm_constant *)hashtab_get(constant_hash, s)) != NULL) {
929 switch (c->type) {
930 case XASM_INTEGER_CONSTANT:
931 result->type = XASM_INTEGER_CONSTANT;
932 result->integer = c->integer;
933 break;
935 case XASM_STRING_CONSTANT:
936 result->string = (char *)malloc(strlen(c->string) + 1);
937 if (result->string != NULL) {
938 strcpy(result->string, c->string);
939 result->type = XASM_STRING_CONSTANT;
941 break;
944 else {
945 result->type = -1;
946 err("unknown symbol `%s' referenced from %s", s, u->_unit_.name);
948 break;
950 case XASM_PC_EXPRESSION:
951 result->type = XASM_INTEGER_CONSTANT;
952 result->integer = pc;
953 break;
958 * Evaluates an expression.
959 * @param u The unit where the expression is contained
960 * @param exid The unique ID of the expression
961 * @param result Where to store the result of the evaluation
963 static void eval_expression(xunit *u, int exid, xasm_constant *result)
965 xasm_expression *exp = u->_unit_.expressions[exid];
966 eval_recursive(u, exp, result);
969 /*--------------------------------------------------------------------------*/
970 /* Functions for incrementing PC, with error handling for wraparound. */
973 * Increases PC by amount.
974 * Issues error if the PC wraps around.
976 static void inc_pc(int amount, void *arg)
978 calc_address_args *aargs;
979 if ((pc <= 0x10000) && ((pc+amount) > 0x10000)) {
980 aargs = (calc_address_args *)arg;
981 err("PC went beyond 64K when linking `%s'", aargs->xu->_unit_.name);
983 pc += amount;
987 * Increases PC by 8-bit value immediately following bytecode command.
989 static void inc_pc_count8(const unsigned char *b, void *arg)
991 int i = 1;
992 inc_pc( get_1(b, &i) + 1, arg );
996 * Increases PC by 16-bit value immediately following bytecode command.
998 static void inc_pc_count16(const unsigned char *b, void *arg)
1000 int i = 1;
1001 inc_pc( get_2(b, &i) + 1, arg );
1005 * Increases PC by 1.
1007 static void inc_pc_1(const unsigned char *b, void *arg)
1009 inc_pc( 1, arg );
1013 * Increases PC by 2.
1015 static void inc_pc_2(const unsigned char *b, void *arg)
1017 inc_pc( 2, arg );
1021 * Increases PC by 4.
1023 static void inc_pc_4(const unsigned char *b, void *arg)
1025 inc_pc( 4, arg );
1029 * Increases PC according to size of define data command.
1031 static void inc_pc_dsb(const unsigned char *b, void *arg)
1033 xasm_constant c;
1034 int exid;
1035 calc_address_args *args = (calc_address_args *)arg;
1036 int i = 1;
1037 /* Get expression ID */
1038 exid = get_2(b, &i);
1039 /* Evaluate expression */
1040 eval_expression(args->xu, exid, &c);
1041 /* Handle the result */
1042 if (c.type == XASM_INTEGER_CONSTANT) {
1043 /* An array of bytes will be located here */
1044 /* Advance PC appropriately */
1045 inc_pc( c.integer, arg );
1047 else if (c.type == XASM_STRING_CONSTANT) {
1048 err("unexpected string operand (`%s') to storage directive", c.string);
1050 else {
1051 //err("unresolved symbol");
1052 assert(0);
1055 finalize_constant(&c);
1059 * Increments PC according to the length of this instruction.
1061 static void inc_pc_instr(const unsigned char *b, void *arg)
1063 xasm_constant c;
1064 unsigned char op, t;
1065 int exid;
1066 calc_address_args *args = (calc_address_args *)arg;
1067 /* Get opcode */
1068 int i = 1;
1069 op = get_1(b, &i);
1070 /* Get expression ID */
1071 exid = get_2(b, &i);
1072 /* Evaluate it */
1073 eval_expression(args->xu, exid, &c);
1074 /* Handle the result */
1075 if (c.type == XASM_INTEGER_CONSTANT) {
1076 /* See if it can be reduced to ZP instruction */
1077 if ((c.integer < 0x100) &&
1078 ((t = opcode_zp_equiv(op)) != 0xFF)) {
1079 /* replace op by ZP-version */
1080 op = t;
1081 ((unsigned char*)b)[1] = t;
1084 else if (c.type == XASM_STRING_CONSTANT) {
1085 err("invalid instruction operand (string)");
1087 else {
1088 /* Address not available yet (forward reference). */
1089 //err("unresolved symbol");
1091 /* Advance PC */
1092 inc_pc( opcode_length(op), arg );
1095 /*--------------------------------------------------------------------------*/
1096 /* Functions for writing pure 6502 binary from bytecodes. */
1099 * Writes an array of bytes.
1101 static void write_bin8(const unsigned char *b, void *arg)
1103 int count;
1104 int i;
1105 write_binary_args *args = (write_binary_args *)arg;
1106 i = 1;
1107 count = get_1(b, &i) + 1;
1108 fwrite(&b[i], 1, count, args->fp);
1109 inc_pc( count, arg );
1113 * Writes an array of bytes.
1115 static void write_bin16(const unsigned char *b, void *arg)
1117 int count;
1118 int i;
1119 write_binary_args *args = (write_binary_args *)arg;
1120 i = 1;
1121 count = get_2(b, &i) + 1;
1122 fwrite(&b[i], 1, count, args->fp);
1123 inc_pc( count, arg );
1127 * Writes an instruction.
1129 static void write_instr(const unsigned char *b, void *arg)
1131 xasm_constant c;
1132 unsigned char op;
1133 int i;
1134 int exid;
1135 write_binary_args *args = (write_binary_args *)arg;
1136 /* Get opcode */
1137 i = 1;
1138 op = get_1(b, &i);
1139 assert(opcode_length(op) > 1);
1140 /* Get expression ID */
1141 exid = get_2(b, &i);
1142 /* Evaluate expression */
1143 eval_expression(args->xu, exid, &c);
1144 assert(c.type == XASM_INTEGER_CONSTANT);
1145 /* Write the opcode */
1146 fputc(op, args->fp);
1147 if (opcode_length(op) == 2) {
1148 /* Operand must fit in 1 byte */
1149 /* Check if it's a relative jump */
1150 switch (op) {
1151 case 0x10:
1152 case 0x30:
1153 case 0x50:
1154 case 0x70:
1155 case 0x90:
1156 case 0xB0:
1157 case 0xD0:
1158 case 0xF0:
1159 /* Calculate difference between target and address of next instruction */
1160 c.integer = c.integer - (pc + 2);
1161 /* Make sure jump is in range */
1162 if ( (c.integer < -128) || (c.integer > 127) ) {
1163 err("branch out of range");
1165 /* Make it a byte value */
1166 c.integer &= 0xFF;
1167 break;
1169 if (c.integer >= 0x100) {
1170 err("instruction operand doesn't fit in 1 byte");
1172 else {
1173 /* Write it */
1174 fputc(c.integer, args->fp);
1176 } else {
1177 assert(opcode_length(op) == 3);
1178 /* Operand must fit in 2 bytes */
1179 if (c.integer >= 0x10000) {
1180 err("instruction operand doesn't fit in 2 bytes");
1182 else {
1183 /* Write it, low byte first */
1184 fputc(c.integer, args->fp);
1185 fputc(c.integer >> 8, args->fp);
1188 inc_pc( opcode_length(op), arg );
1192 * Writes a byte, word or dword.
1194 static void write_dx(const unsigned char *b, void *arg)
1196 xasm_constant c;
1197 int i;
1198 int exid;
1199 write_binary_args *args = (write_binary_args *)arg;
1200 /* Get expression ID */
1201 i = 1;
1202 exid = get_2(b, &i);
1203 /* Evaluate expression */
1204 eval_expression(args->xu, exid, &c);
1206 if (c.type == XASM_INTEGER_CONSTANT) {
1207 /* Write low byte */
1208 fputc(c.integer, args->fp);
1209 /* If 2+ bytes, write high ones */
1210 switch (b[0]) {
1211 case XASM_CMD_DB:
1212 if (c.integer > 0xFF) {
1213 warn("`.DB' operand $%X out of range; truncated", c.integer);
1215 break;
1217 case XASM_CMD_DW:
1218 fputc(c.integer >> 8, args->fp);
1219 if (c.integer > 0xFFFF) {
1220 warn("`.DW' operand $%X out of range; truncated", c.integer);
1222 break;
1224 case XASM_CMD_DD:
1225 fputc(c.integer >> 8, args->fp);
1226 fputc(c.integer >> 16, args->fp);
1227 fputc(c.integer >> 24, args->fp);
1228 break;
1230 /* Advance PC */
1231 switch (b[0]) {
1232 case XASM_CMD_DB: inc_pc( 1, arg ); break;
1233 case XASM_CMD_DW: inc_pc( 2, arg ); break;
1234 case XASM_CMD_DD: inc_pc( 4, arg ); break;
1237 else if (c.type == XASM_STRING_CONSTANT) {
1238 for (i=0; i<strlen(c.string); i++) {
1239 /* Write low byte */
1240 fputc(c.string[i], args->fp);
1241 /* If 2+ bytes, write high ones */
1242 switch (b[0]) {
1243 case XASM_CMD_DW:
1244 fputc(0, args->fp);
1245 break;
1247 case XASM_CMD_DD:
1248 fputc(0, args->fp);
1249 fputc(0, args->fp);
1250 fputc(0, args->fp);
1251 break;
1253 /* Advance PC */
1254 switch (b[0]) {
1255 case XASM_CMD_DB: inc_pc( 1, arg ); break;
1256 case XASM_CMD_DW: inc_pc( 2, arg ); break;
1257 case XASM_CMD_DD: inc_pc( 4, arg ); break;
1260 } else {
1261 assert(0);
1264 finalize_constant(&c);
1268 * Writes a series of zeroes.
1270 static void write_dsi8(const unsigned char *b, void *arg)
1272 int count;
1273 int i;
1274 write_binary_args *args = (write_binary_args *)arg;
1275 i = 1;
1276 count = get_1(b, &i) + 1;
1277 for (i=0; i<count; i++) {
1278 fputc(0, args->fp);
1280 inc_pc( count, arg );
1284 * Writes a series of zeroes.
1286 static void write_dsi16(const unsigned char *b, void *arg)
1288 int count;
1289 int i;
1290 write_binary_args *args = (write_binary_args *)arg;
1291 i = 1;
1292 count = get_2(b, &i) + 1;
1293 for (i=0; i<count; i++) {
1294 fputc(0, args->fp);
1296 inc_pc( count, arg );
1300 * Writes a series of zeroes.
1302 static void write_dsb(const unsigned char *b, void *arg)
1304 xasm_constant c;
1305 int i;
1306 int exid;
1307 write_binary_args *args = (write_binary_args *)arg;
1308 /* Get expression ID */
1309 i = 1;
1310 exid = get_2(b, &i);
1311 /* Evaluate expression */
1312 eval_expression(args->xu, exid, &c);
1313 assert(c.type == XASM_INTEGER_CONSTANT);
1314 if (c.integer < 0) {
1315 err("negative count");
1316 } else if (c.integer > 0) {
1317 for (i=0; i<c.integer; i++) {
1318 fputc(0, args->fp);
1320 inc_pc( c.integer, arg );
1325 * Writes a code segment as fully native 6502 code.
1326 * @param fp File handle
1327 * @param u Unit whose code to write
1329 static void write_as_binary(FILE *fp, xunit *u)
1331 write_binary_args args;
1332 /* Table of callback functions for our purpose. */
1333 static xasm_bytecodeproc handlers[] =
1335 NULL, /* CMD_END */
1336 write_bin8, /* CMD_BIN8 */
1337 write_bin16, /* CMD_BIN16 */
1338 NULL, /* CMD_LABEL */
1339 write_instr, /* CMD_INSTR */
1340 write_dx, /* CMD_DB */
1341 write_dx, /* CMD_DW */
1342 write_dx, /* CMD_DD */
1343 write_dsi8, /* CMD_DSI8 */
1344 write_dsi16, /* CMD_DSI16 */
1345 write_dsb /* CMD_DSB */
1347 /* Fill in args */
1348 args.xu = u;
1349 args.fp = fp;
1350 /* Reset PC */
1351 pc = u->code_origin;
1352 /* Do the walk */
1353 bytecode_walk(u->_unit_.codeseg.bytes, handlers, (void *)&args);
1356 /*--------------------------------------------------------------------------*/
1357 /* Functions for writing 6502 assembly from bytecodes. */
1360 Prints \a size bytes of data defined by \a buf to \a out.
1362 static void print_chunk(FILE *out, const char *label,
1363 const unsigned char *buf, int size, int cols)
1365 int i, j, m;
1366 int pos = 0;
1367 if (label)
1368 fprintf(out, "%s:\n", label);
1369 for (i = 0; i < size / cols; ++i) {
1370 fprintf(out, ".DB ");
1371 for (j = 0; j < cols-1; ++j)
1372 fprintf(out, "$%.2X,", buf[pos++]);
1373 fprintf(out, "$%.2X\n", buf[pos++]);
1375 m = size % cols;
1376 if (m > 0) {
1377 fprintf(out, ".DB ");
1378 for (j = 0; j < m-1; ++j)
1379 fprintf(out, "$%.2X,", buf[pos++]);
1380 fprintf(out, "$%.2X\n", buf[pos++]);
1385 * Writes an array of bytes.
1387 static void asm_write_bin8(const unsigned char *b, void *arg)
1389 int count;
1390 int i;
1391 write_binary_args *args = (write_binary_args *)arg;
1392 i = 1;
1393 count = get_1(b, &i) + 1;
1394 // fprintf(args->fp, "; %d byte(s)\n", count);
1395 print_chunk(args->fp, /*label=*/0, &b[i], count, /*cols=*/16);
1396 inc_pc( count, arg );
1400 * Writes an array of bytes.
1402 static void asm_write_bin16(const unsigned char *b, void *arg)
1404 int count;
1405 int i;
1406 write_binary_args *args = (write_binary_args *)arg;
1407 i = 1;
1408 count = get_2(b, &i) + 1;
1409 // fprintf(args->fp, "; %d byte(s)\n", count);
1410 print_chunk(args->fp, /*label=*/0, &b[i], count, /*cols=*/16);
1411 inc_pc( count, arg );
1415 * Writes a label.
1417 static void asm_write_label(const unsigned char *b, void *arg)
1419 unsigned char flags;
1420 int i= 1;
1421 write_binary_args *args = (write_binary_args *)arg;
1422 fprintf(args->fp, "; label");
1423 flags = get_1(b, &i);
1424 if (flags & XASM_LABEL_FLAG_EXPORT) {
1425 char *name;
1426 int len = get_1(b, &i) + 1;
1427 name = (char *)malloc( len + 1 );
1428 assert(name != 0);
1429 memcpy(name, &b[i], len);
1430 name[len] = '\0';
1431 i += len;
1432 fprintf(args->fp, " %s (PC=$%.4X)", name, pc);
1433 free(name);
1434 } else {
1435 fprintf(args->fp, " PC=$%.4X", pc);
1437 fprintf(args->fp, "\n");
1441 * Writes an instruction.
1443 static void asm_write_instr(const unsigned char *b, void *arg)
1445 xasm_constant c;
1446 unsigned char op;
1447 addressing_mode mode;
1448 int i;
1449 int exid;
1450 write_binary_args *args = (write_binary_args *)arg;
1451 /* Get opcode */
1452 i = 1;
1453 op = get_1(b, &i);
1454 assert(opcode_length(op) > 1);
1455 mode = opcode_addressing_mode(op);
1456 assert(mode != INVALID_MODE);
1457 /* Get expression ID */
1458 exid = get_2(b, &i);
1459 /* Evaluate expression */
1460 eval_expression(args->xu, exid, &c);
1461 assert(c.type == XASM_INTEGER_CONSTANT);
1462 /* Write the opcode */
1463 fprintf(args->fp, "%s", opcode_to_string(op));
1464 switch (mode) {
1465 case IMPLIED_MODE:
1466 case ACCUMULATOR_MODE:
1467 break;
1468 case IMMEDIATE_MODE:
1469 fprintf(args->fp, " #$");
1470 break;
1471 case ZEROPAGE_MODE:
1472 case ZEROPAGE_X_MODE:
1473 case ZEROPAGE_Y_MODE:
1474 case ABSOLUTE_MODE:
1475 case ABSOLUTE_X_MODE:
1476 case ABSOLUTE_Y_MODE:
1477 fprintf(args->fp, " $");
1478 break;
1479 case PREINDEXED_INDIRECT_MODE:
1480 case POSTINDEXED_INDIRECT_MODE:
1481 case INDIRECT_MODE:
1482 fprintf(args->fp, " [$");
1483 break;
1484 case RELATIVE_MODE:
1485 fprintf(args->fp, " $");
1486 break;
1487 case INVALID_MODE:
1488 break;
1490 /* Write the operand */
1491 fprintf(args->fp, "%.4X", (unsigned)c.integer);
1492 switch (mode) {
1493 case IMPLIED_MODE:
1494 case ACCUMULATOR_MODE:
1495 case IMMEDIATE_MODE:
1496 case ZEROPAGE_MODE:
1497 break;
1498 case ZEROPAGE_X_MODE:
1499 fprintf(args->fp, ",X");
1500 break;
1501 case ZEROPAGE_Y_MODE:
1502 fprintf(args->fp, ",Y");
1503 break;
1504 case ABSOLUTE_MODE:
1505 break;
1506 case ABSOLUTE_X_MODE:
1507 fprintf(args->fp, ",X");
1508 break;
1509 case ABSOLUTE_Y_MODE:
1510 fprintf(args->fp, ",Y");
1511 break;
1512 case PREINDEXED_INDIRECT_MODE:
1513 fprintf(args->fp, ",X]");
1514 break;
1515 case POSTINDEXED_INDIRECT_MODE:
1516 fprintf(args->fp, "],Y");
1517 break;
1518 case INDIRECT_MODE:
1519 fprintf(args->fp, "]");
1520 break;
1521 case RELATIVE_MODE:
1522 break;
1523 case INVALID_MODE:
1524 break;
1526 fprintf(args->fp, "\n");
1527 inc_pc( opcode_length(op), arg );
1531 * Writes a byte, word or dword.
1533 static void asm_write_dx(const unsigned char *b, void *arg)
1535 xasm_constant c;
1536 int i;
1537 int exid;
1538 write_binary_args *args = (write_binary_args *)arg;
1539 /* Get expression ID */
1540 i = 1;
1541 exid = get_2(b, &i);
1542 /* Evaluate expression */
1543 eval_expression(args->xu, exid, &c);
1544 if (c.type == XASM_INTEGER_CONSTANT) {
1545 switch (b[0]) {
1546 case XASM_CMD_DB:
1547 fprintf(args->fp, ".DB $%.2X", (unsigned)c.integer);
1548 break;
1549 case XASM_CMD_DW:
1550 fprintf(args->fp, ".DW $%.4X", (unsigned)c.integer);
1551 break;
1552 case XASM_CMD_DD:
1553 fprintf(args->fp, ".DD $%.8X", (unsigned)c.integer);
1554 break;
1556 /* Advance PC */
1557 switch (b[0]) {
1558 case XASM_CMD_DB: inc_pc( 1, arg ); break;
1559 case XASM_CMD_DW: inc_pc( 2, arg ); break;
1560 case XASM_CMD_DD: inc_pc( 4, arg ); break;
1562 } else if (c.type == XASM_STRING_CONSTANT) {
1563 int count = strlen(c.string);
1564 switch (b[0]) {
1565 case XASM_CMD_DB:
1566 fprintf(args->fp, ".DB");
1567 break;
1568 case XASM_CMD_DW:
1569 fprintf(args->fp, ".DW");
1570 break;
1571 case XASM_CMD_DD:
1572 fprintf(args->fp, ".DD");
1573 break;
1575 fprintf(args->fp, " \"%s\"", c.string);
1576 /* Advance PC */
1577 switch (b[0]) {
1578 case XASM_CMD_DB: inc_pc( count * 1, arg ); break;
1579 case XASM_CMD_DW: inc_pc( count * 2, arg ); break;
1580 case XASM_CMD_DD: inc_pc( count * 4, arg ); break;
1582 } else {
1583 assert(0);
1585 fprintf(args->fp, "\n");
1586 finalize_constant(&c);
1590 * Writes a series of zeroes.
1592 static void asm_write_dsi8(const unsigned char *b, void *arg)
1594 int count;
1595 int i;
1596 write_binary_args *args = (write_binary_args *)arg;
1597 i = 1;
1598 count = get_1(b, &i) + 1;
1599 fprintf(args->fp, ".DSB $%X\n", count);
1600 inc_pc( count, arg );
1604 * Writes a series of zeroes.
1606 static void asm_write_dsi16(const unsigned char *b, void *arg)
1608 int count;
1609 int i;
1610 write_binary_args *args = (write_binary_args *)arg;
1611 i = 1;
1612 count = get_2(b, &i) + 1;
1613 fprintf(args->fp, ".DSB $%X\n", count);
1614 inc_pc( count, arg );
1618 * Writes a series of zeroes.
1620 static void asm_write_dsb(const unsigned char *b, void *arg)
1622 xasm_constant c;
1623 int i;
1624 int exid;
1625 write_binary_args *args = (write_binary_args *)arg;
1626 /* Get expression ID */
1627 i = 1;
1628 exid = get_2(b, &i);
1629 /* Evaluate expression */
1630 eval_expression(args->xu, exid, &c);
1631 assert(c.type == XASM_INTEGER_CONSTANT);
1632 if (c.integer < 0) {
1633 err("negative count");
1635 else if (c.integer > 0) {
1636 fprintf(args->fp, ".DSB $%X\n", (unsigned)c.integer);
1637 inc_pc( c.integer, arg );
1642 * Writes a code segment as fully native 6502 code.
1643 * @param fp File handle
1644 * @param u Unit whose code to write
1646 static void write_as_assembly(FILE *fp, xunit *u)
1648 write_binary_args args;
1649 /* Table of callback functions for our purpose. */
1650 static xasm_bytecodeproc handlers[] =
1652 NULL, /* CMD_END */
1653 asm_write_bin8, /* CMD_BIN8 */
1654 asm_write_bin16, /* CMD_BIN16 */
1655 asm_write_label, /* CMD_LABEL */
1656 asm_write_instr, /* CMD_INSTR */
1657 asm_write_dx, /* CMD_DB */
1658 asm_write_dx, /* CMD_DW */
1659 asm_write_dx, /* CMD_DD */
1660 asm_write_dsi8, /* CMD_DSI8 */
1661 asm_write_dsi16, /* CMD_DSI16 */
1662 asm_write_dsb /* CMD_DSB */
1664 /* Fill in args */
1665 args.xu = u;
1666 args.fp = fp;
1667 /* Reset PC */
1668 pc = u->code_origin;
1669 fprintf(fp, "; ***************************************\n");
1670 fprintf(fp, "; * %s, PC=$%.4X\n", u->_unit_.name, pc);
1671 fprintf(fp, "; ***************************************\n");
1672 /* Do the walk */
1673 bytecode_walk(u->_unit_.codeseg.bytes, handlers, (void *)&args);
1676 #define XLNK_NO_DEBUG
1677 #ifndef XLNK_NO_DEBUG
1679 /*--------------------------------------------------------------------------*/
1680 /* Functions for debugging bytecodes. */
1683 * Gets string representation of bytecode command.
1684 * @param cmd CMD_*
1685 * @return String representation ("CMD_*")
1687 static const char *bytecode_to_string(unsigned char cmd)
1689 switch (cmd) {
1690 case XASM_CMD_FILE: return "CMD_FILE";
1691 case XASM_CMD_LINE8: return "CMD_LINE8";
1692 case XASM_CMD_LINE16:return "CMD_LINE16";
1693 case XASM_CMD_LINE24:return "CMD_LINE24";
1694 case XASM_CMD_LINE_INC: return "CMD_LINE_INC";
1695 case XASM_CMD_END: return "CMD_END";
1696 case XASM_CMD_BIN8: return "CMD_BIN8";
1697 case XASM_CMD_BIN16: return "CMD_BIN16";
1698 case XASM_CMD_LABEL: return "CMD_LABEL";
1699 case XASM_CMD_INSTR: return "CMD_INSTR";
1700 case XASM_CMD_DB: return "CMD_DB";
1701 case XASM_CMD_DW: return "CMD_DW";
1702 case XASM_CMD_DD: return "CMD_DD";
1703 case XASM_CMD_DSI8: return "CMD_DSI8";
1704 case XASM_CMD_DSI16: return "CMD_DSI16";
1705 case XASM_CMD_DSB: return "CMD_DSB";
1707 return "bytecode_to_string: invalid bytecode";
1711 * Print a bytecode.
1712 * @param b Bytecodes
1713 * @param arg Not used
1715 static void print_it(const unsigned char *b, void *arg)
1717 printf("%s\n", bytecode_to_string(b[0]) );
1721 * Prints bytecodes.
1722 * @param bytes Bytecodes
1724 static void print_bytecodes(const unsigned char *bytes)
1726 static xasm_bytecodeproc handlers[] =
1728 print_it,print_it,print_it,print_it,print_it,
1729 print_it,print_it,print_it,print_it,print_it,
1730 print_it,print_it,print_it
1732 bytecode_walk(bytes, handlers, NULL);
1736 * Prints a unit.
1737 * @param u Unit
1739 static void print_unit(xasm_unit *u)
1741 print_bytecodes(u->dataseg.bytes);
1742 print_bytecodes(u->codeseg.bytes);
1745 #endif /* !XLNK_NO_DEBUG */
1747 /*--------------------------------------------------------------------------*/
1748 /* Functions for managing arrays of unit locals. */
1751 * Creates array of locals.
1752 * @param size Number of locals
1753 * @param la Local array
1755 static void create_local_array(int size, local_array *la)
1757 la->size = size;
1758 if (size > 0) {
1759 la->entries = (local *)malloc(sizeof(local) * size);
1761 else {
1762 la->entries = NULL;
1767 * Finalizes array of locals.
1769 static void finalize_local_array(local_array *la)
1771 int i;
1772 for (i=0; i<la->size; i++) {
1773 SAFE_FREE(la->entries[i].name);
1775 SAFE_FREE(la->entries);
1778 /*--------------------------------------------------------------------------*/
1779 /* Functions for counting and registering locals in a unit. */
1780 /* In bytecode expressions, locals are referred to by their index.
1781 In order to not have to go through the bytecodes every time to
1782 find a label definition, the following functions build an array
1783 of structures that can be indexed by the local ID to obtain its
1784 information.
1788 * Counts this local.
1790 static void count_one_local(const unsigned char *b, void *arg)
1792 /* Argument points to the counter */
1793 int *count = (int *)arg;
1794 (*count)++;
1798 * Counts the number of locals (labels) in an array of bytecodes.
1799 * @param b Bytecodes, terminated by CMD_END
1800 * @return Number of locals counted
1802 static int count_locals(const unsigned char *b)
1804 int count;
1805 /* Table of callback functions for our purpose. */
1806 static xasm_bytecodeproc handlers[] =
1808 NULL, /* CMD_END */
1809 NULL, /* CMD_BIN8 */
1810 NULL, /* CMD_BIN16 */
1811 count_one_local, /* CMD_LABEL */
1812 NULL, /* CMD_INSTR */
1813 NULL, /* CMD_DB */
1814 NULL, /* CMD_DW */
1815 NULL, /* CMD_DD */
1816 NULL, /* CMD_DSI8 */
1817 NULL, /* CMD_DSI16 */
1818 NULL /* CMD_DSB */
1820 count = 0;
1821 bytecode_walk(b, handlers, (void *)&count);
1822 return count;
1826 * Variable that points to the unit that locals are being registered for.
1828 static xunit *reg_unit = NULL;
1831 * Puts this local into array of locals for current unit.
1833 static void register_one_local(const unsigned char *b, void *arg)
1835 int len;
1836 int i= 1;
1837 /* Argument points to a pointer which points to the local struct to fill in */
1838 local **lpptr = (local **)arg;
1839 local *lptr = *lpptr;
1840 /* Initialize some fields */
1841 lptr->resolved = 0;
1842 lptr->ref_count = 0;
1843 lptr->name = NULL;
1844 lptr->align = 1;
1845 lptr->owner = reg_unit;
1846 /* Get flag byte */
1847 lptr->flags = get_1(b, &i);
1848 /* Test export flag */
1849 if (lptr->flags & XASM_LABEL_FLAG_EXPORT) {
1850 /* Get the length of the name */
1851 len = get_1(b, &i) + 1;
1852 /* Allocate space for name */
1853 lptr->name = (char *)malloc( len + 1 );
1854 if (lptr->name != NULL) {
1855 /* Copy name from bytecodes */
1856 memcpy(lptr->name, &b[i], len);
1857 /* Zero-terminate string */
1858 lptr->name[len] = '\0';
1860 i += len;
1862 if (lptr->flags & XASM_LABEL_FLAG_ALIGN) {
1863 lptr->align = get_1(b, &i);
1865 if (lptr->flags & XASM_LABEL_FLAG_ADDR) {
1866 lptr->phys_addr = get_2(b, &i);
1867 lptr->resolved = 1;
1869 #if 0
1870 if (program_args.verbose) {
1871 verbose(1, " %s align=%d resolved=%d",
1872 lptr->name ? lptr->name : "(anonymous)",
1873 lptr->align, lptr->resolved);
1875 #endif
1876 /* Point to next local in array */
1877 *lpptr += 1;
1881 * Puts all locals found in the array of bytecodes into array.
1882 * @param b Bytecodes, terminated by CMD_END
1883 * @param la Pointer to array to receive locals
1884 * @param xu Owner unit
1886 static void register_locals(const unsigned char *b, local_array *la, xunit *xu)
1888 local *lptr;
1889 local **lpptr;
1890 /* Table of callback functions for our purpose. */
1891 static xasm_bytecodeproc handlers[] =
1893 NULL, /* CMD_END */
1894 NULL, /* CMD_BIN8 */
1895 NULL, /* CMD_BIN16 */
1896 register_one_local, /* CMD_LABEL */
1897 NULL, /* CMD_INSTR */
1898 NULL, /* CMD_DB */
1899 NULL, /* CMD_DW */
1900 NULL, /* CMD_DD */
1901 NULL, /* CMD_DSI8 */
1902 NULL, /* CMD_DSI16 */
1903 NULL /* CMD_DSB */
1905 /* Create array of locals */
1906 create_local_array(count_locals(b), la);
1907 /* Prepare args */
1908 lptr = la->entries;
1909 lpptr = &lptr;
1910 reg_unit = xu;
1911 /* Go! */
1912 bytecode_walk(b, handlers, (void *)lpptr);
1915 /*--------------------------------------------------------------------------*/
1916 /* Functions for entering exported symbols into proper hash table. */
1919 * Enters an exported symbol into a hash table.
1920 * @param tab Hash table to enter it into
1921 * @param key Key
1922 * @param data Data
1923 * @param u Owner unit
1925 static void enter_exported_symbol(hashtab *tab, void *key, void *data, xasm_unit *u)
1927 if ((hashtab_get(label_hash, key) != NULL)
1928 || (hashtab_get(constant_hash, key) != NULL) ) {
1929 err("duplicate symbol `%s' exported from unit `%s'", (char *)key, u->name);
1931 else {
1932 verbose(1, " %s", (char*)key);
1933 hashtab_put(tab, key, data);
1938 * Enters all constants in a unit into the proper hash table.
1939 * @param u Unit whose constants to enter
1941 static void enter_exported_constants(xasm_unit *u)
1943 int i;
1944 xasm_constant *c;
1945 for (i=0; i<u->const_count; i++) {
1946 c = &u->constants[i];
1947 enter_exported_symbol(constant_hash, (void *)c->name, (void *)c, u);
1952 * Enters locals which should be globally visible into the proper hash table.
1953 * @param la Array of locals
1954 * @param u Owner unit
1956 static void enter_exported_locals(local_array *la, xasm_unit *u)
1958 int i;
1959 local *l;
1960 for (i=0; i<la->size; i++) {
1961 l = &la->entries[i];
1962 /* If it has a name, it is exported */
1963 if (l->name != NULL) {
1964 enter_exported_symbol(label_hash, (void *)l->name, (void *)l, u);
1969 /*--------------------------------------------------------------------------*/
1970 /* Functions for calculating addresses of data labels in a unit. */
1973 * Sets the virtual address of this local to current PC value.
1975 static void set_data_address(const unsigned char *b, void *arg)
1977 calc_address_args *args = (calc_address_args *)arg;
1978 local *l = &args->xu->data_locals.entries[args->index];
1979 if (!l->resolved) {
1980 l->virt_addr = pc;
1981 verbose(2, " %.4X %s", l->virt_addr, l->name ? l->name : "");
1983 /* Increase label index */
1984 args->index++;
1988 * Calculates addresses of labels in a data segment relative to 0.
1989 * Only a small set of bytecode commands are allowed in a data segment:
1990 * - label (which we want to assign a virtual address)
1991 * - storage (constant or variable)
1993 static void calc_data_addresses(xunit *u)
1995 calc_address_args args;
1996 /* Table of callback functions for our purpose. */
1997 static xasm_bytecodeproc handlers[] =
1999 NULL, /* CMD_END */
2000 NULL, /* CMD_BIN8 */
2001 NULL, /* CMD_BIN16 */
2002 set_data_address, /* CMD_LABEL */
2003 NULL, /* CMD_INSTR */
2004 NULL, /* CMD_DB */
2005 NULL, /* CMD_DW */
2006 NULL, /* CMD_DD */
2007 inc_pc_count8, /* CMD_DSI8 */
2008 inc_pc_count16, /* CMD_DSI16 */
2009 inc_pc_dsb /* CMD_DSB */
2011 /* Fill in args */
2012 args.xu = u;
2013 args.index = 0;
2014 /* Reset PC */
2015 pc = 0;
2016 verbose(1, " %s", u->_unit_.name);
2017 /* Map away! */
2018 bytecode_walk(u->_unit_.dataseg.bytes, handlers, (void *)&args);
2019 /* Store the end address, which is the total size of data */
2020 u->data_size = pc;
2023 /*--------------------------------------------------------------------------*/
2025 /* Constructs 32-bit sort key for local. */
2026 #define SORT_KEY(l) (unsigned long)((((l)->flags & XASM_LABEL_FLAG_ZEROPAGE) << 30) | ((l)->align << 24) | (0x10000-(l)->size))
2029 * Array is sorted from high to low value.
2031 static int label_partition(local **a, int p, int r)
2033 int x;
2034 int i;
2035 int j;
2036 x = SORT_KEY(a[r]);
2037 i = p - 1;
2038 local *temp;
2039 for (j=p; j<r; j++) {
2040 if (SORT_KEY(a[j]) >= x) {
2041 i = i + 1;
2042 temp = a[i];
2043 a[i] = a[j];
2044 a[j] = temp;
2047 temp = a[i+1];
2048 a[i+1] = a[r];
2049 a[r] = temp;
2050 return i + 1;
2054 * Quicksort implementation used to sort array of pointers to locals.
2056 static void label_qsort(local **a, int p, int r)
2058 int q;
2059 if (p < r) {
2060 q = label_partition(a, p, r);
2061 label_qsort(a, p, q-1);
2062 label_qsort(a, q+1, r);
2067 * Maps all data labels to 6502 RAM locations.
2068 * This is a very important function. It takes all the data labels from all
2069 * the loaded units and attempts to assign them unique physical addresses.
2070 * The list of target RAM blocks given in the linker script is the premise.
2072 static void map_data_to_ram()
2074 int i, k;
2075 local **total_order;
2076 local *l;
2077 int count;
2078 /* Use a bit array to keep track of allocations,
2079 to ensure that there is no overlap */
2080 unsigned char *allocated;
2081 int ram_base, ram_end;
2082 if (ram_block_head == NULL)
2083 return; /* Nothing to do. */
2085 avail_ram_block *b;
2086 ram_base = 10000000;
2087 ram_end = -10000000;
2088 for (b = ram_block_head; b != NULL; b = b->next) {
2089 if (b->start < ram_base)
2090 ram_base = b->start;
2091 if (b->end > ram_end)
2092 ram_end = b->end;
2095 allocated = (unsigned char *)malloc(((ram_end - ram_base) + 7) / 8);
2096 memset(allocated, 0, ((ram_end - ram_base) + 7) / 8);
2097 /* Calculate total number of labels to map */
2098 count = 0;
2099 for (i=0; i<unit_count; i++) {
2100 count += units[i].data_locals.size;
2102 /* Put pointers to all data labels in one big array */
2103 total_order = (local **)malloc( count * sizeof(local *) );
2104 for (i=0, k=0; i<unit_count; i++) {
2105 int j;
2106 local_array *la;
2107 la = &units[i].data_locals;
2108 for (j=0; j<la->size; j++) {
2109 int size;
2110 /* Use virtual addresses to calculate size from this label to next */
2111 if (j == la->size-1) {
2112 size = units[i].data_size;
2114 else {
2115 size = la->entries[j+1].virt_addr;
2117 la->entries[j].size = size - la->entries[j].virt_addr;
2118 /* Put pointer in array */
2119 total_order[k++] = &la->entries[j];
2122 /* Sort them */
2123 label_qsort(total_order, 0, count-1);
2124 /* Map them */
2125 for (i=0; i<count; i++) {
2126 l = total_order[i];
2127 /* Try to allocate it */
2128 if (alloc_ram(l) == 1) {
2129 /* Good, label mapped successfully */
2130 l->resolved = 1;
2131 verbose(1, " %.4X-%.4X %s (%s)", l->phys_addr,
2132 l->phys_addr + l->size-1, l->name ? l->name : "",
2133 l->owner->_unit_.name);
2135 /* Verify that there's no overlap with other variable */
2136 int a;
2137 for (a = l->phys_addr; a < l->phys_addr + l->size; ++a) {
2138 assert((allocated[(a - ram_base) / 8] & (1 << (a & 7))) == 0);
2139 allocated[(a - ram_base) / 8] |= 1 << (a & 7);
2143 else {
2144 err("out of 6502 RAM while allocating unit `%s'", l->owner->_unit_.name);
2145 return;
2148 free(total_order);
2149 free(allocated);
2152 /*--------------------------------------------------------------------------*/
2153 /* Functions for calculating offsets of code labels in a unit. */
2156 * Sets the address of this code label to current PC.
2158 static void set_code_address(const unsigned char *b, void *arg)
2160 calc_address_args *args = (calc_address_args *)arg;
2161 local *l = &args->xu->code_locals.entries[args->index];
2162 if (!l->resolved) {
2163 l->phys_addr = pc;
2164 l->resolved = 1;
2165 if (program_args.verbose) {
2166 fprintf(stdout, " %.4X %s (%s)\n", l->phys_addr,
2167 l->name ? l->name : "", l->owner->_unit_.name);
2170 /* Increase label index */
2171 args->index++;
2175 * Calculates addresses of code labels in a segment.
2176 * NOTE: Only the virtual addresses (relative to 0) are calculated.
2177 * The labels then need to be relocated to obtain the physical address (see below).
2178 * @param u Unit
2180 static void calc_code_addresses(xunit *u)
2182 calc_address_args args;
2183 /* Table of callback functions for our purpose. */
2184 static xasm_bytecodeproc handlers[] =
2186 NULL, /* CMD_END */
2187 inc_pc_count8, /* CMD_BIN8 */
2188 inc_pc_count16, /* CMD_BIN16 */
2189 set_code_address, /* CMD_LABEL */
2190 inc_pc_instr, /* CMD_INSTR */
2191 inc_pc_1, /* CMD_DB -- TODO, error if string */
2192 inc_pc_2, /* CMD_DW */
2193 inc_pc_4, /* CMD_DD */
2194 inc_pc_count8, /* CMD_DSI8 */
2195 inc_pc_count16, /* CMD_DSI16 */
2196 inc_pc_dsb /* CMD_DSB */
2198 /* Fill in args */
2199 args.xu = u;
2200 args.index = 0;
2201 /* Do the walk */
2202 bytecode_walk(u->_unit_.codeseg.bytes, handlers, (void *)&args);
2203 /* Store the total size of code */
2204 u->code_size = pc - u->code_origin;
2207 /*--------------------------------------------------------------------------*/
2210 * Issues a script error.
2212 static void scripterr(xlnk_script *s, xlnk_script_command *c, const char *fmt, ...)
2214 va_list ap;
2215 va_start(ap, fmt);
2217 if (!suppress) {
2218 fprintf(stderr, "error: %s:%d: `%s': ", s->name, c->line, xlnk_script_command_type_to_string(c->type) );
2219 vfprintf(stderr, fmt, ap);
2220 fprintf(stderr, "\n");
2221 err_count++;
2223 va_end(ap);
2226 #define require_arg(s, c, a, d) { \
2227 d = xlnk_script_get_command_arg(c, a); \
2228 if (d == NULL) { \
2229 scripterr(s, c, "missing argument `%s'", a); \
2230 return; \
2234 #define require_arg_in_range(s, c, a, v, l, h) { \
2235 if (((v) < (l)) || ((v) > (h))) { \
2236 scripterr(s, c, "value of argument `%s' is out of range", a); \
2237 return; \
2241 /*--------------------------------------------------------------------------*/
2242 /* Functions for registering RAM blocks in script. */
2245 * Registers one RAM block based on 'ram' script command.
2246 * @param s Linker script
2247 * @param c Command of type RAM_COMMAND
2248 * @param arg Not used
2250 static void register_one_ram_block(xlnk_script *s, xlnk_script_command *c, void *arg)
2252 int start;
2253 int end;
2254 const char *start_str;
2255 const char *end_str;
2256 require_arg(s, c, "start", start_str);
2257 require_arg(s, c, "end", end_str);
2258 start = str_to_int(start_str);
2259 end = str_to_int(end_str);
2260 require_arg_in_range(s, c, "start", start, 0x0000, 0xFFFF);
2261 require_arg_in_range(s, c, "end", end, 0x0000, 0xFFFF);
2262 if (end <= start) {
2263 scripterr(s, c, "`end' is smaller than `start'");
2265 add_ram_block(start, end);
2269 * Registers RAM blocks based on 'ram' commands in a script.
2270 * @param sc Linker script
2272 static void register_ram_blocks(xlnk_script *sc)
2274 /* Table of mappings for our purpose */
2275 static xlnk_script_commandprocmap map[] = {
2276 { XLNK_RAM_COMMAND, register_one_ram_block },
2277 { XLNK_BAD_COMMAND, NULL }
2279 /* Do the walk */
2280 xlnk_script_walk(sc, map, NULL);
2281 /* Calculate total RAM size */
2282 total_ram = ram_left();
2285 /*--------------------------------------------------------------------------*/
2286 /* Functions for loading and initial processing of units in script. */
2289 * Registers (parses etc.) one unit based on 'link' script command.
2290 * @param s Linker script
2291 * @param c Command of type LINK_COMMAND
2292 * @param arg Pointer to unit index
2294 static void register_one_unit(xlnk_script *s, xlnk_script_command *c, void *arg)
2296 const char *file;
2297 int *i;
2298 xunit *xu;
2299 require_arg(s, c, "file", file);
2300 /* arg is pointer to unit index */
2301 i = (int *)arg;
2302 /* Get pointer to xunit to fill in */
2303 xu = &units[*i];
2304 /* Read basic unit from file */
2305 if (xasm_unit_read(file, &xu->_unit_) == 0) {
2306 scripterr(s, c, "failed to load unit `%s'", file);
2307 xu->loaded = 0;
2308 return;
2310 xu->loaded = 1;
2311 verbose(1, " unit `%s' loaded", file);
2313 verbose(1, " registering local symbols...");
2314 register_locals(xu->_unit_.dataseg.bytes, &xu->data_locals, xu);
2315 register_locals(xu->_unit_.codeseg.bytes, &xu->code_locals, xu);
2317 verbose(1, " registering public symbols...");
2318 enter_exported_constants(&xu->_unit_);
2319 enter_exported_locals(&xu->data_locals, &xu->_unit_);
2320 enter_exported_locals(&xu->code_locals, &xu->_unit_);
2322 hashtab_put(unit_hash, (void*)file, xu);
2323 /* Increment unit index */
2324 (*i)++;
2328 * Registers units based on 'link' commands in script.
2329 * @param sc Linker script
2331 static void register_units(xlnk_script *sc)
2333 /* Table of mappings for our purpose */
2334 static xlnk_script_commandprocmap map[] = {
2335 { XLNK_LINK_COMMAND, register_one_unit },
2336 { XLNK_BAD_COMMAND, NULL }
2338 int i = 0;
2339 /* Do the walk */
2340 xlnk_script_walk(sc, map, (void *)&i);
2343 /*--------------------------------------------------------------------------*/
2344 /* Functions for composing a binary file based on a sequential list of
2345 script commands. */
2348 * Sets the output file according to 'output' script command.
2349 * @param s Linker script
2350 * @param c Command of type OUTPUT_COMMAND
2351 * @param arg Pointer to file handle
2353 static void set_output(xlnk_script *s, xlnk_script_command *c, void *arg)
2355 const char *file;
2356 FILE **fpp;
2357 require_arg(s, c, "file", file);
2358 /* Arg is pointer to file handle pointer */
2359 fpp = (FILE **)arg;
2360 if (*fpp != NULL) {
2361 fclose(*fpp);
2363 *fpp = fopen(file, "wb");
2364 if (*fpp == NULL) {
2365 scripterr(s, c, "could not open `%s' for writing", file);
2367 else {
2368 verbose(1, " output goes to `%s'", file);
2373 * Copies a file to output according to 'copy' script command.
2374 * @param s Linker script
2375 * @param c Command of type COPY_COMMAND
2376 * @param arg Pointer to file handle
2378 static void copy_to_output(xlnk_script *s, xlnk_script_command *c, void *arg)
2380 const char *file;
2381 FILE **fpp;
2382 FILE *cf;
2383 unsigned char k;
2384 /* Arg is pointer to file handle pointer */
2385 fpp = (FILE **)arg;
2386 if (*fpp == NULL) {
2387 scripterr(s, c, "no output open");
2389 else {
2390 require_arg(s, c, "file", file);
2391 cf = fopen(file, "rb");
2392 if (cf == NULL) {
2393 scripterr(s, c, "could not open `%s' for reading", file);
2395 else {
2396 verbose(1, " copying `%s' to output at position %ld...", file, ftell(*fpp) );
2397 for (k = fgetc(cf); !feof(cf); k = fgetc(cf) ) {
2398 fputc(k, *fpp);
2400 bank_offset += ftell(cf);
2401 pc += ftell(cf);
2402 fclose(cf);
2403 if (bank_offset > bank_size) {
2404 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2411 * Starts a new bank according to 'bank' script command.
2412 * @param s Linker script
2413 * @param c Command of type BANK_COMMAND
2414 * @param arg Pointer to file handle
2416 static void start_bank(xlnk_script *s, xlnk_script_command *c, void *arg)
2418 const char *size_str;
2419 const char *origin_str;
2420 size_str = xlnk_script_get_command_arg(c, "size");
2421 if (size_str != NULL) {
2422 bank_size = str_to_int(size_str);
2423 if (bank_size <= 0) {
2424 scripterr(s, c, "invalid size");
2427 else {
2428 /* Use bank size of previous bank if there was one */
2429 if (bank_size == 0x7FFFFFFF) {
2430 scripterr(s, c, "no bank size set");
2433 origin_str = xlnk_script_get_command_arg(c, "origin");
2434 if (origin_str != NULL) {
2435 bank_origin = str_to_int(origin_str);
2436 require_arg_in_range(s, c, "origin", bank_origin, 0x0000, 0xFFFF);
2438 else {
2439 /* Use old bank origin */
2441 bank_id++;
2442 bank_offset = 0;
2443 pc = bank_origin;
2447 * Writes unit according to 'link' script command.
2448 * @param s Linker script
2449 * @param c Command of type LINK_COMMAND
2450 * @param arg Pointer to file handle
2452 static void write_unit(xlnk_script *s, xlnk_script_command *c, void *arg)
2454 FILE **fpp;
2455 xunit *xu;
2456 const char *file;
2457 /* Arg is pointer to file handle pointer */
2458 fpp = (FILE **)arg;
2459 if (*fpp == NULL) {
2460 scripterr(s, c, "no output open");
2462 else {
2463 require_arg(s, c, "file", file);
2464 xu = (xunit *)hashtab_get(unit_hash, (void*)file);
2465 verbose(1, " appending unit `%s' to output at position %ld...", file, ftell(*fpp));
2466 write_as_binary(*fpp, xu);
2467 bank_offset += xu->code_size;
2468 if (bank_offset > bank_size) {
2469 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2475 * Pads output file according to 'pad' script command.
2476 * @param s Linker script
2477 * @param c Command of type PAD_COMMAND
2478 * @param arg Pointer to file handle
2480 static void write_pad(xlnk_script *s, xlnk_script_command *c, void *arg)
2482 FILE **fpp;
2483 int i;
2484 int count;
2485 int offset;
2486 int origin;
2487 const char *offset_str;
2488 const char *origin_str;
2489 const char *size_str;
2490 /* Arg is pointer to file handle pointer */
2491 fpp = (FILE **)arg;
2492 if (*fpp == NULL) {
2493 scripterr(s, c, "no output open");
2495 else {
2496 if ((offset_str = xlnk_script_get_command_arg(c, "offset")) != NULL) {
2497 offset = str_to_int(offset_str);
2498 count = offset - bank_offset;
2500 else if ((origin_str = xlnk_script_get_command_arg(c, "origin")) != NULL) {
2501 origin = str_to_int(origin_str);
2502 count = origin - pc;
2504 else if ((size_str = xlnk_script_get_command_arg(c, "size")) != NULL) {
2505 count = str_to_int(size_str);
2507 else {
2508 scripterr(s, c, "missing argument");
2509 count = 0;
2511 if (count < 0) {
2512 scripterr(s, c, "cannot pad backwards");
2513 count = 0;
2515 else if (count > 0) {
2516 verbose(1, " padding %d bytes...", count);
2518 for (i=0; i<count; i++) {
2519 fputc(0, *fpp);
2521 bank_offset += count;
2522 pc += count;
2523 if (bank_offset > bank_size) {
2524 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2530 * Pads to end of bank in file if bank size not reached.
2531 * @param s Linker script
2532 * @param c Command of type BANK_COMMAND
2533 * @param fp File handle
2535 static void maybe_pad_bank(xlnk_script *s, xlnk_script_command *c, FILE *fp)
2537 int i;
2538 if ( (bank_size != 0x7FFFFFFF) && (bank_offset < bank_size) ) {
2539 if (fp == NULL) {
2540 scripterr(s, c, "no output open");
2542 else {
2543 for (i=bank_offset; i<bank_size; i++) {
2544 fputc(0, fp);
2551 * Finishes old bank in output and starts new bank.
2552 * @param s Linker script
2553 * @param c Command of type BANK_COMMAND
2554 * @param arg Pointer to file handle
2556 static void write_bank(xlnk_script *s, xlnk_script_command *c, void *arg)
2558 FILE **fpp;
2559 /* Arg is pointer to file handle pointer */
2560 fpp = (FILE **)arg;
2561 maybe_pad_bank(s, c, *fpp);
2562 start_bank(s, c, arg);
2566 * Generates the final binary output from the linker.
2567 * @param sc Linker script
2568 * @param output_file Default output file (can be NULL)
2570 static void generate_binary_output(xlnk_script *sc, const char *output_file)
2572 FILE *fp = NULL;
2573 /* Table of mappings for our purpose */
2574 static xlnk_script_commandprocmap map[] = {
2575 { XLNK_OUTPUT_COMMAND, set_output },
2576 { XLNK_COPY_COMMAND, copy_to_output },
2577 { XLNK_BANK_COMMAND, write_bank },
2578 { XLNK_LINK_COMMAND, write_unit },
2579 { XLNK_PAD_COMMAND, write_pad },
2580 { XLNK_BAD_COMMAND, NULL }
2582 /* Reset offsets */
2583 bank_size = 0x7FFFFFFF;
2584 bank_offset = 0;
2585 bank_origin = 0;
2586 bank_id = -1;
2587 pc = 0;
2588 /* Open default output if one is provided */
2589 if (output_file) {
2590 fp = fopen(output_file, "wb");
2591 if (fp == NULL)
2592 err("could not open `%s' for writing", output_file);
2594 /* Do the walk */
2595 xlnk_script_walk(sc, map, (void *)&fp);
2596 /* Pad last bank if necessary */
2597 maybe_pad_bank(sc, sc->first_command, fp);
2600 /*--------------------------------------------------------------------------*/
2601 /* Functions for producing assembly code based on a sequential list of
2602 script commands. */
2605 * Sets the output file according to 'output' script command.
2606 * @param s Linker script
2607 * @param c Command of type OUTPUT_COMMAND
2608 * @param arg Pointer to file handle
2610 static void asm_set_output(xlnk_script *s, xlnk_script_command *c, void *arg)
2612 /* No-op when generating assembly. */
2616 * Copies a file to output according to 'copy' script command.
2617 * @param s Linker script
2618 * @param c Command of type COPY_COMMAND
2619 * @param arg Pointer to file handle
2621 static void asm_copy_to_output(xlnk_script *s, xlnk_script_command *c, void *arg)
2623 const char *file;
2624 FILE **fpp;
2625 FILE *cf;
2626 /* Arg is pointer to file handle pointer */
2627 fpp = (FILE **)arg;
2628 require_arg(s, c, "file", file);
2629 cf = fopen(file, "rb");
2630 if (cf == NULL) {
2631 scripterr(s, c, "could not open `%s' for reading", file);
2632 } else {
2633 unsigned char buf[1024];
2634 int count = fread(buf, 1, 1024, cf);
2635 fprintf(*fpp, "; begin %s\n", file);
2636 while (count > 0) {
2637 print_chunk(*fpp, /*label=*/0, buf, count, /*cols=*/16);
2638 count = fread(buf, 1, 1024, cf);
2640 fprintf(*fpp, "; end %s\n", file);
2641 bank_offset += ftell(cf);
2642 pc += ftell(cf);
2643 fclose(cf);
2644 if (bank_offset > bank_size) {
2645 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2651 * Starts a new bank according to 'bank' script command.
2652 * @param s Linker script
2653 * @param c Command of type BANK_COMMAND
2654 * @param arg Pointer to file handle
2656 static void asm_start_bank(xlnk_script *s, xlnk_script_command *c, void *arg)
2658 FILE *fp = *(FILE**)arg;
2659 start_bank(s, c, arg);
2660 fprintf(fp, ".ORG $%.4X\n", pc);
2664 * Writes unit according to 'link' script command.
2665 * @param s Linker script
2666 * @param c Command of type LINK_COMMAND
2667 * @param arg Pointer to file handle
2669 static void asm_write_unit(xlnk_script *s, xlnk_script_command *c, void *arg)
2671 FILE **fpp;
2672 xunit *xu;
2673 const char *file;
2674 /* Arg is pointer to file handle pointer */
2675 fpp = (FILE **)arg;
2676 require_arg(s, c, "file", file);
2677 xu = (xunit *)hashtab_get(unit_hash, (void*)file);
2678 verbose(1, " appending unit `%s' to output at position %ld...", file, ftell(*fpp));
2679 write_as_assembly(*fpp, xu);
2680 bank_offset += xu->code_size;
2681 if (bank_offset > bank_size) {
2682 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2687 * Pads output file according to 'pad' script command.
2688 * @param s Linker script
2689 * @param c Command of type PAD_COMMAND
2690 * @param arg Pointer to file handle
2692 static void asm_write_pad(xlnk_script *s, xlnk_script_command *c, void *arg)
2694 FILE **fpp;
2695 int count;
2696 int offset;
2697 int origin;
2698 const char *offset_str;
2699 const char *origin_str;
2700 const char *size_str;
2701 /* Arg is pointer to file handle pointer */
2702 fpp = (FILE **)arg;
2703 if ((offset_str = xlnk_script_get_command_arg(c, "offset")) != NULL) {
2704 offset = str_to_int(offset_str);
2705 count = offset - bank_offset;
2706 } else if ((origin_str = xlnk_script_get_command_arg(c, "origin")) != NULL) {
2707 origin = str_to_int(origin_str);
2708 count = origin - pc;
2709 } else if ((size_str = xlnk_script_get_command_arg(c, "size")) != NULL) {
2710 count = str_to_int(size_str);
2711 } else {
2712 scripterr(s, c, "missing argument");
2713 count = 0;
2715 if (count < 0) {
2716 scripterr(s, c, "cannot pad backwards");
2717 count = 0;
2718 } else if (count > 0) {
2719 verbose(1, " padding %d bytes...", count);
2721 fprintf(*fpp, ".DSB $%X\n", count);
2722 bank_offset += count;
2723 pc += count;
2724 if (bank_offset > bank_size) {
2725 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2730 * Pads to end of bank in file if bank size not reached.
2731 * @param s Linker script
2732 * @param c Command of type BANK_COMMAND
2733 * @param fp File handle
2735 static void asm_maybe_pad_bank(xlnk_script *s, xlnk_script_command *c, FILE *fp)
2737 if ( (bank_size != 0x7FFFFFFF) && (bank_offset < bank_size) ) {
2738 fprintf(fp, ".DSB $%X\n", bank_size - bank_offset);
2743 * Finishes old bank in output and starts new bank.
2744 * @param s Linker script
2745 * @param c Command of type BANK_COMMAND
2746 * @param arg Pointer to file handle
2748 static void asm_write_bank(xlnk_script *s, xlnk_script_command *c, void *arg)
2750 FILE **fpp = (FILE **)arg;
2751 asm_maybe_pad_bank(s, c, *fpp);
2752 asm_start_bank(s, c, arg);
2755 static void generate_assembly_output(xlnk_script *sc, FILE *fp)
2757 /* Table of mappings for our purpose */
2758 static xlnk_script_commandprocmap map[] = {
2759 { XLNK_OUTPUT_COMMAND, asm_set_output },
2760 { XLNK_COPY_COMMAND, asm_copy_to_output },
2761 { XLNK_BANK_COMMAND, asm_write_bank },
2762 { XLNK_LINK_COMMAND, asm_write_unit },
2763 { XLNK_PAD_COMMAND, asm_write_pad },
2764 { XLNK_BAD_COMMAND, NULL }
2766 /* Reset offsets */
2767 bank_size = 0x7FFFFFFF;
2768 bank_offset = 0;
2769 bank_origin = 0;
2770 bank_id = -1;
2771 pc = 0;
2772 fprintf(fp, ".CODESEG\n");
2773 /* Do the walk */
2774 xlnk_script_walk(sc, map, (void *)&fp);
2775 /* Pad last bank if necessary */
2776 asm_maybe_pad_bank(sc, sc->first_command, fp);
2777 fprintf(fp, ".END\n");
2780 /*--------------------------------------------------------------------------*/
2783 * Increases bank offset and PC according to size of the file specified by
2784 * 'copy' script command.
2785 * @param s Linker script
2786 * @param c Command of type COPY_COMMAND
2787 * @param arg Not used
2789 static void inc_offset_copy(xlnk_script *s, xlnk_script_command *c, void *arg)
2791 const char *file;
2792 FILE *fp;
2793 require_arg(s, c, "file", file);
2794 fp = fopen(file, "rb");
2795 if (fp == NULL) {
2796 scripterr(s, c, "could not open `%s' for reading", file);
2798 else {
2799 fseek(fp, 0, SEEK_END);
2800 bank_offset += ftell(fp);
2801 pc += ftell(fp);
2802 fclose(fp);
2803 if (bank_offset > bank_size) {
2804 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2810 * Checks that all external symbols can be resolved.
2812 static void check_externals(xasm_unit *u)
2814 int i;
2815 for (i = 0; i < u->ext_count; ++i) {
2816 const char *s = u->externals[i].name;
2817 if (!hashtab_get(label_hash, (void*)s)
2818 && !hashtab_get(constant_hash, (void*)s)) {
2819 err("unknown symbol `%s' referenced from %s", s, u->name);
2825 * Sets the origin of a unit and relocates its code to this location.
2826 * @param s Linker script
2827 * @param c Command of type LINK_COMMAND
2828 * @param arg Not used
2830 static void set_unit_origin(xlnk_script *s, xlnk_script_command *c, void *arg)
2832 xunit *xu;
2833 const char *file;
2834 const char *origin_str;
2835 int origin;
2836 require_arg(s, c, "file", file);
2837 xu = (xunit *)hashtab_get(unit_hash, (void*)file);
2838 origin_str = xlnk_script_get_command_arg(c, "origin");
2839 if (origin_str != NULL) {
2840 origin = str_to_int(origin_str);
2841 require_arg_in_range(s, c, "origin", origin, 0x0000, 0xFFFF);
2842 xu->code_origin = origin;
2843 pc = origin;
2845 else {
2846 /* No origin specified. Set to PC. */
2847 xu->code_origin = pc;
2849 xu->bank_id = bank_id;
2850 /* Now we can calculate the physical code addresses of the unit. */
2851 calc_code_addresses(xu);
2852 verbose(1, " unit `%s' relocated to %.4X", xu->_unit_.name, xu->code_origin);
2853 bank_offset += xu->code_size;
2854 check_externals(&xu->_unit_);
2858 * Increases bank offset and PC according to 'pad' script command.
2859 * @param s Linker script
2860 * @param c Command of type PAD_COMMAND
2861 * @param arg Not used
2863 static void inc_offset_pad(xlnk_script *s, xlnk_script_command *c, void *arg)
2865 int count;
2866 int offset;
2867 int origin;
2868 const char *offset_str;
2869 const char *origin_str;
2870 const char *size_str;
2871 if ((offset_str = xlnk_script_get_command_arg(c, "offset")) != NULL) {
2872 offset = str_to_int(offset_str);
2873 count = offset - bank_offset;
2875 else if ((origin_str = xlnk_script_get_command_arg(c, "origin")) != NULL) {
2876 origin = str_to_int(origin_str);
2877 count = origin - pc;
2879 else if ((size_str = xlnk_script_get_command_arg(c, "size")) != NULL) {
2880 count = str_to_int(size_str);
2882 else {
2883 scripterr(s, c, "missing argument");
2884 count = 0;
2886 if (count < 0) {
2887 scripterr(s, c, "cannot pad %d bytes backwards", -count);
2888 count = 0;
2890 bank_offset += count;
2891 pc += count;
2892 if (bank_offset > bank_size) {
2893 scripterr(s, c, "bank size (%d) exceeded by %d bytes", bank_size, bank_offset - bank_size);
2898 * Relocates code of all units according to script commands and/or their position
2899 * in the final binary.
2900 * @param sc Linker script
2902 static void relocate_units(xlnk_script *sc)
2904 /* Table of mappings for our purpose */
2905 static xlnk_script_commandprocmap map[] = {
2906 { XLNK_COPY_COMMAND, inc_offset_copy },
2907 { XLNK_BANK_COMMAND, start_bank },
2908 { XLNK_LINK_COMMAND, set_unit_origin },
2909 { XLNK_PAD_COMMAND, inc_offset_pad },
2910 { XLNK_BAD_COMMAND, NULL }
2912 /* Reset offsets */
2913 bank_size = 0x7FFFFFFF;
2914 bank_offset = 0;
2915 bank_origin = 0;
2916 bank_id = -1;
2917 pc = 0;
2918 /* Do the walk */
2919 xlnk_script_walk(sc, map, NULL);
2925 static void maybe_print_ram_statistics()
2927 int used;
2928 int left;
2929 if (total_ram > 0) {
2930 left = ram_left();
2931 used = total_ram - left;
2932 verbose(1, " total RAM: %d bytes", total_ram);
2933 verbose(1, " RAM used: %d bytes (%d%%)", used, (int)(((float)used / (float)total_ram)*100.0f) );
2934 verbose(1, " RAM left: %d bytes (%d%%)", left, (int)(((float)left / (float)total_ram)*100.0f) );
2938 /*--------------------------------------------------------------------------*/
2941 * Program entrypoint.
2943 int main(int argc, char **argv)
2945 int i;
2946 xlnk_script sc;
2948 parse_arguments(argc, argv);
2950 suppress = 0;
2951 err_count = 0;
2952 warn_count = 0;
2954 verbose(1, "parsing linker script...");
2955 if (xlnk_script_parse(program_args.input_file, &sc) == 0) {
2956 /* Something bad happened when parsing script, halt */
2957 return(1);
2960 verbose(1, "registering RAM blocks...");
2961 register_ram_blocks(&sc);
2963 constant_hash = hashtab_create(23, HASHTAB_STRKEYHSH, HASHTAB_STRKEYCMP);
2964 label_hash = hashtab_create(23, HASHTAB_STRKEYHSH, HASHTAB_STRKEYCMP);
2965 unit_hash = hashtab_create(11, HASHTAB_STRKEYHSH, HASHTAB_STRKEYCMP);
2967 unit_count = xlnk_script_count_command_type(&sc, XLNK_LINK_COMMAND);
2968 if (unit_count > 0) {
2969 units = (xunit *)malloc( sizeof(xunit) * unit_count );
2970 memset(units, 0, sizeof(xunit) * unit_count);
2972 else {
2973 units = NULL;
2975 verbose(1, "loading units...");
2976 register_units(&sc);
2978 /* Only continue with processing if no unresolved symbols */
2979 if (err_count == 0) {
2980 verbose(1, "calculating data addresses...");
2981 for (i=0; i<unit_count; i++) {
2982 calc_data_addresses(&units[i]);
2985 /* TODO: Count references: go through all instructions, find EXTRN and LOCAL operands in expressions */
2986 /* TODO: Find modes of access for each DATA label (i.e. label MUST be allocated in zero page) */
2988 verbose(1, "mapping data to RAM...");
2989 map_data_to_ram();
2990 maybe_print_ram_statistics();
2992 if (err_count == 0) {
2993 verbose(1, "relocating code...");
2994 suppress = 1;
2995 relocate_units(&sc);
2996 suppress = 0;
2997 relocate_units(&sc);
2999 if (err_count == 0) {
3000 verbose(1, "generating output...");
3001 generate_binary_output(&sc, program_args.output_file);
3002 if (generate_assembly)
3003 generate_assembly_output(&sc, stdout);
3008 verbose(1, "cleaning up...");
3010 for (i=0; i<unit_count; i++) {
3011 if (units[i].loaded) {
3012 finalize_local_array( &units[i].data_locals );
3013 finalize_local_array( &units[i].code_locals );
3014 xasm_unit_finalize( &units[i]._unit_ );
3017 hashtab_finalize(label_hash);
3018 hashtab_finalize(constant_hash);
3019 hashtab_finalize(unit_hash);
3020 finalize_ram_blocks();
3021 xlnk_script_finalize(&sc);
3023 return (err_count == 0) ? 0 : 1;