git.y1.nz

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.