Working on code auto-overlay utility.
This commit is contained in:
parent
c49e3289e1
commit
a5ef53c7b6
5 changed files with 311 additions and 1 deletions
22
tools/overlay/CMakeLists.txt
Normal file
22
tools/overlay/CMakeLists.txt
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
|
||||||
|
project(overlay LANGUAGES C)
|
||||||
|
|
||||||
|
#set(HEADERS )
|
||||||
|
#list(TRANSFORM HEADERS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/include/")
|
||||||
|
|
||||||
|
set(SOURCE
|
||||||
|
overlay.c
|
||||||
|
)
|
||||||
|
list(TRANSFORM SOURCE PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/src/")
|
||||||
|
|
||||||
|
add_executable(${CMAKE_PROJECT_NAME}
|
||||||
|
# ${HEADERS}
|
||||||
|
${SOURCE}
|
||||||
|
../shared/util.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../shared
|
||||||
|
)
|
265
tools/overlay/src/overlay.c
Normal file
265
tools/overlay/src/overlay.c
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
/*
|
||||||
|
* 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 "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.
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
void findBank(char *name);
|
||||||
|
void parseCFile(char *filename, FILE *trampoline, int swapSlot);
|
||||||
|
void trimEnd(char *string);
|
||||||
|
|
||||||
|
|
||||||
|
void findBank(char *name) {
|
||||||
|
int x;
|
||||||
|
|
||||||
|
// 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, FILE *trampoline, int swapSlot) {
|
||||||
|
FILE *in;
|
||||||
|
FILE *out;
|
||||||
|
int c;
|
||||||
|
int brackets = 0;
|
||||||
|
int pos = 0;
|
||||||
|
char buffer[BUFFER_SIZE];
|
||||||
|
char *temp;
|
||||||
|
char *newCFile;
|
||||||
|
char *start;
|
||||||
|
char *b;
|
||||||
|
bool found;
|
||||||
|
char *segDef = "#define SEGMENT_";
|
||||||
|
int x;
|
||||||
|
|
||||||
|
temp = utilGetLastPathComponent(filename);
|
||||||
|
newCFile = utilReplaceExtension(temp, ".tc");
|
||||||
|
free(temp);
|
||||||
|
|
||||||
|
in = fopen(filename, "rt");
|
||||||
|
if (in == NULL) {
|
||||||
|
fclose(trampoline);
|
||||||
|
fprintf(stderr, "Cannot read %s!\n", filename);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
out = fopen(newCFile, "wt");
|
||||||
|
if (out == NULL) {
|
||||||
|
fclose(in);
|
||||||
|
fclose(trampoline);
|
||||||
|
fprintf(stderr, "Cannot create %s!\n", newCFile);
|
||||||
|
free(newCFile);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
// Read next byte from C input file.
|
||||||
|
if ((c = fgetc(in)) == EOF) break;
|
||||||
|
|
||||||
|
// Count brackets so we know if we're inside a function or not.
|
||||||
|
if (c == '{') brackets++;
|
||||||
|
if (c == '}') brackets--;
|
||||||
|
|
||||||
|
// End of line?
|
||||||
|
if ((c == 13) || (c == 10)) {
|
||||||
|
buffer[pos] = 0;
|
||||||
|
trimEnd(buffer);
|
||||||
|
if (brackets != 1) {
|
||||||
|
// Is this a segment definition? "#define SEGMENT_<something>"
|
||||||
|
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)]);
|
||||||
|
} else {
|
||||||
|
// Just barf it out again.
|
||||||
|
fprintf(out, "%s\n", buffer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 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] == ')') {
|
||||||
|
// Function. Annotate it!
|
||||||
|
fprintf(out, "__attribute__((noinline, section(\".block%d\")))\n", _currentBank);
|
||||||
|
// Scan forward to find function name.
|
||||||
|
found = false;
|
||||||
|
start = buffer;
|
||||||
|
do {
|
||||||
|
b = start;
|
||||||
|
while (*b != ' ') b++;
|
||||||
|
// This could be 'struct' or 'static'. Put a null there and check. (Then put the space back!)
|
||||||
|
*b = 0;
|
||||||
|
if ((strcmp(start, "struct") == 0) || (strcmp(start, "static") == 0)) {
|
||||||
|
// Keep looking.
|
||||||
|
start = ++b;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
*b = ' ';
|
||||||
|
} while (found);
|
||||||
|
// Now that we could have a 'static struct' or something, put the zero back for later.
|
||||||
|
*b = 0;
|
||||||
|
// We're past the types. Now find the '(' after the function name.
|
||||||
|
start = ++b;
|
||||||
|
while (*b != '(') b++;
|
||||||
|
// Yank the function name out.
|
||||||
|
*b = 0;
|
||||||
|
temp = strdup(start);
|
||||||
|
*b = '(';
|
||||||
|
// Write out new function definition.
|
||||||
|
fprintf(out, "%s FAR%d_%s {\n", buffer, 8, start);
|
||||||
|
// Create trampoline macro.
|
||||||
|
fprintf(trampoline, "#define %s(...) ({\n"
|
||||||
|
"\t\tunsigned char ___mmu = (unsigned char)*(volatile unsigned char *)%#06x;\n"
|
||||||
|
"\t\t*(volatile unsigned char *)%#06x = %d;\n"
|
||||||
|
"\t\tFAR%d_%s(__VA_ARGS__);\n"
|
||||||
|
"\t\t*(volatile unsigned char *)%#06x = ___mmu;\n"
|
||||||
|
"\t})\n\n", temp, swapSlot, swapSlot, _currentBank, _currentBank, temp, swapSlot);
|
||||||
|
free(temp);
|
||||||
|
} else {
|
||||||
|
// Not a function. Write as is. (Put the '{' back.)
|
||||||
|
fprintf(out, "%s {\n", buffer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Didn't find '{'.
|
||||||
|
fprintf(out, "%s\n", buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fflush(out);
|
||||||
|
// Reset buffer for next line.
|
||||||
|
pos = 0;
|
||||||
|
} else {
|
||||||
|
// Add to buffer.
|
||||||
|
buffer[pos++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
fclose(in);
|
||||||
|
|
||||||
|
free(newCFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
int x;
|
||||||
|
|
||||||
|
// Create trampoline for all C files.
|
||||||
|
out = fopen("trampoline.h", "wt");
|
||||||
|
if (out == NULL) {
|
||||||
|
fprintf(stderr, "Cannot create trampoline.h!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fprintf(out, "// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.\n\n");
|
||||||
|
fprintf(out, "#ifndef TRAMPOLINE_H\n");
|
||||||
|
fprintf(out, "#define TRAMPOLINE_H\n\n");
|
||||||
|
|
||||||
|
// Do all the C files in a project, one at a time.
|
||||||
|
parseCFile("/home/scott/code/f256/tools/overlay/interpreter.c", out, 0x000D);
|
||||||
|
|
||||||
|
fprintf(out, "#endif // TRAMPOLINE_H\n");
|
||||||
|
fclose(out);
|
||||||
|
|
||||||
|
// Generate linker script data.
|
||||||
|
out = fopen("output.ld", "wt");
|
||||||
|
if (out == NULL) {
|
||||||
|
fprintf(stderr, "Cannot create output.ld!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
fclose(out);
|
||||||
|
|
||||||
|
// Clean up.
|
||||||
|
for (x=0; x<_totalBanks; x++) {
|
||||||
|
free(_bankName[x]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -60,6 +60,23 @@ char *utilCreateStringVArgs(char *format, va_list args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *utilGetLastPathComponent(char *pathname) {
|
||||||
|
static char *start;
|
||||||
|
int32_t x;
|
||||||
|
|
||||||
|
start = pathname;
|
||||||
|
|
||||||
|
// Scan through name and find the last path separator
|
||||||
|
for (x=0; x<(int32_t)strlen(pathname); x++) {
|
||||||
|
if (pathname[x] == '\\' || pathname[x] == '/') {
|
||||||
|
start = &pathname[x + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strdup(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
char *utilReplaceExtension(char *org, char *new_ext) {
|
char *utilReplaceExtension(char *org, char *new_ext) {
|
||||||
char *ext;
|
char *ext;
|
||||||
char *tmp = strdup(org);
|
char *tmp = strdup(org);
|
||||||
|
|
|
@ -25,6 +25,9 @@
|
||||||
#define UTIL_H
|
#define UTIL_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
|
||||||
typedef unsigned char byte;
|
typedef unsigned char byte;
|
||||||
|
|
||||||
typedef struct colorS {
|
typedef struct colorS {
|
||||||
|
@ -36,6 +39,7 @@ typedef struct colorS {
|
||||||
|
|
||||||
char *utilCreateString(char *format, ...);
|
char *utilCreateString(char *format, ...);
|
||||||
char *utilCreateStringVArgs(char *format, va_list args);
|
char *utilCreateStringVArgs(char *format, va_list args);
|
||||||
|
char *utilGetLastPathComponent(char *pathname);
|
||||||
char *utilReplaceExtension(char *org, char *new_ext);
|
char *utilReplaceExtension(char *org, char *new_ext);
|
||||||
int utilStricmp(char *a, char *b);
|
int utilStricmp(char *a, char *b);
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,9 @@ fi
|
||||||
pushd merlin-code/merlin32/jr
|
pushd merlin-code/merlin32/jr
|
||||||
for FILE in $(ls *.asm); do
|
for FILE in $(ls *.asm); do
|
||||||
HEADER=${INC}/$(basename ${FILE} .asm).h
|
HEADER=${INC}/$(basename ${FILE} .asm).h
|
||||||
BLOCK=_$(basename ${FILE^^} .ASM)_H_
|
# Using tr because MacOS uses ancient bash.
|
||||||
|
UCASEFILE=$(echo ${FILE} | tr '[:lower:]' '[:upper:]')
|
||||||
|
BLOCK=_$(basename ${UCASEFILE} .ASM)_H_
|
||||||
echo "#ifndef ${BLOCK}" > ${HEADER}
|
echo "#ifndef ${BLOCK}" > ${HEADER}
|
||||||
echo "#define ${BLOCK}" >> ${HEADER}
|
echo "#define ${BLOCK}" >> ${HEADER}
|
||||||
echo >> ${HEADER}
|
echo >> ${HEADER}
|
||||||
|
|
Loading…
Add table
Reference in a new issue