2 * $Id: xlnk.c,v 1.20 2007/11/11 22:35:22 khansen Exp $
4 * Revision 1.20 2007/11/11 22:35:22 khansen
7 * Revision 1.19 2007/08/12 19:01:11 khansen
10 * Revision 1.18 2007/08/07 22:43:01 khansen
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
18 * prints code/data addresses of public symbols when --verbose
20 * Revision 1.15 2005/01/05 09:33:37 kenth
22 * fixed RAM allocator bug
23 * print RAM statistics when --verbose
25 * Revision 1.14 2005/01/05 01:52:19 kenth
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
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
40 * Revision 1.9 2004/12/19 19:58:54 kenth
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
53 * Revision 1.5 2004/12/14 01:50:21 kenth
56 * Revision 1.4 2004/12/11 02:06:18 kenth
59 * Revision 1.3 2004/12/06 04:53:18 kenth
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
68 * Revision 1.1 2004/06/30 07:42:03 kenth
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
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.
115 #define SAFE_FREE(m) if ((m) != NULL) { free(m); m = NULL; }
118 * Parses a string to an integer.
122 static int str_to_int(const char *s
)
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
{
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' },
160 /* Prints usage message and exits. */
164 Usage: xlnk [-qsvV] [--quiet] [--silent] [--verbose] [--help] [--usage]\n\
170 /* Prints help message and exits. */
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\
188 /* Prints version and exits. */
189 static void version()
191 printf("%s\n", program_version
);
195 /* Parses program arguments. */
197 parse_arguments (int argc
, char **argv
)
200 /* getopt_long stores the option index here. */
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) {
211 program_args
.silent
= 1;
215 ++program_args
.verbose
;
219 /* Use index to differentiate between options */
220 if (strcmp(long_options
[index
].name
, "usage") == 0) {
223 else if (strcmp(long_options
[index
].name
, "help") == 0) {
233 /* Error message has been printed by getopt_long */
238 /* Forgot to handle a short option, most likely */
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");
250 program_args
.input_file
= argv
[optind
];
254 /*--------------------------------------------------------------------------*/
255 /* Data structures. */
257 /* Describes a local label in the unit. */
260 char *name
; /* NULL if not exported */
261 int resolved
; /* 0 initially, set to 1 when phys_addr has been assigned */
266 struct tag_xunit
*owner
;
267 unsigned short align
;
271 typedef struct tag_local local
;
273 /* Describes an array of local labels. */
274 struct tag_local_array
280 typedef struct tag_local_array local_array
;
283 * eXtended unit, has extra info built from basic unit ++
287 xasm_unit _unit_
; /* NB!!! "Superclass", must be first field for casting to work */
288 local_array data_locals
;
289 local_array code_locals
;
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
;
312 struct tag_calc_address_args
318 typedef struct tag_calc_address_args calc_address_args
;
321 struct tag_write_binary_args
327 typedef struct tag_write_binary_args write_binary_args
;
329 /*--------------------------------------------------------------------------*/
331 /** Array containing the units to link. */
333 /* Number of units in above array. */
334 static int unit_count
;
336 /** Holds the current memory address. */
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
;
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;
357 static int bank_offset
;
358 static int bank_size
;
359 static int bank_origin
;
363 static 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()
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
);
389 fprintf(stderr
, "%s:%d: ", str
, unit_line
);
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");
408 * @param fmt format string for printf
410 static void err(char *fmt
, ...)
415 maybe_print_location();
416 fprintf(stderr
, "error: ");
417 vfprintf(stderr
, fmt
, ap
);
418 fprintf(stderr
, "\n");
419 maybe_print_debug_tip();
427 * @param fmt format string for printf
429 static void warn(char *fmt
, ...)
434 maybe_print_location();
435 fprintf(stderr
, "warning: ");
436 vfprintf(stderr
, fmt
, ap
);
437 fprintf(stderr
, "\n");
438 maybe_print_debug_tip();
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
, char *fmt
, ...)
453 if (!suppress
&& program_args
.verbose
>= level
) {
454 vfprintf(stdout
, fmt
, ap
);
455 fprintf(stdout
, "\n");
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()
473 for (sum
= 0, b
= ram_block_head
; b
!= NULL
; b
= b
->next
) {
474 sum
+= b
->end
- b
->start
;
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
)
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
) {
495 ram_block_head
= new_block
;
498 for (b
= ram_block_head
; b
->next
!= NULL
; b
= b
->next
) ;
501 verbose(1, " added RAM block: %.4X-%.4X", new_block
->start
, new_block
->end
);
506 * Allocates a chunk of 6502 RAM to a 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. */
515 avail_ram_block
*p
= NULL
;
516 for (b
= ram_block_head
; b
!= NULL
; p
= b
, b
= b
->next
) {
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);
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
;
537 n
= (avail_ram_block
*)malloc(sizeof(avail_ram_block
));
539 n
->end
= n
->start
+ pad
;
542 if (b
== ram_block_head
) {
543 ram_block_head
= n
; /* New head */
551 l
->phys_addr
= b
->start
;
552 /* Decrease block size by moving start address ahead */
554 /* If there's no more space left in this block, discard it */
555 if (left
== l
->size
) {
556 /* Remove from linked list */
558 /* Set successor block as new head */
559 ram_block_head
= b
->next
;
573 * Frees up memory associated with list of RAM blocks.
575 static void finalize_ram_blocks()
579 for (b
= ram_block_head
; b
!= NULL
; b
= t
) {
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(unsigned char *b
, int *i
)
593 /* Gets big-endian short from buffer and increments index. */
594 static unsigned short get_2(unsigned char *b
, int *i
)
596 unsigned short result
= get_1(b
, i
) << 8;
597 result
|= get_1(b
, i
);
600 /* Gets big-endian 24-bit integer from buffer and increments index. */
601 static unsigned int get_3(unsigned char *b
, int *i
)
603 unsigned int result
= get_2(b
, i
) << 8;
604 result
|= get_1(b
, i
);
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);
615 /*--------------------------------------------------------------------------*/
618 * Calculates the storage occupied by a CMD_LABEL bytecode's arguments.
620 static int label_cmd_args_size(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 */
630 /** Signature for procedure to process a bytecode */
631 typedef void (*bytecodeproc
)(unsigned char *, void *);
634 * Walks an array of bytecodes, calling corresponding bytecode handlers
636 * @param bytes Array of bytecodes, terminated by CMD_END
637 * @param handlers Array of bytecode handlers (entries can be NULL)
638 * @param arg Argument passed to bytecode handler, can be anything
640 static void bytecode_walk(unsigned char *bytes
, bytecodeproc
*handlers
, void *arg
)
646 if (bytes
== NULL
) { return; }
649 cmd
= get_1(bytes
, &i
);
651 /* Check if debug command */
652 if (cmd
< XASM_CMD_END
) {
655 unit_file
= &bytes
[i
];
656 i
+= get_1(bytes
, &i
) + 1; /* Skip count and array of bytes */
658 case XASM_CMD_LINE8
: unit_line
= get_1(bytes
, &i
); break;
659 case XASM_CMD_LINE16
: unit_line
= get_2(bytes
, &i
); break;
660 case XASM_CMD_LINE24
: unit_line
= get_3(bytes
, &i
); break;
661 case XASM_CMD_LINE_INC
: unit_line
++; break;
666 if (handlers
[cmd
-XASM_CMD_END
] != NULL
) {
667 handlers
[cmd
-XASM_CMD_END
](&bytes
[i
-1], arg
);
669 /* Skip any bytecode arguments */
671 case XASM_CMD_END
: break;
672 case XASM_CMD_BIN8
: i
+= get_1(bytes
, &i
) + 1; break; /* Skip count and array of bytes */
673 case XASM_CMD_BIN16
: i
+= get_2(bytes
, &i
) + 1; break; /* Skip count and array of bytes */
674 case XASM_CMD_LABEL
: i
+= label_cmd_args_size(&bytes
[i
]); break; /* Skip flag byte and possibly name and alignment */
675 case XASM_CMD_INSTR
: i
+= 3; break; /* Skip 6502 opcode and 16-bit expr id */
676 case XASM_CMD_DB
: i
+= 2; break; /* Skip 16-bit expr id */
677 case XASM_CMD_DW
: i
+= 2; break; /* Skip 16-bit expr id */
678 case XASM_CMD_DD
: i
+= 2; break; /* Skip 16-bit expr id */
679 case XASM_CMD_DSI8
: i
+= 1; break; /* Skip 8-bit count */
680 case XASM_CMD_DSI16
: i
+= 2; break; /* Skip 16-bit count */
681 case XASM_CMD_DSB
: i
+= 2; break; /* Skip 16-bit expr id */
684 err("invalid bytecode");
687 } while (cmd
!= XASM_CMD_END
);
690 /*--------------------------------------------------------------------------*/
691 /* Functions for expression evaluation. */
694 * Finalizes a constant.
695 * @param c Constant to finalize
697 /* ### Merge with finalize_constant() in unit.c? */
698 static void finalize_constant(xasm_constant
*c
)
700 if (c
->type
== XASM_STRING_CONSTANT
) {
701 SAFE_FREE(c
->string
);
706 * Evaluates an expression recursively.
707 * The result will either be a integer or string literal, indicating successful
708 * evaluation; or an invalid type indicating that a symbol could not be translated
709 * to a constant (in other words, it could not be resolved). In this case,
710 * result->string contains the name of the symbol which couldn't be evaluated.
711 * @param u The unit where the expression is contained
712 * @param e The expression to evaluate
713 * @param result Pointer to resulting value
715 static void eval_recursive(xunit
*u
, xasm_expression
*e
, xasm_constant
*result
)
720 xasm_constant lhs_result
, rhs_result
;
722 case XASM_OPERATOR_EXPRESSION
:
723 switch (e
->op_expr
.operator) {
724 /* Binary operators */
741 /* Evaluate both sides */
742 eval_recursive(u
, e
->op_expr
.lhs
, &lhs_result
);
743 eval_recursive(u
, e
->op_expr
.rhs
, &rhs_result
);
744 /* If either side is unresolved, then result is unresolved. */
745 if ((lhs_result
.type
== -1) || (rhs_result
.type
== -1)) {
748 /* If both sides are integer, then result is integer. */
749 else if ((lhs_result
.type
== XASM_INTEGER_CONSTANT
) &&
750 (rhs_result
.type
== XASM_INTEGER_CONSTANT
)) {
751 result
->type
= XASM_INTEGER_CONSTANT
;
752 /* Perform the proper operation to obtain result. */
753 switch (e
->op_expr
.operator) {
754 case XASM_OP_PLUS
: result
->integer
= lhs_result
.integer
+ rhs_result
.integer
; break;
755 case XASM_OP_MINUS
: result
->integer
= lhs_result
.integer
- rhs_result
.integer
; break;
756 case XASM_OP_MUL
: result
->integer
= lhs_result
.integer
* rhs_result
.integer
; break;
757 case XASM_OP_DIV
: result
->integer
= lhs_result
.integer
/ rhs_result
.integer
; break;
758 case XASM_OP_MOD
: result
->integer
= lhs_result
.integer
% rhs_result
.integer
; break;
759 case XASM_OP_SHL
: result
->integer
= lhs_result
.integer
<< rhs_result
.integer
; break;
760 case XASM_OP_SHR
: result
->integer
= lhs_result
.integer
>> rhs_result
.integer
; break;
761 case XASM_OP_AND
: result
->integer
= lhs_result
.integer
& rhs_result
.integer
; break;
762 case XASM_OP_OR
: result
->integer
= lhs_result
.integer
| rhs_result
.integer
; break;
763 case XASM_OP_XOR
: result
->integer
= lhs_result
.integer
^ rhs_result
.integer
; break;
764 case XASM_OP_EQ
: result
->integer
= lhs_result
.integer
== rhs_result
.integer
; break;
765 case XASM_OP_NE
: result
->integer
= lhs_result
.integer
!= rhs_result
.integer
; break;
766 case XASM_OP_LT
: result
->integer
= lhs_result
.integer
< rhs_result
.integer
; break;
767 case XASM_OP_GT
: result
->integer
= lhs_result
.integer
> rhs_result
.integer
; break;
768 case XASM_OP_LE
: result
->integer
= lhs_result
.integer
<= rhs_result
.integer
; break;
769 case XASM_OP_GE
: result
->integer
= lhs_result
.integer
>= rhs_result
.integer
; break;
772 /* If both sides are string... */
773 else if ((lhs_result
.type
== XASM_STRING_CONSTANT
) &&
774 (rhs_result
.type
== XASM_STRING_CONSTANT
)) {
775 switch (e
->op_expr
.operator) {
778 result
->string
= (char *)malloc(strlen(lhs_result
.string
)+strlen(rhs_result
.string
)+1);
779 if (result
->string
!= NULL
) {
780 strcpy(result
->string
, lhs_result
.string
);
781 strcat(result
->string
, rhs_result
.string
);
782 result
->type
= XASM_STRING_CONSTANT
;
786 /* String comparison: using strcmp() */
787 case XASM_OP_EQ
: result
->integer
= strcmp(lhs_result
.string
, rhs_result
.string
) == 0; break;
788 case XASM_OP_NE
: result
->integer
= strcmp(lhs_result
.string
, rhs_result
.string
) != 0; break;
789 case XASM_OP_LT
: result
->integer
= strcmp(lhs_result
.string
, rhs_result
.string
) < 0; break;
790 case XASM_OP_GT
: result
->integer
= strcmp(lhs_result
.string
, rhs_result
.string
) > 0; break;
791 case XASM_OP_LE
: result
->integer
= strcmp(lhs_result
.string
, rhs_result
.string
) <= 0; break;
792 case XASM_OP_GE
: result
->integer
= strcmp(lhs_result
.string
, rhs_result
.string
) >= 0; break;
795 /* Not defined operator for string operation... */
802 err("incompatible operands to `%s' in expression", xasm_operator_to_string(e
->op_expr
.operator) );
804 /* Discard the operands */
805 finalize_constant(&lhs_result
);
806 finalize_constant(&rhs_result
);
807 break; /* Binary operator */
809 /* Unary operators */
815 /* Evaluate the single operand */
816 eval_recursive(u
, e
->op_expr
.lhs
, &lhs_result
);
817 /* If operand is unresolved then result is unresolved. */
818 if (lhs_result
.type
== -1) {
821 /* If operand is integer then result is integer. */
822 else if (lhs_result
.type
== XASM_INTEGER_CONSTANT
) {
823 result
->type
= XASM_INTEGER_CONSTANT
;
824 /* Perform the proper operation to obtain result. */
825 switch (e
->op_expr
.operator) {
826 case XASM_OP_NOT
: result
->integer
= !lhs_result
.integer
; break;
827 case XASM_OP_NEG
: result
->integer
= ~lhs_result
.integer
; break;
828 case XASM_OP_LO
: result
->integer
= lhs_result
.integer
& 0xFF; break;
829 case XASM_OP_HI
: result
->integer
= (lhs_result
.integer
>> 8) & 0xFF; break;
830 case XASM_OP_UMINUS
: result
->integer
= -lhs_result
.integer
; break;
834 /* Error, invalid operand */
835 err("incompatible operand to `%s' in expression", xasm_operator_to_string(e
->op_expr
.operator) );
838 /* Discard the operand */
839 finalize_constant(&lhs_result
);
840 break; /* Unary operator */
843 switch (e
->op_expr
.lhs
->type
) {
844 case XASM_LOCAL_EXPRESSION
:
845 /* Simple, it must be in the same (current) bank */
846 result
->integer
= bank_id
;
847 result
->type
= XASM_INTEGER_CONSTANT
;
850 case XASM_EXTERNAL_EXPRESSION
:
851 s
= u
->_unit_
.externals
[e
->op_expr
.lhs
->extrn_id
].name
;
852 if ((l
= (local
*)hashtab_get(label_hash
, s
)) != NULL
) {
854 result
->integer
= l
->owner
->bank_id
;
855 result
->type
= XASM_INTEGER_CONSTANT
;
857 else if ((c
= (xasm_constant
*)hashtab_get(constant_hash
, s
)) != NULL
) {
858 /* It's a constant */
859 result
->integer
= ((xunit
*)c
->unit
)->bank_id
;
860 result
->type
= XASM_INTEGER_CONSTANT
;
875 case XASM_INTEGER_EXPRESSION
:
876 result
->type
= XASM_INTEGER_CONSTANT
;
877 result
->integer
= e
->integer
;
880 case XASM_STRING_EXPRESSION
:
881 result
->string
= (char *)malloc(strlen(e
->string
) + 1);
882 if (result
->string
!= NULL
) {
883 strcpy(result
->string
, e
->string
);
884 result
->type
= XASM_STRING_CONSTANT
;
888 case XASM_LOCAL_EXPRESSION
:
889 if (e
->local_id
>= u
->data_locals
.size
) {
890 /* It's a code local */
891 l
= &u
->code_locals
.entries
[e
->local_id
- u
->data_locals
.size
];
894 /* It's a data local */
895 l
= &u
->data_locals
.entries
[e
->local_id
];
898 result
->type
= XASM_INTEGER_CONSTANT
;
899 result
->integer
= l
->phys_addr
;
902 /* Not resolved (yet, at least) */
907 case XASM_EXTERNAL_EXPRESSION
:
908 s
= u
->_unit_
.externals
[e
->extrn_id
].name
;
909 if ((l
= (local
*)hashtab_get(label_hash
, s
)) != NULL
) {
912 result
->type
= XASM_INTEGER_CONSTANT
;
913 result
->integer
= l
->phys_addr
;
916 /* Not resolved (yet) */
920 else if ((c
= (xasm_constant
*)hashtab_get(constant_hash
, s
)) != NULL
) {
922 case XASM_INTEGER_CONSTANT
:
923 result
->type
= XASM_INTEGER_CONSTANT
;
924 result
->integer
= c
->integer
;
927 case XASM_STRING_CONSTANT
:
928 result
->string
= (char *)malloc(strlen(c
->string
) + 1);
929 if (result
->string
!= NULL
) {
930 strcpy(result
->string
, c
->string
);
931 result
->type
= XASM_STRING_CONSTANT
;
938 err("unknown symbol `%s' referenced from %s", s
, u
->_unit_
.name
);
942 case XASM_PC_EXPRESSION
:
943 result
->type
= XASM_INTEGER_CONSTANT
;
944 result
->integer
= pc
;
950 * Evaluates an expression.
951 * @param u The unit where the expression is contained
952 * @param exid The unique ID of the expression
953 * @param result Where to store the result of the evaluation
955 static void eval_expression(xunit
*u
, int exid
, xasm_constant
*result
)
957 xasm_expression
*exp
= u
->_unit_
.expressions
[exid
];
958 eval_recursive(u
, exp
, result
);
961 /*--------------------------------------------------------------------------*/
962 /* Functions for incrementing PC, with error handling for wraparound. */
965 * Increases PC by amount.
966 * Issues error if the PC wraps around.
968 static void inc_pc(int amount
, void *arg
)
970 calc_address_args
*aargs
;
971 if ((pc
<= 0x10000) && ((pc
+amount
) > 0x10000)) {
972 aargs
= (calc_address_args
*)arg
;
973 err("PC went beyond 64K when linking `%s'", aargs
->xu
->_unit_
.name
);
979 * Increases PC by 8-bit value immediately following bytecode command.
981 static void inc_pc_count8(unsigned char *b
, void *arg
)
984 inc_pc( get_1(b
, &i
) + 1, arg
);
988 * Increases PC by 16-bit value immediately following bytecode command.
990 static void inc_pc_count16(unsigned char *b
, void *arg
)
993 inc_pc( get_2(b
, &i
) + 1, arg
);
999 static void inc_pc_1(unsigned char *b
, void *arg
)
1005 * Increases PC by 2.
1007 static void inc_pc_2(unsigned char *b
, void *arg
)
1013 * Increases PC by 4.
1015 static void inc_pc_4(unsigned char *b
, void *arg
)
1021 * Increases PC according to size of define data command.
1023 static void inc_pc_dsb(unsigned char *b
, void *arg
)
1027 calc_address_args
*args
= (calc_address_args
*)arg
;
1029 /* Get expression ID */
1030 exid
= get_2(b
, &i
);
1031 /* Evaluate expression */
1032 eval_expression(args
->xu
, exid
, &c
);
1033 /* Handle the result */
1034 if (c
.type
== XASM_INTEGER_CONSTANT
) {
1035 /* An array of bytes will be located here */
1036 /* Advance PC appropriately */
1037 inc_pc( c
.integer
, arg
);
1039 else if (c
.type
== XASM_STRING_CONSTANT
) {
1040 err("unexpected string operand (`%s') to storage directive", c
.string
);
1043 //err("unresolved symbol");
1047 finalize_constant(&c
);
1051 * Increments PC according to the length of this instruction.
1053 static void inc_pc_instr(unsigned char *b
, void *arg
)
1056 unsigned char op
, t
;
1058 calc_address_args
*args
= (calc_address_args
*)arg
;
1062 /* Get expression ID */
1063 exid
= get_2(b
, &i
);
1065 eval_expression(args
->xu
, exid
, &c
);
1066 /* Handle the result */
1067 if (c
.type
== XASM_INTEGER_CONSTANT
) {
1068 /* See if it can be reduced to ZP instruction */
1069 if ((c
.integer
< 0x100) &&
1070 ((t
= opcode_zp_equiv(op
)) != 0xFF)) {
1071 /* replace op by ZP-version */
1076 else if (c
.type
== XASM_STRING_CONSTANT
) {
1077 err("invalid instruction operand (string)");
1080 /* Address not available yet (forward reference). */
1081 //err("unresolved symbol");
1084 inc_pc( opcode_length(op
), arg
);
1087 /*--------------------------------------------------------------------------*/
1088 /* Functions for writing pure 6502 binary from bytecodes. */
1091 * Writes an array of bytes.
1093 static void write_bin8(unsigned char *b
, void *arg
)
1097 write_binary_args
*args
= (write_binary_args
*)arg
;
1099 count
= get_1(b
, &i
) + 1;
1100 fwrite(&b
[i
], 1, count
, args
->fp
);
1101 inc_pc( count
, arg
);
1105 * Writes an array of bytes.
1107 static void write_bin16(unsigned char *b
, void *arg
)
1111 write_binary_args
*args
= (write_binary_args
*)arg
;
1113 count
= get_2(b
, &i
) + 1;
1114 fwrite(&b
[i
], 1, count
, args
->fp
);
1115 inc_pc( count
, arg
);
1119 * Writes an instruction.
1121 static void write_instr(unsigned char *b
, void *arg
)
1127 write_binary_args
*args
= (write_binary_args
*)arg
;
1131 assert(opcode_length(op
) > 1);
1132 /* Get expression ID */
1133 exid
= get_2(b
, &i
);
1134 /* Evaluate expression */
1135 eval_expression(args
->xu
, exid
, &c
);
1136 assert(c
.type
== XASM_INTEGER_CONSTANT
);
1137 /* Write the opcode */
1138 fputc(op
, args
->fp
);
1139 if (opcode_length(op
) == 2) {
1140 /* Operand must fit in 1 byte */
1141 /* Check if it's a relative jump */
1151 /* Calculate difference between target and address of next instruction */
1152 c
.integer
= c
.integer
- (pc
+ 2);
1153 /* Make sure jump is in range */
1154 if ( (c
.integer
< -128) || (c
.integer
> 127) ) {
1155 err("branch out of range");
1157 /* Make it a byte value */
1161 if (c
.integer
>= 0x100) {
1162 err("instruction operand doesn't fit in 1 byte");
1166 fputc(c
.integer
, args
->fp
);
1169 assert(opcode_length(op
) == 3);
1170 /* Operand must fit in 2 bytes */
1171 if (c
.integer
>= 0x10000) {
1172 err("instruction operand doesn't fit in 2 bytes");
1175 /* Write it, low byte first */
1176 fputc(c
.integer
, args
->fp
);
1177 fputc(c
.integer
>> 8, args
->fp
);
1180 inc_pc( opcode_length(op
), arg
);
1184 * Writes a byte, word or dword.
1186 static void write_dx(unsigned char *b
, void *arg
)
1191 write_binary_args
*args
= (write_binary_args
*)arg
;
1192 /* Get expression ID */
1194 exid
= get_2(b
, &i
);
1195 /* Evaluate expression */
1196 eval_expression(args
->xu
, exid
, &c
);
1198 if (c
.type
== XASM_INTEGER_CONSTANT
) {
1199 /* Write low byte */
1200 fputc(c
.integer
, args
->fp
);
1201 /* If 2+ bytes, write high ones */
1204 if (c
.integer
> 0xFF) {
1205 warn("`.DB' operand $%X out of range; truncated", c
.integer
);
1210 fputc(c
.integer
>> 8, args
->fp
);
1211 if (c
.integer
> 0xFFFF) {
1212 warn("`.DW' operand $%X out of range; truncated", c
.integer
);
1217 fputc(c
.integer
>> 8, args
->fp
);
1218 fputc(c
.integer
>> 16, args
->fp
);
1219 fputc(c
.integer
>> 24, args
->fp
);
1224 case XASM_CMD_DB
: inc_pc( 1, arg
); break;
1225 case XASM_CMD_DW
: inc_pc( 2, arg
); break;
1226 case XASM_CMD_DD
: inc_pc( 4, arg
); break;
1229 else if (c
.type
== XASM_STRING_CONSTANT
) {
1230 for (i
=0; i
<strlen(c
.string
); i
++) {
1231 /* Write low byte */
1232 fputc(c
.string
[i
], args
->fp
);
1233 /* If 2+ bytes, write high ones */
1247 case XASM_CMD_DB
: inc_pc( 1, arg
); break;
1248 case XASM_CMD_DW
: inc_pc( 2, arg
); break;
1249 case XASM_CMD_DD
: inc_pc( 4, arg
); break;
1256 finalize_constant(&c
);
1260 * Writes a series of zeroes.
1262 static void write_dsi8(unsigned char *b
, void *arg
)
1266 write_binary_args
*args
= (write_binary_args
*)arg
;
1268 count
= get_1(b
, &i
) + 1;
1269 for (i
=0; i
<count
; i
++) {
1272 inc_pc( count
, arg
);
1276 * Writes a series of zeroes.
1278 static void write_dsi16(unsigned char *b
, void *arg
)
1282 write_binary_args
*args
= (write_binary_args
*)arg
;
1284 count
= get_2(b
, &i
) + 1;
1285 for (i
=0; i
<count
; i
++) {
1288 inc_pc( count
, arg
);
1292 * Writes a series of zeroes.
1294 static void write_dsb(unsigned char *b
, void *arg
)
1299 write_binary_args
*args
= (write_binary_args
*)arg
;
1300 /* Get expression ID */
1302 exid
= get_2(b
, &i
);
1303 /* Evaluate expression */
1304 eval_expression(args
->xu
, exid
, &c
);
1305 assert(c
.type
== XASM_INTEGER_CONSTANT
);
1306 if (c
.integer
< 0) {
1307 err("negative count");
1308 } else if (c
.integer
> 0) {
1309 for (i
=0; i
<c
.integer
; i
++) {
1312 inc_pc( c
.integer
, arg
);
1317 * Writes a code segment as fully native 6502 code.
1318 * @param fp File handle
1319 * @param u Unit whose code to write
1321 static void write_as_binary(FILE *fp
, xunit
*u
)
1323 write_binary_args args
;
1324 /* Table of callback functions for our purpose. */
1325 bytecodeproc handlers
[] =
1328 write_bin8
, /* CMD_BIN8 */
1329 write_bin16
, /* CMD_BIN16 */
1330 NULL
, /* CMD_LABEL */
1331 write_instr
, /* CMD_INSTR */
1332 write_dx
, /* CMD_DB */
1333 write_dx
, /* CMD_DW */
1334 write_dx
, /* CMD_DD */
1335 write_dsi8
, /* CMD_DSI8 */
1336 write_dsi16
, /* CMD_DSI16 */
1337 write_dsb
/* CMD_DSB */
1343 pc
= u
->code_origin
;
1345 bytecode_walk(u
->_unit_
.codeseg
.bytes
, handlers
, (void *)&args
);
1348 /*--------------------------------------------------------------------------*/
1349 /* Functions for writing 6502 assembly from bytecodes. */
1352 Prints \a size bytes of data defined by \a buf to \a out.
1354 static void print_chunk(FILE *out
, const char *label
,
1355 const unsigned char *buf
, int size
, int cols
)
1360 fprintf(out
, "%s:\n", label
);
1361 for (i
= 0; i
< size
/ cols
; ++i
) {
1362 fprintf(out
, ".DB ");
1363 for (j
= 0; j
< cols
-1; ++j
)
1364 fprintf(out
, "$%.2X,", buf
[pos
++]);
1365 fprintf(out
, "$%.2X\n", buf
[pos
++]);
1369 fprintf(out
, ".DB ");
1370 for (j
= 0; j
< m
-1; ++j
)
1371 fprintf(out
, "$%.2X,", buf
[pos
++]);
1372 fprintf(out
, "$%.2X\n", buf
[pos
++]);
1377 * Writes an array of bytes.
1379 static void asm_write_bin8(unsigned char *b
, void *arg
)
1383 write_binary_args
*args
= (write_binary_args
*)arg
;
1385 count
= get_1(b
, &i
) + 1;
1386 // fprintf(args->fp, "; %d byte(s)\n", count);
1387 print_chunk(args
->fp
, /*label=*/0, &b
[i
], count
, /*cols=*/16);
1388 inc_pc( count
, arg
);
1392 * Writes an array of bytes.
1394 static void asm_write_bin16(unsigned char *b
, void *arg
)
1398 write_binary_args
*args
= (write_binary_args
*)arg
;
1400 count
= get_2(b
, &i
) + 1;
1401 // fprintf(args->fp, "; %d byte(s)\n", count);
1402 print_chunk(args
->fp
, /*label=*/0, &b
[i
], count
, /*cols=*/16);
1403 inc_pc( count
, arg
);
1409 static void asm_write_label(unsigned char *b
, void *arg
)
1411 unsigned char flags
;
1413 write_binary_args
*args
= (write_binary_args
*)arg
;
1414 fprintf(args
->fp
, "; label");
1415 flags
= get_1(b
, &i
);
1416 if (flags
& XASM_LABEL_FLAG_EXPORT
) {
1418 int len
= get_1(b
, &i
) + 1;
1419 name
= (char *)malloc( len
+ 1 );
1421 memcpy(name
, &b
[i
], len
);
1424 fprintf(args
->fp
, " %s (PC=$%.4X)", name
, pc
);
1427 fprintf(args
->fp
, " PC=$%.4X", pc
);
1429 fprintf(args
->fp
, "\n");
1433 * Writes an instruction.
1435 static void asm_write_instr(unsigned char *b
, void *arg
)
1439 addressing_mode mode
;
1442 write_binary_args
*args
= (write_binary_args
*)arg
;
1446 assert(opcode_length(op
) > 1);
1447 mode
= opcode_addressing_mode(op
);
1448 assert(mode
!= INVALID_MODE
);
1449 /* Get expression ID */
1450 exid
= get_2(b
, &i
);
1451 /* Evaluate expression */
1452 eval_expression(args
->xu
, exid
, &c
);
1453 assert(c
.type
== XASM_INTEGER_CONSTANT
);
1454 /* Write the opcode */
1455 fprintf(args
->fp
, "%s", opcode_to_string(op
));
1458 case ACCUMULATOR_MODE
:
1460 case IMMEDIATE_MODE
:
1461 fprintf(args
->fp
, " #$");
1464 case ZEROPAGE_X_MODE
:
1465 case ZEROPAGE_Y_MODE
:
1467 case ABSOLUTE_X_MODE
:
1468 case ABSOLUTE_Y_MODE
:
1469 fprintf(args
->fp
, " $");
1471 case PREINDEXED_INDIRECT_MODE
:
1472 case POSTINDEXED_INDIRECT_MODE
:
1474 fprintf(args
->fp
, " [$");
1477 fprintf(args
->fp
, " $");
1482 /* Write the operand */
1483 fprintf(args
->fp
, "%.4X", (unsigned)c
.integer
);
1486 case ACCUMULATOR_MODE
:
1487 case IMMEDIATE_MODE
:
1490 case ZEROPAGE_X_MODE
:
1491 fprintf(args
->fp
, ",X");
1493 case ZEROPAGE_Y_MODE
:
1494 fprintf(args
->fp
, ",Y");
1498 case ABSOLUTE_X_MODE
:
1499 fprintf(args
->fp
, ",X");
1501 case ABSOLUTE_Y_MODE
:
1502 fprintf(args
->fp
, ",Y");
1504 case PREINDEXED_INDIRECT_MODE
:
1505 fprintf(args
->fp
, ",X]");
1507 case POSTINDEXED_INDIRECT_MODE
:
1508 fprintf(args
->fp
, "],Y");
1511 fprintf(args
->fp
, "]");
1518 fprintf(args
->fp
, "\n");
1519 inc_pc( opcode_length(op
), arg
);
1523 * Writes a byte, word or dword.
1525 static void asm_write_dx(unsigned char *b
, void *arg
)
1530 write_binary_args
*args
= (write_binary_args
*)arg
;
1531 /* Get expression ID */
1533 exid
= get_2(b
, &i
);
1534 /* Evaluate expression */
1535 eval_expression(args
->xu
, exid
, &c
);
1536 if (c
.type
== XASM_INTEGER_CONSTANT
) {
1539 fprintf(args
->fp
, ".DB $%.2X", (unsigned)c
.integer
);
1542 fprintf(args
->fp
, ".DW $%.4X", (unsigned)c
.integer
);
1545 fprintf(args
->fp
, ".DD $%.8X", (unsigned)c
.integer
);
1550 case XASM_CMD_DB
: inc_pc( 1, arg
); break;
1551 case XASM_CMD_DW
: inc_pc( 2, arg
); break;
1552 case XASM_CMD_DD
: inc_pc( 4, arg
); break;
1554 } else if (c
.type
== XASM_STRING_CONSTANT
) {
1555 int count
= strlen(c
.string
);
1558 fprintf(args
->fp
, ".DB");
1561 fprintf(args
->fp
, ".DW");
1564 fprintf(args
->fp
, ".DD");
1567 fprintf(args
->fp
, " \"%s\"", c
.string
);
1570 case XASM_CMD_DB
: inc_pc( count
* 1, arg
); break;
1571 case XASM_CMD_DW
: inc_pc( count
* 2, arg
); break;
1572 case XASM_CMD_DD
: inc_pc( count
* 4, arg
); break;
1577 fprintf(args
->fp
, "\n");
1578 finalize_constant(&c
);
1582 * Writes a series of zeroes.
1584 static void asm_write_dsi8(unsigned char *b
, void *arg
)
1588 write_binary_args
*args
= (write_binary_args
*)arg
;
1590 count
= get_1(b
, &i
) + 1;
1591 fprintf(args
->fp
, ".DSB $%X\n", count
);
1592 inc_pc( count
, arg
);
1596 * Writes a series of zeroes.
1598 static void asm_write_dsi16(unsigned char *b
, void *arg
)
1602 write_binary_args
*args
= (write_binary_args
*)arg
;
1604 count
= get_2(b
, &i
) + 1;
1605 fprintf(args
->fp
, ".DSB $%X\n", count
);
1606 inc_pc( count
, arg
);
1610 * Writes a series of zeroes.
1612 static void asm_write_dsb(unsigned char *b
, void *arg
)
1617 write_binary_args
*args
= (write_binary_args
*)arg
;
1618 /* Get expression ID */
1620 exid
= get_2(b
, &i
);
1621 /* Evaluate expression */
1622 eval_expression(args
->xu
, exid
, &c
);
1623 assert(c
.type
== XASM_INTEGER_CONSTANT
);
1624 if (c
.integer
< 0) {
1625 err("negative count");
1627 else if (c
.integer
> 0) {
1628 fprintf(args
->fp
, ".DSB $%X\n", (unsigned)c
.integer
);
1629 inc_pc( c
.integer
, arg
);
1634 * Writes a code segment as fully native 6502 code.
1635 * @param fp File handle
1636 * @param u Unit whose code to write
1638 static void write_as_assembly(FILE *fp
, xunit
*u
)
1640 write_binary_args args
;
1641 /* Table of callback functions for our purpose. */
1642 bytecodeproc handlers
[] =
1645 asm_write_bin8
, /* CMD_BIN8 */
1646 asm_write_bin16
, /* CMD_BIN16 */
1647 asm_write_label
, /* CMD_LABEL */
1648 asm_write_instr
, /* CMD_INSTR */
1649 asm_write_dx
, /* CMD_DB */
1650 asm_write_dx
, /* CMD_DW */
1651 asm_write_dx
, /* CMD_DD */
1652 asm_write_dsi8
, /* CMD_DSI8 */
1653 asm_write_dsi16
, /* CMD_DSI16 */
1654 asm_write_dsb
/* CMD_DSB */
1660 pc
= u
->code_origin
;
1661 fprintf(fp
, "; ***************************************\n");
1662 fprintf(fp
, "; * %s, PC=$%.4X\n", u
->_unit_
.name
, pc
);
1663 fprintf(fp
, "; ***************************************\n");
1665 bytecode_walk(u
->_unit_
.codeseg
.bytes
, handlers
, (void *)&args
);
1668 #define XLNK_NO_DEBUG
1669 #ifndef XLNK_NO_DEBUG
1671 /*--------------------------------------------------------------------------*/
1672 /* Functions for debugging bytecodes. */
1675 * Gets string representation of bytecode command.
1677 * @return String representation ("CMD_*")
1679 static const char *bytecode_to_string(unsigned char cmd
)
1682 case XASM_CMD_FILE
: return "CMD_FILE";
1683 case XASM_CMD_LINE8
: return "CMD_LINE8";
1684 case XASM_CMD_LINE16
:return "CMD_LINE16";
1685 case XASM_CMD_LINE24
:return "CMD_LINE24";
1686 case XASM_CMD_LINE_INC
: return "CMD_LINE_INC";
1687 case XASM_CMD_END
: return "CMD_END";
1688 case XASM_CMD_BIN8
: return "CMD_BIN8";
1689 case XASM_CMD_BIN16
: return "CMD_BIN16";
1690 case XASM_CMD_LABEL
: return "CMD_LABEL";
1691 case XASM_CMD_INSTR
: return "CMD_INSTR";
1692 case XASM_CMD_DB
: return "CMD_DB";
1693 case XASM_CMD_DW
: return "CMD_DW";
1694 case XASM_CMD_DD
: return "CMD_DD";
1695 case XASM_CMD_DSI8
: return "CMD_DSI8";
1696 case XASM_CMD_DSI16
: return "CMD_DSI16";
1697 case XASM_CMD_DSB
: return "CMD_DSB";
1699 return "bytecode_to_string: invalid bytecode";
1704 * @param b Bytecodes
1705 * @param arg Not used
1707 static void print_it(unsigned char *b
, void *arg
)
1709 printf("%s\n", bytecode_to_string(b
[0]) );
1714 * @param bytes Bytecodes
1716 static void print_bytecodes(unsigned char *bytes
)
1718 bytecodeproc handlers
[] =
1720 print_it
,print_it
,print_it
,print_it
,print_it
,
1721 print_it
,print_it
,print_it
,print_it
,print_it
,
1722 print_it
,print_it
,print_it
1724 bytecode_walk(bytes
, handlers
, NULL
);
1731 static void print_unit(xasm_unit
*u
)
1733 print_bytecodes(u
->dataseg
.bytes
);
1734 print_bytecodes(u
->codeseg
.bytes
);
1737 #endif /* !XLNK_NO_DEBUG */
1739 /*--------------------------------------------------------------------------*/
1740 /* Functions for managing arrays of unit locals. */
1743 * Creates array of locals.
1744 * @param size Number of locals
1745 * @param la Local array
1747 static void create_local_array(int size
, local_array
*la
)
1751 la
->entries
= (local
*)malloc(sizeof(local
) * size
);
1759 * Finalizes array of locals.
1761 static void finalize_local_array(local_array
*la
)
1764 for (i
=0; i
<la
->size
; i
++) {
1765 SAFE_FREE(la
->entries
[i
].name
);
1767 SAFE_FREE(la
->entries
);
1770 /*--------------------------------------------------------------------------*/
1771 /* Functions for counting and registering locals in a unit. */
1772 /* In bytecode expressions, locals are referred to by their index.
1773 In order to not have to go through the bytecodes every time to
1774 find a label definition, the following functions build an array
1775 of structures that can be indexed by the local ID to obtain its
1780 * Counts this local.
1782 static void count_one_local(unsigned char *b
, void *arg
)
1784 /* Argument points to the counter */
1785 int *count
= (int *)arg
;
1790 * Counts the number of locals (labels) in an array of bytecodes.
1791 * @param b Bytecodes, terminated by CMD_END
1792 * @return Number of locals counted
1794 static int count_locals(unsigned char *b
)
1797 /* Table of callback functions for our purpose. */
1798 bytecodeproc handlers
[] =
1801 NULL
, /* CMD_BIN8 */
1802 NULL
, /* CMD_BIN16 */
1803 count_one_local
, /* CMD_LABEL */
1804 NULL
, /* CMD_INSTR */
1808 NULL
, /* CMD_DSI8 */
1809 NULL
, /* CMD_DSI16 */
1813 bytecode_walk(b
, handlers
, (void *)&count
);
1818 * Variable that points to the unit that locals are being registered for.
1820 static xunit
*reg_unit
= NULL
;
1823 * Puts this local into array of locals for current unit.
1825 static void register_one_local(unsigned char *b
, void *arg
)
1829 /* Argument points to a pointer which points to the local struct to fill in */
1830 local
**lpptr
= (local
**)arg
;
1831 local
*lptr
= *lpptr
;
1832 /* Initialize some fields */
1834 lptr
->ref_count
= 0;
1837 lptr
->owner
= reg_unit
;
1839 lptr
->flags
= get_1(b
, &i
);
1840 /* Test export flag */
1841 if (lptr
->flags
& XASM_LABEL_FLAG_EXPORT
) {
1842 /* Get the length of the name */
1843 len
= get_1(b
, &i
) + 1;
1844 /* Allocate space for name */
1845 lptr
->name
= (char *)malloc( len
+ 1 );
1846 if (lptr
->name
!= NULL
) {
1847 /* Copy name from bytecodes */
1848 memcpy(lptr
->name
, &b
[i
], len
);
1849 /* Zero-terminate string */
1850 lptr
->name
[len
] = '\0';
1854 if (lptr
->flags
& XASM_LABEL_FLAG_ALIGN
) {
1855 lptr
->align
= get_1(b
, &i
);
1857 if (lptr
->flags
& XASM_LABEL_FLAG_ADDR
) {
1858 lptr
->phys_addr
= get_2(b
, &i
);
1862 if (program_args
.verbose
) {
1863 verbose(1, " %s align=%d resolved=%d",
1864 lptr
->name
? lptr
->name
: "(anonymous)",
1865 lptr
->align
, lptr
->resolved
);
1868 /* Point to next local in array */
1873 * Puts all locals found in the array of bytecodes into array.
1874 * @param b Bytecodes, terminated by CMD_END
1875 * @param la Pointer to array to receive locals
1876 * @param xu Owner unit
1878 static void register_locals(unsigned char *b
, local_array
*la
, xunit
*xu
)
1882 /* Table of callback functions for our purpose. */
1883 bytecodeproc handlers
[] =
1886 NULL
, /* CMD_BIN8 */
1887 NULL
, /* CMD_BIN16 */
1888 register_one_local
, /* CMD_LABEL */
1889 NULL
, /* CMD_INSTR */
1893 NULL
, /* CMD_DSI8 */
1894 NULL
, /* CMD_DSI16 */
1897 /* Create array of locals */
1898 create_local_array(count_locals(b
), la
);
1904 bytecode_walk(b
, handlers
, (void *)lpptr
);
1907 /*--------------------------------------------------------------------------*/
1908 /* Functions for entering exported symbols into proper hash table. */
1911 * Enters an exported symbol into a hash table.
1912 * @param tab Hash table to enter it into
1915 * @param u Owner unit
1917 static void enter_exported_symbol(hashtab
*tab
, void *key
, void *data
, xasm_unit
*u
)
1919 if ((hashtab_get(label_hash
, key
) != NULL
)
1920 || (hashtab_get(constant_hash
, key
) != NULL
) ) {
1921 err("duplicate symbol `%s' exported from unit `%s'", (char *)key
, u
->name
);
1924 verbose(1, " %s", (char*)key
);
1925 hashtab_put(tab
, key
, data
);
1930 * Enters all constants in a unit into the proper hash table.
1931 * @param u Unit whose constants to enter
1933 static void enter_exported_constants(xasm_unit
*u
)
1937 for (i
=0; i
<u
->const_count
; i
++) {
1938 c
= &u
->constants
[i
];
1939 enter_exported_symbol(constant_hash
, (void *)c
->name
, (void *)c
, u
);
1944 * Enters locals which should be globally visible into the proper hash table.
1945 * @param la Array of locals
1946 * @param u Owner unit
1948 static void enter_exported_locals(local_array
*la
, xasm_unit
*u
)
1952 for (i
=0; i
<la
->size
; i
++) {
1953 l
= &la
->entries
[i
];
1954 /* If it has a name, it is exported */
1955 if (l
->name
!= NULL
) {
1956 enter_exported_symbol(label_hash
, (void *)l
->name
, (void *)l
, u
);
1961 /*--------------------------------------------------------------------------*/
1962 /* Functions for calculating addresses of data labels in a unit. */
1965 * Sets the virtual address of this local to current PC value.
1967 static void set_data_address(unsigned char *b
, void *arg
)
1969 calc_address_args
*args
= (calc_address_args
*)arg
;
1970 local
*l
= &args
->xu
->data_locals
.entries
[args
->index
];
1973 verbose(2, " %.4X %s", l
->virt_addr
, l
->name
? l
->name
: "");
1975 /* Increase label index */
1980 * Calculates addresses of labels in a data segment relative to 0.
1981 * Only a small set of bytecode commands are allowed in a data segment:
1982 * - label (which we want to assign a virtual address)
1983 * - storage (constant or variable)
1985 static void calc_data_addresses(xunit
*u
)
1987 calc_address_args args
;
1988 /* Table of callback functions for our purpose. */
1989 bytecodeproc handlers
[] =
1992 NULL
, /* CMD_BIN8 */
1993 NULL
, /* CMD_BIN16 */
1994 set_data_address
, /* CMD_LABEL */
1995 NULL
, /* CMD_INSTR */
1999 inc_pc_count8
, /* CMD_DSI8 */
2000 inc_pc_count16
, /* CMD_DSI16 */
2001 inc_pc_dsb
/* CMD_DSB */
2008 verbose(1, " %s", u
->_unit_
.name
);
2010 bytecode_walk(u
->_unit_
.dataseg
.bytes
, handlers
, (void *)&args
);
2011 /* Store the end address, which is the total size of data */
2015 /*--------------------------------------------------------------------------*/
2017 /* Constructs 32-bit sort key for local. */
2018 #define SORT_KEY(l) (unsigned long)((((l)->flags & XASM_LABEL_FLAG_ZEROPAGE) << 30) | ((l)->align << 24) | (0x10000-(l)->size))
2021 * Array is sorted from high to low value.
2023 static int label_partition(local
**a
, int p
, int r
)
2031 for (j
=p
; j
<r
; j
++) {
2032 if (SORT_KEY(a
[j
]) >= x
) {
2046 * Quicksort implementation used to sort array of pointers to locals.
2048 static void label_qsort(local
**a
, int p
, int r
)
2052 q
= label_partition(a
, p
, r
);
2053 label_qsort(a
, p
, q
-1);
2054 label_qsort(a
, q
+1, r
);
2059 * Maps all data labels to 6502 RAM locations.
2060 * This is a very important function. It takes all the data labels from all
2061 * the loaded units and attempts to assign them unique physical addresses.
2062 * The list of target RAM blocks given in the linker script is the premise.
2064 static void map_data_to_ram()
2067 local
**total_order
;
2070 /* Use a bit array to keep track of allocations,
2071 to ensure that there is no overlap */
2072 unsigned char *allocated
;
2073 int ram_base
, ram_end
;
2074 if (ram_block_head
== NULL
)
2075 return; /* Nothing to do. */
2078 ram_base
= 10000000;
2079 ram_end
= -10000000;
2080 for (b
= ram_block_head
; b
!= NULL
; b
= b
->next
) {
2081 if (b
->start
< ram_base
)
2082 ram_base
= b
->start
;
2083 if (b
->end
> ram_end
)
2087 allocated
= (unsigned char *)malloc(((ram_end
- ram_base
) + 7) / 8);
2088 memset(allocated
, 0, ((ram_end
- ram_base
) + 7) / 8);
2089 /* Calculate total number of labels to map */
2091 for (i
=0; i
<unit_count
; i
++) {
2092 count
+= units
[i
].data_locals
.size
;
2094 /* Put pointers to all data labels in one big array */
2095 total_order
= (local
**)malloc( count
* sizeof(local
*) );
2096 for (i
=0, k
=0; i
<unit_count
; i
++) {
2099 la
= &units
[i
].data_locals
;
2100 for (j
=0; j
<la
->size
; j
++) {
2102 /* Use virtual addresses to calculate size from this label to next */
2103 if (j
== la
->size
-1) {
2104 size
= units
[i
].data_size
;
2107 size
= la
->entries
[j
+1].virt_addr
;
2109 la
->entries
[j
].size
= size
- la
->entries
[j
].virt_addr
;
2110 /* Put pointer in array */
2111 total_order
[k
++] = &la
->entries
[j
];
2115 label_qsort(total_order
, 0, count
-1);
2117 for (i
=0; i
<count
; i
++) {
2119 /* Try to allocate it */
2120 if (alloc_ram(l
) == 1) {
2121 /* Good, label mapped successfully */
2123 verbose(1, " %.4X-%.4X %s (%s)", l
->phys_addr
,
2124 l
->phys_addr
+ l
->size
-1, l
->name
? l
->name
: "",
2125 l
->owner
->_unit_
.name
);
2127 /* Verify that there's no overlap with other variable */
2129 for (a
= l
->phys_addr
; a
< l
->phys_addr
+ l
->size
; ++a
) {
2130 assert((allocated
[(a
- ram_base
) / 8] & (1 << (a
& 7))) == 0);
2131 allocated
[(a
- ram_base
) / 8] |= 1 << (a
& 7);
2136 err("out of 6502 RAM while allocating unit `%s'", l
->owner
->_unit_
.name
);
2144 /*--------------------------------------------------------------------------*/
2145 /* Functions for calculating offsets of code labels in a unit. */
2148 * Sets the address of this code label to current PC.
2150 static void set_code_address(unsigned char *b
, void *arg
)
2152 calc_address_args
*args
= (calc_address_args
*)arg
;
2153 local
*l
= &args
->xu
->code_locals
.entries
[args
->index
];
2157 if (program_args
.verbose
) {
2158 fprintf(stdout
, " %.4X %s (%s)\n", l
->phys_addr
,
2159 l
->name
? l
->name
: "", l
->owner
->_unit_
.name
);
2162 /* Increase label index */
2167 * Calculates addresses of code labels in a segment.
2168 * NOTE: Only the virtual addresses (relative to 0) are calculated.
2169 * The labels then need to be relocated to obtain the physical address (see below).
2172 static void calc_code_addresses(xunit
*u
)
2174 calc_address_args args
;
2175 /* Table of callback functions for our purpose. */
2176 bytecodeproc handlers
[] =
2179 inc_pc_count8
, /* CMD_BIN8 */
2180 inc_pc_count16
, /* CMD_BIN16 */
2181 set_code_address
, /* CMD_LABEL */
2182 inc_pc_instr
, /* CMD_INSTR */
2183 inc_pc_1
, /* CMD_DB -- TODO, error if string */
2184 inc_pc_2
, /* CMD_DW */
2185 inc_pc_4
, /* CMD_DD */
2186 inc_pc_count8
, /* CMD_DSI8 */
2187 inc_pc_count16
, /* CMD_DSI16 */
2188 inc_pc_dsb
/* CMD_DSB */
2194 bytecode_walk(u
->_unit_
.codeseg
.bytes
, handlers
, (void *)&args
);
2195 /* Store the total size of code */
2196 u
->code_size
= pc
- u
->code_origin
;
2199 /*--------------------------------------------------------------------------*/
2202 * Issues a script error.
2204 static void scripterr(xlnk_script
*s
, xlnk_script_command
*c
, const char *fmt
, ...)
2210 fprintf(stderr
, "error: %s:%d: `%s': ", s
->name
, c
->line
, xlnk_script_command_type_to_string(c
->type
) );
2211 vfprintf(stderr
, fmt
, ap
);
2212 fprintf(stderr
, "\n");
2218 #define require_arg(s, c, a, d) { \
2219 d = xlnk_script_get_command_arg(c, a); \
2221 scripterr(s, c, "missing argument `%s'", a); \
2226 #define require_arg_in_range(s, c, a, v, l, h) { \
2227 if (((v) < (l)) || ((v) > (h))) { \
2228 scripterr(s, c, "value of argument `%s' is out of range", a); \
2233 /*--------------------------------------------------------------------------*/
2234 /* Functions for registering RAM blocks in script. */
2237 * Registers one RAM block based on 'ram' script command.
2238 * @param s Linker script
2239 * @param c Command of type RAM_COMMAND
2240 * @param arg Not used
2242 static void register_one_ram_block(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2246 const char *start_str
;
2247 const char *end_str
;
2248 require_arg(s
, c
, "start", start_str
);
2249 require_arg(s
, c
, "end", end_str
);
2250 start
= str_to_int(start_str
);
2251 end
= str_to_int(end_str
);
2252 require_arg_in_range(s
, c
, "start", start
, 0x0000, 0xFFFF);
2253 require_arg_in_range(s
, c
, "end", end
, 0x0000, 0xFFFF);
2255 scripterr(s
, c
, "`end' is smaller than `start'");
2257 add_ram_block(start
, end
);
2261 * Registers RAM blocks based on 'ram' commands in a script.
2262 * @param sc Linker script
2264 static void register_ram_blocks(xlnk_script
*sc
)
2266 /* Table of mappings for our purpose */
2267 static xlnk_script_commandprocmap map
[] = {
2268 { XLNK_RAM_COMMAND
, register_one_ram_block
},
2269 { XLNK_BAD_COMMAND
, NULL
}
2272 xlnk_script_walk(sc
, map
, NULL
);
2273 /* Calculate total RAM size */
2274 total_ram
= ram_left();
2277 /*--------------------------------------------------------------------------*/
2278 /* Functions for loading and initial processing of units in script. */
2281 * Registers (parses etc.) one unit based on 'link' script command.
2282 * @param s Linker script
2283 * @param c Command of type LINK_COMMAND
2284 * @param arg Pointer to unit index
2286 static void register_one_unit(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2291 require_arg(s
, c
, "file", file
);
2292 /* arg is pointer to unit index */
2294 /* Get pointer to xunit to fill in */
2296 /* Read basic unit from file */
2297 if (xasm_unit_read(file
, &xu
->_unit_
) == 0) {
2298 scripterr(s
, c
, "failed to load unit `%s'", file
);
2303 verbose(1, " unit `%s' loaded", file
);
2305 verbose(1, " registering local symbols...");
2306 register_locals(xu
->_unit_
.dataseg
.bytes
, &xu
->data_locals
, xu
);
2307 register_locals(xu
->_unit_
.codeseg
.bytes
, &xu
->code_locals
, xu
);
2309 verbose(1, " registering public symbols...");
2310 enter_exported_constants(&xu
->_unit_
);
2311 enter_exported_locals(&xu
->data_locals
, &xu
->_unit_
);
2312 enter_exported_locals(&xu
->code_locals
, &xu
->_unit_
);
2314 hashtab_put(unit_hash
, (void*)file
, xu
);
2315 /* Increment unit index */
2320 * Registers units based on 'link' commands in script.
2321 * @param sc Linker script
2323 static void register_units(xlnk_script
*sc
)
2325 /* Table of mappings for our purpose */
2326 static xlnk_script_commandprocmap map
[] = {
2327 { XLNK_LINK_COMMAND
, register_one_unit
},
2328 { XLNK_BAD_COMMAND
, NULL
}
2332 xlnk_script_walk(sc
, map
, (void *)&i
);
2335 /*--------------------------------------------------------------------------*/
2336 /* Functions for composing a binary file based on a sequential list of
2340 * Sets the output file according to 'output' script command.
2341 * @param s Linker script
2342 * @param c Command of type OUTPUT_COMMAND
2343 * @param arg Pointer to file handle
2345 static void set_output(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2349 require_arg(s
, c
, "file", file
);
2350 /* Arg is pointer to file handle pointer */
2355 *fpp
= fopen(file
, "wb");
2357 scripterr(s
, c
, "could not open `%s' for writing", file
);
2360 verbose(1, " output goes to `%s'", file
);
2365 * Copies a file to output according to 'copy' script command.
2366 * @param s Linker script
2367 * @param c Command of type COPY_COMMAND
2368 * @param arg Pointer to file handle
2370 static void copy_to_output(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2376 /* Arg is pointer to file handle pointer */
2379 scripterr(s
, c
, "no output open");
2382 require_arg(s
, c
, "file", file
);
2383 cf
= fopen(file
, "rb");
2385 scripterr(s
, c
, "could not open `%s' for reading", file
);
2388 verbose(1, " copying `%s' to output at position %ld...", file
, ftell(*fpp
) );
2389 for (k
= fgetc(cf
); !feof(cf
); k
= fgetc(cf
) ) {
2392 bank_offset
+= ftell(cf
);
2395 if (bank_offset
> bank_size
) {
2396 scripterr(s
, c
, "bank size (%d) exceeded by %d bytes", bank_size
, bank_offset
- bank_size
);
2403 * Starts a new bank according to 'bank' script command.
2404 * @param s Linker script
2405 * @param c Command of type BANK_COMMAND
2406 * @param arg Pointer to file handle
2408 static void start_bank(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2410 const char *size_str
;
2411 const char *origin_str
;
2412 size_str
= xlnk_script_get_command_arg(c
, "size");
2413 if (size_str
!= NULL
) {
2414 bank_size
= str_to_int(size_str
);
2415 if (bank_size
<= 0) {
2416 scripterr(s
, c
, "invalid size");
2420 /* Use bank size of previous bank if there was one */
2421 if (bank_size
== 0x7FFFFFFF) {
2422 scripterr(s
, c
, "no bank size set");
2425 origin_str
= xlnk_script_get_command_arg(c
, "origin");
2426 if (origin_str
!= NULL
) {
2427 bank_origin
= str_to_int(origin_str
);
2428 require_arg_in_range(s
, c
, "origin", bank_origin
, 0x0000, 0xFFFF);
2431 /* Use old bank origin */
2439 * Writes unit according to 'link' script command.
2440 * @param s Linker script
2441 * @param c Command of type LINK_COMMAND
2442 * @param arg Pointer to file handle
2444 static void write_unit(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2449 /* Arg is pointer to file handle pointer */
2452 scripterr(s
, c
, "no output open");
2455 require_arg(s
, c
, "file", file
);
2456 xu
= (xunit
*)hashtab_get(unit_hash
, (void*)file
);
2457 verbose(1, " appending unit `%s' to output at position %ld...", file
, ftell(*fpp
));
2458 write_as_binary(*fpp
, xu
);
2459 bank_offset
+= xu
->code_size
;
2460 if (bank_offset
> bank_size
) {
2461 scripterr(s
, c
, "bank size (%d) exceeded by %d bytes", bank_size
, bank_offset
- bank_size
);
2467 * Pads output file according to 'pad' script command.
2468 * @param s Linker script
2469 * @param c Command of type PAD_COMMAND
2470 * @param arg Pointer to file handle
2472 static void write_pad(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2479 const char *offset_str
;
2480 const char *origin_str
;
2481 const char *size_str
;
2482 /* Arg is pointer to file handle pointer */
2485 scripterr(s
, c
, "no output open");
2488 if ((offset_str
= xlnk_script_get_command_arg(c
, "offset")) != NULL
) {
2489 offset
= str_to_int(offset_str
);
2490 count
= offset
- bank_offset
;
2492 else if ((origin_str
= xlnk_script_get_command_arg(c
, "origin")) != NULL
) {
2493 origin
= str_to_int(origin_str
);
2494 count
= origin
- pc
;
2496 else if ((size_str
= xlnk_script_get_command_arg(c
, "size")) != NULL
) {
2497 count
= str_to_int(size_str
);
2500 scripterr(s
, c
, "missing argument");
2504 scripterr(s
, c
, "cannot pad backwards");
2507 else if (count
> 0) {
2508 verbose(1, " padding %d bytes...", count
);
2510 for (i
=0; i
<count
; i
++) {
2513 bank_offset
+= count
;
2515 if (bank_offset
> bank_size
) {
2516 scripterr(s
, c
, "bank size (%d) exceeded by %d bytes", bank_size
, bank_offset
- bank_size
);
2522 * Pads to end of bank in file if bank size not reached.
2523 * @param s Linker script
2524 * @param c Command of type BANK_COMMAND
2525 * @param fp File handle
2527 static void maybe_pad_bank(xlnk_script
*s
, xlnk_script_command
*c
, FILE *fp
)
2530 if ( (bank_size
!= 0x7FFFFFFF) && (bank_offset
< bank_size
) ) {
2532 scripterr(s
, c
, "no output open");
2535 for (i
=bank_offset
; i
<bank_size
; i
++) {
2543 * Finishes old bank in output and starts new bank.
2544 * @param s Linker script
2545 * @param c Command of type BANK_COMMAND
2546 * @param arg Pointer to file handle
2548 static void write_bank(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2551 /* Arg is pointer to file handle pointer */
2553 maybe_pad_bank(s
, c
, *fpp
);
2554 start_bank(s
, c
, arg
);
2558 * Generates the final binary output from the linker.
2559 * @param sc Linker script
2561 static void generate_binary_output(xlnk_script
*sc
)
2564 /* Table of mappings for our purpose */
2565 static xlnk_script_commandprocmap map
[] = {
2566 { XLNK_OUTPUT_COMMAND
, set_output
},
2567 { XLNK_COPY_COMMAND
, copy_to_output
},
2568 { XLNK_BANK_COMMAND
, write_bank
},
2569 { XLNK_LINK_COMMAND
, write_unit
},
2570 { XLNK_PAD_COMMAND
, write_pad
},
2571 { XLNK_BAD_COMMAND
, NULL
}
2574 bank_size
= 0x7FFFFFFF;
2580 xlnk_script_walk(sc
, map
, (void *)&fp
);
2581 /* Pad last bank if necessary */
2582 maybe_pad_bank(sc
, sc
->first_command
, fp
);
2585 /*--------------------------------------------------------------------------*/
2586 /* Functions for producing assembly code based on a sequential list of
2590 * Sets the output file according to 'output' script command.
2591 * @param s Linker script
2592 * @param c Command of type OUTPUT_COMMAND
2593 * @param arg Pointer to file handle
2595 static void asm_set_output(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2597 /* No-op when generating assembly. */
2601 * Copies a file to output according to 'copy' script command.
2602 * @param s Linker script
2603 * @param c Command of type COPY_COMMAND
2604 * @param arg Pointer to file handle
2606 static void asm_copy_to_output(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2611 /* Arg is pointer to file handle pointer */
2613 require_arg(s
, c
, "file", file
);
2614 cf
= fopen(file
, "rb");
2616 scripterr(s
, c
, "could not open `%s' for reading", file
);
2618 unsigned char buf
[1024];
2619 int count
= fread(buf
, 1, 1024, cf
);
2620 fprintf(*fpp
, "; begin %s\n", file
);
2622 print_chunk(*fpp
, /*label=*/0, buf
, count
, /*cols=*/16);
2623 count
= fread(buf
, 1, 1024, cf
);
2625 fprintf(*fpp
, "; end %s\n", file
);
2626 bank_offset
+= ftell(cf
);
2629 if (bank_offset
> bank_size
) {
2630 scripterr(s
, c
, "bank size (%d) exceeded by %d bytes", bank_size
, bank_offset
- bank_size
);
2636 * Starts a new bank according to 'bank' script command.
2637 * @param s Linker script
2638 * @param c Command of type BANK_COMMAND
2639 * @param arg Pointer to file handle
2641 static void asm_start_bank(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2643 FILE *fp
= *(FILE**)arg
;
2644 start_bank(s
, c
, arg
);
2645 fprintf(fp
, ".ORG $%.4X\n", pc
);
2649 * Writes unit according to 'link' script command.
2650 * @param s Linker script
2651 * @param c Command of type LINK_COMMAND
2652 * @param arg Pointer to file handle
2654 static void asm_write_unit(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2659 /* Arg is pointer to file handle pointer */
2661 require_arg(s
, c
, "file", file
);
2662 xu
= (xunit
*)hashtab_get(unit_hash
, (void*)file
);
2663 verbose(1, " appending unit `%s' to output at position %ld...", file
, ftell(*fpp
));
2664 write_as_assembly(*fpp
, xu
);
2665 bank_offset
+= xu
->code_size
;
2666 if (bank_offset
> bank_size
) {
2667 scripterr(s
, c
, "bank size (%d) exceeded by %d bytes", bank_size
, bank_offset
- bank_size
);
2672 * Pads output file according to 'pad' script command.
2673 * @param s Linker script
2674 * @param c Command of type PAD_COMMAND
2675 * @param arg Pointer to file handle
2677 static void asm_write_pad(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2683 const char *offset_str
;
2684 const char *origin_str
;
2685 const char *size_str
;
2686 /* Arg is pointer to file handle pointer */
2688 if ((offset_str
= xlnk_script_get_command_arg(c
, "offset")) != NULL
) {
2689 offset
= str_to_int(offset_str
);
2690 count
= offset
- bank_offset
;
2691 } else if ((origin_str
= xlnk_script_get_command_arg(c
, "origin")) != NULL
) {
2692 origin
= str_to_int(origin_str
);
2693 count
= origin
- pc
;
2694 } else if ((size_str
= xlnk_script_get_command_arg(c
, "size")) != NULL
) {
2695 count
= str_to_int(size_str
);
2697 scripterr(s
, c
, "missing argument");
2701 scripterr(s
, c
, "cannot pad backwards");
2703 } else if (count
> 0) {
2704 verbose(1, " padding %d bytes...", count
);
2706 fprintf(*fpp
, ".DSB $%X\n", count
);
2707 bank_offset
+= count
;
2709 if (bank_offset
> bank_size
) {
2710 scripterr(s
, c
, "bank size (%d) exceeded by %d bytes", bank_size
, bank_offset
- bank_size
);
2715 * Pads to end of bank in file if bank size not reached.
2716 * @param s Linker script
2717 * @param c Command of type BANK_COMMAND
2718 * @param fp File handle
2720 static void asm_maybe_pad_bank(xlnk_script
*s
, xlnk_script_command
*c
, FILE *fp
)
2722 if ( (bank_size
!= 0x7FFFFFFF) && (bank_offset
< bank_size
) ) {
2723 fprintf(fp
, ".DSB $%X\n", bank_size
- bank_offset
);
2728 * Finishes old bank in output and starts new bank.
2729 * @param s Linker script
2730 * @param c Command of type BANK_COMMAND
2731 * @param arg Pointer to file handle
2733 static void asm_write_bank(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2735 FILE **fpp
= (FILE **)arg
;
2736 asm_maybe_pad_bank(s
, c
, *fpp
);
2737 asm_start_bank(s
, c
, arg
);
2740 static void generate_assembly_output(xlnk_script
*sc
, FILE *fp
)
2742 /* Table of mappings for our purpose */
2743 static xlnk_script_commandprocmap map
[] = {
2744 { XLNK_OUTPUT_COMMAND
, asm_set_output
},
2745 { XLNK_COPY_COMMAND
, asm_copy_to_output
},
2746 { XLNK_BANK_COMMAND
, asm_write_bank
},
2747 { XLNK_LINK_COMMAND
, asm_write_unit
},
2748 { XLNK_PAD_COMMAND
, asm_write_pad
},
2749 { XLNK_BAD_COMMAND
, NULL
}
2752 bank_size
= 0x7FFFFFFF;
2757 fprintf(fp
, ".CODESEG\n");
2759 xlnk_script_walk(sc
, map
, (void *)&fp
);
2760 /* Pad last bank if necessary */
2761 asm_maybe_pad_bank(sc
, sc
->first_command
, fp
);
2762 fprintf(fp
, ".END\n");
2765 /*--------------------------------------------------------------------------*/
2768 * Increases bank offset and PC according to size of the file specified by
2769 * 'copy' script command.
2770 * @param s Linker script
2771 * @param c Command of type COPY_COMMAND
2772 * @param arg Not used
2774 static void inc_offset_copy(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2778 require_arg(s
, c
, "file", file
);
2779 fp
= fopen(file
, "rb");
2781 scripterr(s
, c
, "could not open `%s' for reading", file
);
2784 fseek(fp
, 0, SEEK_END
);
2785 bank_offset
+= ftell(fp
);
2788 if (bank_offset
> bank_size
) {
2789 scripterr(s
, c
, "bank size (%d) exceeded by %d bytes", bank_size
, bank_offset
- bank_size
);
2795 * Sets the origin of a unit and relocates its code to this location.
2796 * @param s Linker script
2797 * @param c Command of type LINK_COMMAND
2798 * @param arg Not used
2800 static void set_unit_origin(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2804 const char *origin_str
;
2806 require_arg(s
, c
, "file", file
);
2807 xu
= (xunit
*)hashtab_get(unit_hash
, (void*)file
);
2808 origin_str
= xlnk_script_get_command_arg(c
, "origin");
2809 if (origin_str
!= NULL
) {
2810 origin
= str_to_int(origin_str
);
2811 require_arg_in_range(s
, c
, "origin", origin
, 0x0000, 0xFFFF);
2812 xu
->code_origin
= origin
;
2816 /* No origin specified. Set to PC. */
2817 xu
->code_origin
= pc
;
2819 xu
->bank_id
= bank_id
;
2820 /* Now we can calculate the physical code addresses of the unit. */
2821 calc_code_addresses(xu
);
2822 verbose(1, " unit `%s' relocated to %.4X", xu
->_unit_
.name
, xu
->code_origin
);
2823 bank_offset
+= xu
->code_size
;
2827 * Increases bank offset and PC according to 'pad' script command.
2828 * @param s Linker script
2829 * @param c Command of type PAD_COMMAND
2830 * @param arg Not used
2832 static void inc_offset_pad(xlnk_script
*s
, xlnk_script_command
*c
, void *arg
)
2837 const char *offset_str
;
2838 const char *origin_str
;
2839 const char *size_str
;
2840 if ((offset_str
= xlnk_script_get_command_arg(c
, "offset")) != NULL
) {
2841 offset
= str_to_int(offset_str
);
2842 count
= offset
- bank_offset
;
2844 else if ((origin_str
= xlnk_script_get_command_arg(c
, "origin")) != NULL
) {
2845 origin
= str_to_int(origin_str
);
2846 count
= origin
- pc
;
2848 else if ((size_str
= xlnk_script_get_command_arg(c
, "size")) != NULL
) {
2849 count
= str_to_int(size_str
);
2852 scripterr(s
, c
, "missing argument");
2856 scripterr(s
, c
, "cannot pad %d bytes backwards", -count
);
2859 bank_offset
+= count
;
2861 if (bank_offset
> bank_size
) {
2862 scripterr(s
, c
, "bank size (%d) exceeded by %d bytes", bank_size
, bank_offset
- bank_size
);
2867 * Relocates code of all units according to script commands and/or their position
2868 * in the final binary.
2869 * @param sc Linker script
2871 static void relocate_units(xlnk_script
*sc
)
2873 /* Table of mappings for our purpose */
2874 static xlnk_script_commandprocmap map
[] = {
2875 { XLNK_COPY_COMMAND
, inc_offset_copy
},
2876 { XLNK_BANK_COMMAND
, start_bank
},
2877 { XLNK_LINK_COMMAND
, set_unit_origin
},
2878 { XLNK_PAD_COMMAND
, inc_offset_pad
},
2879 { XLNK_BAD_COMMAND
, NULL
}
2882 bank_size
= 0x7FFFFFFF;
2888 xlnk_script_walk(sc
, map
, NULL
);
2894 static void maybe_print_ram_statistics()
2898 if (total_ram
> 0) {
2900 used
= total_ram
- left
;
2901 verbose(1, " total RAM: %d bytes", total_ram
);
2902 verbose(1, " RAM used: %d bytes (%d%%)", used
, (int)(((float)used
/ (float)total_ram
)*100.0f
) );
2903 verbose(1, " RAM left: %d bytes (%d%%)", left
, (int)(((float)left
/ (float)total_ram
)*100.0f
) );
2907 /*--------------------------------------------------------------------------*/
2910 * Program entrypoint.
2912 int main(int argc
, char **argv
)
2917 parse_arguments(argc
, argv
);
2923 verbose(1, "parsing linker script...");
2924 if (xlnk_script_parse(program_args
.input_file
, &sc
) == 0) {
2925 /* Something bad happened when parsing script, halt */
2929 verbose(1, "registering RAM blocks...");
2930 register_ram_blocks(&sc
);
2932 constant_hash
= hashtab_create(23, HASHTAB_STRKEYHSH
, HASHTAB_STRKEYCMP
);
2933 label_hash
= hashtab_create(23, HASHTAB_STRKEYHSH
, HASHTAB_STRKEYCMP
);
2934 unit_hash
= hashtab_create(11, HASHTAB_STRKEYHSH
, HASHTAB_STRKEYCMP
);
2936 unit_count
= xlnk_script_count_command_type(&sc
, XLNK_LINK_COMMAND
);
2937 if (unit_count
> 0) {
2938 units
= (xunit
*)malloc( sizeof(xunit
) * unit_count
);
2943 verbose(1, "loading units...");
2944 register_units(&sc
);
2945 if (err_count
!= 0) {
2950 /* Only continue with processing if no unresolved symbols */
2951 if (err_count
== 0) {
2952 verbose(1, "calculating data addresses...");
2953 for (i
=0; i
<unit_count
; i
++) {
2954 calc_data_addresses(&units
[i
]);
2957 /* TODO: Count references: go through all instructions, find EXTRN and LOCAL operands in expressions */
2958 /* TODO: Find modes of access for each DATA label (i.e. label MUST be allocated in zero page) */
2960 verbose(1, "mapping data to RAM...");
2962 maybe_print_ram_statistics();
2964 if (err_count
== 0) {
2965 verbose(1, "relocating code...");
2967 relocate_units(&sc
);
2969 relocate_units(&sc
);
2971 if (err_count
== 0) {
2972 verbose(1, "generating output...");
2973 generate_binary_output(&sc
);
2974 if (generate_assembly
)
2975 generate_assembly_output(&sc
, stdout
);
2980 verbose(1, "cleaning up...");
2982 for (i
=0; i
<unit_count
; i
++) {
2983 if (units
[i
].loaded
) {
2984 finalize_local_array( &units
[i
].data_locals
);
2985 finalize_local_array( &units
[i
].code_locals
);
2986 xasm_unit_finalize( &units
[i
]._unit_
);
2989 hashtab_finalize(label_hash
);
2990 hashtab_finalize(constant_hash
);
2991 hashtab_finalize(unit_hash
);
2992 finalize_ram_blocks();
2993 xlnk_script_finalize(&sc
);
2995 return (err_count
== 0) ? 0 : 1;