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/cdb_file.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 <unistd.h>
      9 #include <stdbool.h>
     10 #include <stdint.h>
     11 
     12 #include "common.h"
     13 #include "logging.h"
     14 #include "list.h"
     15 #include "banks.h"
     16 #include "cdb_file.h"
     17 
     18 
     19 list_type symbol_list;
     20 
     21 #define CDB_L_REC_FUNC_START_GLOBAL 'G'
     22 #define CDB_L_REC_FUNC_START_LOCAL  'F'
     23 #define CDB_L_REC_FUNC_END_GLOBAL   "XG"
     24 #define CDB_L_REC_FUNC_END_LOCAL    "XF"
     25 
     26 // More compelte CDB coverage is affected by this SDCC bug
     27 // #3662 Adding a function removes const arrays above it from .adb/.cdb output
     28 // https://sourceforge.net/p/sdcc/bugs/3662/
     29 
     30 
     31 // Example data to parse from a .cdb file:
     32 //
     33 // 0 1 2---------- 3-- 4 5----
     34 // L:G$big_const_4$0_0$0:24039 <-- bank 2
     35 // L:G$big_const_3$0_0$0:1784E <-- Bank 1
     36 //
     37 // S:Fsrcfile_2$func_2_local_scope$0_0$0({2}DF,SV:S),C,0,0
     38 // L:Fsrcfile_2$func_2_local_scope$0$0:14000
     39 // L:XFsrcfile_2$func_2_local_scope$0$0:14003
     40 // F:Fsrcfile_2$func_2_local_scope$0_0$0({2}DF,SV:S),C,0,0,0,0,0
     41 //
     42 // 0 1 2---------- 3-- 4  5-- 6----- 7  8  9 10 11
     43 // S:G$big_const_3$0_0$0({256}DA256d,SC:U),D,0,0     <--- size 256 bytes (SC=signed char)
     44 // S:G$big_const_4$0_0$0({15360}DA15360d,SC:U),D,0,0 <--- size 15360 bytes (SC=signed char)
     45 //
     46 // 0 1--------- 2------------ 3-- 4  5---
     47 // L:Fsrcfile_2$local_const_2$0_0$0:14027
     48 // 0 1--------- 2------------ 3-- 4  5---- 6------- 7  8  9 10 11
     49 // S:Fsrcfile_2$local_const_2$0_0$0({14336}DA14336d,SC:U),D,0,0 <--- size 14336 bytes
     50 
     51 // 4.8 Link Address of Symbol
     52 //
     53 // L    Link record type indicator
     54 // G   Symbol has file scope
     55 // F <Filename>    Symbol has file scope.
     56 // L <Function>    Symbol has function scope
     57 //
     58 // functions use L:XG to mark their last line
     59 //   basxto:    L:XG$init_map$0$0:BCA - L:G$init_map$0$0:AFB = should be the length
     60 //     L:G$board_handle_new_piece$0$0:6284
     61 //     L:XG$board_handle_new_piece$0$0:62DD
     62 //
     63 // https://github.com/roybaer/sdcc-wiki/wiki/CDB-File-Format
     64 //
     65 // Address Space field (S: record)
     66 // A   External stack
     67 // B   Internal stack
     68 // C   Code
     69 // D   Code / static segment
     70 // E   Internal ram (lower 128) bytes
     71 // F   External ram
     72 // G   Internal ram
     73 // H   Bit addressable
     74 // I   SFR space
     75 // J   SBIT space
     76 // R   Register space
     77 // Z   Used for function records, or any undefined space code
     78 //
     79 // Types in a Section 4.4 Type Chain Record (S: record)
     80 // DA <n>  Array of n elements
     81 // DF  Function
     82 // DG  Generic pointer
     83 // DC  Code pointer
     84 // DX  External ram pointer
     85 // DD  Internal ram pointer
     86 // DP  Paged pointer
     87 // DI  Upper 128 byte pointer
     88 // SL  long
     89 // SI  int
     90 // SC  char
     91 // SS  short
     92 // SV  void
     93 // SF  float
     94 // ST <name>   Structure of name <name>
     95 // SX  sbit
     96 // SB <n>  Bit field of <n> bits
     97 
     98 
     99 // Initialize the symbol list
    100 void cdb_init(void) {
    101 
    102     list_init(&symbol_list, sizeof(area_item));
    103 }
    104 
    105 
    106 // Free the symbol list
    107 void cdb_cleanup(void) {
    108 
    109     list_cleanup(&symbol_list);
    110 }
    111 
    112 
    113 // Find a matching symbol, if none matches a new one is added and returned
    114 static int symbollist_get_id_by_name(char * symbol_name) {
    115 
    116     area_item * symbols = (area_item *)symbol_list.p_array;
    117     area_item new_symbol;
    118 
    119     int c;
    120 
    121     // Check for matching symbol name
    122     for(c=0;c < symbol_list.count; c++) {
    123         // Return matching symbol index if present
    124         if (strncmp(symbol_name, symbols[c].name, AREA_MAX_STR) == 0) {
    125             return c;
    126         }
    127     }
    128 
    129     snprintf(new_symbol.name, sizeof(new_symbol.name), "%s", symbol_name);
    130     new_symbol.start  = AREA_VAL_UNSET;
    131     new_symbol.end    = AREA_VAL_UNSET;
    132     new_symbol.length = AREA_VAL_UNSET;
    133     if (strstr(symbol_name,"HEADER"))
    134         new_symbol.exclusive = false; // HEADER symbols almost always overlap, ignore them
    135     else
    136         new_symbol.exclusive = option_all_areas_exclusive; // Default is false
    137 
    138     list_additem(&symbol_list, &new_symbol);
    139 
    140     return (symbol_list.count - 1);
    141 }
    142 
    143 
    144 // Process list of symbols and add them to banks
    145 static void cdb_symbollist_add_all_to_banks() {
    146 
    147     area_item * symbols = (area_item *)symbol_list.p_array;
    148     int c;
    149 
    150     // Only process completed symbols (start and length both set)
    151     for(c=0;c < symbol_list.count; c++) {
    152 
    153         // Functions need length calculated from start and end
    154         if ((symbols[c].length == AREA_VAL_UNSET) &&
    155             (symbols[c].start != AREA_VAL_UNSET) &&
    156             (symbols[c].end != AREA_VAL_UNSET)) {
    157             symbols[c].length = symbols[c].end - symbols[c].start + 1;
    158         }
    159 
    160 
    161         if ((symbols[c].start != AREA_VAL_UNSET) &&
    162             (symbols[c].length != AREA_VAL_UNSET)) {
    163             symbols[c].end = symbols[c].start + symbols[c].length - 1;
    164             banks_check(symbols[c]);
    165         }
    166     }
    167 }
    168 
    169 
    170 // Adds start/end address from a Linker Record
    171 // Requires either separate calls for Start and End, or one call for
    172 // start and a separate cdb_add_record_symbol() call to set length
    173 static void cdb_add_record_linker(char * type, char * name, char * address) {
    174 
    175     area_item * symbols = (area_item *)symbol_list.p_array;
    176 
    177     // Retrieve existing symbol or create a new one
    178     int symbol_id = symbollist_get_id_by_name(name);
    179 
    180     if (symbol_id != ERR_NO_AREAS_LEFT) {
    181 
    182         // Check Linker record for start-address or end-address
    183         // Bank number is in the address bits, no need to modify it
    184         if ((strncmp(type, CDB_L_REC_FUNC_END_GLOBAL, 4) == 0) ||
    185             (strncmp(type, CDB_L_REC_FUNC_END_LOCAL, 4) == 0)) {
    186             symbols[symbol_id].end = strtol(address, NULL, 16);     // End address
    187         }
    188         else if ((type[0] == CDB_L_REC_FUNC_START_GLOBAL) ||
    189                  (type[0] == CDB_L_REC_FUNC_START_LOCAL)) {
    190             symbols[symbol_id].start = strtol(address, NULL, 16);   // Start address
    191         }
    192         // else
    193         // printf("Rejected L record %s, %s, %s\n", type, name, address);
    194     }
    195 }
    196 
    197 
    198 // Adds length from a symbol record
    199 // To get a complete entry requires a start address call to cdb_add_record_linker()
    200 static void cdb_add_record_symbol(char * addr_space, char * name, char * length, char * dcl_type) {
    201 
    202     area_item * symbols = (area_item *)symbol_list.p_array;
    203 
    204     // Only allow certain address spaces
    205     if ((addr_space[0] == 'C') || // Address Space: Code
    206         (addr_space[0] == 'D') || // Address Space: Code / static segment
    207         (addr_space[0] == 'E') || // Address Space: Internal RAM (lower 128) bytes
    208         (addr_space[0] == 'F') || // Address Space: External RAM
    209         (addr_space[0] == 'G')) { // Address Space: Internal RAM
    210 
    211         // Exclude zero length entries
    212         // Don't let function ~entry points override function bodies(<DCLType> = DF, function which are two bytes in size)
    213         if ((strtol(length, NULL, 10) > 0) &&
    214             (!strstr(dcl_type, "DF")))
    215         {
    216             // Retrieve existing symbol or create a new one
    217             int symbol_id = symbollist_get_id_by_name(name); // [2] Area Name
    218             if (symbol_id != ERR_NO_AREAS_LEFT) {
    219                     symbols[symbol_id].length = strtol(length, NULL, 10); // [5] Symbol decimal length
    220             }
    221         }
    222     }
    223     // else
    224     // printf("Rejected S record %s, %s, %s, %s\n", addr_space, name, length, dcl_type);
    225 }
    226 
    227 
    228 int cdb_file_process_symbols(char * filename_in) {
    229 
    230     int cols;
    231     char * p_str;
    232     char * p_words[CDB_MAX_SPLIT_WORDS];
    233     char strline_in[CDB_MAX_STR_LEN] = "";
    234     FILE * cdb_file = fopen(filename_in, "r");
    235     area_item symbol;
    236     int symbol_id;
    237 
    238     // CDB defaults to showing areas
    239     banks_output_show_areas(true);
    240 
    241     // CDB defaults to size descending, but don't override explicit options
    242     if (get_option_area_sort() == OPT_AREA_SORT_DEFAULT)
    243         set_option_area_sort(OPT_AREA_SORT_SIZE_DESC);
    244 
    245     set_option_input_source(OPT_INPUT_SRC_CDB);
    246 
    247     if (cdb_file) {
    248 
    249         // Read one line at a time into \0 terminated string
    250         while ( fgets(strline_in, sizeof(strline_in), cdb_file) != NULL) {
    251 
    252             // Require minimum length to match
    253             if (strlen(strline_in) >= CDB_REC_START_LEN) {
    254 
    255                 // Match either _S_egment or _L_ength records
    256                 if ( (strncmp(strline_in, "L:", CDB_REC_START_LEN) == 0) ||
    257                      (strncmp(strline_in, "S:", CDB_REC_START_LEN) == 0)) {
    258 
    259                     // Split string into words separated by spaces
    260                     cols = 0;
    261                     p_str = strtok(strline_in,":$({}),");
    262                     while (p_str != NULL)
    263                     {
    264                         p_words[cols++] = p_str;
    265                         p_str = strtok(NULL, ":$({}),");
    266                         if (cols >= CDB_MAX_SPLIT_WORDS) break;
    267                     }
    268 
    269                     // Linker record (start or end address)
    270                     if ((p_words[0][0] == CDB_REC_L) &&
    271                         (cols == CDB_REC_L_COUNT_MATCH)) {
    272                         // [1] Start/End, [2] Area Name1, [5] Address
    273                         cdb_add_record_linker(p_words[1], p_words[2], p_words[5]);
    274                     }
    275                     // Symbol record (length)
    276                     if ((p_words[0][0] == CDB_REC_S) &&
    277                         (cols == CDB_REC_S_COUNT_MATCH)) {
    278                         // [9] address space, [2] Area Name, [5] Symbol decimal length, [6] DCLType
    279                         cdb_add_record_symbol(p_words[9], p_words[2], p_words[5], p_words[6]);
    280                     }
    281 
    282                 } // end: if valid start of line
    283             } // end: valid min chars to process line
    284         } // end: while still lines to process
    285 
    286         fclose(cdb_file);
    287 
    288         // Process all the symbols
    289         cdb_symbollist_add_all_to_banks();
    290 
    291     } // end: if valid file
    292     else {
    293         log_error("Error: Failed to open input file %s\n", filename_in);
    294         return false;
    295     }
    296 
    297    return true;
    298 }

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.