1 /* ".COD" file output for gpasm
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 "gpasm.h"
28 #include "gpmsg.h"
29 #include "lst.h"
30 #include "cod.h"
31 
32 #define COD_FILE_MAX_NUM            1000
33 
34 static DirBlockInfo *main_dir  = NULL;
35 static DirBlockInfo *dbi       = NULL;
36 static unsigned int  _64k_base = 0;
37 
38 /*------------------------------------------------------------------------------------------------*/
39 
40 /*
41  * _write_file_block - Write a code block that contains a list of the source files.
42  */
43 
44 static void
_write_source_file_block(void)45 _write_source_file_block(void)
46 {
47   const file_context_t *fc;
48   BlockList            *fb;
49   unsigned int          length;
50   gp_boolean            truncated;
51 
52   if (state.file_list.first == NULL) {
53     return;
54   }
55 
56   if (state.file_list.num_nodes >= COD_FILE_MAX_NUM) {
57     /* Too many files to handle in the .cod file. */
58     assert(0);
59   }
60 
61   fb = NULL;
62   fc = state.file_list.first;
63   while (fc != NULL) {
64     if ((fb == NULL) || (main_dir->file.offset >= (COD_FILE_NAMES_PER_BLOCK * COD_FILE_NAME_SIZE))) {
65       fb = gp_cod_block_append(&main_dir->file, gp_cod_block_new());
66     }
67 
68     /* The file id is used to define the index at which the file name is written within
69      * the file code block. (The id's are sequentially assigned when the files are opened.)
70      * If there are too many files, then gpasm will abort. Note: .cod files can handle
71      * larger file lists...
72      */
73 
74     length = gp_Pstr_from_str(&fb->block[main_dir->file.offset], COD_FILE_NAME_SIZE, fc->name, &truncated);
75 
76     if (truncated && (state.strict_level > 0)) {
77       gpmsg_vwarning(GPW_STRING_TRUNCATE, "(.COD)", fc->name, length);
78     }
79 
80     main_dir->file.offset += COD_FILE_NAME_SIZE;
81 
82     fc = fc->next;
83   }
84 }
85 
86 /*------------------------------------------------------------------------------------------------*/
87 
88 /* cod_init - Initialize the .cod file. */
89 
90 void
cod_init(void)91 cod_init(void)
92 {
93   if (state.cod_file != OUT_NAMED) {
94     snprintf(state.cod_file_name, sizeof(state.cod_file_name), "%s.cod", state.base_file_name);
95   }
96 
97   if (state.cod_file == OUT_SUPPRESS) {
98     state.cod.f       = NULL;
99     state.cod.enabled = false;
100     unlink(state.cod_file_name);
101   }
102   else {
103     state.cod.f = fopen(state.cod_file_name, "wb");
104     if (state.cod.f == NULL) {
105       perror(state.cod_file_name);
106       exit(1);
107     }
108     state.cod.enabled = true;
109   }
110 
111   if (!state.cod.enabled) {
112     main_dir = NULL;
113     return;
114   }
115 
116   main_dir  = gp_cod_init_dir_block(state.cod_file_name, "gpasm");
117   dbi       = NULL;
118   _64k_base = 0;
119 }
120 
121 /*------------------------------------------------------------------------------------------------*/
122 
123 /* cod_lst_line - Add a line of information that cross references the
124  *                the opcode's address, the source file, and the list file.
125  */
126 
127 #define COD_LST_FIRST_LINE              7
128 
129 void
cod_lst_line(unsigned int List_line)130 cod_lst_line(unsigned int List_line)
131 {
132   source_context_t *ctx;
133   uint8_t           smod_flag;
134   BlockList        *lb;
135   gp_boolean        first_time;
136   unsigned int      address;
137   unsigned int      high_address;
138 
139   if (!state.cod.enabled) {
140     return;
141   }
142 
143   ctx = state.src_list.last;
144 
145   /* Don't start until after the source is open. */
146   if (ctx == NULL) {
147     return;
148   }
149 
150   /* Ignore the first few line numbers. */
151 /*  if (state.lst.line_number < COD_LST_FIRST_LINE) {*/
152   if (state.lst.line_number < List_line) {
153     return;
154   }
155 
156   address      = gp_processor_insn_from_byte_c(state.device.class, state.lst.line.was_byte_addr);
157   high_address = IMemBaseFromAddr(address);
158 
159   if ((dbi == NULL) || (high_address != _64k_base)) {
160     _64k_base = high_address;
161     dbi       = gp_cod_find_dir_block_by_high_addr(main_dir, _64k_base);
162   }
163 
164   first_time = (gp_cod_block_get_last(&dbi->list) == NULL) ? true : false;
165 
166   lb = gp_cod_block_get_last_or_new(&dbi->list);
167 
168   if (dbi->list.offset >= (COD_MAX_LINE_SYM * COD_LINE_SYM_SIZE)) {
169     lb = gp_cod_block_append(&dbi->list, gp_cod_block_new());
170   }
171 
172   assert(ctx->fc != NULL);
173 
174   smod_flag = (first_time) ? COD_LS_SMOD_FLAG_ALL :
175                              ((state.cod.emitting != 0) ? COD_LS_SMOD_FLAG_C1 :
176                                                           (COD_LS_SMOD_FLAG_C1 | COD_LS_SMOD_FLAG_D));
177 
178   dbi->list.offset += gp_cod_put_line_number(&lb->block[dbi->list.offset], ctx->fc->id, ctx->line_number,
179                                              address, smod_flag);
180 }
181 
182 /*------------------------------------------------------------------------------------------------*/
183 
184 /* cod_write_symbols - Write the symbol table to the .cod file.
185  *
186  * This routine will read the symbol table that gpasm has created and convert it into
187  * a format suitable for .cod files. So far, only three variable types are supported:
188  * address, register and constants
189  *
190  */
191 
192 void
cod_write_symbols(const symbol_t ** Symbol_list,size_t Num_symbols)193 cod_write_symbols(const symbol_t **Symbol_list, size_t Num_symbols)
194 {
195   size_t            i;
196   unsigned int      length;
197   unsigned int      type;
198   const variable_t *var;
199   const char       *name;
200   BlockList        *sb;
201   gp_boolean        truncated;
202 
203   if ((Symbol_list == NULL) || (Num_symbols == 0)) {
204     return;
205   }
206 
207   if (!state.cod.enabled) {
208     return;
209   }
210 
211   sb = NULL;
212   for (i = 0; i < Num_symbols; i++) {
213     name = gp_sym_get_symbol_name(Symbol_list[i]);
214     var  = (const variable_t *)gp_sym_get_symbol_annotation(Symbol_list[i]);
215     assert(var != NULL);
216 
217     if (FlagIsSet(var->flags, VATRR_HAS_NO_VALUE)) {
218       msg_has_no_value("(.COD)", name);
219     }
220 
221     length = gp_strlen_Plimit(name, COD_LSYMBOL_NAME_MAX_SIZE, &truncated);
222 
223     if (truncated && (state.strict_level > 0)) {
224       /* This symbol name is too long. */
225       gpmsg_vwarning(GPW_STRING_TRUNCATE, "(.COD)", name, length);
226     }
227 
228     /* If this symbol extends past the end of the cod block then write this block out. */
229 
230     if ((sb == NULL) || ((main_dir->lsym.offset + length + COD_LSYMBOL_EXTRA) >= COD_BLOCK_SIZE)) {
231       sb = gp_cod_block_append(&main_dir->lsym, gp_cod_block_new());
232     }
233 
234     switch (var->type) {
235       case VAL_CBLOCK:
236         type = COD_ST_C_SHORT;  /* Byte Craft's nomenclature for a memory byte. */
237         break;
238 
239       case VAL_ADDRESS:
240         type = COD_ST_ADDRESS;
241         break;
242 
243       case VAL_CONSTANT:
244       case VAL_VARIABLE:
245       default:
246         type = COD_ST_CONSTANT;
247     }
248 
249     main_dir->lsym.offset += gp_cod_put_long_symbol(&sb->block[main_dir->lsym.offset], name, var->value, type);
250   }
251 }
252 
253 /*------------------------------------------------------------------------------------------------*/
254 
255 void
cod_close_file(void)256 cod_close_file(void)
257 {
258   unsigned int  length;
259   gp_boolean    truncated;
260   const char   *name;
261 
262   if (!state.cod.enabled) {
263     return;
264   }
265 
266   name = gp_processor_name(state.processor, 2);
267   /* The processor is unknown if not defined in command line at cod_init() call
268      so it should be set here. */
269   length = gp_Pstr_from_str(&main_dir->dir[COD_DIR_PROCESSOR], COD_DIR_PROCESSOR_SIZE, name, &truncated);
270 
271   if (truncated && (state.strict_level > 0)) {
272     gpmsg_vwarning(GPW_STRING_TRUNCATE, "(.COD)", name, length);
273   }
274 
275   _write_source_file_block();
276   gp_cod_write_code(state.device.class, state.i_memory, main_dir);
277   gp_cod_enumerate_directory(main_dir);
278   gp_cod_write_directory(state.cod.f, main_dir);
279   gp_cod_free_directory(main_dir);
280   fclose(state.cod.f);
281 
282   main_dir  = NULL;
283   dbi       = NULL;
284   _64k_base = 0;
285 }
286