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