cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

obj_data.c (26552B)


      1
      2// This is free and unencumbered software released into the public domain.
      3// For more information, please refer to <https://unlicense.org>
      4// bbbbbr 2020
      5
      6#include <stdio.h>
      7#include <string.h>
      8#include <stdlib.h>
      9#include <unistd.h>
     10#include <stdbool.h>
     11#include <stdint.h>
     12
     13#include "common.h"
     14#include "list.h"
     15#include "files.h"
     16#include "obj_data.h"
     17
     18
     19static bool bank_check_mbc1_ok(uint32_t bank_num);
     20static void bank_update_assigned_minmax(uint16_t bank_num);
     21static void bank_update_all_max(uint16_t bank_num);
     22static void bank_add_area(bank_item * p_bank, uint16_t bank_num, area_item * p_area);
     23static void bank_check_area_size(area_item * p_area);
     24static void banks_assign_area(area_item * p_area);
     25static int area_item_compare(const void* a, const void* b);
     26static void areas_sort(void);
     27static bool symbol_banked_check_rewrite_ok(char *, uint32_t);
     28
     29extern bool g_option_verbose;
     30
     31
     32list_type banklist;
     33list_type arealist;
     34list_type symbollist;
     35list_type symbol_matchlist;
     36
     37uint16_t bank_limit_rom_min = BANK_NUM_ROM_MIN;
     38uint16_t bank_limit_rom_max = BANK_NUM_ROM_MAX;
     39
     40
     41uint16_t bank_assign_rom_min = BANK_NUM_ROM_MAX;
     42uint16_t bank_assign_rom_max = 0;
     43uint16_t bank_all_rom_max = 0;
     44
     45int g_mbc_type = MBC_TYPE_DEFAULT;
     46int g_platform = PLATFORM_DEFAULT;
     47bool g_opt_random_assign = false;
     48
     49
     50int banks_get_platform(void) {
     51    return g_platform;
     52}
     53
     54void banks_set_platform(char * platform_str) {
     55
     56    if (strcmp(platform_str, PLATFORM_STR_GB) == 0)
     57        g_platform = PLATFORM_GB;
     58    else if (strcmp(platform_str, PLATFORM_STR_AP) == 0)
     59        g_platform = PLATFORM_GB;  // Analogue Pocket uses GB platform
     60    else if (strcmp(platform_str, PLATFORM_STR_DUCK) == 0)
     61        g_platform = PLATFORM_GB;  // Megaduck uses GB platform
     62    else if (strcmp(platform_str, PLATFORM_STR_SMS) == 0)
     63        g_platform = PLATFORM_SMS;
     64    else if (strcmp(platform_str, PLATFORM_STR_GG) == 0)
     65        g_platform = PLATFORM_SMS; // GG uses SMS platform
     66    else if (strcmp(platform_str, PLATFORM_STR_MSXDOS) == 0)
     67        g_platform = PLATFORM_SMS; // MSXDOS uses SMS platform
     68    else
     69        printf("BankPack: Warning: Invalid platform option %s\n", platform_str);
     70}
     71
     72
     73
     74int banks_get_mbc_type(void) {
     75    return g_mbc_type;
     76}
     77
     78
     79// Set MBC type by interpreting from byte 149
     80//
     81//  For lcc linker option: -Wl-ytN where N is one of the numbers below
     82//  (from makebin.c in SDCC)
     83//
     84//  ROM Byte 0147: Cartridge type:
     85//  0-ROM ONLY            12-ROM+MBC3+RAM
     86//  1-ROM+MBC1            13-ROM+MBC3+RAM+BATT
     87//  2-ROM+MBC1+RAM        19-ROM+MBC5
     88//  3-ROM+MBC1+RAM+BATT   1A-ROM+MBC5+RAM
     89//  5-ROM+MBC2            1B-ROM+MBC5+RAM+BATT
     90//  6-ROM+MBC2+BATTERY    1C-ROM+MBC5+RUMBLE
     91//  8-ROM+RAM             1D-ROM+MBC5+RUMBLE+SRAM
     92//  9-ROM+RAM+BATTERY     1E-ROM+MBC5+RUMBLE+SRAM+BATT
     93//  B-ROM+MMM01           1F-Pocket Camera
     94//  C-ROM+MMM01+SRAM      FD-Bandai TAMA5
     95//  D-ROM+MMM01+SRAM+BATT FE - Hudson HuC-3
     96//  F-ROM+MBC3+TIMER+BATT FF - Hudson HuC-1
     97//  10-ROM+MBC3+TIMER+RAM+BATT
     98//  11-ROM+MBC3
     99void banks_set_mbc_by_rom_byte_149(int mbc_type_rom_byte) {
    100
    101    switch (mbc_type_rom_byte) {
    102        // NO MBC
    103        case 0x00U: //  0-ROM ONLY
    104        case 0x08U: //  2-ROM+MBC1+RAM
    105        case 0x09U: //  8-ROM+RAM
    106        case 0x0BU: //  B-ROM+MMM01
    107        case 0x0CU: //  C-ROM+MMM01+SRAM
    108        case 0x0DU: //  D-ROM+MMM01+SRAM+BATT
    109            banks_set_mbc(MBC_TYPE_NONE);
    110            break;
    111
    112        // MBC 1
    113        case 0x01U: //  1-ROM+MBC1
    114        case 0x02U: //  2-ROM+MBC1+RAM
    115        case 0x03U: //  3-ROM+MBC1+RAM+BATT
    116            banks_set_mbc(MBC_TYPE_MBC1);
    117            break;
    118
    119        // MBC 2
    120        case 0x05U: //  5-ROM+MBC2
    121        case 0x06U: //  6-ROM+MBC2+BATTERY
    122            banks_set_mbc(MBC_TYPE_MBC2);
    123            break;
    124
    125        // MBC 3
    126        case 0x0FU: //  F-ROM+MBC3+TIMER+BATT
    127        case 0x10U: //  10-ROM+MBC3+TIMER+RAM+BATT
    128        case 0x11U: //  11-ROM+MBC3
    129        case 0x12U: //  12-ROM+MBC3+RAM
    130        case 0x13U: //  13-ROM+MBC3+RAM+BATT
    131            banks_set_mbc(MBC_TYPE_MBC3);
    132            break;
    133
    134        // MBC 5
    135        case 0x19U: //  19-ROM+MBC5
    136        case 0x1AU: //  1A-ROM+MBC5+RAM
    137        case 0x1BU: //  1B-ROM+MBC5+RAM+BATT
    138        case 0x1CU: //  1C-ROM+MBC5+RUMBLE
    139        case 0x1DU: //  1D-ROM+MBC5+RUMBLE+SRAM
    140        case 0x1EU: //  1E-ROM+MBC5+RUMBLE+SRAM+BATT
    141            banks_set_mbc(MBC_TYPE_MBC5);
    142            break;
    143
    144        default:
    145            printf("BankPack: Warning: unrecognized MBC option -yt=%x\n", mbc_type_rom_byte);
    146            break;
    147    }
    148}
    149
    150
    151// Set MBC type directly
    152void banks_set_mbc(int mbc_type) {
    153
    154    uint16_t mbc_bank_limit_rom_max;
    155
    156    switch (mbc_type) {
    157        case MBC_TYPE_NONE:
    158            g_mbc_type = MBC_TYPE_NONE;
    159            break;
    160        case MBC_TYPE_MBC1:
    161            g_mbc_type = mbc_type;
    162            mbc_bank_limit_rom_max = BANK_NUM_ROM_MAX_MBC1;
    163            break;
    164        case MBC_TYPE_MBC2:
    165            g_mbc_type = mbc_type;
    166            mbc_bank_limit_rom_max = BANK_NUM_ROM_MAX_MBC2;
    167            break;
    168        case MBC_TYPE_MBC3:
    169            g_mbc_type = mbc_type;
    170            mbc_bank_limit_rom_max = BANK_NUM_ROM_MAX_MBC3;
    171            break;
    172        case MBC_TYPE_MBC5:
    173            g_mbc_type = mbc_type;
    174            mbc_bank_limit_rom_max = BANK_NUM_ROM_MAX_MBC5;
    175            break;
    176        default:
    177            printf("BankPack: Warning: unrecognized MBC option -mbc%d!\n", mbc_type);
    178            break;
    179    }
    180    if (mbc_bank_limit_rom_max < bank_limit_rom_max)
    181        bank_limit_rom_max = mbc_bank_limit_rom_max;
    182}
    183
    184
    185
    186// From makebin
    187//  Byte
    188//  0148 - ROM size             -yt<N>
    189//  0    - 256Kbit = 32KByte  =   2 banks
    190//  1    - 512Kbit = 64KByte  =   4 banks
    191//  2    - 1Mbit   = 128KByte =   8 banks
    192//  3    - 2Mbit   = 256KByte =  16 banks
    193//  4    - 4Mbit   = 512KByte =  32 banks
    194//  5    - 8Mbit   = 1MByte   =  64 banks
    195//  6    - 16Mbit  = 2MByte   = 128 banks
    196//  7    - 16Mbit  = 2MByte   = 256 banks
    197//  8    - 16Mbit  = 2MByte   = 512 banks
    198//
    199//  Not supported by makebin:
    200//  $52 - 9Mbit = 1.1MByte = 72 banks
    201//  $53 - 10Mbit = 1.2MByte = 80 banks
    202//  $54 - 12Mbit = 1.5MByte = 96 banks
    203uint32_t banks_calc_cart_size(void) {
    204
    205    uint32_t req_banks = 1;
    206
    207    if ((bank_all_rom_max + 1) > BANK_ROM_CALC_MAX) {
    208        printf("BankPack: Warning! Can't calc cart size, too many banks: %d (max %d)\n", (bank_all_rom_max + 1), BANK_ROM_CALC_MAX);
    209        return (0);
    210    }
    211
    212    // Calculate nearest upper power of 2
    213    while (req_banks < (bank_all_rom_max + 1))
    214        req_banks *= 2;
    215
    216    return (req_banks);
    217}
    218
    219
    220bool banks_set_min(uint16_t bank_num) {
    221    if (bank_num < BANK_NUM_ROM_MIN)
    222        return false;
    223    else bank_limit_rom_min = bank_num;
    224
    225    return true;
    226}
    227
    228bool banks_set_max(uint16_t bank_num) {
    229    if (bank_num > BANK_NUM_ROM_MAX)
    230        return false;
    231    else bank_limit_rom_max = bank_num;
    232
    233    return true;
    234}
    235
    236
    237void banks_set_random(bool is_random) {
    238    g_opt_random_assign = is_random;
    239}
    240
    241
    242
    243void obj_data_init(void) {
    244    int c;
    245    bank_item newbank;
    246
    247    list_init(&banklist,      sizeof(bank_item));
    248    list_init(&arealist,      sizeof(area_item));
    249    list_init(&symbollist,    sizeof(symbol_item));
    250    list_init(&symbol_matchlist, sizeof(symbol_match_item));
    251
    252    // Pre-populate bank list with max number of of banks
    253    // to allow handling fixed-bank (non-autobank) areas
    254    newbank.size = newbank.free = BANK_SIZE_ROM;
    255    newbank.type = BANK_TYPE_UNSET;
    256    newbank.item_count = 0;
    257    for (c=0; c < BANK_ROM_TOTAL; c++)
    258        list_additem(&banklist, &newbank);
    259
    260    // Add default symbol match and replace
    261    symbol_match_add("___bank_");
    262}
    263
    264
    265void obj_data_cleanup(void) {
    266    list_cleanup(&banklist);
    267    list_cleanup(&arealist);
    268    list_cleanup(&symbollist);
    269    list_cleanup(&symbol_matchlist);
    270}
    271
    272
    273// Add a symbol to the match and replace list
    274void symbol_match_add(char * symbol_str) {
    275
    276    symbol_match_item newmatch;
    277
    278    if (snprintf(newmatch.name, sizeof(newmatch.name), "%s", symbol_str) > sizeof(newmatch.name))
    279        printf("BankPack: Warning: truncated symbol match string to:%s\n",newmatch.name);
    280
    281    list_additem(&symbol_matchlist, &newmatch);
    282}
    283
    284
    285
    286// Add an area into the pool of areas to assign (if it's banked CODE)
    287int areas_add(char * area_str, uint32_t file_id) {
    288
    289    area_item newarea;
    290
    291    // Only match areas which are banked ("_CODE_" vs "_CODE") and ("_LIT_")
    292    if (AREA_LINE_RECORDS == sscanf(area_str,"A _CODE _%3d size %4x flags %*4x addr %*4x",
    293                                    &newarea.bank_num_in, &newarea.size)) {
    294        newarea.type = BANK_TYPE_DEFAULT;
    295    }
    296    else if (AREA_LINE_RECORDS == sscanf(area_str,"A _LIT_%3d size %4x flags %*4x addr %*4x",
    297                                        &newarea.bank_num_in, &newarea.size)) {
    298        newarea.type = BANK_TYPE_LIT_EXCLUSIVE;
    299    }
    300    else
    301        return false;
    302
    303    // Only process areas with (size > 0)
    304    if (newarea.size > 0) {
    305        if (newarea.type == BANK_TYPE_LIT_EXCLUSIVE)
    306            sprintf(newarea.name, "_LIT_");  // Hardwired to _LIT_ for now
    307        else
    308            sprintf(newarea.name, "_CODE_"); // Hardwired to _CODE_ for now
    309        newarea.file_id = file_id;
    310        newarea.bank_num_out = BANK_NUM_UNASSIGNED;
    311        list_additem(&arealist, &newarea);
    312        return true;
    313    } else
    314        return false;
    315}
    316
    317
    318// Add an area into the pool of areas to assign (if it's banked CODE)
    319int symbols_add(char * symbol_str, uint32_t file_id) {
    320
    321    symbol_item newsymbol;
    322
    323     if (SYMBOL_LINE_RECORDS == sscanf(symbol_str,"S %" TOSTR(OBJ_NAME_MAX_STR_LEN) "s Def00%4x",
    324                                       newsymbol.name, &newsymbol.bank_num_in)) {
    325
    326        // Symbols that start with b_ store the bank num for the matching symbol without 'b'
    327        newsymbol.is_banked_def          = (newsymbol.name[0] == 'b');
    328        newsymbol.file_id                = file_id;
    329        newsymbol.found_matching_symbol  = false;
    330
    331        // Don't add banked symbols if they're not set to the autobank bank #
    332        if ((newsymbol.is_banked_def) && (newsymbol.bank_num_in != BANK_NUM_AUTO))
    333            return false;
    334
    335        list_additem(&symbollist, &newsymbol);
    336        return true;
    337    }
    338    return false;
    339}
    340
    341
    342// Track Min/Max assigned banks used
    343static void bank_update_assigned_minmax(uint16_t bank_num) {
    344
    345    if (bank_num > bank_assign_rom_max)
    346        bank_assign_rom_max = bank_num;
    347
    348    if (bank_num < bank_assign_rom_min)
    349        bank_assign_rom_min = bank_num;
    350
    351    bank_update_all_max(bank_num);
    352}
    353
    354
    355// Tracks Max bank for *all* banks used, including fixed banks outside max limits
    356static void bank_update_all_max(uint16_t bank_num) {
    357
    358    if (bank_num > bank_all_rom_max)
    359        bank_all_rom_max = bank_num;
    360}
    361
    362
    363// Add an area to a bank.
    364// It should only error out when there isn't enough
    365// room with a fixed-bank area (non-autobank)
    366static void bank_add_area(bank_item * p_bank, uint16_t bank_num, area_item * p_area) {
    367
    368    // Make sure there is room and then update free space
    369    if (p_area->size > p_bank->free) {
    370
    371        // Trying to add an auto-bank area to a full bank should be prevented by previous tests, but just in case
    372        if (p_area->bank_num_in == BANK_NUM_AUTO) {
    373            printf("BankPack: ERROR! Auto-banked Area %s, bank %d, size %d won't fit in assigned bank %d (free %d)\n",
    374                    p_area->name, p_area->bank_num_in, p_area->size, bank_num, p_bank->free);
    375            exit(EXIT_FAILURE);
    376        } else {
    377            // Only warn for fixed bank areas. Don't exit and add the area anyway
    378            printf("BankPack: Warning: Fixed-bank Area %s, bank %d, size %d won't fit in assigned bank %d (free %d)\n",
    379                    p_area->name, p_area->bank_num_in, p_area->size, bank_num, p_bank->free);
    380        }
    381
    382        // Force bank space to zero, subtracting at this point would overflow
    383        p_bank->free = 0;
    384    } else
    385        p_bank->free -= p_area->size;
    386
    387    // Copy bank type from area: some platforms (sms) don't allow mixing of area types in the same bank
    388    // Assign outbound bank number for the area
    389    p_bank->type = p_area->type;
    390    p_bank->item_count++;
    391    p_area->bank_num_out = bank_num;
    392    bank_update_assigned_minmax(bank_num);
    393}
    394
    395
    396// Check to see if the specified bank is allowed with MBC1
    397static bool bank_check_mbc1_ok(uint32_t bank_num) {
    398
    399    if ((bank_num == 0x20U) || (bank_num == 0x40U) || (bank_num == 0x60U))
    400        return false;
    401    else
    402        return true;
    403}
    404
    405
    406// Checks to make sure area size is not larger than an entire bank
    407static void bank_check_area_size(area_item * p_area) {
    408    if (p_area->size > BANK_SIZE_ROM) {
    409        printf("BankPack: ERROR! Area %s, bank %d, size %d is too large for bank size %d (file %s)\n",
    410                p_area->name, p_area->bank_num_in, p_area->size, BANK_SIZE_ROM, file_get_name_in_by_id(p_area->file_id));
    411        exit(EXIT_FAILURE);
    412    }
    413}
    414
    415
    416// Display a error message about bank mixing
    417static void bank_report_mixed_area_error(bank_item * p_bank, uint16_t bank_num, area_item * p_area) {
    418
    419    printf("BankPack: ERROR! Bank %d already assigned different area type.\n"
    420           "  Can't mix _CODE_ and _LIT_ areas in the same bank for this platform.\n"
    421           "  Area %s, bank %d, file:%s\n",
    422           p_area->bank_num_in, p_area->name, bank_num, file_get_name_in_by_id(p_area->file_id));
    423
    424    if (g_option_verbose)
    425        banks_show();
    426}
    427
    428
    429// Checks whether mixing area types in the same bankshould be rejected
    430static bool bank_check_mixed_area_types_ok(bank_item * p_bank, uint16_t bank_num, area_item * p_area) {
    431
    432    // Don't allow mixing of _CODE_ and _LIT_ for sms/gg ports
    433    // If one type has already been assigned to the bank, lock others out
    434    if ((g_platform == PLATFORM_SMS) &&
    435        (p_bank->type != BANK_TYPE_UNSET) &&
    436        (p_area->type != p_bank->type))
    437        return false;
    438    else
    439        return true;
    440}
    441
    442
    443// Verify that a bank is available for an area
    444// Checks: Size, MBC Availability, Mixed area restrictions
    445static bool bank_check_ok_for_area(uint16_t bank_num, area_item * p_area, bank_item * banks) {
    446
    447    // Check for MBC bank restrictions
    448    if ((g_mbc_type != MBC_TYPE_MBC1) || (bank_check_mbc1_ok(bank_num))) {
    449        // Check for allowed area mixing if needed
    450        if (bank_check_mixed_area_types_ok(&banks[bank_num], bank_num, p_area)) {
    451            // Make sure there is enough space for the area
    452            if (p_area->size <= banks[bank_num].free) {
    453                return true;
    454            }
    455        }
    456    }
    457
    458    return false;
    459}
    460
    461
    462// Assign areas randomly and distributed as sparsely as possible
    463static bool banks_assign_area_random(area_item * p_area, bank_item * banks) {
    464
    465    uint16_t bank_num;
    466    uint16_t item_count_min = BANK_ITEM_COUNT_MAX;
    467    uint16_t list_of_banks[BANK_ROM_TOTAL];
    468    uint16_t list_count = 0;
    469
    470    // Find lowest number of areas per bank among banks which have sufficient space for the area
    471    for (bank_num = bank_limit_rom_min; bank_num <= bank_limit_rom_max; bank_num++)
    472        if ((banks[bank_num].item_count < item_count_min) && bank_check_ok_for_area(bank_num, p_area, banks))
    473            item_count_min = banks[bank_num].item_count;
    474
    475    // Now make a list of suitable banks below that threshold
    476    for (bank_num = bank_limit_rom_min; bank_num <= bank_limit_rom_max; bank_num++)
    477        if ((banks[bank_num].item_count <= item_count_min) && bank_check_ok_for_area(bank_num, p_area, banks))
    478            list_of_banks[list_count++] = bank_num;
    479
    480    if (list_count > 0) {
    481        // Choose a random bank from the selected banks and add the area to it
    482        bank_num = list_of_banks[ rand() % list_count ];
    483        bank_add_area(&banks[bank_num], bank_num, p_area);
    484
    485        return true;
    486    } else
    487        return false; // Fail if no banks were available
    488}
    489
    490
    491// Assign areas linearly, trying to fill up lowest banks first
    492static bool banks_assign_area_linear(area_item * p_area, bank_item * banks) {
    493
    494    uint16_t bank_num;
    495
    496    // Try to assign area to first allowed bank with enough free space
    497    // Bank array index maps directly to bank numbers, so [2] will be _CODE_2
    498    for (bank_num = bank_limit_rom_min; bank_num <= bank_limit_rom_max; bank_num++) {
    499        if (bank_check_ok_for_area(bank_num, p_area, banks)) {
    500            bank_add_area(&banks[bank_num], bank_num, p_area);
    501            return true;
    502        }
    503    }
    504    return false; // Fail if no banks were available
    505}
    506
    507
    508// Find a bank for a given area using First Fit Decreasing (FFD)
    509// All possible banks (0-255) were pre-created and initialized [in obj_data_init()],
    510// so there is no need to add when using a fresh bank
    511static void banks_assign_area(area_item * p_area) {
    512
    513    uint16_t bank_num;
    514    bank_item * banks = (bank_item *)banklist.p_array;
    515    bool result;
    516
    517    bank_check_area_size(p_area);
    518
    519    // Try to assign fixed bank areas to their expected bank.
    520    // (ignore the area if it's outside the processing range)
    521    if (p_area->bank_num_in != BANK_NUM_AUTO) {
    522
    523        bank_num = p_area->bank_num_in;
    524
    525        // Update max bank var that tracks regardless of limits
    526        // For auto banks this will get updated via bank_add_area()
    527        bank_update_all_max(bank_num);
    528
    529        if ((bank_num >= bank_limit_rom_min) &&
    530            (bank_num <= bank_limit_rom_max)) {
    531
    532            if ((g_mbc_type == MBC_TYPE_MBC1) && (!bank_check_mbc1_ok(bank_num)))
    533                printf("BankPack: Warning: Area in fixed bank assigned to MBC1 excluded bank: %d, file: %s\n", bank_num, file_get_name_in_by_id(p_area->file_id));
    534
    535            if (!bank_check_mixed_area_types_ok(&banks[bank_num], bank_num, p_area)) {
    536                bank_report_mixed_area_error(&banks[bank_num], bank_num, p_area);
    537                exit(EXIT_FAILURE);
    538            }
    539
    540            bank_add_area(&banks[bank_num], bank_num, p_area);
    541        }
    542        //else
    543        //    printf("BankPack: Notice: Ignoring Area in fixed bank %d outside specified range %d - %d, file: %s\n", bank_num, bank_limit_rom_min, bank_limit_rom_max, file_get_name_in_by_id(p_area->file_id));
    544
    545        return;
    546    }
    547    else if (p_area->bank_num_in == BANK_NUM_AUTO) {
    548
    549        if (g_opt_random_assign)
    550            result = banks_assign_area_random(p_area, banks);
    551        else
    552            result = banks_assign_area_linear(p_area, banks);
    553
    554        if (result) // Success
    555            return;
    556    }
    557
    558    if (g_option_verbose)
    559        banks_show();
    560    printf("BankPack: ERROR! Failed to assign bank for Area %s, bank %d, size %d. Out of banks!\n",
    561            p_area->name, p_area->bank_num_in, p_area->size);
    562    exit(EXIT_FAILURE);
    563}
    564
    565
    566#define QSORT_A_FIRST -1
    567#define QSORT_A_SAME   0
    568#define QSORT_A_AFTER  1
    569
    570// qsort compare rule function for sorting areas
    571static int area_item_compare(const void* a, const void* b) {
    572
    573    // sort by bank [asc] (fixed vs auto-bank), then by size [desc]
    574    if (((area_item *)a)->bank_num_in != ((area_item *)b)->bank_num_in)
    575        return  (((area_item *)a)->bank_num_in < ((area_item *)b)->bank_num_in) ? QSORT_A_FIRST : QSORT_A_AFTER;
    576    else if (((area_item *)a)->size != ((area_item *)b)->size)
    577        return (((area_item *)a)->size > ((area_item *)b)->size) ? QSORT_A_FIRST : QSORT_A_AFTER;
    578    else
    579        return QSORT_A_SAME;
    580}
    581
    582static void areas_sort(void) {
    583    // Sort banks by name
    584    qsort (arealist.p_array, arealist.count, sizeof(area_item), area_item_compare);
    585}
    586
    587
    588// Assigns all areas -> banks, and bank numbers -> files
    589// Fixed bank areas are placed first, then auto-banks fill the rest in
    590// Only call after all areas have been collected from object files
    591void obj_data_process(list_type * p_filelist) {
    592    uint32_t c, s;
    593    area_item   * areas   = (area_item *)arealist.p_array;
    594    symbol_item * symbols = (symbol_item *)symbollist.p_array;
    595    file_item   * files   = (file_item *)(p_filelist->p_array);
    596
    597    areas_sort();
    598
    599    // Assign areas to banks
    600    for (c = 0; c < arealist.count; c++) {
    601        banks_assign_area(&(areas[c]));
    602
    603        // If areas was auto-banked then set bank number in associated file
    604        if ((areas[c].bank_num_in == BANK_NUM_AUTO) &&
    605            (areas[c].bank_num_out != BANK_NUM_UNASSIGNED)) {
    606
    607            if (files[ areas[c].file_id ].bank_num != BANK_NUM_UNASSIGNED) {
    608                printf("BankPack: ERROR! Can't assign bank number to a file more than once! Already %d, new is %d\n",
    609                        files[ areas[c].file_id ].bank_num, areas[c].bank_num_out);
    610                exit(EXIT_FAILURE);
    611            }
    612
    613            files[ areas[c].file_id ].bank_num = areas[c].bank_num_out;
    614            files[ areas[c].file_id ].rewrite_needed = true;
    615        }
    616    }
    617
    618    // Check all symbols for matches to banked entries, flag if match found
    619    // TODO: ineffecient to loop over symbols for all files
    620    for (c = 0; c < symbollist.count; c++) {
    621        if (symbols[c].is_banked_def) {
    622            for (s = 0; s < symbollist.count; s++) {
    623                if (symbols[c].file_id == symbols[s].file_id) {
    624                    // offset +1 bast the "b" char at start of banekd symbol entry name
    625                    if (strcmp(symbols[c].name + 1, symbols[s].name) == 0) {
    626                        symbols[c].found_matching_symbol = true;
    627                        break;
    628                    } else if (s == symbollist.count -1)
    629                        printf("  -> NO MATCH FOUND%s\n", symbols[c].name);
    630                }
    631            }
    632        }
    633    }
    634
    635}
    636
    637
    638// Display file/area/bank assignment
    639// Should be called after obj_data_process()
    640void banks_show(void) {
    641
    642    uint32_t c;
    643    uint32_t a;
    644
    645    printf("\n=== Banks assigned: %d -> %d (allowed range %d -> %d). Max including fixed: %d) ===\n",
    646            bank_assign_rom_min, bank_assign_rom_max,
    647            bank_limit_rom_min,  bank_limit_rom_max,
    648            bank_all_rom_max);
    649
    650    bank_item * banks = (bank_item *)banklist.p_array;
    651    area_item * areas = (area_item *)arealist.p_array;
    652    for (c = 0; c < banklist.count; c++) {
    653        if (banks[c].free != BANK_SIZE_ROM) {
    654            printf("Bank %d: size=%5d, free=%5d\n", c, banks[c].size, banks[c].free);
    655            for (a = 0; a < arealist.count; a++) {
    656                if (areas[a].bank_num_out == c) {
    657                    printf(" +- Area: name=%8s, size=%5d, bank_in=%3d, bank_out=%3d, file=%s -> %s\n",
    658                        areas[a].name,
    659                        areas[a].size,
    660                        areas[a].bank_num_in,
    661                        areas[a].bank_num_out,
    662                        file_get_name_in_by_id(areas[a].file_id),
    663                        file_get_name_out_by_id(areas[a].file_id));
    664                }
    665            }
    666        }
    667    }
    668
    669    printf("\n");
    670}
    671
    672
    673// Accepts an input string line and writes it
    674// out with an **updated bank num** to a file
    675// * Adds trailing \n if missing
    676bool area_modify_and_write_to_file(char * strline_in, FILE * out_file, uint16_t bank_num) {
    677
    678    // Only rewrite area bank number for unset banked CODE
    679    if (strline_in[0] == 'A') {
    680        // For lines: A _CODE_255 ...
    681        if (strstr(strline_in, "A _CODE_255")) {
    682            fprintf(out_file, "A _CODE_%d %s", bank_num, strstr(strline_in, "size"));
    683            // Add trailing \n if missing (may be, due to using strtok() to split the string)
    684            if (strline_in[(strlen(strline_in)-1)] != '\n')
    685                fprintf(out_file, "\n");
    686            return true;
    687        }
    688        else if (strstr(strline_in, "A _LIT_255")) {
    689            fprintf(out_file, "A _LIT_%d %s", bank_num, strstr(strline_in, "size"));
    690            // Add trailing \n if missing (may be, due to using strtok() to split the string)
    691            if (strline_in[(strlen(strline_in)-1)] != '\n')
    692                fprintf(out_file, "\n");
    693            return true;
    694        }
    695    }
    696    return false;
    697}
    698
    699
    700// Check to make sure a banked symbol has a matching symbol in the same file
    701// This prevents mistakenly rewriting symbol b_<something> entries from asm files
    702bool symbol_banked_check_rewrite_ok(char * symbol_name, uint32_t file_id) {
    703
    704    uint32_t c;
    705    symbol_item * symbols = (symbol_item *)symbollist.p_array;
    706
    707    for (c = 0; c < symbollist.count; c++) {
    708        if ((symbols[c].file_id == file_id) &&
    709            (symbols[c].is_banked_def) &&
    710            (symbols[c].found_matching_symbol)) {
    711            // Make sure b_<name> matches
    712            if (strcmp(symbols[c].name + 2, symbol_name) == 0)
    713                return true;
    714        }
    715    }
    716    return false;
    717}
    718
    719
    720// Accepts an input string line
    721// and writes it out with an updated bank to a file
    722// * Adds trailing \n if missing
    723bool symbol_modify_and_write_to_file(char * strline_in, FILE * out_file, uint16_t bank_num, uint32_t file_id) {
    724
    725    uint32_t c;
    726    uint32_t bank_in = 0;
    727    char strmatch[OBJ_NAME_MAX_STR_LEN];
    728    char symbol_name[OBJ_NAME_MAX_STR_LEN];
    729    symbol_match_item * sym_match = (symbol_match_item *)symbol_matchlist.p_array;
    730
    731    // Only rewrite banked symbol entries
    732    if (strline_in[0] == 'S') {
    733        // For lines: S b_<symbol name>... Def0000FF
    734        if (SYMBOL_REWRITE_RECORDS == sscanf(strline_in,"S b_%" TOSTR(OBJ_NAME_MAX_STR_LEN) "s Def%06x", symbol_name, &bank_in)) {
    735            if (bank_in == BANK_NUM_AUTO) {
    736                if (symbol_banked_check_rewrite_ok(symbol_name, file_id))  {
    737                    fprintf(out_file, "S b_%s Def0000%02x", symbol_name, bank_num);
    738                    if (strline_in[(strlen(strline_in)-1)] != '\n')
    739                        fprintf(out_file, "\n");
    740                    return true;
    741                }
    742            }
    743        } // For lines: S ___bank_<trailing symbol name>... Def0000FF
    744        else {
    745            for (c = 0; c < symbol_matchlist.count; c++) {
    746                // Prepare a sscanf match test string for the current symbol name
    747                if (snprintf(strmatch, sizeof(strmatch), "S %s%%" TOSTR(OBJ_NAME_MAX_STR_LEN) "s Def%%06x", sym_match[c].name) > sizeof(strmatch))
    748                    printf("BankPack: Warning: truncated symbol match string to:%s\n",strmatch);
    749
    750                if (SYMBOL_REWRITE_RECORDS == sscanf(strline_in, strmatch, symbol_name, &bank_in)) {
    751                    if (bank_in == BANK_NUM_AUTO) {
    752                        fprintf(out_file, "S %s%s Def0000%02x", sym_match[c].name, symbol_name, bank_num);
    753                        if (strline_in[(strlen(strline_in)-1)] != '\n')
    754                            fprintf(out_file, "\n");
    755                        return true;
    756                    }
    757                }
    758            }
    759        }
    760    }
    761    return false;
    762}