mirror of
https://github.com/gbdk-2020/gbdk-2020.git
synced 2026-03-09 16:57:55 +01:00
600 lines
23 KiB
C
600 lines
23 KiB
C
|
|
// This is free and unencumbered software released into the public domain.
|
|
// For more information, please refer to <https://unlicense.org>
|
|
// bbbbbr 2020
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include "common.h"
|
|
#include "list.h"
|
|
#include "files.h"
|
|
#include "options.h"
|
|
#include "obj_data.h"
|
|
|
|
|
|
static bool bank_check_mbc1_ok(uint32_t bank_num);
|
|
static void bank_update_assigned_minmax(uint16_t bank_num);
|
|
static void bank_update_all_max(uint16_t bank_num);
|
|
static void bank_add_area(bank_item * p_bank, uint16_t bank_num, area_item * p_area);
|
|
static void bank_check_area_size(area_item * p_area);
|
|
static void banks_assign_area(area_item * p_area);
|
|
static int area_item_compare(const void* a, const void* b);
|
|
static void areas_sort(void);
|
|
static bool symbol_banked_check_rewrite_ok(char *, uint32_t);
|
|
|
|
|
|
|
|
list_type banklist;
|
|
list_type arealist;
|
|
list_type symbollist;
|
|
list_type symbol_matchlist;
|
|
|
|
uint16_t bank_limit_rom_min = BANK_NUM_ROM_DEFAULT;
|
|
uint16_t bank_limit_rom_max = BANK_NUM_ROM_MAX;
|
|
|
|
|
|
uint16_t bank_assigned_rom_min = BANK_NUM_ROM_MAX;
|
|
uint16_t bank_assigned_rom_max = 0;
|
|
uint16_t bank_assigned_rom_max_alltypes = 0;
|
|
|
|
|
|
void obj_data_init(void) {
|
|
int c;
|
|
bank_item newbank;
|
|
|
|
list_init(&banklist, sizeof(bank_item));
|
|
list_init(&arealist, sizeof(area_item));
|
|
list_init(&symbollist, sizeof(symbol_item));
|
|
list_init(&symbol_matchlist, sizeof(symbol_match_item));
|
|
|
|
// Pre-populate bank list with max number of banks
|
|
// to allow handling fixed-bank (non-autobank) areas
|
|
newbank.size = newbank.free = BANK_SIZE_ROM;
|
|
newbank.reserved = 0;
|
|
newbank.type = BANK_TYPE_UNSET;
|
|
newbank.item_count = 0;
|
|
for (c=0; c < BANK_ROM_TOTAL; c++)
|
|
list_additem(&banklist, &newbank);
|
|
|
|
// Add default symbol match and replace
|
|
symbol_match_add("___bank_");
|
|
}
|
|
|
|
|
|
void obj_data_cleanup(void) {
|
|
list_cleanup(&banklist);
|
|
list_cleanup(&arealist);
|
|
list_cleanup(&symbollist);
|
|
list_cleanup(&symbol_matchlist);
|
|
}
|
|
|
|
|
|
// Add a symbol to the match and replace list
|
|
void symbol_match_add(char * symbol_str) {
|
|
|
|
symbol_match_item newmatch;
|
|
|
|
if (snprintf(newmatch.name, sizeof(newmatch.name), "%s", symbol_str) > sizeof(newmatch.name))
|
|
printf("BankPack: Warning: truncated symbol match string to:%s\n",newmatch.name);
|
|
|
|
list_additem(&symbol_matchlist, &newmatch);
|
|
}
|
|
|
|
|
|
|
|
// Add an area into the pool of areas to assign (if it's banked CODE)
|
|
int areas_add(char * area_str, uint32_t file_id) {
|
|
|
|
area_item newarea;
|
|
|
|
// Only match areas which are banked ("_CODE_" vs "_CODE") and ("_LIT_")
|
|
if (AREA_LINE_RECORDS == sscanf(area_str,"A _CODE _%3d size %4x flags %*4x addr %*4x",
|
|
&newarea.bank_num_in, &newarea.size)) {
|
|
newarea.type = BANK_TYPE_CODE;
|
|
}
|
|
else if (AREA_LINE_RECORDS == sscanf(area_str,"A _LIT_%3d size %4x flags %*4x addr %*4x",
|
|
&newarea.bank_num_in, &newarea.size)) {
|
|
newarea.type = BANK_TYPE_LIT_EXCLUSIVE;
|
|
}
|
|
else
|
|
return false;
|
|
|
|
// Only process areas with (size > 0)
|
|
if (newarea.size > 0) {
|
|
if (newarea.type == BANK_TYPE_LIT_EXCLUSIVE)
|
|
sprintf(newarea.name, "_LIT_"); // Hardwired to _LIT_ for now
|
|
else
|
|
sprintf(newarea.name, "_CODE_"); // Hardwired to _CODE_ for now
|
|
newarea.file_id = file_id;
|
|
newarea.bank_num_out = BANK_NUM_UNASSIGNED;
|
|
list_additem(&arealist, &newarea);
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
|
|
// Check and add a symbol record (if it's banked CODE)
|
|
int symbols_add(char * symbol_str, uint32_t file_id, unsigned int obj_file_format) {
|
|
|
|
symbol_item newsymbol;
|
|
|
|
const char * s_def_matches[] = {[OBJ_FILE_XL3_24BIT_ADDR] = "S %" TOSTR(OBJ_NAME_MAX_STR_LEN) "s Def00%4x",
|
|
[OBJ_FILE_XL4_32BIT_ADDR] = "S %" TOSTR(OBJ_NAME_MAX_STR_LEN) "s Def0000%4x"};
|
|
if (obj_file_format >= OBJ_FILE_UNKNOWN) return false;
|
|
|
|
if (SYMBOL_LINE_RECORDS == sscanf(symbol_str, s_def_matches[obj_file_format], newsymbol.name, &newsymbol.bank_num_in)) {
|
|
|
|
// Symbols that start with b_ store the bank num for the matching symbol without 'b'
|
|
newsymbol.is_banked_def = (newsymbol.name[0] == 'b');
|
|
newsymbol.file_id = file_id;
|
|
newsymbol.found_matching_symbol = false;
|
|
|
|
// Don't add banked symbols if they're not set to the autobank bank #
|
|
if ((newsymbol.is_banked_def) && (newsymbol.bank_num_in != BANK_NUM_AUTO))
|
|
return false;
|
|
|
|
list_additem(&symbollist, &newsymbol);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// Track Min/Max assigned banks used
|
|
static void bank_update_assigned_minmax(uint16_t bank_num) {
|
|
|
|
if (bank_num > bank_assigned_rom_max)
|
|
bank_assigned_rom_max = bank_num;
|
|
|
|
if (bank_num < bank_assigned_rom_min)
|
|
bank_assigned_rom_min = bank_num;
|
|
|
|
bank_update_all_max(bank_num);
|
|
}
|
|
|
|
|
|
// Tracks Max bank for *all* banks used, including fixed banks outside max limits
|
|
static void bank_update_all_max(uint16_t bank_num) {
|
|
|
|
if (bank_num > bank_assigned_rom_max_alltypes)
|
|
bank_assigned_rom_max_alltypes = bank_num;
|
|
}
|
|
|
|
|
|
// Add an area to a bank.
|
|
// It should only error out when there isn't enough
|
|
// room with a fixed-bank area (non-autobank)
|
|
static void bank_add_area(bank_item * p_bank, uint16_t bank_num, area_item * p_area) {
|
|
|
|
// Make sure there is room and then update free space
|
|
if (p_area->size > p_bank->free) {
|
|
|
|
// Trying to add an auto-bank area to a full bank should be prevented by previous tests, but just in case
|
|
if (p_area->bank_num_in == BANK_NUM_AUTO) {
|
|
printf("BankPack: ERROR! Auto-banked Area %s, bank %d, size %d won't fit in assigned bank %d (%d bytes free, %d bytes reserved)\n",
|
|
p_area->name, p_area->bank_num_in, p_area->size, bank_num, p_bank->free, p_bank->reserved);
|
|
exit(EXIT_FAILURE);
|
|
} else {
|
|
// Only warn for fixed bank areas. Don't exit and add the area anyway
|
|
printf("BankPack: Warning: Fixed-bank Area %s, bank %d, size %d won't fit in assigned bank %d (%d bytes free, %d bytes reserved)\n",
|
|
p_area->name, p_area->bank_num_in, p_area->size, bank_num, p_bank->free, p_bank->reserved);
|
|
}
|
|
|
|
// Force bank space to zero, subtracting at this point would overflow
|
|
p_bank->free = 0;
|
|
} else
|
|
p_bank->free -= p_area->size;
|
|
|
|
// Copy bank type from area: some platforms (sms) don't allow mixing of area types in the same bank
|
|
// Assign outbound bank number for the area
|
|
p_bank->type = p_area->type;
|
|
p_bank->item_count++;
|
|
p_area->bank_num_out = bank_num;
|
|
bank_update_assigned_minmax(bank_num);
|
|
}
|
|
|
|
|
|
// Check to see if the specified bank is allowed with MBC1
|
|
static bool bank_check_mbc1_ok(uint32_t bank_num) {
|
|
|
|
if ((bank_num == 0x20U) || (bank_num == 0x40U) || (bank_num == 0x60U))
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
|
|
// Checks to make sure area size is not larger than an entire bank
|
|
static void bank_check_area_size(area_item * p_area) {
|
|
if (p_area->size > BANK_SIZE_ROM) {
|
|
printf("BankPack: ERROR! Area %s, bank %d, size %d is too large for bank size %d (file %s)\n",
|
|
p_area->name, p_area->bank_num_in, p_area->size, BANK_SIZE_ROM, file_get_name_in_by_id(p_area->file_id));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
|
|
// Display a error message about bank mixing
|
|
static void bank_report_mixed_area_error(bank_item * p_bank, uint16_t bank_num, area_item * p_area) {
|
|
|
|
printf("BankPack: ERROR! Bank %d already assigned different area type.\n"
|
|
" Can't mix _CODE_ and _LIT_ areas in the same bank for this platform.\n"
|
|
" Rejecting: Area %s, bank %d, file:%s\n",
|
|
p_area->bank_num_in, p_area->name, bank_num, file_get_name_in_by_id(p_area->file_id));
|
|
|
|
if (option_get_verbose())
|
|
banks_show();
|
|
}
|
|
|
|
|
|
// Checks whether mixing area types in the same bank should be rejected
|
|
static bool bank_check_mixed_area_types_ok(bank_item * p_bank, uint16_t bank_num, area_item * p_area) {
|
|
|
|
// Don't allow mixing of _CODE_ and _LIT_ for sms/gg ports
|
|
// If one type has already been assigned to the bank, lock others out
|
|
if ((option_get_platform() == PLATFORM_SMS) &&
|
|
(p_bank->type != BANK_TYPE_UNSET) &&
|
|
(p_area->type != p_bank->type))
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
|
|
// Verify that a bank is available for an area
|
|
// Checks: Size, MBC Availability, Mixed area restrictions
|
|
static bool bank_check_ok_for_area(uint16_t bank_num, area_item * p_area, bank_item * banks) {
|
|
|
|
// Check for MBC bank restrictions
|
|
if ((option_get_mbc_type() != MBC_TYPE_MBC1) || (bank_check_mbc1_ok(bank_num))) {
|
|
// Check for allowed area mixing if needed
|
|
if (bank_check_mixed_area_types_ok(&banks[bank_num], bank_num, p_area)) {
|
|
// Make sure there is enough space for the area
|
|
if (p_area->size <= banks[bank_num].free) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// Assign areas randomly and distributed as sparsely as possible
|
|
static bool banks_assign_area_random(area_item * p_area, bank_item * banks) {
|
|
|
|
uint16_t bank_num;
|
|
uint16_t item_count_min = BANK_ITEM_COUNT_MAX;
|
|
uint16_t list_of_banks[BANK_ROM_TOTAL];
|
|
uint16_t list_count = 0;
|
|
|
|
// Find lowest number of areas per bank among banks which have sufficient space for the area
|
|
for (bank_num = bank_limit_rom_min; bank_num <= bank_limit_rom_max; bank_num++)
|
|
if ((banks[bank_num].item_count < item_count_min) && bank_check_ok_for_area(bank_num, p_area, banks))
|
|
item_count_min = banks[bank_num].item_count;
|
|
|
|
// Now make a list of suitable banks below that threshold
|
|
for (bank_num = bank_limit_rom_min; bank_num <= bank_limit_rom_max; bank_num++)
|
|
if ((banks[bank_num].item_count <= item_count_min) && bank_check_ok_for_area(bank_num, p_area, banks))
|
|
list_of_banks[list_count++] = bank_num;
|
|
|
|
if (list_count > 0) {
|
|
// Choose a random bank from the selected banks and add the area to it
|
|
bank_num = list_of_banks[ rand() % list_count ];
|
|
bank_add_area(&banks[bank_num], bank_num, p_area);
|
|
|
|
return true;
|
|
} else
|
|
return false; // Fail if no banks were available
|
|
}
|
|
|
|
|
|
// Assign areas linearly, trying to fill up lowest banks first
|
|
static bool banks_assign_area_linear(area_item * p_area, bank_item * banks) {
|
|
|
|
uint16_t bank_num;
|
|
|
|
// Try to assign area to first allowed bank with enough free space
|
|
// Bank array index maps directly to bank numbers, so [2] will be _CODE_2
|
|
for (bank_num = bank_limit_rom_min; bank_num <= bank_limit_rom_max; bank_num++) {
|
|
if (bank_check_ok_for_area(bank_num, p_area, banks)) {
|
|
bank_add_area(&banks[bank_num], bank_num, p_area);
|
|
return true;
|
|
}
|
|
}
|
|
return false; // Fail if no banks were available
|
|
}
|
|
|
|
|
|
// Find a bank for a given area using First Fit Decreasing (FFD)
|
|
// All possible banks (0-255) were pre-created and initialized [in obj_data_init()],
|
|
// so there is no need to add when using a fresh bank
|
|
static void banks_assign_area(area_item * p_area) {
|
|
|
|
uint16_t bank_num;
|
|
bank_item * banks = (bank_item *)banklist.p_array;
|
|
bool result;
|
|
|
|
bank_check_area_size(p_area);
|
|
|
|
// Try to assign fixed bank areas to their expected bank.
|
|
// (ignore the area if it's outside the processing range)
|
|
if (p_area->bank_num_in != BANK_NUM_AUTO) {
|
|
|
|
bank_num = p_area->bank_num_in;
|
|
|
|
// Update max bank var that tracks regardless of limits
|
|
// For auto banks this will get updated via bank_add_area()
|
|
bank_update_all_max(bank_num);
|
|
|
|
if ((bank_num >= bank_limit_rom_min) &&
|
|
(bank_num <= bank_limit_rom_max)) {
|
|
|
|
if ((option_get_mbc_type() == MBC_TYPE_MBC1) && (!bank_check_mbc1_ok(bank_num)))
|
|
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));
|
|
|
|
if (!bank_check_mixed_area_types_ok(&banks[bank_num], bank_num, p_area)) {
|
|
bank_report_mixed_area_error(&banks[bank_num], bank_num, p_area);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
bank_add_area(&banks[bank_num], bank_num, p_area);
|
|
}
|
|
//else
|
|
// 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));
|
|
|
|
return;
|
|
}
|
|
else if (p_area->bank_num_in == BANK_NUM_AUTO) {
|
|
|
|
if (option_get_random_assign())
|
|
result = banks_assign_area_random(p_area, banks);
|
|
else
|
|
result = banks_assign_area_linear(p_area, banks);
|
|
|
|
if (result) // Success
|
|
return;
|
|
}
|
|
|
|
if (option_get_verbose())
|
|
banks_show();
|
|
printf("BankPack: ERROR! Failed to assign bank for Area %s, bank %d, size %d. Out of banks!\n",
|
|
p_area->name, p_area->bank_num_in, p_area->size);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
|
|
// qsort compare rule function for sorting areas
|
|
static int area_item_compare(const void* a, const void* b) {
|
|
|
|
// Sort:
|
|
// - First by bank num [asc] (implied fixed first, since fixed [0..254] < auto [255])
|
|
// - Then by size [desc]
|
|
//
|
|
// This assumes the autobank indicator (255) is larger than all fixed bank numbers (< 255)
|
|
if (((area_item *)a)->bank_num_in != ((area_item *)b)->bank_num_in)
|
|
return (((area_item *)a)->bank_num_in < ((area_item *)b)->bank_num_in) ? QSORT_A_FIRST : QSORT_A_AFTER;
|
|
else if (((area_item *)a)->size != ((area_item *)b)->size)
|
|
return (((area_item *)a)->size > ((area_item *)b)->size) ? QSORT_A_FIRST : QSORT_A_AFTER;
|
|
else
|
|
return QSORT_A_SAME;
|
|
}
|
|
|
|
static void areas_sort(void) {
|
|
// Sort banks by name
|
|
qsort (arealist.p_array, arealist.count, sizeof(area_item), area_item_compare);
|
|
}
|
|
|
|
|
|
// Assigns all areas -> banks, and bank numbers -> files
|
|
// Fixed bank areas are placed first, then auto-banks fill the rest in
|
|
// Only call after all areas have been collected from object files
|
|
void obj_data_process(list_type * p_filelist) {
|
|
uint32_t c, s;
|
|
area_item * areas = (area_item *)arealist.p_array;
|
|
symbol_item * symbols = (symbol_item *)symbollist.p_array;
|
|
file_item * files = (file_item *)(p_filelist->p_array);
|
|
|
|
// Sort all areas (see function for sort order)
|
|
areas_sort();
|
|
|
|
// Assign areas to banks
|
|
for (c = 0; c < arealist.count; c++) {
|
|
banks_assign_area(&(areas[c]));
|
|
// Set linkerfile order based on sort order above
|
|
//
|
|
// All files get processed here, including Fixed bank files.
|
|
// The Fixed vs Auto sorting guarantees Fixed are always before Auto,
|
|
// they are then sub-sorted by size
|
|
//
|
|
// The + 1 differentiates it from the default LINKERFILE_ORDER_FIRST
|
|
files[ areas[c].file_id ].linkerfile_order = c + 1;
|
|
|
|
// If area was auto-banked then set bank number in associated file
|
|
if ((areas[c].bank_num_in == BANK_NUM_AUTO) &&
|
|
(areas[c].bank_num_out != BANK_NUM_UNASSIGNED)) {
|
|
|
|
if (files[ areas[c].file_id ].bank_num != BANK_NUM_UNASSIGNED) {
|
|
printf("BankPack: ERROR! Can't assign bank number to a file more than once! Already %d, new is %d\n",
|
|
files[ areas[c].file_id ].bank_num, areas[c].bank_num_out);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
files[ areas[c].file_id ].bank_num = areas[c].bank_num_out;
|
|
files[ areas[c].file_id ].rewrite_needed = true;
|
|
}
|
|
}
|
|
|
|
// Check all symbols for matching banked entries ("b<symbol>"), flag if match found
|
|
// TODO: inefficient to loop over symbols for all files
|
|
for (c = 0; c < symbollist.count; c++) {
|
|
if (symbols[c].is_banked_def) {
|
|
for (s = 0; s < symbollist.count; s++) {
|
|
if (symbols[c].file_id == symbols[s].file_id) {
|
|
// offset +1 past the "b" char at start of banked symbol entry name
|
|
if (strcmp(symbols[c].name + 1, symbols[s].name) == 0) {
|
|
symbols[c].found_matching_symbol = true;
|
|
break;
|
|
} else if (s == symbollist.count -1)
|
|
printf(" -> NO MATCH FOUND%s\n", symbols[c].name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// Display file/area/bank assignment
|
|
// Should be called after obj_data_process()
|
|
void banks_show(void) {
|
|
|
|
uint32_t c;
|
|
uint32_t a;
|
|
|
|
printf("\n== Banks assigned: %d -> %d (allowed %d -> %d). Max including fixed: %d) ==\n",
|
|
bank_assigned_rom_min, bank_assigned_rom_max,
|
|
bank_limit_rom_min, bank_limit_rom_max,
|
|
bank_assigned_rom_max_alltypes);
|
|
|
|
bank_item * banks = (bank_item *)banklist.p_array;
|
|
area_item * areas = (area_item *)arealist.p_array;
|
|
for (c = 0; c < banklist.count; c++) {
|
|
if (banks[c].free != BANK_SIZE_ROM) {
|
|
printf("Bank %d: Size=%5d, Free=%5d, Reserved=%5d\n", c, banks[c].size, banks[c].free, banks[c].reserved);
|
|
printf(" Area Size Bank in->out File in->out\n");
|
|
for (a = 0; a < arealist.count; a++) {
|
|
if (areas[a].bank_num_out == c) {
|
|
printf(" %8s %5d %3d -> %3d ",
|
|
areas[a].name,
|
|
areas[a].size,
|
|
areas[a].bank_num_in,
|
|
areas[a].bank_num_out);
|
|
|
|
// Split in/out files to separate lines if output would extend
|
|
// beyond 80 chars (28 leading chars from print above, 4 for " -> ")
|
|
bool no_line_break = ((strlen(file_get_name_in_by_id(areas[a].file_id)) +
|
|
strlen(file_get_name_out_by_id(areas[a].file_id)) + 4 + 28) <= 80);
|
|
printf("%s%s-> %s\n",
|
|
file_get_name_in_by_id(areas[a].file_id),
|
|
(no_line_break) ? " " : "\n ",
|
|
file_get_name_out_by_id(areas[a].file_id));
|
|
}
|
|
}
|
|
// End of bank line break
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Accepts an input string line and writes it
|
|
// out with an **updated bank num** to a file
|
|
// * Adds trailing \n if missing
|
|
bool area_modify_and_write_to_file(char * strline_in, FILE * out_file, uint16_t bank_num) {
|
|
|
|
// Only rewrite area bank number for unset banked CODE
|
|
if (strline_in[0] == 'A') {
|
|
// For lines: A _CODE_255 ...
|
|
if (strstr(strline_in, "A _CODE_255")) {
|
|
fprintf(out_file, "A _CODE_%d %s", bank_num, strstr(strline_in, "size"));
|
|
// Add trailing \n if missing (may be, due to using strtok() to split the string)
|
|
if (strline_in[(strlen(strline_in)-1)] != '\n')
|
|
fprintf(out_file, "\n");
|
|
return true;
|
|
}
|
|
else if (strstr(strline_in, "A _LIT_255")) {
|
|
fprintf(out_file, "A _LIT_%d %s", bank_num, strstr(strline_in, "size"));
|
|
// Add trailing \n if missing (may be, due to using strtok() to split the string)
|
|
if (strline_in[(strlen(strline_in)-1)] != '\n')
|
|
fprintf(out_file, "\n");
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// Check to make sure a banked symbol has a matching symbol in the same file
|
|
// This prevents mistakenly rewriting symbol b_<something> entries from asm files
|
|
bool symbol_banked_check_rewrite_ok(char * symbol_name, uint32_t file_id) {
|
|
|
|
uint32_t c;
|
|
symbol_item * symbols = (symbol_item *)symbollist.p_array;
|
|
|
|
for (c = 0; c < symbollist.count; c++) {
|
|
if ((symbols[c].file_id == file_id) &&
|
|
(symbols[c].is_banked_def) &&
|
|
(symbols[c].found_matching_symbol)) {
|
|
// Make sure b_<name> matches
|
|
if (strcmp(symbols[c].name + 2, symbol_name) == 0)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// Accepts an input string line
|
|
// and writes it out with an updated bank to a file
|
|
// * Adds trailing \n if missing
|
|
bool symbol_modify_and_write_to_file(char * strline_in, FILE * out_file, uint16_t bank_num, uint32_t file_id, unsigned int obj_file_format) {
|
|
|
|
uint32_t c;
|
|
uint32_t bank_in = 0;
|
|
char strmatch[OBJ_NAME_MAX_STR_LEN];
|
|
char symbol_name[OBJ_NAME_MAX_STR_LEN];
|
|
symbol_match_item * sym_match = (symbol_match_item *)symbol_matchlist.p_array;
|
|
|
|
const char * s_def_b_matches[] = {[OBJ_FILE_XL3_24BIT_ADDR] = "S b_%" TOSTR(OBJ_NAME_MAX_STR_LEN) "s Def%06x",
|
|
[OBJ_FILE_XL4_32BIT_ADDR] = "S b_%" TOSTR(OBJ_NAME_MAX_STR_LEN) "s Def%08x"};
|
|
const char * s_def_b_rewrite[] = {[OBJ_FILE_XL3_24BIT_ADDR] = "S b_%s Def0000%02x",
|
|
[OBJ_FILE_XL4_32BIT_ADDR] = "S b_%s Def0000%04x"};
|
|
|
|
const char * s_def_matches[] = {[OBJ_FILE_XL3_24BIT_ADDR] = "S %s%%" TOSTR(OBJ_NAME_MAX_STR_LEN) "s Def%%06x",
|
|
[OBJ_FILE_XL4_32BIT_ADDR] = "S %s%%" TOSTR(OBJ_NAME_MAX_STR_LEN) "s Def%%08x"};
|
|
const char * s_def_rewrite[] = {[OBJ_FILE_XL3_24BIT_ADDR] = "S %s%s Def0000%02x",
|
|
[OBJ_FILE_XL4_32BIT_ADDR] = "S %s%s Def0000%04x"};
|
|
|
|
if (obj_file_format >= OBJ_FILE_UNKNOWN) return false;
|
|
|
|
// Only rewrite banked symbol entries
|
|
if (strline_in[0] == 'S') {
|
|
// For lines: S b_<symbol name>... Def0000FF (XL3 version)
|
|
// For lines: S b_<symbol name>... Def000000FF (XL4 version)
|
|
if (SYMBOL_REWRITE_RECORDS == sscanf(strline_in, s_def_b_matches[obj_file_format], symbol_name, &bank_in)) {
|
|
if (bank_in == BANK_NUM_AUTO) {
|
|
if (symbol_banked_check_rewrite_ok(symbol_name, file_id)) {
|
|
fprintf(out_file, s_def_b_rewrite[obj_file_format], symbol_name, bank_num);
|
|
if (strline_in[(strlen(strline_in)-1)] != '\n')
|
|
fprintf(out_file, "\n");
|
|
return true;
|
|
}
|
|
}
|
|
} // For lines: S ___bank_<trailing symbol name>... Def0000FF
|
|
else {
|
|
for (c = 0; c < symbol_matchlist.count; c++) {
|
|
// Prepare a sscanf match test string for the current symbol name
|
|
if (snprintf(strmatch, sizeof(strmatch), s_def_matches[obj_file_format], sym_match[c].name) > sizeof(strmatch))
|
|
printf("BankPack: Warning: truncated symbol match string to:%s\n",strmatch);
|
|
|
|
if (SYMBOL_REWRITE_RECORDS == sscanf(strline_in, strmatch, symbol_name, &bank_in)) {
|
|
if (bank_in == BANK_NUM_AUTO) {
|
|
fprintf(out_file, s_def_rewrite[obj_file_format], sym_match[c].name, symbol_name, bank_num);
|
|
if (strline_in[(strlen(strline_in)-1)] != '\n')
|
|
fprintf(out_file, "\n");
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|