ref: ba944527ec029c92d84fb6c815e944c634ea8f0d
dir: /src/link/assign.c/
#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> #include "extern/err.h" #include "link/assign.h" #include "link/mylink.h" #include "link/main.h" #include "link/script.h" #include "link/symbol.h" struct sFreeArea { int32_t nOrg; int32_t nSize; struct sFreeArea *pPrev, *pNext; }; struct sSectionAttributes { const char *name; int32_t bank; int32_t offset; // bank + offset = bank originally stored in a section struct int32_t minBank; int32_t bankCount; }; struct sFreeArea *BankFree[MAXBANKS]; int32_t MaxAvail[MAXBANKS]; int32_t MaxBankUsed; int32_t MaxWBankUsed; int32_t MaxSBankUsed; int32_t MaxVBankUsed; const enum eSectionType SECT_MIN = SECT_WRAM0; const enum eSectionType SECT_MAX = SECT_OAM; const struct sSectionAttributes SECT_ATTRIBUTES[] = { {"WRAM0", BANK_WRAM0, 0, 0, BANK_COUNT_WRAM0}, {"VRAM", BANK_VRAM, 0, 0, BANK_COUNT_VRAM}, {"ROMX", BANK_ROMX, -1, 1, BANK_COUNT_ROMX}, {"ROM0", BANK_ROM0, 0, 0, BANK_COUNT_ROM0}, {"HRAM", BANK_HRAM, 0, 0, BANK_COUNT_HRAM}, {"WRAMX", BANK_WRAMX, 0, 0, BANK_COUNT_WRAMX}, {"SRAM", BANK_SRAM, 0, 0, BANK_COUNT_SRAM}, {"OAM", BANK_OAM, 0, 0, BANK_COUNT_OAM} }; #define DOMAXBANK(x, y) {switch (x) { \ case SECT_ROMX: DOMAXRBANK(y); break; \ case SECT_WRAMX: DOMAXWBANK(y); break; \ case SECT_SRAM: DOMAXSBANK(y); break; \ case SECT_VRAM: DOMAXVBANK(y); break; \ default: break; }} #define DOMAXRBANK(x) {if( (x)>MaxBankUsed ) MaxBankUsed=(x);} #define DOMAXWBANK(x) {if( (x)>MaxWBankUsed ) MaxWBankUsed=(x);} #define DOMAXSBANK(x) {if( (x)>MaxSBankUsed ) MaxSBankUsed=(x);} #define DOMAXVBANK(x) {if( (x)>MaxVBankUsed ) MaxVBankUsed=(x);} void ensureSectionTypeIsValid(enum eSectionType type) { if (type < SECT_MIN || type > SECT_MAX) { errx(1, "(INTERNAL) Invalid section type found."); } } int32_t area_Avail(int32_t bank) { int32_t r; struct sFreeArea *pArea; r = 0; pArea = BankFree[bank]; while (pArea) { r += pArea->nSize; pArea = pArea->pNext; } return (r); } int32_t area_doAlloc(struct sFreeArea *pArea, int32_t org, int32_t size) { if (org >= pArea->nOrg && (org + size) <= (pArea->nOrg + pArea->nSize)) { if (org == pArea->nOrg) { pArea->nOrg += size; pArea->nSize -= size; return org; } else { if ((org + size) == (pArea->nOrg + pArea->nSize)) { pArea->nSize -= size; return org; } else { struct sFreeArea *pNewArea; if ((pNewArea = malloc(sizeof(struct sFreeArea))) != NULL) { *pNewArea = *pArea; pNewArea->pPrev = pArea; pArea->pNext = pNewArea; pArea->nSize = org - pArea->nOrg; pNewArea->nOrg = org + size; pNewArea->nSize -= size + pArea->nSize; return org; } else { err(1, NULL); } } } } return -1; } int32_t area_AllocAbs(struct sFreeArea ** ppArea, int32_t org, int32_t size) { struct sFreeArea *pArea; pArea = *ppArea; while (pArea) { int32_t result = area_doAlloc(pArea, org, size); if (result != -1) { return result; } ppArea = &(pArea->pNext); pArea = *ppArea; } return -1; } int32_t area_AllocAbsAnyBank(int32_t org, int32_t size, enum eSectionType type) { ensureSectionTypeIsValid(type); int32_t startBank = SECT_ATTRIBUTES[type].bank; int32_t bankCount = SECT_ATTRIBUTES[type].bankCount; for (int i = 0; i < bankCount; i++) { if (area_AllocAbs(&BankFree[startBank + i], org, size) != -1) { return startBank + i; } } return -1; } int32_t area_Alloc(struct sFreeArea ** ppArea, int32_t size, int32_t alignment) { struct sFreeArea *pArea; if (alignment < 1) { alignment = 1; } pArea = *ppArea; while (pArea) { int32_t org = pArea->nOrg; if (org % alignment) { org += alignment; } org -= org % alignment; int32_t result = area_doAlloc(pArea, org, size); if (result != -1) { return result; } ppArea = &(pArea->pNext); pArea = *ppArea; } return -1; } int32_t area_AllocAnyBank(int32_t size, int32_t alignment, enum eSectionType type) { ensureSectionTypeIsValid(type); int32_t startBank = SECT_ATTRIBUTES[type].bank; int32_t bankCount = SECT_ATTRIBUTES[type].bankCount; for (int i = 0; i < bankCount; i++) { int32_t org = area_Alloc(&BankFree[startBank + i], size, alignment); if (org != -1) { return ((startBank + i) << 16) | org; } } return -1; } struct sSection * FindLargestSection(enum eSectionType type, bool bankFixed) { struct sSection *pSection, *r = NULL; int32_t nLargest = 0; int32_t nLargestAlignment = 0; pSection = pSections; while (pSection) { if (pSection->oAssigned == 0 && pSection->Type == type && (bankFixed ^ (pSection->nBank == -1))) { if (pSection->nAlign > nLargestAlignment || (pSection->nAlign == nLargestAlignment && pSection->nByteSize > nLargest)) { nLargest = pSection->nByteSize; nLargestAlignment = pSection->nAlign; r = pSection; } } pSection = pSection->pNext; } return r; } int IsSectionNameInUse(const char *name) { struct sSection *pSection; pSection = pSections; while (pSection) { if (strcmp(pSection->pzName, name) == 0) return 1; pSection = pSection->pNext; } return 0; } int IsSectionSameTypeBankAndFloating(const char *name, enum eSectionType type, int bank) { struct sSection *pSection; pSection = pSections; while (pSection) { if (pSection->oAssigned == 0) { if (strcmp(pSection->pzName, name) == 0) { /* Section must be floating in source */ if (pSection->nOrg != -1 || pSection->nAlign != 1) return 0; /* It must have the same type in source and linkerscript */ if (pSection->Type != type) return 0; /* Bank number must be unassigned in source or equal */ if (pSection->nBank != -1 && pSection->nBank != bank) return 0; return 1; } } pSection = pSection->pNext; } errx(1, "Section \"%s\" not found (or already used).\n", name); } unsigned int AssignSectionAddressAndBankByName(const char *name, unsigned int address, int bank) { struct sSection *pSection; pSection = pSections; while (pSection) { if (pSection->oAssigned == 0) { if (strcmp(pSection->pzName, name) == 0) { if (pSection->nOrg != -1 || pSection->nAlign != 1) errx(1, "Section \"%s\" from linkerscript isn't floating.\n", name); if (pSection->nBank != -1 && pSection->nBank != bank) errx(1, "Section \"%s\" from linkerscript has different bank number than in the source.\n", name); pSection->nOrg = address; pSection->nBank = bank; pSection->nAlign = -1; return pSection->nByteSize; } } pSection = pSection->pNext; } errx(1, "Section \"%s\" not found (or already used).\n", name); } bool VerifyAndSetBank(struct sSection *pSection) { ensureSectionTypeIsValid(pSection->Type); if (pSection->nBank >= SECT_ATTRIBUTES[pSection->Type].minBank && pSection->nBank < SECT_ATTRIBUTES[pSection->Type].minBank + SECT_ATTRIBUTES[pSection->Type].bankCount) { pSection->nBank += SECT_ATTRIBUTES[pSection->Type].bank + SECT_ATTRIBUTES[pSection->Type].offset; return true; } else { return false; } } void AssignFixedBankSections(enum eSectionType type) { ensureSectionTypeIsValid(type); struct sSection *pSection; while ((pSection = FindLargestSection(type, true))) { if (VerifyAndSetBank(pSection) && (pSection->nOrg = area_Alloc(&BankFree[pSection->nBank], pSection->nByteSize, pSection->nAlign)) != -1) { pSection->oAssigned = 1; DOMAXBANK(pSection->Type, pSection->nBank); } else { if (pSection->nAlign <= 1) { errx(1, "Unable to place '%s' (%s section) in bank $%02lX", pSection->pzName, SECT_ATTRIBUTES[pSection->Type].name, pSection->nBank); } else { errx(1, "Unable to place '%s' (%s section) in bank $%02lX (with $%lX-byte alignment)", pSection->pzName, SECT_ATTRIBUTES[pSection->Type].name, pSection->nBank, pSection->nAlign); } } } } void AssignFloatingBankSections(enum eSectionType type) { ensureSectionTypeIsValid(type); struct sSection *pSection; while ((pSection = FindLargestSection(type, false))) { int32_t org; if ((org = area_AllocAnyBank(pSection->nByteSize, pSection->nAlign, type)) != -1) { if (options & OPT_OVERLAY) { errx(1, "All sections must be fixed when using an overlay file."); } pSection->nOrg = org & 0xFFFF; pSection->nBank = org >> 16; pSection->oAssigned = 1; DOMAXBANK(pSection->Type, pSection->nBank); } else { const char *locality = "anywhere"; if (SECT_ATTRIBUTES[pSection->Type].bankCount > 1) { locality = "in any bank"; } if (pSection->nAlign <= 1) { errx(1, "Unable to place '%s' (%s section) %s", pSection->pzName, SECT_ATTRIBUTES[type].name, locality); } else { errx(1, "Unable to place '%s' (%s section) %s (with $%lX-byte alignment)", pSection->pzName, SECT_ATTRIBUTES[type].name, locality, pSection->nAlign); } } } } char *tzLinkerscriptName = NULL; void SetLinkerscriptName(char *tzLinkerscriptFile) { tzLinkerscriptName = tzLinkerscriptFile; } void AssignSections(void) { int32_t i; struct sSection *pSection; MaxBankUsed = 0; /* * Initialize the memory areas * */ for (i = 0; i < MAXBANKS; i += 1) { BankFree[i] = malloc(sizeof *BankFree[i]); if (!BankFree[i]) { err(1, NULL); } if (i == BANK_ROM0) { /* ROM0 bank */ BankFree[i]->nOrg = 0x0000; if (options & OPT_TINY) { BankFree[i]->nSize = 0x8000; } else { BankFree[i]->nSize = 0x4000; } } else if (i >= BANK_ROMX && i < BANK_ROMX + BANK_COUNT_ROMX) { /* Swappable ROM bank */ BankFree[i]->nOrg = 0x4000; BankFree[i]->nSize = 0x4000; } else if (i == BANK_WRAM0) { /* WRAM */ BankFree[i]->nOrg = 0xC000; if (options & OPT_CONTWRAM) { BankFree[i]->nSize = 0x2000; } else { BankFree[i]->nSize = 0x1000; } } else if (i >= BANK_SRAM && i < BANK_SRAM + BANK_COUNT_SRAM) { /* Swappable SRAM bank */ BankFree[i]->nOrg = 0xA000; BankFree[i]->nSize = 0x2000; } else if (i >= BANK_WRAMX && i < BANK_WRAMX + BANK_COUNT_WRAMX) { /* Swappable WRAM bank */ BankFree[i]->nOrg = 0xD000; BankFree[i]->nSize = 0x1000; } else if (i >= BANK_VRAM && i < BANK_VRAM + BANK_COUNT_VRAM) { /* Swappable VRAM bank */ BankFree[i]->nOrg = 0x8000; if (options & OPT_DMG_MODE && i != BANK_VRAM) { BankFree[i]->nSize = 0; } else { BankFree[i]->nSize = 0x2000; } } else if (i == BANK_OAM) { BankFree[i]->nOrg = 0xFE00; BankFree[i]->nSize = 0x00A0; } else if (i == BANK_HRAM) { /* HRAM */ BankFree[i]->nOrg = 0xFF80; BankFree[i]->nSize = 0x007F; } else { errx(1, "(INTERNAL) Unknown bank type!"); } MaxAvail[i] = BankFree[i]->nSize; BankFree[i]->pPrev = NULL; BankFree[i]->pNext = NULL; } /* * First, let's parse the linkerscript. * */ if (tzLinkerscriptName) { script_InitSections(); script_Parse(tzLinkerscriptName); } /* * Second, let's assign all the fixed sections... * */ pSection = pSections; while (pSection) { if ((pSection->nOrg != -1 || pSection->nBank != -1) && pSection->oAssigned == 0) { /* User wants to have a say... */ switch (pSection->Type) { case SECT_WRAM0: case SECT_HRAM: case SECT_ROM0: case SECT_OAM: pSection->nBank = SECT_ATTRIBUTES[pSection->Type].bank; if (area_AllocAbs(&BankFree[pSection->nBank], pSection->nOrg, pSection->nByteSize) == -1) { errx(1, "Unable to place '%s' (%s section) at $%lX", pSection->pzName, SECT_ATTRIBUTES[pSection->Type].name, pSection->nOrg); } pSection->oAssigned = 1; break; case SECT_SRAM: case SECT_WRAMX: case SECT_VRAM: case SECT_ROMX: if (pSection->nBank != -1 && pSection->nOrg != -1) { if (VerifyAndSetBank(pSection) && area_AllocAbs(&BankFree[pSection->nBank], pSection->nOrg, pSection->nByteSize) != -1) { DOMAXBANK(pSection->Type, pSection->nBank); pSection->oAssigned = 1; } else { errx(1, "Unable to place '%s' (%s section) at $%lX in bank $%02lX", pSection->pzName, SECT_ATTRIBUTES[pSection->Type].name, pSection->nOrg, pSection->nBank); } } break; } } pSection = pSection->pNext; } /* * Next, let's assign all the bankfixed ONLY sections... * */ for (enum eSectionType i = SECT_MIN; i <= SECT_MAX; i++) { AssignFixedBankSections(i); } /* * Now, let's assign all the floating bank but fixed ROMX sections... * */ pSection = pSections; while (pSection) { if (pSection->oAssigned == 0 && pSection->nOrg != -1 && pSection->nBank == -1) { if (options & OPT_OVERLAY) { errx(1, "All sections must be fixed when using an overlay file."); } switch (pSection->Type) { case SECT_ROMX: case SECT_VRAM: case SECT_SRAM: case SECT_WRAMX: if ((pSection->nBank = area_AllocAbsAnyBank(pSection->nOrg, pSection->nByteSize, pSection->Type)) == -1) { errx(1, "Unable to place '%s' (%s section) at $%lX in any bank", pSection->pzName, SECT_ATTRIBUTES[pSection->Type].name, pSection->nOrg); } pSection->oAssigned = 1; DOMAXBANK(pSection->Type, pSection->nBank); break; default: // Handle other sections later break; } } pSection = pSection->pNext; } /* * OK, all that nasty stuff is done so let's assign all the other * sections * */ for (enum eSectionType i = SECT_MIN; i <= SECT_MAX; i++) { AssignFloatingBankSections(i); } } void CreateSymbolTable(void) { struct sSection *pSect; sym_Init(); pSect = pSections; while (pSect) { int32_t i; i = pSect->nNumberOfSymbols; while (i--) { if ((pSect->tSymbols[i]->Type == SYM_EXPORT) && ((pSect->tSymbols[i]->pSection == pSect) || (pSect->tSymbols[i]->pSection == NULL))) { if (pSect->tSymbols[i]->pSection == NULL) sym_CreateSymbol( pSect->tSymbols[i]->pzName, pSect->tSymbols[i]->nOffset, -1, pSect->tSymbols[i]->pzObjFileName, pSect->tSymbols[i]->pzFileName, pSect->tSymbols[i]->nFileLine); else sym_CreateSymbol( pSect->tSymbols[i]->pzName, pSect->nOrg + pSect->tSymbols[i]->nOffset, pSect->nBank, pSect->tSymbols[i]->pzObjFileName, pSect->tSymbols[i]->pzFileName, pSect->tSymbols[i]->nFileLine); } } pSect = pSect->pNext; } }