697 lines
20 KiB
C
697 lines
20 KiB
C
/*
|
|
* Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <dirent.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
|
|
#include "../shared/util.h"
|
|
|
|
|
|
#define BUFFER_SIZE 4096 // If you have a line of code longer than this, you deserve the crash.
|
|
#define BLOCK_COUNT 15 // Could be up to 56 blocks, 8-64. f256.ld needs updated for more.
|
|
|
|
|
|
typedef struct EmbedS {
|
|
char *name;
|
|
char *filename;
|
|
uint64_t address;
|
|
struct EmbedS *next;
|
|
} EmbedT;
|
|
|
|
|
|
unsigned char _currentBank = 0; // 0 == lower 64k
|
|
unsigned char _totalBanks = 0; // Not including lower 64k
|
|
char *_bankName[BLOCK_COUNT]; // Far blocks (8+), shifted down 8.
|
|
|
|
EmbedT *_embeds = NULL;
|
|
EmbedT *_embedsHead = NULL;
|
|
|
|
|
|
void findBank(char *name);
|
|
void parseCFile(char *filename, char *targetFile, FILE *trampoline, char *trampolineFile, int swapSlot);
|
|
void trimEnd(char *string);
|
|
|
|
|
|
void findBank(char *name) {
|
|
int x;
|
|
|
|
printf("Found Segment: %s\n", name);
|
|
|
|
// Is this the MAIN segment?
|
|
if (strcmp(name, "MAIN") == 0) {
|
|
_currentBank = 0;
|
|
return;
|
|
}
|
|
|
|
// Do we know this segment already?
|
|
for (x=0; x<_totalBanks; x++) {
|
|
if (strcmp(name, _bankName[x]) == 0) {
|
|
_currentBank = x + 8;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// We have space?
|
|
if (_totalBanks == BLOCK_COUNT) {
|
|
fprintf(stderr, "Too many segments!\n");
|
|
exit(1);
|
|
}
|
|
|
|
// Add this new segment!
|
|
_bankName[_totalBanks++] = strdup(name);
|
|
_currentBank = _totalBanks + 7;
|
|
}
|
|
|
|
|
|
void parseCFile(char *filename, char *targetFile, FILE *trampoline, char *trampolineFile, int swapSlot) {
|
|
FILE *in;
|
|
FILE *out;
|
|
int c;
|
|
char arguments[BUFFER_SIZE];
|
|
char buffer[BUFFER_SIZE];
|
|
char *functionName;
|
|
char *start;
|
|
char *a;
|
|
char *b;
|
|
char *d;
|
|
char *e;
|
|
char *f;
|
|
bool isPointer;
|
|
bool found;
|
|
int x;
|
|
int line = 1;
|
|
int comments = 0;
|
|
bool inComment = false;
|
|
int brackets = 0;
|
|
int crSinceStart = 0;
|
|
int pos = 0;
|
|
char *segDef = "#define SEGMENT_";
|
|
EmbedT *newEmbed;
|
|
|
|
/*
|
|
* This parser sucks.
|
|
*
|
|
* - It only handles C, not C++.
|
|
* - It can only handle single-line function definitions with the opening '{' on the same line as the function.
|
|
* - It always generates trampolines, not just when they're needed.
|
|
* - Comments after function definitions will probably break it.
|
|
* - Comments after SEGMENT defines WILL break it.
|
|
*
|
|
* Someone should fix it. :-)
|
|
*/
|
|
|
|
// Always start in the lower 64k.
|
|
_currentBank = 0;
|
|
|
|
in = fopen(filename, "rt");
|
|
if (in == NULL) {
|
|
fclose(trampoline);
|
|
fprintf(stderr, "Cannot read %s!\n", filename);
|
|
exit(1);
|
|
}
|
|
|
|
out = fopen(targetFile, "wt");
|
|
if (out == NULL) {
|
|
fclose(in);
|
|
fclose(trampoline);
|
|
fprintf(stderr, "Cannot create %s!\n", targetFile);
|
|
free(targetFile);
|
|
exit(1);
|
|
}
|
|
|
|
printf("Processing %s -> %s...\n", filename, targetFile);
|
|
|
|
// Add trampoline include automatically.
|
|
fprintf(out, "#include \"%s\"\n\n", trampolineFile);
|
|
|
|
do {
|
|
// Read next byte from C input file.
|
|
if ((c = fgetc(in)) == EOF) break;
|
|
|
|
if (pos > 0) {
|
|
// Look for '//' comments.
|
|
if ((c == '/') && (pos > 0) && (buffer[pos-1] == '/')) inComment = true;
|
|
|
|
// Look for '/*' comment start.
|
|
if ((c == '*') && (pos > 0) && (buffer[pos-1] == '/') && !inComment) comments++;
|
|
|
|
// Look for '*/' comment end.
|
|
if ((c == '/') && (pos > 0) && (buffer[pos-1] == '*') && !inComment) comments--;
|
|
}
|
|
|
|
// Count brackets so we know if we're inside a function or not.
|
|
if ((!inComment) && (comments == 0)) {
|
|
if (c == '{') {
|
|
brackets++;
|
|
crSinceStart = 0;
|
|
}
|
|
if (c == '}') brackets--;
|
|
}
|
|
|
|
// End of line?
|
|
if ((c == 13) || (c == 10)) {
|
|
|
|
line++;
|
|
|
|
inComment = false;
|
|
crSinceStart++;
|
|
|
|
// End the line and trim the tail.
|
|
buffer[pos] = 0;
|
|
trimEnd(buffer);
|
|
|
|
if ((brackets == 1) && (crSinceStart == 1)) {
|
|
|
|
// Is the last character a '{'? If we're not in a far memory bank, short-circuit this entire mess.
|
|
if ((_currentBank > 0) && (buffer[strlen(buffer) - 1] == '{')) {
|
|
// Remove it and any spaces.
|
|
buffer[strlen(buffer) - 1] = 0;
|
|
trimEnd(buffer);
|
|
// Functions end with a closing parenthesis.
|
|
if (buffer[strlen(buffer) - 1] == ')') {
|
|
// Found function! Scan forward to find the '(' after the function name.
|
|
b = buffer;
|
|
while (*b != '(') b++;
|
|
// Now go backwards and look for a space.
|
|
start = b;
|
|
while (*start != ' ') start--;
|
|
// Are they returning a pointer?
|
|
isPointer = false;
|
|
if (*(start+1) == '*') {
|
|
isPointer = true;
|
|
++start;
|
|
*start++ = 0;
|
|
} else {
|
|
*start++ = 0;
|
|
isPointer = false;
|
|
}
|
|
|
|
// 'start' is now at the beginning of the function name.
|
|
// 'buffer' is truncated after the return type.
|
|
// 'b' is on the argument opening parenthesis.
|
|
// 'isPointer' is if the return value is a pointer or not.
|
|
|
|
// Yank the function name out.
|
|
*b = 0;
|
|
functionName = strdup(start);
|
|
*b = '(';
|
|
|
|
// Find argument names by scanning from 'b'+1 until we hit a
|
|
// comma or closing parenthesis. Then back up until we find
|
|
// a space, comma, or opening parenthesis.
|
|
b++;
|
|
x = 0;
|
|
found = false;
|
|
while (!found) {
|
|
while ((*b != ',') && (*b != ')')) b++;
|
|
if (*b == ')') found = true;
|
|
a = b - 1;
|
|
while ((*a != '(') && (*a != ',') && (*a != ' ')) a--;
|
|
a++;
|
|
while (a < b) {
|
|
arguments[x++] = *a;
|
|
a++;
|
|
}
|
|
if (!found) {
|
|
arguments[x++] = ',';
|
|
arguments[x++] = ' ';
|
|
}
|
|
b++;
|
|
}
|
|
arguments[x++] = 0;
|
|
trimEnd(arguments); //***TODO*** Should also trim start.
|
|
if (strcmp(arguments, "void") == 0) arguments[0] = 0;
|
|
|
|
// Create trampoline function.
|
|
fprintf(out, "#pragma clang optimize off\n");
|
|
fprintf(out, "__attribute__((noinline))\n");
|
|
fprintf(out, "%s%c%s {\n", buffer, isPointer ? '*' : ' ', start);
|
|
fprintf(out, "\tvolatile unsigned char ___mmu = (unsigned char)*(volatile unsigned char *)%#06x;\n", swapSlot);
|
|
if (strcmp(buffer, "void") != 0) fprintf(out, "\t%s%cr;\n", buffer, isPointer ? '*' : ' ');
|
|
fprintf(out, "\t*(volatile unsigned char *)%#06x = %d;\n", swapSlot, _currentBank);
|
|
fprintf(out, "\t");
|
|
if (strcmp(buffer, "void") != 0) fprintf(out, "r = ");
|
|
fprintf(out, "FAR%d_%s(%s);\n", _currentBank, functionName, arguments);
|
|
fprintf(out, "\t*(volatile unsigned char *)%#06x = ___mmu;\n", swapSlot);
|
|
if (strcmp(buffer, "void") != 0) fprintf(out, "\treturn r;");
|
|
fprintf(out, "}\n");
|
|
fprintf(out, "#pragma clang optimize on\n\n");
|
|
|
|
// Write out new function definition.
|
|
fprintf(out, "__attribute__((noinline, section(\".block%d\")))\n", _currentBank);
|
|
fprintf(out, "%s%cFAR%d_%s {\n", buffer, isPointer ? '*' : ' ', _currentBank, start);
|
|
|
|
// Create trampoline prototype.
|
|
fprintf(trampoline, "%s%cFAR%d_%s;\n", buffer, isPointer ? '*' : ' ', _currentBank, start);
|
|
|
|
free(functionName);
|
|
} else {
|
|
// Not a function. Write as is. (Put the '{' back.)
|
|
fprintf(out, "%s {\n", buffer);
|
|
}
|
|
} else {
|
|
// Didn't find '{'.
|
|
fprintf(out, "%s\n", buffer);
|
|
}
|
|
|
|
} else { // Brackets
|
|
|
|
// Is this a segment definition? "#define SEGMENT_<something>"
|
|
// ***TODO*** This doesn't allow the definition to be indented.
|
|
found = false;
|
|
if (pos > strlen(segDef)) {
|
|
found = true;
|
|
for (x=0; x<strlen(segDef); x++) {
|
|
if (buffer[x] != segDef[x]) {
|
|
found = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (found) {
|
|
// New segment!
|
|
findBank(&buffer[strlen(segDef)]);
|
|
}
|
|
|
|
// Is this an EMBED? "EMBED(name, file, address);"
|
|
start = strstr(buffer, "EMBED");
|
|
if (start != NULL) {
|
|
// Scan for opening paren, three arguments.
|
|
// We use 'c' to track what we're currently looking for.
|
|
c = 0;
|
|
a = NULL; // Start of first argument.
|
|
b = NULL; // End of first argument.
|
|
d = NULL; // Start of second argument.
|
|
e = NULL; // End of second argument.
|
|
f = NULL; // Start of third argument.
|
|
found = false; // This is tracking if we're quoted or not.
|
|
for (x=0; x<strlen(buffer); x++) {
|
|
switch (c) {
|
|
case 0: // Opening parenthesis.
|
|
if (buffer[x] == '(') c++;
|
|
break;
|
|
case 1: // Start of first argument.
|
|
if (buffer[x] != ' ') {
|
|
a = &buffer[x];
|
|
c++;
|
|
}
|
|
break;
|
|
case 2: // End of first argument.
|
|
if ((buffer[x] == ' ') || (buffer[x] == ',')) {
|
|
b = &buffer[x];
|
|
c++;
|
|
if (buffer[x] == ',') c++;
|
|
}
|
|
break;
|
|
case 3: // Comma at end of first argument.
|
|
if (buffer[x] == ',') c++;
|
|
break;
|
|
case 4: // Filename to embed.
|
|
if (buffer[x] == '"') {
|
|
// ***TODO*** This will barf if the filename has a " in it.
|
|
if (!found) {
|
|
d = &buffer[x+1];
|
|
} else {
|
|
e = &buffer[x];
|
|
}
|
|
found = !found;
|
|
} else {
|
|
if ((!found) && (buffer[x] == ',')) c++;
|
|
}
|
|
break;
|
|
case 5: // Address to load.
|
|
if (buffer[x] != ' ') {
|
|
f = &buffer[x];
|
|
c++;
|
|
}
|
|
break;
|
|
}
|
|
if (c == 6) break; // Exit FOR loop.
|
|
}
|
|
// Did we find what we needed?
|
|
if (c == 6) {
|
|
newEmbed = (EmbedT *)calloc(1, sizeof(EmbedT));
|
|
// Name.
|
|
x = *b;
|
|
*b = 0;
|
|
newEmbed->name = strdup(a);
|
|
*b = x;
|
|
// Filename.
|
|
x = *e;
|
|
*e = 0;
|
|
newEmbed->filename = strdup(d);
|
|
*e = x;
|
|
// Address.
|
|
newEmbed->address = strtol(f, NULL, 0);
|
|
// Add to list.
|
|
if (_embeds == NULL) {
|
|
_embeds = newEmbed;
|
|
_embedsHead = _embeds;
|
|
} else {
|
|
_embedsHead->next = newEmbed;
|
|
_embedsHead = newEmbed;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write to output.
|
|
fprintf(out, "%s\n", buffer);
|
|
|
|
} // Brackets.
|
|
|
|
// Reset buffer for next line.
|
|
pos = 0;
|
|
|
|
// Makes debugging nicer.
|
|
fflush(out);
|
|
|
|
} else { // EOL
|
|
|
|
// Add to buffer.
|
|
buffer[pos++] = c;
|
|
|
|
} // EOL
|
|
|
|
} while (1);
|
|
|
|
fclose(in);
|
|
}
|
|
|
|
|
|
void trimEnd(char *string) {
|
|
int x;
|
|
|
|
// Trim end of line.
|
|
for (x=strlen(string)-1; x>0; x--) {
|
|
if ((string[x] == ' ') || (string[x] == 9)) {
|
|
string[x] = 0;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
FILE *out;
|
|
FILE *out2;
|
|
FILE *out3;
|
|
FILE *in;
|
|
int x;
|
|
int r;
|
|
int nearSlot;
|
|
char *targetDir;
|
|
char *sourceDir;
|
|
char *trampolineFile;
|
|
char *linkerFile;
|
|
char *linkerFile2;
|
|
char *linkerFile3;
|
|
char *cFile;
|
|
char *targetFile;
|
|
char *thisDir;
|
|
int sourceDirOffset;
|
|
int thisOffset;
|
|
uint64_t length;
|
|
DIR *dir;
|
|
struct dirent *dirent;
|
|
struct stat fileStat;
|
|
char *generated = "This is generated by the overlay tool - DO NOT EDIT.";
|
|
|
|
/*
|
|
* Command line:
|
|
*
|
|
* ./overlay [nearSlot#] [targetDir] [sourceDir1] ... {sourceDirX}
|
|
*
|
|
*/
|
|
|
|
if (argc < 4) {
|
|
printf("Usage: %s [nearSlot#] [targetDir] [sourceDir1] ... {sourceDirX}\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
// Find near memory slot to swap into.
|
|
nearSlot = atoi(argv[1]);
|
|
if (nearSlot > 7) {
|
|
printf("ERROR! Only slots 0-7 are in near memory.\n");
|
|
return 1;
|
|
}
|
|
if (nearSlot == 0) {
|
|
printf("WARNING! Slot 0 contains the zero page, stack, and MMU!\n");
|
|
}
|
|
if ((nearSlot == 6) || (nearSlot == 7)) {
|
|
printf("WARNING! Slots 6 and 7 contain the microkernel!\n");
|
|
}
|
|
|
|
// Does the target directory exist?
|
|
targetDir = strdup(argv[2]);
|
|
utilFixPathSeparators(&targetDir, false);
|
|
if (!utilMkDirP(targetDir, 0777)) {
|
|
printf("ERROR! Cannot create target directory %s!\n", targetDir);
|
|
return 1;
|
|
}
|
|
|
|
// Create trampoline for all C files.
|
|
trampolineFile = utilCreateString("%s%ctrampoline.h", targetDir, utilGetPathSeparator());
|
|
out = fopen(trampolineFile, "wt");
|
|
if (out == NULL) {
|
|
fprintf(stderr, "ERROR! Cannot create %s!\n", trampolineFile);
|
|
free(trampolineFile);
|
|
free(targetDir);
|
|
return 1;
|
|
}
|
|
fprintf(out, "// %s\n\n", generated);
|
|
fprintf(out, "#ifndef TRAMPOLINE_H\n");
|
|
fprintf(out, "#define TRAMPOLINE_H\n\n");
|
|
|
|
// Find common inital path so we can remove it later.
|
|
sourceDir = strdup(argv[3]);
|
|
utilFixPathSeparators(&sourceDir, true);
|
|
sourceDirOffset = strlen(sourceDir) - 1;
|
|
if (argc > 4) {
|
|
for (x=4; x<argc; x++) {
|
|
thisOffset = 0;
|
|
thisDir = strdup(argv[x]);
|
|
utilFixPathSeparators(&thisDir, true);
|
|
for (thisOffset=0; thisOffset < (strlen(sourceDir) < strlen(thisDir) ? strlen(sourceDir) : strlen(thisDir)); thisOffset++) {
|
|
if (sourceDir[thisOffset] != thisDir[thisOffset]) break;
|
|
}
|
|
if (thisOffset < sourceDirOffset) sourceDirOffset = thisOffset - 1;
|
|
free(thisDir);
|
|
}
|
|
}
|
|
free(sourceDir);
|
|
|
|
// Do all the C files in a project, one at a time.
|
|
for (x=3; x<argc; x++) {
|
|
sourceDir = strdup(argv[x]);
|
|
utilFixPathSeparators(&sourceDir, true);
|
|
if ((dir = opendir(sourceDir)) == NULL) {
|
|
fprintf(stderr, "ERROR! Cannot open directory %s!\n", argv[x]);
|
|
free(trampolineFile);
|
|
free(targetDir);
|
|
free(sourceDir);
|
|
return 1;
|
|
}
|
|
for (;;) {
|
|
if ((dirent = readdir(dir)) == NULL) break;
|
|
cFile = utilCreateString("%s%s", sourceDir, dirent->d_name); // May not be a C file. We check later.
|
|
r = stat(cFile, &fileStat);
|
|
if (r < 0) printf("stat() failed with %d! %s\n", r, strerror(errno));
|
|
#ifdef _WIN32
|
|
//printf("%s = %d\n", dirent->d_name, S_ISREG(fileStat.st_mode));
|
|
if (S_ISREG(fileStat.st_mode)) {
|
|
#else
|
|
if (S_ISREG(fileStat.st_mode) || S_ISLNK(fileStat.st_mode)) {
|
|
#endif
|
|
// Is this a C file?
|
|
if ((dirent->d_name[strlen(dirent->d_name) - 2] == '.') && (dirent->d_name[strlen(dirent->d_name) - 1] == 'c')) {
|
|
targetFile = utilCreateString("%s%s%s", targetDir, &sourceDir[sourceDirOffset], dirent->d_name);
|
|
thisDir = utilGetUpToLastPathComponent(targetFile);
|
|
utilFixPathSeparators(&thisDir, false);
|
|
if (!utilMkDirP(thisDir, 0777)) {
|
|
printf("ERROR! Cannot create target directory %s!\n", thisDir);
|
|
free(thisDir);
|
|
free(trampolineFile);
|
|
free(targetDir);
|
|
free(sourceDir);
|
|
free(cFile);
|
|
closedir(dir);
|
|
return 1;
|
|
}
|
|
free(thisDir);
|
|
parseCFile(cFile, targetFile, out, trampolineFile, nearSlot + 8);
|
|
//printf("%s --> %s\n", cFile, targetFile);
|
|
free(targetFile);
|
|
}
|
|
}
|
|
free(cFile);
|
|
}
|
|
closedir(dir);
|
|
free(sourceDir);
|
|
}
|
|
|
|
fprintf(out, "\n#endif // TRAMPOLINE_H\n");
|
|
fclose(out);
|
|
free(trampolineFile);
|
|
|
|
// Generate linker script declaration data.
|
|
linkerFile = utilCreateString("%s%cdeclare.ld", targetDir, utilGetPathSeparator());
|
|
out = fopen(linkerFile, "wt");
|
|
if (out == NULL) {
|
|
fprintf(stderr, "ERROR! Cannot create %s!\n", linkerFile);
|
|
free(targetDir);
|
|
free(linkerFile);
|
|
return 1;
|
|
}
|
|
|
|
// Generate linker script MEMORY data.
|
|
linkerFile2 = utilCreateString("%s%cmemory.ld", targetDir, utilGetPathSeparator());
|
|
out2 = fopen(linkerFile2, "wt");
|
|
if (out2 == NULL) {
|
|
fprintf(stderr, "ERROR! Cannot create %s!\n", linkerFile2);
|
|
free(targetDir);
|
|
free(linkerFile);
|
|
free(linkerFile2);
|
|
fclose(out);
|
|
return 1;
|
|
}
|
|
|
|
// Generate linker script SECTIONS data.
|
|
linkerFile3 = utilCreateString("%s%csections.ld", targetDir, utilGetPathSeparator());
|
|
out3 = fopen(linkerFile3, "wt");
|
|
if (out3 == NULL) {
|
|
fprintf(stderr, "ERROR! Cannot create %s!\n", linkerFile3);
|
|
free(targetDir);
|
|
free(linkerFile);
|
|
free(linkerFile2);
|
|
free(linkerFile3);
|
|
fclose(out);
|
|
fclose(out2);
|
|
return 1;
|
|
}
|
|
|
|
fprintf(out, "/* %s */\n\n", generated);
|
|
fprintf(out2, "/* %s */\n\n", generated);
|
|
fprintf(out3, "/* %s */\n\n", generated);
|
|
|
|
// Populate three linker sections.
|
|
_embedsHead = _embeds;
|
|
while (_embedsHead != NULL) {
|
|
// Declaration.
|
|
length = 0;
|
|
// Try to open embedded file relative to each provided directory.
|
|
// This includes the target directory.
|
|
for (x=2; x<argc; x++) {
|
|
sourceDir = strdup(argv[x]);
|
|
utilFixPathSeparators(&sourceDir, true);
|
|
targetFile = utilCreateString("%s%s", sourceDir, _embedsHead->filename);
|
|
printf("Checking [%s]\n", targetFile);
|
|
free(sourceDir);
|
|
in = fopen(targetFile, "rb");
|
|
free(targetFile);
|
|
if (in != NULL) {
|
|
fseek(in, 0, SEEK_END);
|
|
length = ftell(in) + 1; // Without the +1 the linker will overflow the segment.
|
|
fclose(in);
|
|
break;
|
|
}
|
|
}
|
|
if (length == 0) {
|
|
fprintf(stderr, "ERROR! Cannot locate %s!\n", _embedsHead->filename);
|
|
free(targetDir);
|
|
free(linkerFile);
|
|
free(linkerFile2);
|
|
free(linkerFile3);
|
|
fclose(out);
|
|
fclose(out2);
|
|
fclose(out3);
|
|
return 1;
|
|
}
|
|
fprintf(out, " __%s_lma = 0x%lx;\n", _embedsHead->name, _embedsHead->address);
|
|
fprintf(out, " __%s_size = 0x%lx;\n\n", _embedsHead->name, length);
|
|
// MEMORY.
|
|
fprintf(out2, " %s : ORIGIN = __%s_lma, LENGTH = __%s_size\n", _embedsHead->name, _embedsHead->name, _embedsHead->name);
|
|
// SECTIONS.
|
|
fprintf(out3, " .%s : { *(.%s .%s*) } >%s end_%s = .;\n", _embedsHead->name, _embedsHead->name, _embedsHead->name, _embedsHead->name, _embedsHead->name);
|
|
_embedsHead = _embedsHead->next;
|
|
}
|
|
|
|
free(linkerFile);
|
|
free(linkerFile2);
|
|
free(linkerFile3);
|
|
fclose(out);
|
|
fclose(out2);
|
|
fclose(out3);
|
|
|
|
// Generate linker script OUTPUT_FORMAT data.
|
|
linkerFile = utilCreateString("%s%coutput.ld", targetDir, utilGetPathSeparator());
|
|
out = fopen(linkerFile, "wt");
|
|
if (out == NULL) {
|
|
fprintf(stderr, "ERROR! Cannot create %s!\n", linkerFile);
|
|
free(targetDir);
|
|
free(linkerFile);
|
|
return 1;
|
|
}
|
|
fprintf(out, "/* %s */\n\n", generated);
|
|
for (x=0; x<_totalBanks; x++) {
|
|
fprintf(out, " SHORT(%d*0x2000)\n", x+8);
|
|
fprintf(out, " BYTE(%d/8)\n", x+8);
|
|
fprintf(out, " SHORT(end_block%d - __block%d_lma)\n", x+8, x+8);
|
|
fprintf(out, " BYTE(0x00)\n");
|
|
fprintf(out, " TRIM(block%d)\n\n", x+8);
|
|
}
|
|
_embedsHead = _embeds;
|
|
while (_embedsHead != NULL) {
|
|
fprintf(out, " SHORT(ORIGIN(%s))\n", _embedsHead->name);
|
|
fprintf(out, " BYTE(ORIGIN(%s)>>16)\n", _embedsHead->name);
|
|
fprintf(out, " SHORT(end_%s - __%s_lma)\n", _embedsHead->name, _embedsHead->name);
|
|
fprintf(out, " BYTE((end_%s - __%s_lma)>>16)\n", _embedsHead->name, _embedsHead->name);
|
|
fprintf(out, " TRIM(%s)\n\n", _embedsHead->name);
|
|
_embedsHead = _embedsHead->next;
|
|
}
|
|
fclose(out);
|
|
free(linkerFile);
|
|
|
|
// Clean up.
|
|
for (x=0; x<_totalBanks; x++) {
|
|
free(_bankName[x]);
|
|
}
|
|
free(targetDir);
|
|
_embedsHead = _embeds;
|
|
while (_embedsHead != NULL) {
|
|
free(_embedsHead->name);
|
|
free(_embedsHead->filename);
|
|
_embeds = _embedsHead;
|
|
_embedsHead = _embedsHead->next;
|
|
free(_embeds);
|
|
}
|
|
|
|
return 0;
|
|
}
|