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}