/* address.c: Functions for identifying and handling memory addresses. * Copyright (C) 1999,2011 by Brian Raiter * License GPLv2+: GNU GPL version 2 or later. * This is free software; you are free to change and redistribute it. * There is NO WARRANTY, to the extent permitted by law. */ #include #include #include #include "gen.h" #include "outbase.h" #include "outitems.h" #include "address.h" /* Information collected on each address segment. */ struct addrinfo { long address; /* the address of a segment */ long from; /* the lowest address used in this segment */ long to; /* the highest address used in this segment */ int count; /* how many addresses are in this segment */ int maxchunk; /* size of the largest single chunk in this segment */ char name[64]; /* the preprocessor name assigned to this segment */ }; /* The list of memory segments. */ static struct addrinfo *addrs = NULL; static int addrcount = 0; /* Adds an address to the collection. addr is the address, offset is * the (presumed) offset within the memory segment, size is the size * of the chunk of memory starting at that address, and name is the * name associated with that chunk. The largest chunk in that memory * address will determine which name is used to name the memory * segment. */ void recordaddress(long address, long offset, long size, char const *name) { static int allocated = 0; long base; int i; base = address - offset; for (i = 0 ; i < addrcount ; ++i) if (addrs[i].address == base) break; if (i == addrcount) { if (addrcount == allocated) { allocated = allocated ? 2 * allocated : 4; addrs = reallocate(addrs, allocated * sizeof *addrs); memset(addrs + addrcount, 0, (allocated - addrcount) * sizeof *addrs); } ++addrcount; addrs[i].address = base; } ++addrs[i].count; if (!*addrs[i].name || addrs[i].maxchunk < size) { strcpy(addrs[i].name, name); addrs[i].maxchunk = size; } if (!addrs[i].from || addrs[i].from > address) addrs[i].from = address; if (addrs[i].to < address + size) addrs[i].to = address + size; } /* Finalizes the list of addresses. This function weeds through the * collection of memory segments and picks out the ones that are * likely to be real (i.e., the ones that are used in more than one * place and/or begin on a page boundary). These addresses are then * assigned unique names. */ void setaddressnames(void) { char *str; int len; int i, j, n; for (i = 0 ; i < addrcount ; ) { if (addrs[i].count == 1 && (addrs[i].address & 0x0FFF) > 0) { addrs[i] = addrs[addrcount]; --addrcount; } else { ++i; } } for (i = 0 ; i < addrcount ; ++i) { str = addrs[i].name; while (*str && !isalnum(*str)) ++str; memmove(addrs[i].name + 5, str, strlen(str) + 1); memcpy(addrs[i].name, "ADDR_", 5); for (str = addrs[i].name + 5 ; *str ; ++str) *str = isalnum(*str) ? toupper(*str) : '_'; } for (i = 0 ; i < addrcount - 1 ; ++i) { n = 1; len = strlen(addrs[i].name); for (j = i + 1 ; j < addrcount ; ++j) if (!memcmp(addrs[j].name, addrs[i].name, len + 1)) sprintf(addrs[j].name + len, "%d", ++n); if (n > 1) strcpy(addrs[i].name + len, "1"); } } /* Returns the name and base address of the segment that contains the * given address. */ char const *getbaseaddress(long address, long *base) { struct addrinfo const *candidate; int i; candidate = NULL; for (i = 0 ; i < addrcount ; ++i) { if (!candidate || addrs[i].address > candidate->address) if (addrs[i].from <= address && address < addrs[i].to) candidate = addrs + i; } if (!candidate) { for (i = 0 ; i < addrcount ; ++i) { if (address == addrs[i].to) { candidate = addrs + i; break; } } } if (candidate) { if (base) *base = candidate->address; return candidate->address ? candidate->name : ""; } else { if (base) *base = 0; return NULL; } } /* Translates a memory address back into a file offset. If the address * does not fall within any of the recorded memory segments, it is * assumed to already be a valid offset. */ long getaddressoffset(long address) { int i; for (i = 0 ; i < addrcount ; ++i) if (addrs[i].from <= address && address < addrs[i].to) return address - addrs[i].address; return address; } /* Outputs the list of recorded addresses as a series of macro * definitions. */ void outputaddresses(void) { int width; int n, i; width = 0; for (i = 0 ; i < addrcount ; ++i) { if (addrs[i].address && addrs[i].name) { n = strlen(addrs[i].name); if (width < n) width = n; } } if (width == 0) return; for (i = 0 ; i < addrcount ; ++i) if (addrs[i].address && addrs[i].name) outf("#define %-*s 0x%08lX\n", width, addrs[i].name, addrs[i].address); out("\n"); }