1 /* ".COD" file output for gplink
2 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
3 James Bowman, Scott Dattalo
4 Copyright (C) 2012 Borut Razem
5 Copyright (C) 2016 Molnar Karoly
6
7 This file is part of gputils.
8
9 gputils is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13
14 gputils is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with gputils; see the file COPYING. If not, write to
21 the Free Software Foundation, 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA. */
23
24 #include "stdhdr.h"
25
26 #include "libgputils.h"
27 #include "gplink.h"
28 #include "cod.h"
29
30 static DirBlockInfo *main_dir;
31
32 /*------------------------------------------------------------------------------------------------*/
33
34 /* Assign each file name unique file number. A file may appear in the symbol table more than once. */
35
36 static void
_assign_file_id(void)37 _assign_file_id(void)
38 {
39 symbol_table_t *file_table;
40 gp_symbol_t *symbol;
41 gp_aux_t *aux;
42 symbol_t *sym;
43 int file_id;
44 int *value;
45
46 /* Build a case sensitive file table. */
47 file_table = gp_sym_push_table(NULL, false);
48 file_id = 0;
49
50 symbol = state.object->symbol_list.first;
51 while (symbol != NULL) {
52 if (symbol->class == C_FILE) {
53 aux = symbol->aux_list.first;
54 assert(aux != NULL);
55 sym = gp_sym_get_symbol(file_table, aux->_aux_symbol._aux_file.filename);
56
57 if (sym != NULL) {
58 /* Fetch the file number. */
59 value = (int *)gp_sym_get_symbol_annotation(sym);
60 }
61 else {
62 /* The file hasn't been assigned a value. */
63 value = (int *)GP_Malloc(sizeof(int));
64 *value = file_id++;
65 sym = gp_sym_add_symbol(file_table, aux->_aux_symbol._aux_file.filename);
66 gp_sym_annotate_symbol(sym, value);
67 }
68
69 symbol->number = *value;
70 }
71
72 symbol = symbol->next;
73 }
74
75 /* Destroy the table. */
76 file_table = gp_sym_pop_table(file_table);
77 }
78
79 /*------------------------------------------------------------------------------------------------*/
80
81 /* cod_write_symbols - Write the symbol table to the .cod file.
82 *
83 * This routine will read the symbol table that gplink has created
84 * and convert it into a format suitable for .cod files. So far, only
85 * three variable types are supported: address, register, and constants.
86 *
87 */
88
89 static void
_write_symbols(const symbol_t ** Symbol_list,size_t Num_symbols)90 _write_symbols(const symbol_t **Symbol_list, size_t Num_symbols)
91 {
92 size_t i;
93 unsigned int length;
94 unsigned int type;
95 const gp_coffsymbol_t *var;
96 const gp_symbol_t *symbol;
97 const gp_section_t *section;
98 const char *name;
99 BlockList *sb;
100 gp_boolean truncated;
101
102 if ((Symbol_list == NULL) || (Num_symbols == 0)) {
103 return;
104 }
105
106 sb = NULL;
107 for (i = 0; i < Num_symbols; i++) {
108 name = gp_sym_get_symbol_name(Symbol_list[i]);
109 var = (const gp_coffsymbol_t *)gp_sym_get_symbol_annotation(Symbol_list[i]);
110 assert(var != NULL);
111 length = gp_strlen_Plimit(name, COD_LSYMBOL_NAME_MAX_SIZE, &truncated);
112
113 if (truncated) {
114 gp_warning("This symbol name (\"%s\") too long to .COD file, it will be truncated to %u bytes length.",
115 name, length);
116 }
117
118 /* If this symbol extends past the end of the cod block then write this block out. */
119
120 if ((sb == NULL) || ((main_dir->lsym.offset + length + COD_LSYMBOL_EXTRA) >= COD_BLOCK_SIZE)) {
121 sb = gp_cod_block_append(&main_dir->lsym, gp_cod_block_new());
122 }
123
124 symbol = var->symbol;
125 assert(symbol != NULL);
126 section = symbol->section;
127 assert(section != NULL);
128
129 if (FlagIsSet(section->flags, STYP_TEXT)) {
130 type = COD_ST_ADDRESS;
131 }
132 else if (FlagIsSet(section->flags, STYP_RAM_AREA)) {
133 type = COD_ST_C_SHORT;
134 }
135 else {
136 type = COD_ST_CONSTANT;
137 }
138
139 gp_cod_put_long_symbol(&sb->block[main_dir->lsym.offset], name, symbol->value, type);
140 main_dir->lsym.offset += length + COD_LSYMBOL_EXTRA;
141 }
142 }
143
144 /*------------------------------------------------------------------------------------------------*/
145
146 static void
_write_symbol_table(const symbol_table_t * Table)147 _write_symbol_table(const symbol_table_t *Table)
148 {
149 const symbol_t **lst;
150 size_t sym_count;
151
152 sym_count = gp_sym_get_symbol_count(Table);
153
154 if (sym_count == 0) {
155 return;
156 }
157
158 lst = gp_sym_clone_symbol_array(Table, gp_sym_compare_fn);
159 _write_symbols(lst, sym_count);
160 free(lst);
161 }
162
163 /*------------------------------------------------------------------------------------------------*/
164
165 /* _write_source_file_block - Write a code block that contains a list of the source files. */
166
167 static void
_write_source_file_block(void)168 _write_source_file_block(void)
169 {
170 const gp_symbol_t *symbol;
171 BlockList *fb;
172 int file_id;
173 unsigned int length;
174 const char *string;
175 gp_boolean truncated;
176
177 fb = NULL;
178 file_id = 0;
179 symbol = state.object->symbol_list.first;
180 while (symbol != NULL) {
181 if ((fb == NULL) || (main_dir->file.offset >= (COD_FILE_NAMES_PER_BLOCK * COD_FILE_NAME_SIZE))) {
182 fb = gp_cod_block_append(&main_dir->file, gp_cod_block_new());
183 }
184
185 if ((symbol->class == C_FILE) && (symbol->number == file_id)) {
186 /* Skip the duplicate file symbols. */
187 file_id++;
188
189 /* The file id is used to define the index at which the file name is written within
190 * the file code block. (The id's are sequentially assigned when the files are opened.)
191 * If there are too many files, then gpasm will abort.
192 * Note: The .cod files can handle larger file lists...
193 */
194
195 string = symbol->aux_list.first->_aux_symbol._aux_file.filename;
196 length = gp_Pstr_from_str(&fb->block[main_dir->file.offset], COD_FILE_NAME_SIZE, string, &truncated);
197
198 if (truncated) {
199 gp_warning("This source name (\"%s\") too long to .COD file, it will be truncated to %u bytes length.",
200 string, length);
201 }
202
203 main_dir->file.offset += COD_FILE_NAME_SIZE;
204 }
205
206 symbol = symbol->next;
207 }
208 }
209
210 /*------------------------------------------------------------------------------------------------*/
211
212 /* _write_debug - Write debug symbols to the .cod file. */
213
214 static void
_write_debug(void)215 _write_debug(void)
216 {
217 unsigned int length;
218 const gp_symbol_t *symbol;
219 const gp_aux_t *aux;
220 BlockList *db;
221 char command;
222 const char *string;
223 gp_boolean truncated;
224
225 db = NULL;
226 symbol = state.object->symbol_list.first;
227 while (symbol != NULL) {
228 if (strcasecmp(".direct", symbol->name) == 0) {
229 assert(symbol->aux_list.num_nodes == 1);
230 aux = symbol->aux_list.first;
231 assert(aux != NULL);
232
233 command = aux->_aux_symbol._aux_direct.command;
234 string = aux->_aux_symbol._aux_direct.string;
235 length = gp_strlen_Plimit(string, COD_DEBUG_MSG_MAX_SIZE, &truncated);
236
237 if (truncated) {
238 gp_warning("This .direct string (\"%s\") too long to .COD file, it will be truncated to %u bytes length.",
239 string, length);
240 }
241
242 /* If this message extends past the end of the cod block then write this block out. */
243
244 if ((db == NULL) || ((main_dir->debug.offset + length + COD_DEBUG_EXTRA) >= COD_BLOCK_SIZE)) {
245 db = gp_cod_block_append(&main_dir->debug, gp_cod_block_new());
246 }
247
248 main_dir->debug.offset += gp_cod_put_debug_symbol(&db->block[main_dir->debug.offset], string,
249 symbol->value, command);
250 }
251
252 symbol = symbol->next;
253 }
254 }
255
256 /*------------------------------------------------------------------------------------------------*/
257
258 /* init_cod - Initialize the .cod file. */
259
260 void
cod_init(void)261 cod_init(void)
262 {
263 if (state.cod_file != OUT_NAMED) {
264 snprintf(state.cod_file_name, sizeof(state.cod_file_name), "%s.cod", state.base_file_name);
265 }
266
267 if ((gp_num_errors > 0) || (state.cod_file == OUT_SUPPRESS)) {
268 state.cod.f = NULL;
269 state.cod.enabled = false;
270 unlink(state.cod_file_name);
271 }
272 else {
273 state.cod.f = fopen(state.cod_file_name, "wb");
274
275 if (state.cod.f == NULL) {
276 perror(state.cod_file_name);
277 exit(1);
278 }
279 state.cod.enabled = true;
280 }
281
282 if (!state.cod.enabled) {
283 main_dir = NULL;
284 return;
285 }
286
287 main_dir = gp_cod_init_dir_block(state.cod_file_name, "gplink");
288 _assign_file_id();
289 }
290
291 /*------------------------------------------------------------------------------------------------*/
292
293 /* cod_lst_line - Add a line of information that cross references the
294 * the opcode's address, the source file and the list file.
295 */
296
297 void
cod_lst_line(int line_type)298 cod_lst_line(int line_type)
299 {
300 static DirBlockInfo *dbi = NULL;
301 static unsigned int _64k_base = 0;
302
303 uint8_t smod_flag;
304 BlockList *lb;
305 gp_boolean first_time;
306 unsigned int address;
307 unsigned int high_address;
308 uint8_t *record;
309
310 if (!state.cod.enabled) {
311 return;
312 }
313
314 address = gp_processor_insn_from_byte_c(state.class, state.lst.was_byte_addr);
315 high_address = IMemBaseFromAddr(address);
316
317 if ((dbi == NULL) || (high_address != _64k_base)) {
318 _64k_base = high_address;
319 dbi = gp_cod_find_dir_block_by_high_addr(main_dir, _64k_base);
320 }
321
322 first_time = (gp_cod_block_get_last(&dbi->list) == NULL) ? true : false;
323
324 lb = gp_cod_block_get_last_or_new(&dbi->list);
325
326 if (dbi->list.offset >= (COD_MAX_LINE_SYM * COD_LINE_SYM_SIZE)) {
327 lb = gp_cod_block_append(&dbi->list, gp_cod_block_new());
328 }
329
330 assert(state.lst.src != NULL);
331 assert(state.lst.src->symbol != NULL);
332
333 smod_flag = (first_time) ? COD_LS_SMOD_FLAG_ALL :
334 ((state.cod.emitting) ? COD_LS_SMOD_FLAG_C1 :
335 (COD_LS_SMOD_FLAG_C1 | COD_LS_SMOD_FLAG_D));
336
337 record = &lb->block[dbi->list.offset];
338
339 record[COD_LS_SFILE] = state.lst.src->symbol->number;
340 record[COD_LS_SMOD] = smod_flag;
341
342 /* Write the source file line number corresponding to the list file line number. */
343 gp_putl16(&record[COD_LS_SLINE], state.lst.src->line_number);
344
345 /* Write the address of the opcode. */
346 gp_putl16(&record[COD_LS_SLOC], address);
347
348 dbi->list.offset += COD_LINE_SYM_SIZE;
349 }
350
351 /*------------------------------------------------------------------------------------------------*/
352
353 void
cod_close_file(void)354 cod_close_file(void)
355 {
356 unsigned int length;
357 gp_boolean truncated;
358 const char *name;
359
360 if (!state.cod.enabled) {
361 return;
362 }
363
364 name = gp_processor_name(state.processor, 2);
365 /* The processor is unknown if not defined in command line at cod_init() call
366 so it should be set here. */
367 length = gp_Pstr_from_str(&main_dir->dir[COD_DIR_PROCESSOR], COD_DIR_PROCESSOR_SIZE, name, &truncated);
368
369 if (truncated) {
370 gp_warning("This processor name (\"%s\") too long to .COD file, it will be truncated to %u bytes length.",
371 name, length);
372 }
373
374 /* All external and global symbols are written. */
375 _write_symbol_table(state.symbol.extern_global);
376 /* All local symbols are written. */
377 _write_symbol_table(state.symbol.local);
378 _write_source_file_block();
379 gp_cod_write_code(state.class, state.i_memory, main_dir);
380 _write_debug();
381 gp_cod_enumerate_directory(main_dir);
382 gp_cod_write_directory(state.cod.f, main_dir);
383 gp_cod_free_directory(main_dir);
384 main_dir = NULL;
385 fclose(state.cod.f);
386 }
387