From a5ef53c7b6782737b162ac71fe60d428cf1cb45b Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Thu, 15 Feb 2024 20:52:28 -0600 Subject: [PATCH] Working on code auto-overlay utility. --- tools/overlay/CMakeLists.txt | 22 +++ tools/overlay/src/overlay.c | 265 +++++++++++++++++++++++++++++++++++ tools/shared/util.c | 17 +++ tools/shared/util.h | 4 + update-defines.sh | 4 +- 5 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 tools/overlay/CMakeLists.txt create mode 100644 tools/overlay/src/overlay.c diff --git a/tools/overlay/CMakeLists.txt b/tools/overlay/CMakeLists.txt new file mode 100644 index 0000000..7e31c9e --- /dev/null +++ b/tools/overlay/CMakeLists.txt @@ -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 +) diff --git a/tools/overlay/src/overlay.c b/tools/overlay/src/overlay.c new file mode 100644 index 0000000..ecfb528 --- /dev/null +++ b/tools/overlay/src/overlay.c @@ -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 +#include +#include +#include + +#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_" + found = false; + if (pos > strlen(segDef)) { + found = true; + for (x=0; x 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; +} diff --git a/tools/shared/util.c b/tools/shared/util.c index 15b6324..40c5720 100644 --- a/tools/shared/util.c +++ b/tools/shared/util.c @@ -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 *ext; char *tmp = strdup(org); diff --git a/tools/shared/util.h b/tools/shared/util.h index 54e3207..4727911 100644 --- a/tools/shared/util.h +++ b/tools/shared/util.h @@ -25,6 +25,9 @@ #define UTIL_H +#include + + typedef unsigned char byte; typedef struct colorS { @@ -36,6 +39,7 @@ typedef struct colorS { char *utilCreateString(char *format, ...); char *utilCreateStringVArgs(char *format, va_list args); +char *utilGetLastPathComponent(char *pathname); char *utilReplaceExtension(char *org, char *new_ext); int utilStricmp(char *a, char *b); diff --git a/update-defines.sh b/update-defines.sh index c1d2e25..d128b11 100755 --- a/update-defines.sh +++ b/update-defines.sh @@ -37,7 +37,9 @@ fi pushd merlin-code/merlin32/jr for FILE in $(ls *.asm); do 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 "#define ${BLOCK}" >> ${HEADER} echo >> ${HEADER}