gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
gbdk-support/romusage/src/banks.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 <stdbool.h>
9 #include <stdint.h>
10
11 #include "common.h"
12 #include "logging.h"
13 #include "list.h"
14 #include "banks.h"
15 #include "bank_templates.h"
16 #include "banks_print.h"
17 #include "banks_summarized.h"
18
19
20 static int area_item_compare(const void* a, const void* b);
21 static int bank_item_compare(const void* a, const void* b);
22 static bool banks_check_larger_than_32k(void);
23 static void areas_check_rom0_overflow(void);
24
25 // Not ready for use until a call to banks_init_templates()
26 bank_item bank_templates[BANK_TEMPLATES_MAX];
27 int bank_templates_count;
28
29 list_type bank_list;
30 list_type bank_list_summarized;
31
32 #define AREA_MANUAL_QUEUE_SZ 20
33 int area_manual_queue_count = 0;
34 area_item areas_manual_queue[AREA_MANUAL_QUEUE_SZ];
35
36 // Initialize the main banklist
37 void banks_init(void) {
38
39 list_init(&bank_list, sizeof(bank_item));
40 list_init(&bank_list_summarized, sizeof(bank_item));
41 }
42
43
44 // Free all banks and their areas
45 void banks_cleanup(void) {
46
47 bank_item * banks = (bank_item *)bank_list.p_array;
48 int c;
49
50 for (c = 0; c < bank_list.count; c++) {
51 list_cleanup(&(banks[c].area_list));
52 }
53 list_cleanup(&bank_list);
54 }
55
56
57 // Load templates used for assigning areas to banks
58 void banks_init_templates(void) {
59 bank_templates_count = bank_templates_load(bank_templates);
60 }
61
62
63 // Returns size of overlap between two address ranges,
64 // if zero then no overlap
65 static uint32_t addrs_get_overlap(uint32_t a_start, uint32_t a_end, uint32_t b_start, uint32_t b_end) {
66
67 uint32_t size_used;
68
69 // Check whether the address range *doesn't* overlap
70 if ((b_start > a_end) || (b_end < a_start)) {
71 size_used = 0; // no overlap, size = 0
72 } else {
73 size_used = min(b_end, a_end) - max(b_start, a_start) + 1; // Calculate minimum overlap
74 }
75 return size_used;
76
77 }
78
79
80 // Fixes up the missing bank number virtual addressing mask bits
81 // for when ROM0 overflows into ROM1. Ex: 0x00004000 -> 0x00014000
82 //
83 // Note: When called from banks_check() the address will be
84 // clipped to the start of the matched bank template
85 static uint32_t addr_fixup_ROM0_overflow_bank_num(uint32_t addr) {
86
87 // If it's in the upper bank range yet has
88 // a bank number of zero then it needs fixing
89 if ((WITHOUT_BANK(addr) >= BANK_ADDR_ROM_UPPER_ST) &&
90 (WITHOUT_BANK(addr) <= BANK_ADDR_ROM_UPPER_END) &&
91 (BANK_GET_NUM(addr) == BANK_NUM_ROM0)) {
92 // OR in the virtual addressing equivalent of bank ROM1
93 addr |= BANK_NUM_ROM1_VADDR;
94 }
95
96 return addr;
97 }
98
99
100 // Clips an address range to be within a bank address range
101 static void area_clip_to_range(uint32_t start, uint32_t end, area_item * p_area) {
102 // Clip address range to bank range
103 p_area->start = max(p_area->start, start);
104 p_area->end = min(p_area->end, end);
105 // Trim to zero length if end is before start
106 if (p_area->end < p_area->start) p_area->end = p_area->start - 1;
107 }
108
109
110 static void area_check_region_overflow(area_item area) {
111
112 int c;
113
114 // Find bank template the area starts in and check to see
115 // whether the area extends past the end of it's memory region.
116 //
117 // Non-banked areas with banks above them have the upper bound
118 // set to the end of the bank above them.
119 for(c = 0; c < bank_templates_count; c++) {
120
121 // Warn about overflow in any ROM bank GBZ80 areas that cross past the (relative) end of their region
122 if ((WITHOUT_BANK(area.start) >= bank_templates[c].start) &&
123 (WITHOUT_BANK(area.start) <= bank_templates[c].end) &&
124 (area.end > (BANK_ONLY(area.start) + bank_templates[c].overflow_end))) {
125 log_warning("* WARNING: Area %-8s at %5x -> %5x extends past end of memory region at %5x (Overflow by %d bytes)\n",
126 area.name,
127 // BANK_GET_NUM(area.start),
128 area.start, area.end,
129 BANK_ONLY(area.start) + bank_templates[c].overflow_end,
130 area.end - (BANK_ONLY(area.start) + bank_templates[c].overflow_end));
131
132 if (option_error_on_warning)
133 set_exit_error();
134 }
135 }
136 }
137
138
139 // Warn if length extends past end of unbanked address space (0xFFFF)
140 // and will underflow / wrap around
141 static bool area_check_underflow(area_item area, bool notify) {
142
143 if (area.end > (BANK_ONLY(area.start) + MAX_ADDR_UNBANKED)) {
144
145 if (notify) {
146 log_warning("* WARNING: Area %-8s at %5x -> %5x extends past end of address space at %5x (Underflow error by %d bytes)\n",
147 area.name,
148 area.start, area.end,
149 BANK_ONLY(area.start) + MAX_ADDR_UNBANKED,
150 area.end - (BANK_ONLY(area.start) + MAX_ADDR_UNBANKED));
151
152 if (option_error_on_warning)
153 set_exit_error();
154 }
155
156 return true;
157 }
158 else return false;
159 }
160
161
162 // Attempt to flag if non-banked areas have overflowed ROM0 and rom size is > 32k
163 // Must be called after all areas are processed so that it can check rom size accurately
164 static void areas_check_rom0_overflow(void) {
165
166 bank_item * banks = (bank_item *)bank_list.p_array;
167 area_item * areas;
168 int b, c;
169 bool has_overflow = false;
170
171 if (banks_check_larger_than_32k() == false) return;
172 if (get_option_platform() != OPT_PLAT_GAMEBOY) return;
173
174 for (b=0; b < bank_list.count; b++) {
175 areas = (area_item *)banks[b].area_list.p_array;
176
177 for(c=0;c < banks[b].area_list.count; c++) {
178
179 if (areas[c].end >= BANK_ADDR_ROM_UPPER_ST) {
180 if ((strcmp(areas[c].name,"_CODE") == 0) ||
181 (strcmp(areas[c].name,"_HOME") == 0) ||
182 (strcmp(areas[c].name,"_INITIALIZER") == 0) ||
183 (strcmp(areas[c].name,"_GSINIT") == 0) ||
184 (strcmp(areas[c].name,"_GSFINAL") == 0)) {
185
186 log_warning("* WARNING: Possible overflow beyond Bank 0 for non-banked area %s (0x%x -> 0x%x). \n",
187 areas[c].name, areas[c].start, areas[c].end);
188 has_overflow = true;
189 }
190 }
191 }
192 }
193
194 if (option_error_on_warning && has_overflow)
195 set_exit_error();
196 }
197
198
199 static void area_check_warnings(area_item area, uint32_t size_assigned) {
200
201 // Unassigned warning is mostly redundant with area_check_bank_overflow()
202 //
203 // // Warn if there are unassigned bytes left over
204 // if (size_assigned < RANGE_SIZE(area.start, area.end)) {
205 // log_warning("\n* Warning: Area %s 0x%x -> 0x%x (%d bytes): %d bytes not assigned to any bank (overflow error)\n",
206 // area.name,
207 // area.start, area.end,
208 // RANGE_SIZE(area.start, area.end),
209 // RANGE_SIZE(area.start, area.end) - size_assigned);
210 // }
211
212 area_check_underflow(area, true);
213 area_check_region_overflow(area);
214 }
215
216
217 static void area_check_warn_overlap(area_item area_a, area_item area_b) {
218
219 uint32_t overlap_size;
220
221 // HEADER areas almost always overlap, ignore them
222 if ((strstr(area_a.name,"HEADER")) || (strstr(area_b.name,"HEADER")))
223 return;
224
225 // Check to see if an there are overlaps with exclusive areas
226 if (area_a.exclusive || area_b.exclusive) {
227 overlap_size = addrs_get_overlap(WITHOUT_BANK(area_a.start), WITHOUT_BANK(area_a.end),
228 WITHOUT_BANK(area_b.start), WITHOUT_BANK(area_b.end));
229 if (overlap_size > 0) {
230 log_warning("\n* WARNING: Areas overlapp by %d bytes: Possible bank overflow.\n"
231 "%15s 0x%04x -> 0x%04x (%d bytes%s)\n"
232 "%15s 0x%04x -> 0x%04x (%d bytes%s)\n",
233 overlap_size,
234 area_a.name, area_a.start, area_a.end, RANGE_SIZE(area_a.start, area_a.end),
235 (area_a.exclusive) ? ", EXCLUSIVE" : " ",
236 area_b.name, area_b.start, area_b.end, RANGE_SIZE(area_b.start, area_b.end),
237 (area_b.exclusive) ? ", EXCLUSIVE" : " ");
238
239 if (option_error_on_warning)
240 set_exit_error();
241 }
242 }
243 }
244
245
246 static bool banks_check_larger_than_32k(void) {
247
248 bank_item * banks = (bank_item *)bank_list.p_array;
249 int c;
250
251 for (c=0; c < bank_list.count; c++) {
252 if ((banks[c].bank_num > BANK_NUM_ROM1) &&
253 (banks[c].bank_mem_type == BANK_MEM_TYPE_ROM)) {
254
255 return true;
256 }
257 }
258
259 return false;
260 }
261
262
263 // Calculate free and used percentages of space in a given bank
264 int bank_calc_percent_free(bank_item * p_bank) {
265
266 // Round to nearest whole percent instead of truncate (hence: + total / 2)
267 return (int)(((p_bank->size_total - p_bank->size_used) * 100)
268 + (p_bank->size_total / 2 )) / p_bank->size_total;
269 }
270
271 int bank_calc_percent_used(bank_item * p_bank) {
272
273 // Base Used amount on Free space so that total comes out to 100%
274 return (100 - bank_calc_percent_free(p_bank));
275 }
276
277
278 // Calculates amount of space used by areas in a bank.
279 // Attempts to merge overlapping areas to avoid
280 // counting shared space multiple times.
281 //
282 uint32_t bank_areas_calc_used(bank_item * p_bank, uint32_t clip_start, uint32_t clip_end) {
283
284 area_item * areas = (area_item *)p_bank->area_list.p_array;
285 int c,sub;
286 uint32_t start, end;
287 uint32_t size_used;
288 area_item t_area, sub_area;
289
290 size_used = 0;
291
292 // The calculation requires areas to first be
293 // sorted ascending by .start addr then by .end addr
294 qsort (p_bank->area_list.p_array, p_bank->area_list.count, sizeof(area_item), area_item_compare);
295
296 // Iterate over all areas
297 c = 0;
298 while (c < p_bank->area_list.count) {
299
300 // Copy area so it can be clipped, then clip to param range
301 t_area = areas[c];
302 area_clip_to_range(clip_start, clip_end, &t_area); // clip to param range
303
304 // // Store start/end of range for current area
305 start = t_area.start;
306 end = t_area.end;
307
308 // Iterate over remaining areas and stop when they cease to overlap
309 sub = c + 1;
310 while (sub < p_bank->area_list.count) {
311
312 // Copy area so it can be clipped, then clip to param range
313 sub_area = areas[sub];
314 area_clip_to_range(clip_start, clip_end, &sub_area);
315
316 // Check for overlap with next entry
317 if (addrs_get_overlap(start, end, sub_area.start, sub_area.end)) {
318
319 // Expand overlapped area to new end size
320 // Just end, start shouldn't be necessary due to expected sorting
321 if (sub_area.end > end) {
322 end = sub_area.end;
323 }
324
325 // Update main loop to next area after current merged,
326 c = sub;
327 }
328 // Increment to next area to check for overlap
329 sub++;
330 }
331 // Move to next area
332 c++;
333
334 // Store space used by updated range
335 size_used += RANGE_SIZE(start, end);
336 // fprintf(stdout," * %d, %d Final Size> 0x%04X -> 0x%04X = %d ((%d))\n",c, sub, start, end, RANGE_SIZE(start, end), size_used);
337 }
338
339 return size_used;
340 }
341
342
343
344 // Add an area to a bank's list of areas
345 static void bank_add_area(bank_item * p_bank, area_item area) {
346
347 area_item * areas = (area_item *)p_bank->area_list.p_array;
348 int c;
349
350 // Make sure the area length/size is set
351 area.length = RANGE_SIZE(area.start, area.end);
352
353 // Check for duplicate entries
354 // (happens due to paginating in .map file)
355 for(c=0;c < p_bank->area_list.count; c++) {
356 // Abort add if it's already present
357 if (option_suppress_duplicates == true) {
358 if ((strstr(area.name, areas[c].name)) &&
359 (area.start == areas[c].start) &&
360 (area.end == areas[c].end)) {
361 return;
362 }
363 }
364
365 area_check_warn_overlap(area, areas[c]);
366 }
367
368 // no match was found, add area
369 list_additem(&(p_bank->area_list), &area);
370 p_bank->size_used += area.length;
371 }
372
373
374 // Add/Update a bank with an area entry
375 static void banklist_addto(bank_item bank_template, area_item area, int bank_num) {
376
377 int c;
378 bank_item * banks = (bank_item *)bank_list.p_array;
379 bank_item newbank;
380
381 // Strip bank indicator bits and limit area range to within bank
382 area.start = area.start_unbanked;
383 area.end = area.end_unbanked;
384 area_clip_to_range(bank_template.start, bank_template.end, &area);
385
386 // Check to see if key matches any entries,
387 for (c=0; c < bank_list.count; c++) {
388
389 // If a match was found, update it
390 if ((bank_template.start == banks[c].start) &&
391 (bank_num == banks[c].bank_num)) {
392
393 // Append area
394 bank_add_area(&(banks[c]), area);
395 return;
396 }
397 }
398
399 // No match was found, initialize new bank
400
401 // Copy bank info from template
402 newbank = bank_template;
403
404 // Update size used, total size and append bank name if needed
405 newbank.size_used = 0;
406 newbank.size_total = RANGE_SIZE(bank_template.start, bank_template.end);
407 newbank.bank_num = bank_num;
408
409 // Don't append bank name for merged banks
410 if ((bank_template.is_banked == BANKED_YES) && (!bank_template.is_merged_bank)) {
411 if (snprintf(newbank.name, sizeof(newbank.name), "%s%d", bank_template.name, bank_num) > sizeof(newbank.name))
412 log_warning("Warning: truncated bank name to :%s\n", newbank.name);
413 }
414
415 // Initialize new bank's area list and add the area
416 list_init(&(newbank.area_list), sizeof(area_item));
417 bank_add_area(&newbank, area);
418
419 // Now add the new bank to the main list
420 list_additem(&bank_list, &newbank);
421 }
422
423
424 // Strip banks from address start and end, set start/end_unbanked
425 static void area_calc_unbanked_range(area_item * p_area) {
426
427 p_area->start_unbanked = WITHOUT_BANK(p_area->start);
428
429 // * Calculating End relative to start is important for
430 // not accidentally loosing it's full size.
431 // * Unbanked End is also capped at 0xFFFF to
432 // prevent wraparound range size errors
433 if (area_check_underflow(*p_area, false)) {
434 // area_check_warnings() will warn about this later
435 p_area->end_unbanked = MAX_ADDR_UNBANKED;
436 } else {
437 p_area->end_unbanked = UNBANKED_END(p_area->start, p_area->end);
438 }
439 }
440
441
442 // Returns true if the template should be skipped
443 //
444 // On GBDK SMS/GG the banked LIT_ and DATA_ areas get mapped into the
445 // same memory region (only one active at a time) : 0x8000 - 0xBFFF
446 //
447 // So skip the template of one type if the area is of the other type
448 bool banks_sms_gg_checkskip_template(bool sms_gg_is_banked_DATA, bool sms_gg_is_banked_LIT, char * template_name) {
449 if (sms_gg_is_banked_DATA) {
450 if (strstr(template_name,"LIT_"))
451 return true;
452 } else if (sms_gg_is_banked_LIT) {
453 if (strstr(template_name,"DATA_"))
454 return true;
455 }
456 return false;
457 }
458
459 // Check to see if an area overlaps with any of the bank templates.
460 // If it does then try to create/update a bank entry
461 // and add/append the area entry
462 void banks_check(area_item area) {
463
464 int c;
465 uint32_t size_used;
466 uint32_t size_assigned = 0;
467 int bank_num;
468
469 // Set the unbanked address range for comparison
470 // with (unbanked) bank templates
471 area_calc_unbanked_range(&area);
472
473 // On GBDK SMS/GG the banked LIT_ and DATA_ areas get mapped into the
474 // same memory region (only one active at a time) : 0x8000 - 0xBFFF
475 // TODO: kind of sloppy, could be moved to a function
476 bool sms_gg_is_banked_DATA = false;
477 bool sms_gg_is_banked_LIT = false;
478 if ((get_option_platform() == OPT_PLAT_SMS_GG_GBDK) && strstr(area.name,"DATA_"))
479 sms_gg_is_banked_DATA = true;
480 if ((get_option_platform() == OPT_PLAT_SMS_GG_GBDK) && strstr(area.name,"LIT_"))
481 sms_gg_is_banked_LIT = true;
482
483 // Loop through all banks and log any that overlap
484 // (may be more than one)
485 for(c = 0; c < bank_templates_count; c++) {
486
487 // Skip LIT_X banked template if this is a DATA_X area (and same for inverse)
488 if (banks_sms_gg_checkskip_template(sms_gg_is_banked_DATA, sms_gg_is_banked_LIT, bank_templates[c].name))
489 continue;
490
491 // Check a given ROM/RAM bank template for overlap
492 size_used = addrs_get_overlap(bank_templates[c].start, bank_templates[c].end,
493 area.start_unbanked, area.end_unbanked);
494
495 // If overlap was found, determine bank number and log it
496 if (size_used > 0) {
497 // Area items can span multiple banks, so don't use area.start
498 // on it's own to get bank number since it might originate
499 // in a lower bank (handled in a previous iteration of the loop).
500 // Instead use the current matched bank template start address.
501 // Then fixup missing bank number if needed
502 uint32_t addr_start_banknum = BANK_ONLY(area.start) | WITHOUT_BANK(bank_templates[c].start);
503 addr_start_banknum = addr_fixup_ROM0_overflow_bank_num(addr_start_banknum);
504 bank_num = BANK_GET_NUM(addr_start_banknum);
505
506 // Area range added to bank will get clipped to bank range
507 banklist_addto(bank_templates[c], area, bank_num);
508 size_assigned += size_used; // Log space assigned to bank
509
510 // Only allow overflow to other banks if first bank is non-banked
511 if (bank_templates[c].is_banked != BANKED_NO)
512 break;
513 }
514 }
515
516 area_check_warnings(area, size_assigned);
517 }
518
519 #define MAX_SPLIT_WORDS 4
520 #define ARG_AREA_REC_COUNT_MATCH 4
521
522
523 // Apply manually queued areas
524 void area_manual_apply_queued(void) {
525 for (int c = 0; c < area_manual_queue_count; c++) {
526 banks_check(areas_manual_queue[c]);
527 }
528 }
529
530
531 // -m:NAME:HEX_ADDR:HEX_LENGTH or -e[same]
532 // Queue areas to manually add from command line arguments
533 //
534 // Note: They're added to a queue for processing add AFTER
535 // banks_init_templates() has been called, otherwise they get erased.
536 // Follow up call is area_manual_apply_queued()
537 bool area_manual_queue(char * arg_str) {
538
539 int cols;
540 char * p_str;
541 char * p_words[MAX_SPLIT_WORDS];
542
543 // Split string into words separated by spaces
544 cols = 0;
545 p_str = strtok(arg_str,"-:");
546 while (p_str != NULL)
547 {
548 p_words[cols++] = p_str;
549 p_str = strtok(NULL, "-:");
550 if (cols >= MAX_SPLIT_WORDS) break;
551 }
552
553 if (cols == ARG_AREA_REC_COUNT_MATCH) {
554 area_item * p_area_to_queue = &areas_manual_queue[area_manual_queue_count++];
555
556 snprintf(p_area_to_queue->name, sizeof(p_area_to_queue->name), "%s", p_words[1]); // [1] Area Name
557 p_area_to_queue->start = strtol(p_words[2], NULL, 16); // [2] Area Hex Address Start
558 p_area_to_queue->end = p_area_to_queue->start + strtol(p_words[3], NULL, 16) - 1; // Start + [3] Hex Size - 1 = Area End
559 p_area_to_queue->exclusive = (p_words[0][0] == 'e') ? true : false; // [0] shared/exclusive
560 return true;
561 } else
562 return false; // Signal failure
563 }
564
565 // NOTE: All the comparisons and their particular order are
566 // required for bank_areas_calc_used() to work properly.
567 // qsort compare rule function
568 static int area_item_compare(const void* a, const void* b) {
569
570 // First sort by start address
571 if (((area_item *)a)->start != ((area_item *)b)->start)
572 return (((area_item *)a)->start < ((area_item *)b)->start) ? -1 : 1;
573
574 // Otherwise end address
575 if (((area_item *)a)->end != ((area_item *)b)->end)
576 return (((area_item *)a)->end < ((area_item *)b)->end) ? -1 : 1;
577
578 // If above match, then sort based on name
579 return strcmp(((area_item *)a)->name, ((area_item *)b)->name);
580
581 }
582
583
584 // qsort compare rule function: sort by size descending first, then name
585 static int area_item_compare_size_desc(const void* a, const void* b) {
586
587 if (((area_item *)a)->length != ((area_item *)b)->length)
588 return (((area_item *)a)->length < ((area_item *)b)->length) ? 1 : -1;
589 else
590 return strcmp(((area_item *)a)->name, ((area_item *)b)->name);
591 }
592
593
594 // qsort compare rule function: sort by start address ascending
595 static int area_item_compare_addr_asc(const void* a, const void* b) {
596
597 return (((area_item *)a)->start < ((area_item *)b)->start) ? -1 : 1;
598 }
599
600
601 // qsort compare rule function
602 static int bank_item_compare(const void* a, const void* b) {
603
604 // First sort by start address
605 if (((bank_item *)a)->start != ((bank_item *)b)->start)
606 return (((bank_item *)a)->start < ((bank_item *)b)->start) ? -1 : 1;
607
608 // Otherwise based on bank number
609 if (((bank_item *)a)->bank_num != ((bank_item *)b)->bank_num)
610 return (((bank_item *)a)->bank_num < ((bank_item *)b)->bank_num) ? -1 : 1;
611
612 return 0; // Otherwise return equivalent
613 }
614
615
616
617 // Fill in gaps between symbols with "?" symbols --TODO: rename function to symbols
618 static void bank_fill_area_gaps_with_unknown(void) {
619
620 bank_item * banks = (bank_item *)bank_list.p_array;
621 area_item * areas;
622 uint32_t last_addr, cur_addr;
623 int c, b, t_area_count;
624 area_item area;
625
626 for (c = 0; c < bank_list.count; c++) {
627 // Load the area list for the bank
628 areas = (area_item *)banks[c].area_list.p_array;
629
630 // Sort areas by ascending address so that gaps can be found
631 qsort (banks[c].area_list.p_array, banks[c].area_list.count, sizeof(area_item), area_item_compare_addr_asc);
632
633 t_area_count = banks[c].area_list.count; // Temp area count to avoid processing newly added areas
634 last_addr = banks[c].start; // Set last to start of current bank
635
636 for(b = 0; b < t_area_count; b++) {
637
638 if ((banks_display_headers) || !(strstr(areas[b].name,"HEADER"))) {
639
640 cur_addr = areas[b].start;
641
642 if (cur_addr > last_addr + 1) {
643
644 snprintf(area.name, sizeof(area.name), "-?-");
645 area.start = last_addr + 1;
646 area.end = cur_addr - 1;
647 area.length = area.end - area.start + 1;
648 area.exclusive = false;
649 bank_add_area(&(banks[c]), area); // Add to bank, skip bank_check since parent bank is known
650 }
651
652 // Update previous area reference
653 last_addr = areas[b].end;
654 }
655 }
656 }
657 }
658
659
660 // Check if a bank name matches any substrings on the hide list
661 static bool bank_name_check_hidden(char * str_bank_name) {
662
663 for (int c = 0; c < banks_hide_count; c++) {
664 if (strstr(str_bank_name, banks_hide_list[c])) return true;
665 }
666 return false;
667 }
668
669
670 // Print banks to output
671 void banklist_finalize_and_show(void) {
672
673 bank_item * banks = (bank_item *)bank_list.p_array;
674 int c;
675
676 // Sort banks by start address then bank num
677 qsort (bank_list.p_array, bank_list.count, sizeof(bank_item), bank_item_compare);
678
679 if (get_option_input_source() == OPT_INPUT_SRC_CDB)
680 bank_fill_area_gaps_with_unknown();
681
682 for (c = 0; c < bank_list.count; c++) {
683 // Sort areas in bank and calculate usage
684 banks[c].size_used = bank_areas_calc_used(&banks[c], banks[c].start, banks[c].end);
685 banks[c].hidden = bank_name_check_hidden(banks[c].name);
686
687 if (get_option_area_sort() == OPT_AREA_SORT_SIZE_DESC)
688 qsort (banks[c].area_list.p_array, banks[c].area_list.count, sizeof(area_item), area_item_compare_size_desc);
689 else if (get_option_area_sort() == OPT_AREA_SORT_ADDR_ASC)
690 qsort (banks[c].area_list.p_array, banks[c].area_list.count, sizeof(area_item), area_item_compare_addr_asc);
691 else
692 qsort (banks[c].area_list.p_array, banks[c].area_list.count, sizeof(area_item), area_item_compare);
693 }
694
695 areas_check_rom0_overflow();
696
697 // Only print if quiet mode is not enabled
698 if (!option_quiet_mode) {
699 if (option_summarized_mode) {
700 banklist_collapse_to_summary(&bank_list, &bank_list_summarized);
701 if (option_json_output)
702 banklist_printall_json(&bank_list_summarized);
703 else
704 banklist_printall(&bank_list_summarized);
705 }
706 else {
707 if (option_json_output)
708 banklist_printall_json(&bank_list);
709 else
710 banklist_printall(&bank_list);
711 }
712 }
713 }
714
715
716 // Split a banks usage into N buckets
717 //
718 // Attempts to merge overlapping areas to avoid
719 // counting shared space multiple times.
720 //
721 // Avoids losing some address slots to integer rounding errors (when
722 // bucket_count is an imperfect divisor of range size) by using floats,
723 // with the trade-off that bucket size is slightly variable between buckets.
724 void bank_areas_split_to_buckets(bank_item * p_bank, uint32_t range_start, uint32_t range_size, uint32_t bucket_count, uint32_t * p_buckets) {
725
726 float bucket_size = (float)range_size / (float)bucket_count;
727 if (bucket_size == 0.0) return;
728
729 uint32_t range_end = range_start + (range_size - 1);
730 uint32_t bucket_start, bucket_end;
731 uint32_t bucket_id;
732 uint32_t start, end;
733
734 // Make a working copy of the bank and it's areas to modify since the
735 // required sorting of areas would override any user level sorting option
736 bank_item bank_copy = *p_bank;
737 bank_copy.area_list.p_array = (void *)malloc(bank_copy.area_list.size * bank_copy.area_list.typesize);
738 if (!bank_copy.area_list.p_array) {
739 log_error("Error: Failed to reallocate memory for list!\n");
740 exit(EXIT_FAILURE);
741 }
742 // Copy main list of areas to copy of bank for modification
743 memcpy(bank_copy.area_list.p_array, p_bank->area_list.p_array,
744 bank_copy.area_list.size * bank_copy.area_list.typesize);
745
746 area_item * areas = (area_item *)bank_copy.area_list.p_array;
747
748 // The calculation requires areas to be sorted ascending by .start addr then by .end addr
749 qsort (bank_copy.area_list.p_array, bank_copy.area_list.count, sizeof(area_item), area_item_compare);
750
751 // Iterate over all areas, splitting areas into any buckets they overlaps with
752 int c = 0;
753 uint32_t highest_addr_used = range_start;
754 while (c < bank_copy.area_list.count) {
755
756 // Only process areas not entirely covered by previous area
757 // Works since areas are sorted so current will never start before previous,
758 // and highest_addr_used is set to max from all processed areas so far
759 if (areas[c].end > highest_addr_used) {
760
761 // Calc starting bucket to skip non-overlapping ones
762 bucket_id = ((areas[c].start - range_start) / bucket_size);
763
764 // Break out if bucket exceeds range or
765 while (bucket_id < bucket_count) {
766
767 bucket_start = (uint32_t)(bucket_size * (float)bucket_id) + range_start;
768 bucket_end = (uint32_t)((bucket_size * ((float)bucket_id + 1.0)) - 1.0) + range_start;
769
770 // Break out of bucket updates for this area once past area end
771 if (bucket_start > areas[c].end)
772 break;
773
774 // Factor in highest addr used if it's been initialized
775 // Use that to avoid counting parts where areas overlap multiple times.
776 // +1 since start should be the address _after_ the highest used
777 if (highest_addr_used != bucket_start)
778 start = max(bucket_start, highest_addr_used + 1);
779
780 // Clip area to be within the bucket range
781 start = max(areas[c].start, bucket_start);
782 end = min(areas[c].end, bucket_end);
783
784 if (start <= end)
785 p_buckets[bucket_id] += (end - start) + 1;
786
787 // Move to next bucket and track high water mark for end of all areas
788 bucket_id++;
789 highest_addr_used = max(end, highest_addr_used);
790
791 } // End processing buckets for a given area
792 }
793 // Move to next area
794 c++;
795 }
796
797 if (bank_copy.area_list.p_array) {
798 free(bank_copy.area_list.p_array);
799 bank_copy.area_list.p_array = NULL; // Pointless, but out of habit
800 }
801 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.