gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
gbdk-support/romusage/src/cdb_file.c
1 // This is free and unencumbered software released into the public domain.
2 // For more information, please refer to <https://unlicense.org>
3 // bbbbbr 2020
4
5 #include <stdio.h>
6 #include <string.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <stdbool.h>
10 #include <stdint.h>
11
12 #include "common.h"
13 #include "logging.h"
14 #include "list.h"
15 #include "banks.h"
16 #include "cdb_file.h"
17
18
19 list_type symbol_list;
20
21 #define CDB_L_REC_FUNC_START_GLOBAL 'G'
22 #define CDB_L_REC_FUNC_START_LOCAL 'F'
23 #define CDB_L_REC_FUNC_END_GLOBAL "XG"
24 #define CDB_L_REC_FUNC_END_LOCAL "XF"
25
26 // More compelte CDB coverage is affected by this SDCC bug
27 // #3662 Adding a function removes const arrays above it from .adb/.cdb output
28 // https://sourceforge.net/p/sdcc/bugs/3662/
29
30
31 // Example data to parse from a .cdb file:
32 //
33 // 0 1 2---------- 3-- 4 5----
34 // L:G$big_const_4$0_0$0:24039 <-- bank 2
35 // L:G$big_const_3$0_0$0:1784E <-- Bank 1
36 //
37 // S:Fsrcfile_2$func_2_local_scope$0_0$0({2}DF,SV:S),C,0,0
38 // L:Fsrcfile_2$func_2_local_scope$0$0:14000
39 // L:XFsrcfile_2$func_2_local_scope$0$0:14003
40 // F:Fsrcfile_2$func_2_local_scope$0_0$0({2}DF,SV:S),C,0,0,0,0,0
41 //
42 // 0 1 2---------- 3-- 4 5-- 6----- 7 8 9 10 11
43 // S:G$big_const_3$0_0$0({256}DA256d,SC:U),D,0,0 <--- size 256 bytes (SC=signed char)
44 // S:G$big_const_4$0_0$0({15360}DA15360d,SC:U),D,0,0 <--- size 15360 bytes (SC=signed char)
45 //
46 // 0 1--------- 2------------ 3-- 4 5---
47 // L:Fsrcfile_2$local_const_2$0_0$0:14027
48 // 0 1--------- 2------------ 3-- 4 5---- 6------- 7 8 9 10 11
49 // S:Fsrcfile_2$local_const_2$0_0$0({14336}DA14336d,SC:U),D,0,0 <--- size 14336 bytes
50
51 // 4.8 Link Address of Symbol
52 //
53 // L Link record type indicator
54 // G Symbol has file scope
55 // F <Filename> Symbol has file scope.
56 // L <Function> Symbol has function scope
57 //
58 // functions use L:XG to mark their last line
59 // basxto: L:XG$init_map$0$0:BCA - L:G$init_map$0$0:AFB = should be the length
60 // L:G$board_handle_new_piece$0$0:6284
61 // L:XG$board_handle_new_piece$0$0:62DD
62 //
63 // https://github.com/roybaer/sdcc-wiki/wiki/CDB-File-Format
64 //
65 // Address Space field (S: record)
66 // A External stack
67 // B Internal stack
68 // C Code
69 // D Code / static segment
70 // E Internal ram (lower 128) bytes
71 // F External ram
72 // G Internal ram
73 // H Bit addressable
74 // I SFR space
75 // J SBIT space
76 // R Register space
77 // Z Used for function records, or any undefined space code
78 //
79 // Types in a Section 4.4 Type Chain Record (S: record)
80 // DA <n> Array of n elements
81 // DF Function
82 // DG Generic pointer
83 // DC Code pointer
84 // DX External ram pointer
85 // DD Internal ram pointer
86 // DP Paged pointer
87 // DI Upper 128 byte pointer
88 // SL long
89 // SI int
90 // SC char
91 // SS short
92 // SV void
93 // SF float
94 // ST <name> Structure of name <name>
95 // SX sbit
96 // SB <n> Bit field of <n> bits
97
98
99 // Initialize the symbol list
100 void cdb_init(void) {
101
102 list_init(&symbol_list, sizeof(area_item));
103 }
104
105
106 // Free the symbol list
107 void cdb_cleanup(void) {
108
109 list_cleanup(&symbol_list);
110 }
111
112
113 // Find a matching symbol, if none matches a new one is added and returned
114 static int symbollist_get_id_by_name(char * symbol_name) {
115
116 area_item * symbols = (area_item *)symbol_list.p_array;
117 area_item new_symbol;
118
119 int c;
120
121 // Check for matching symbol name
122 for(c=0;c < symbol_list.count; c++) {
123 // Return matching symbol index if present
124 if (strncmp(symbol_name, symbols[c].name, AREA_MAX_STR) == 0) {
125 return c;
126 }
127 }
128
129 snprintf(new_symbol.name, sizeof(new_symbol.name), "%s", symbol_name);
130 new_symbol.start = AREA_VAL_UNSET;
131 new_symbol.end = AREA_VAL_UNSET;
132 new_symbol.length = AREA_VAL_UNSET;
133 if (strstr(symbol_name,"HEADER"))
134 new_symbol.exclusive = false; // HEADER symbols almost always overlap, ignore them
135 else
136 new_symbol.exclusive = option_all_areas_exclusive; // Default is false
137
138 list_additem(&symbol_list, &new_symbol);
139
140 return (symbol_list.count - 1);
141 }
142
143
144 // Process list of symbols and add them to banks
145 static void cdb_symbollist_add_all_to_banks() {
146
147 area_item * symbols = (area_item *)symbol_list.p_array;
148 int c;
149
150 // Only process completed symbols (start and length both set)
151 for(c=0;c < symbol_list.count; c++) {
152
153 // Functions need length calculated from start and end
154 if ((symbols[c].length == AREA_VAL_UNSET) &&
155 (symbols[c].start != AREA_VAL_UNSET) &&
156 (symbols[c].end != AREA_VAL_UNSET)) {
157 symbols[c].length = symbols[c].end - symbols[c].start + 1;
158 }
159
160
161 if ((symbols[c].start != AREA_VAL_UNSET) &&
162 (symbols[c].length != AREA_VAL_UNSET)) {
163 symbols[c].end = symbols[c].start + symbols[c].length - 1;
164 banks_check(symbols[c]);
165 }
166 }
167 }
168
169
170 // Adds start/end address from a Linker Record
171 // Requires either separate calls for Start and End, or one call for
172 // start and a separate cdb_add_record_symbol() call to set length
173 static void cdb_add_record_linker(char * type, char * name, char * address) {
174
175 area_item * symbols = (area_item *)symbol_list.p_array;
176
177 // Retrieve existing symbol or create a new one
178 int symbol_id = symbollist_get_id_by_name(name);
179
180 if (symbol_id != ERR_NO_AREAS_LEFT) {
181
182 // Check Linker record for start-address or end-address
183 // Bank number is in the address bits, no need to modify it
184 if ((strncmp(type, CDB_L_REC_FUNC_END_GLOBAL, 4) == 0) ||
185 (strncmp(type, CDB_L_REC_FUNC_END_LOCAL, 4) == 0)) {
186 symbols[symbol_id].end = strtol(address, NULL, 16); // End address
187 }
188 else if ((type[0] == CDB_L_REC_FUNC_START_GLOBAL) ||
189 (type[0] == CDB_L_REC_FUNC_START_LOCAL)) {
190 symbols[symbol_id].start = strtol(address, NULL, 16); // Start address
191 }
192 // else
193 // printf("Rejected L record %s, %s, %s\n", type, name, address);
194 }
195 }
196
197
198 // Adds length from a symbol record
199 // To get a complete entry requires a start address call to cdb_add_record_linker()
200 static void cdb_add_record_symbol(char * addr_space, char * name, char * length, char * dcl_type) {
201
202 area_item * symbols = (area_item *)symbol_list.p_array;
203
204 // Only allow certain address spaces
205 if ((addr_space[0] == 'C') || // Address Space: Code
206 (addr_space[0] == 'D') || // Address Space: Code / static segment
207 (addr_space[0] == 'E') || // Address Space: Internal RAM (lower 128) bytes
208 (addr_space[0] == 'F') || // Address Space: External RAM
209 (addr_space[0] == 'G')) { // Address Space: Internal RAM
210
211 // Exclude zero length entries
212 // Don't let function ~entry points override function bodies(<DCLType> = DF, function which are two bytes in size)
213 if ((strtol(length, NULL, 10) > 0) &&
214 (!strstr(dcl_type, "DF")))
215 {
216 // Retrieve existing symbol or create a new one
217 int symbol_id = symbollist_get_id_by_name(name); // [2] Area Name
218 if (symbol_id != ERR_NO_AREAS_LEFT) {
219 symbols[symbol_id].length = strtol(length, NULL, 10); // [5] Symbol decimal length
220 }
221 }
222 }
223 // else
224 // printf("Rejected S record %s, %s, %s, %s\n", addr_space, name, length, dcl_type);
225 }
226
227
228 int cdb_file_process_symbols(char * filename_in) {
229
230 int cols;
231 char * p_str;
232 char * p_words[CDB_MAX_SPLIT_WORDS];
233 char strline_in[CDB_MAX_STR_LEN] = "";
234 FILE * cdb_file = fopen(filename_in, "r");
235 area_item symbol;
236 int symbol_id;
237
238 // CDB defaults to showing areas
239 banks_output_show_areas(true);
240
241 // CDB defaults to size descending, but don't override explicit options
242 if (get_option_area_sort() == OPT_AREA_SORT_DEFAULT)
243 set_option_area_sort(OPT_AREA_SORT_SIZE_DESC);
244
245 set_option_input_source(OPT_INPUT_SRC_CDB);
246
247 if (cdb_file) {
248
249 // Read one line at a time into \0 terminated string
250 while ( fgets(strline_in, sizeof(strline_in), cdb_file) != NULL) {
251
252 // Require minimum length to match
253 if (strlen(strline_in) >= CDB_REC_START_LEN) {
254
255 // Match either _S_egment or _L_ength records
256 if ( (strncmp(strline_in, "L:", CDB_REC_START_LEN) == 0) ||
257 (strncmp(strline_in, "S:", CDB_REC_START_LEN) == 0)) {
258
259 // Split string into words separated by spaces
260 cols = 0;
261 p_str = strtok(strline_in,":$({}),");
262 while (p_str != NULL)
263 {
264 p_words[cols++] = p_str;
265 p_str = strtok(NULL, ":$({}),");
266 if (cols >= CDB_MAX_SPLIT_WORDS) break;
267 }
268
269 // Linker record (start or end address)
270 if ((p_words[0][0] == CDB_REC_L) &&
271 (cols == CDB_REC_L_COUNT_MATCH)) {
272 // [1] Start/End, [2] Area Name1, [5] Address
273 cdb_add_record_linker(p_words[1], p_words[2], p_words[5]);
274 }
275 // Symbol record (length)
276 if ((p_words[0][0] == CDB_REC_S) &&
277 (cols == CDB_REC_S_COUNT_MATCH)) {
278 // [9] address space, [2] Area Name, [5] Symbol decimal length, [6] DCLType
279 cdb_add_record_symbol(p_words[9], p_words[2], p_words[5], p_words[6]);
280 }
281
282 } // end: if valid start of line
283 } // end: valid min chars to process line
284 } // end: while still lines to process
285
286 fclose(cdb_file);
287
288 // Process all the symbols
289 cdb_symbollist_add_all_to_banks();
290
291 } // end: if valid file
292 else {
293 log_error("Error: Failed to open input file %s\n", filename_in);
294 return false;
295 }
296
297 return true;
298 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.