diff options
Diffstat (limited to 'gbdk/gbdk-support/makecom')
| -rw-r--r-- | gbdk/gbdk-support/makecom/Makefile | 32 | ||||
| -rw-r--r-- | gbdk/gbdk-support/makecom/bin_to_com.c | 164 | ||||
| -rw-r--r-- | gbdk/gbdk-support/makecom/bin_to_com.h | 31 | ||||
| -rw-r--r-- | gbdk/gbdk-support/makecom/common.h | 25 | ||||
| -rw-r--r-- | gbdk/gbdk-support/makecom/files.c | 62 | ||||
| -rw-r--r-- | gbdk/gbdk-support/makecom/files.h | 7 | ||||
| -rw-r--r-- | gbdk/gbdk-support/makecom/list.c | 70 | ||||
| -rw-r--r-- | gbdk/gbdk-support/makecom/list.h | 20 | ||||
| -rw-r--r-- | gbdk/gbdk-support/makecom/makecom.c | 147 | ||||
| -rw-r--r-- | gbdk/gbdk-support/makecom/noi_file.c | 164 | ||||
| -rw-r--r-- | gbdk/gbdk-support/makecom/noi_file.h | 40 | ||||
| -rw-r--r-- | gbdk/gbdk-support/makecom/pascal/Makefile | 31 | ||||
| -rw-r--r-- | gbdk/gbdk-support/makecom/pascal/makecom.dpr | 184 | ||||
| -rw-r--r-- | gbdk/gbdk-support/makecom/path_ops.c | 145 | ||||
| -rw-r--r-- | gbdk/gbdk-support/makecom/path_ops.h | 15 |
15 files changed, 1137 insertions, 0 deletions
diff --git a/gbdk/gbdk-support/makecom/Makefile b/gbdk/gbdk-support/makecom/Makefile new file mode 100644 index 00000000..7e46e311 --- /dev/null +++ b/gbdk/gbdk-support/makecom/Makefile @@ -0,0 +1,32 @@ +# makecom makefile + +ifndef TARGETDIR +TARGETDIR = /opt/gbdk +endif + +ifeq ($(OS),Windows_NT) + BUILD_OS := Windows_NT +else + BUILD_OS := $(shell uname -s) +endif + +# Target older macOS version than whatever build OS is for better compatibility +ifeq ($(BUILD_OS),Darwin) + export MACOSX_DEPLOYMENT_TARGET=10.10 +endif + +CC = $(TOOLSPREFIX)gcc +CFLAGS = -ggdb -O -Wno-incompatible-pointer-types -DGBDKLIBDIR=\"$(TARGETDIR)\" +OBJ = makecom.o noi_file.o files.o bin_to_com.o list.o path_ops.o +BIN = makecom + +all: $(BIN) + +$(BIN): $(OBJ) + +clean: + rm -f *.o $(BIN) *~ + rm -f tmp.* + rm -f *.exe + + diff --git a/gbdk/gbdk-support/makecom/bin_to_com.c b/gbdk/gbdk-support/makecom/bin_to_com.c new file mode 100644 index 00000000..dd3b7722 --- /dev/null +++ b/gbdk/gbdk-support/makecom/bin_to_com.c @@ -0,0 +1,164 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdbool.h> +#include <stdint.h> + +#include "common.h" +#include "list.h" +#include "files.h" +#include "noi_file.h" +#include "bin_to_com.h" + + +uint8_t * banks[BANKS_MAX_COUNT] = {NULL}; +uint16_t banks_len[BANKS_MAX_COUNT] = {0}; +uint16_t banks_count = 0; + + +void banks_cleanup(void) { + for (int c = 0; c <= BANKS_MAX_ID; c++) { + if (banks[c] != NULL) { + + free(banks[c]); + banks[c] = NULL; + } + } +} + + +// Allocate memory for banks as banks are requested +// If preceding banks are not yet allocated then do those too +uint8_t * bank_get_ptr(uint16_t bank_num) { + + if (bank_num > BANKS_MAX_ID) { + printf("makecom: ERROR: Requested bank %d is larger than max bank num %d!\n", bank_num, BANKS_MAX_ID); + exit(EXIT_FAILURE); + } + + if (banks[bank_num] == NULL) { + + // Make sure all banks leading up to requested are also allocated + for (int c = 0; c <= bank_num; c++) { + if (banks[c] == NULL) { + + banks[c] = malloc(BANK_SIZE); + + if (!banks[c]) { + printf("makecom: ERROR: Failed to allocate memory for bank %d!\n", c); + exit(EXIT_FAILURE); + } + + // zero out buffer (though unused space will not get written out) + memset(banks[c], 0x00u, BANK_SIZE); + } + } + } + + return banks[bank_num]; +} + + + +// Copy data from source bin file at offset address into requested bank buffer +void copy_to_bank(uint16_t bank_num, uint32_t rom_src_addr, uint32_t bank_out_addr, uint32_t length) { + + // TODO: make sure bin src addr doesn't exceed source file buffer length + if ((rom_src_addr + length) > rom_buf_in_len) { + printf("makecom: ERROR: Can't copy %d bytes from %x for bank %d, would overflow past end of ROM source buffer of size %d!\n", length, rom_src_addr, bank_num, (uint32_t)rom_buf_in_len); + exit(EXIT_FAILURE); + } + + if ((bank_out_addr + length) > BANK_SIZE) { + printf("makecom: ERROR: Can't copy %d bytes to %04x for bank %d, would overflow past end of bank buffer of size %d!\n",length, bank_out_addr, bank_num, BANK_SIZE); + exit(EXIT_FAILURE); + } + + if (bank_num > banks_count) + banks_count = bank_num; + + // Update length of bank data buffers (to allow for truncating output later) + if ((bank_out_addr + length) > banks_len[bank_num]) + banks_len[bank_num] = (bank_out_addr + length); + + // printf("* copying... bank:%d from:%x to:%x len:%d\n", + // bank_num, rom_src_addr, bank_out_addr, length); + + memcpy(bank_get_ptr(bank_num) + bank_out_addr, p_rom_buf_in + rom_src_addr, length); +} + + +void banks_write_out(void) { + + // If any of below fails it will trigger exit() and cleanup() will be called + + // Write first bank out, handled differently than rest + if ((banks[0] != NULL) && (banks_len[0] > 0)) { + // printf("Bank %d: Writing %d bytes to %s\n",0, banks_len[0], filename_out_com); + file_write_from_buffer(filename_out_com, banks[0], banks_len[0]); + } else { + printf("makecom: Error: Bank 0 was empty, no data written\n"); + exit; + } + + // Write out remaining banks if applicable + char bank_fname[MAX_STR_LEN] = ""; + for (int c = 1; c <= BANKS_MAX_ID; c++) { + if ((banks[c] != NULL) && (banks_len[0] > 0)) { + // Format to 8.3 filename with bank num as zero padded extension + sprintf(bank_fname, "%s.%03d", filename_banks_base, c); + // printf("Bank %d: Writing %d bytes to %s\n",c, banks_len[c], bank_fname); + file_write_from_buffer(bank_fname, banks[c], banks_len[c]); + } else + break; // Exit loop on first unpopulated bank + } + +} + + + +int bin2com(void) { + + symbol_item * symbols = (symbol_item *)symbol_list.p_array; + + for(int c = 0; c < symbol_list.count; c++) { + + // Only process completed symbols (start and length both set, lenth not zero) + if ((symbols[c].addr_start != SYM_VAL_UNSET) && + (symbols[c].length != SYM_VAL_UNSET) && + (symbols[c].length > 0)) { + + // If symbol is for a bank > 0 + if ((strncmp(symbols[c].name, "_CODE_", (sizeof("_CODE_") - 1)) == 0) && (symbols[c].bank_num > 0)) { + // Copy data from bank addr in source ROM to start of separate bank buffer + copy_to_bank(symbols[c].bank_num, symbols[c].src_rom_addr, BANK_START_ADDR, symbols[c].length); + } else { + // For remaining symbols (assume bank 0), relocate them by -100 when possible (addr -> addr - 100) + copy_to_bank(BANK_0, symbols[c].addr_start, BANK_0_RELOC_ADDR(symbols[c].addr_start), symbols[c].length); + } + } + } + + // Patch in updated bank / overlay count and bank filename + if ((banks_count > 0) && (overlay_count_addr != SYM_VAL_UNSET) && (overlay_name_addr != SYM_VAL_UNSET)) { + + if (overlay_count_addr > BANK_0_ADDR_OFFSET) + *(banks[0] + (overlay_count_addr - BANK_0_ADDR_OFFSET)) = banks_count; + + if (overlay_name_addr > BANK_0_ADDR_OFFSET) + memcpy(banks[0] + (overlay_name_addr - BANK_0_ADDR_OFFSET), filename_overlay, COM_OVERLAY_NAME_LEN-1); + } + + // No write the data out + banks_write_out(); + + return EXIT_SUCCESS; +} + + + diff --git a/gbdk/gbdk-support/makecom/bin_to_com.h b/gbdk/gbdk-support/makecom/bin_to_com.h new file mode 100644 index 00000000..79d38c2b --- /dev/null +++ b/gbdk/gbdk-support/makecom/bin_to_com.h @@ -0,0 +1,31 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#ifndef _BIN_TO_COM_H +#define _BIN_TO_COM_H + +#define MAX(a, b) ((a > b) ? a : b) +#define BANKS_MAX_COUNT 256 +#define BANKS_MAX_ID (BANKS_MAX_COUNT - 1) +#define BANK_SIZE (0x4000u) +#define BANK_START_ADDR (0x0000u) +#define BANK_0 0 +#define BANK_0_ADDR_OFFSET 0x100 + +#define BANK_GET_NUM(addr) ((addr & 0xFFFF0000U) >> 16) +#define WITHOUT_BANK(addr) (addr & 0x0000FFFFU) +// Converts a symbol bank address such as 0x34000 to a bin/rom address such as 0x12000 (i.e: ~0x4000 * 3) +// Make sure base bank addess doesn't go negative +#define BANK_GET_ROM_ADDR(addr) ( (MAX(WITHOUT_BANK(addr), BANK_SIZE) - BANK_SIZE) + (BANK_SIZE * BANK_GET_NUM(addr)) ) + +// Apply bank 0 address offset and make sure it doesn't go negative +#define BANK_0_RELOC_ADDR(addr) (MAX((addr), BANK_0_ADDR_OFFSET) - BANK_0_ADDR_OFFSET) + +void copy_data(uint16_t bank_num, uint32_t rom_src_addr, uint32_t bank_out_addr, uint32_t length); +uint8_t * bank_get_ptr(uint16_t bank_num); +void copy_to_bank(uint16_t bank_num, uint32_t rom_src_addr, uint32_t bank_out_addr, uint32_t length); +void banks_cleanup(void); +int bin2com(void); + +#endif // _BIN_TO_COM_H
\ No newline at end of file diff --git a/gbdk/gbdk-support/makecom/common.h b/gbdk/gbdk-support/makecom/common.h new file mode 100644 index 00000000..b38600aa --- /dev/null +++ b/gbdk/gbdk-support/makecom/common.h @@ -0,0 +1,25 @@ +#ifndef _COMMON_H +#define _COMMON_H + +#include <stdint.h> + +#define ARRAY_LEN(A) (sizeof(A) / sizeof(A[0])) + +#define MAX_STR_LEN 4096 +#define MAX_FILE_STR (MAX_STR_LEN) +#define SYM_MAX_STR_LEN 1024 + +#define BANK_FNAME_LEN (8+1+3 +1) // 8.3 filename style + terminator, ex: MYCOMFIL.001 (from "mycomfile.com") +#define COM_OVERLAY_NAME_LEN (8 + 1) // 8 chars for overlay string + terminator "MYCOMFIL" + + +extern char filename_in_bin[]; +extern char filename_in_noi[]; +extern char filename_out_com[]; +extern char filename_banks_base[]; +extern char filename_overlay[]; + +extern uint8_t * p_rom_buf_in; +extern size_t rom_buf_in_len; + +#endif // _COMMON_H
\ No newline at end of file diff --git a/gbdk/gbdk-support/makecom/files.c b/gbdk/gbdk-support/makecom/files.c new file mode 100644 index 00000000..c6f78e9d --- /dev/null +++ b/gbdk/gbdk-support/makecom/files.c @@ -0,0 +1,62 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + + + +// Read from a file into a buffer (will allocate needed memory) +// Returns NULL if reading file didn't succeed +uint8_t * file_read_into_buffer(char * filename, uint32_t *ret_size) { + + long fsize; + FILE * file_in = fopen(filename, "rb"); + uint8_t * filedata = NULL; + + if (file_in) { + // Get file size + fseek(file_in, 0, SEEK_END); + fsize = ftell(file_in); + if (fsize != -1L) { + fseek(file_in, 0, SEEK_SET); + + filedata = malloc(fsize); + if (filedata) { + if (fsize != fread(filedata, 1, fsize, file_in)) { + printf("makecom: Warning: File read size didn't match expected for %s\n", filename); + filedata = NULL; + } + // Read was successful, set return size + *ret_size = fsize; + } else printf("makecom: ERROR: Failed to allocate memory to read file %s\n", filename); + + } else printf("makecom: ERROR: Failed to read size of file %s\n", filename); + + fclose(file_in); + } else printf("makecom: ERROR: Failed to open input file %s\n", filename); + + return filedata; +} + + + +// Writes a buffer to a file +bool file_write_from_buffer(char * filename, uint8_t * p_buf, uint32_t data_len) { + + bool status = false; + size_t wrote_bytes; + FILE * file_out = fopen(filename, "wb"); + if (file_out) { + if (data_len == fwrite(p_buf, 1, data_len, file_out)) + status = true; + else + printf("makecom: Warning: File write size didn't match expected for %s\n", filename); + + fclose(file_out); + } else { + printf("makecom: ERROR: Failed to open output file %s!\n",filename); + exit(EXIT_FAILURE); + } + + return status; +} diff --git a/gbdk/gbdk-support/makecom/files.h b/gbdk/gbdk-support/makecom/files.h new file mode 100644 index 00000000..c027b173 --- /dev/null +++ b/gbdk/gbdk-support/makecom/files.h @@ -0,0 +1,7 @@ +#ifndef _FILES_H +#define _FILES_H + +uint8_t * file_read_into_buffer(char * filename, uint32_t *ret_size); +bool file_write_from_buffer(char * filename, uint8_t * p_buf, uint32_t data_len); + +#endif // _FILES_H diff --git a/gbdk/gbdk-support/makecom/list.c b/gbdk/gbdk-support/makecom/list.c new file mode 100644 index 00000000..868bee3a --- /dev/null +++ b/gbdk/gbdk-support/makecom/list.c @@ -0,0 +1,70 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "common.h" +#include "list.h" + +#define LIST_GROW_SIZE 100 // 50 // grow array by N entries at a time + +// Initialize the list and it's array +// typesize *must* match the type that will be used with the array +void list_init(list_type * p_list, size_t array_typesize) { + p_list->typesize = array_typesize; + p_list->count = 0; + p_list->size = LIST_GROW_SIZE; + p_list->p_array = (void *)malloc(p_list->size * p_list->typesize); + + if (!p_list->p_array) { + printf("makecom: ERROR: Failed to allocate memory for list!\n"); + exit(EXIT_FAILURE); + } +} + + +// Free the array memory allocated for the list +void list_cleanup(list_type * p_list) { + if (p_list->p_array) { + free (p_list->p_array); + p_list->p_array = NULL; + } +} + + +// Add a new item to the lists array, resize if needed +// p_newitem *must* be the same type the list was initialized with +void list_additem(list_type * p_list, void * p_newitem) { + + void * tmp_list; + + p_list->count++; + + // Grow array if needed + if (p_list->count == p_list->size) { + // Save a copy in case reallocation fails + tmp_list = p_list->p_array; + + p_list->size += p_list->typesize * LIST_GROW_SIZE; + p_list->p_array = (void *)realloc(p_list->p_array, p_list->size * p_list->typesize); + // If realloc failed, free original buffer before quitting + if (!p_list->p_array) { + printf("makecom: ERROR: Failed to reallocate memory for list!\n"); + if (tmp_list) { + free(tmp_list); + tmp_list = NULL; + } + exit(EXIT_FAILURE); + } + } + + // Copy new entry + memcpy((uint_least8_t *)p_list->p_array + ((p_list->count - 1) * p_list->typesize), + p_newitem, + p_list->typesize); +} + diff --git a/gbdk/gbdk-support/makecom/list.h b/gbdk/gbdk-support/makecom/list.h new file mode 100644 index 00000000..ef9d3d52 --- /dev/null +++ b/gbdk/gbdk-support/makecom/list.h @@ -0,0 +1,20 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#ifndef _LIST_H +#define _LIST_H + + +typedef struct list_type { + void * p_array; + uint32_t size; + uint32_t count; + size_t typesize; +} list_type; + +void list_init(list_type *, size_t); +void list_cleanup(list_type *); +void list_additem(list_type *, void *); + +#endif // _LIST_H
\ No newline at end of file diff --git a/gbdk/gbdk-support/makecom/makecom.c b/gbdk/gbdk-support/makecom/makecom.c new file mode 100644 index 00000000..a6abd51d --- /dev/null +++ b/gbdk/gbdk-support/makecom/makecom.c @@ -0,0 +1,147 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2022 + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> +#include <ctype.h> + +#include "path_ops.h" +#include "common.h" +#include "files.h" +#include "noi_file.h" +#include "bin_to_com.h" + +#define EXT_NOI "noi" + +char filename_in_bin[MAX_STR_LEN] = ""; +char filename_in_noi[MAX_STR_LEN] = ""; +char filename_out_com[MAX_STR_LEN] = ""; +char filename_banks_base[MAX_STR_LEN] = ""; +char filename_overlay[COM_OVERLAY_NAME_LEN] = ""; + +uint8_t * p_rom_buf_in = NULL; +size_t rom_buf_in_len = 0; + +static void str_to_upper(char * str); +static void filenames_out_prepare(void); +static void display_help(void); +static int handle_args(int argc, char * argv[]); +void cleanup(void); + + +static void str_to_upper(char * str) { + while (*str) { + *str = toupper(*str); + str++; + } +} + + + +// Generate names for the exported bank/overlay files +// based on the output COM filename and path +static void filenames_out_prepare(void) { + + char str_banks_name[MAX_STR_LEN]; + char str_path_only[MAX_STR_LEN]; + + snprintf(str_banks_name, sizeof(str_banks_name), "%s", filename_out_com); + + if (!get_path_without_filename(filename_out_com, str_path_only, sizeof(str_path_only))) { + printf("makecom: Error: source path %s exceeds max length\n", filename_out_com); + exit; + } + + // Convert filename to uppercase and remove extension + str_to_upper(str_banks_name); + filename_remove_extension(str_banks_name); + + // Then truncate it to 8 characters with no padding for bank filenames + int ret = snprintf(filename_banks_base, sizeof(filename_banks_base), "%s%.8s", str_path_only, get_filename_from_path(str_banks_name)); + if (ret < 0) printf("makecom: Warning, banks output path truncated\n"); + // Then with trailing space char padding for the overlay filename patch + snprintf(filename_overlay, sizeof(filename_overlay), "%-8.8s", get_filename_from_path(str_banks_name)); +} + + +static void display_help(void) { + + fprintf(stdout, + "makecom image.rom image.noi output.com\n" + "Use: convert a binary .rom file to .msxdos com format.\n" + ); +} + + +int handle_args(int argc, char * argv[]) { + + if (argc == 3) { + // Copy input and output filenames from arguments + snprintf(filename_in_bin, sizeof(filename_in_bin), "%s", argv[1]); + snprintf(filename_out_com, sizeof(filename_out_com), "%s", argv[2]); + // Generate .noi file name from input .bin file name + snprintf(filename_in_noi, sizeof(filename_in_noi), "%s", argv[1]); + filename_replace_extension(filename_in_noi, EXT_NOI, MAX_STR_LEN); + } else if (argc == 4) { + // Copy input and output filenames from arguments + snprintf(filename_in_bin, sizeof(filename_in_bin), "%s", argv[1]); + snprintf(filename_in_noi, sizeof(filename_in_noi), "%s", argv[2]); + snprintf(filename_out_com, sizeof(filename_out_com), "%s", argv[3]); + } else { + display_help(); + return false; + } + + filenames_out_prepare(); + + + // printf("bin: %s\n",filename_in_bin); + // printf("noi: %s\n",filename_in_noi); + // printf("com: %s\n",filename_out_com); + + // printf("banks base: _%s_\n",filename_banks_base); + // printf("overlay : _%s_\n",filename_overlay); + + + return true; +} + + +// Registered as atexit() handler +// Free resources during normal shutdown or from a call to exit() +void cleanup(void) { + + banks_cleanup(); + noi_cleanup(); + + if (p_rom_buf_in != NULL) { + free(p_rom_buf_in); + p_rom_buf_in = NULL; + } +} + + +int main( int argc, char *argv[] ) { + + // Exit with failure by default + int ret = EXIT_FAILURE; + + // Register cleanup with exit handler + atexit(cleanup); + + if (handle_args(argc, argv)) { + + p_rom_buf_in = file_read_into_buffer(filename_in_bin, &rom_buf_in_len); + + if (p_rom_buf_in) + if (noi_file_load_symbols(filename_in_noi)) + ret = bin2com(); + } + cleanup(); + + return ret; // Exit with failure by default +} diff --git a/gbdk/gbdk-support/makecom/noi_file.c b/gbdk/gbdk-support/makecom/noi_file.c new file mode 100644 index 00000000..f2fc935d --- /dev/null +++ b/gbdk/gbdk-support/makecom/noi_file.c @@ -0,0 +1,164 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdbool.h> +#include <stdint.h> + +#include "common.h" +#include "noi_file.h" +#include "bin_to_com.h" + +// Example data to parse from a .noi file: +/* +DEF s__CODE 0x200 +DEF l__BASE 0x2A3 +DEF __shadow_OAM_OFF 0x455 +DEF .mode 0x456 +*/ + +const char * known_section_names[] = + {"_CODE","_HOME","_BASE","_CODE_0","_INITIALIZER","_LIT","_GSINIT","_GSFINAL"}; + + +list_type symbol_list; +uint32_t overlay_count_addr = SYM_VAL_UNSET; +uint32_t overlay_name_addr = SYM_VAL_UNSET; + + +// Initialize the symbol list +void noi_init(void) { + + list_init(&symbol_list, sizeof(symbol_item)); +} + + +// Free the symbol list +void noi_cleanup(void) { + + list_cleanup(&symbol_list); +} + + +// Find a matching symbol, if none matches a new one is added and returned +static int symbollist_get_id_by_name(char * symbol_name) { + + symbol_item * symbols = (symbol_item *)symbol_list.p_array; + + // Check for matching symbol name + for(int c = 0;c < symbol_list.count; c++) { + // Return matching symbol index if present + if (strcmp(symbol_name, symbols[c].name) == 0) { + return c; + } + } + + // no match was found, add symbol + symbol_item new_symbol = {.name = "", .addr_start = SYM_VAL_UNSET, .length = SYM_VAL_UNSET, .bank_num = 0x00, .src_rom_addr = SYM_VAL_UNSET}; + snprintf(new_symbol.name, sizeof(new_symbol.name), "%s", symbol_name); + list_additem(&symbol_list, &new_symbol); + + return (symbol_list.count - 1); +} + + +// Add the start address or length for a s_ or l_ symbol record +static void noi_symbollist_add(char rec_type, char * name, char * value) { + + // Only add symbols if they're in the known section name list + for (int c = 0; c < ARRAY_LEN(known_section_names); c++) { + + if ((strcmp(name, known_section_names[c]) == 0) || + (strncmp(name, "_CODE_", (sizeof("_CODE_") - 1)) == 0)) { + + symbol_item * symbols = (symbol_item *)symbol_list.p_array; + + // Check to see if there is an existing record to update + // If one isn't found a new record will have been created automatically + int symbol_id = symbollist_get_id_by_name(name); + if (symbol_id != ERR_NO_SYMBOLS_LEFT) { + + // Handle whether it's a start-of-address or a length record for the given symbol + if (rec_type == NOI_REC_START) { + symbols[symbol_id].addr_start = strtol(value, NULL, 16); + symbols[symbol_id].bank_num = BANK_GET_NUM(symbols[symbol_id].addr_start); + symbols[symbol_id].src_rom_addr = BANK_GET_ROM_ADDR(symbols[symbol_id].addr_start); + } + else if (rec_type == NOI_REC_LENGTH) { + symbols[symbol_id].length = strtol(value, NULL, 16); + } + } + + return; + } + } +} + + +// Load symbols from a .noi file and create paired records for start & length entries +// Plus, look for and store for overlay symbol information +int noi_file_load_symbols(char * filename_in) { + + char cols; + char * p_str; + char * p_words[MAX_SPLIT_WORDS]; + char strline_in[MAX_STR_LEN] = ""; + FILE * noi_file = fopen(filename_in, "r"); + symbol_item symbol; + int symbol_id; + + noi_init(); + + if (noi_file) { + + // Read one line at a time into \0 terminated string + while ( fgets(strline_in, sizeof(strline_in), noi_file) != NULL) { + + // Require minimum length to match + if (strlen(strline_in) >= NOI_REC_START_LEN) { + + // Split string into words separated by spaces + cols = 0; + p_str = strtok(strline_in," "); + while (p_str != NULL) + { + p_words[cols++] = p_str; + // Only split on underscore for the second match + p_str = strtok(NULL, " "); + if (cols >= MAX_SPLIT_WORDS) break; + } + + // [0] = "DEF" + // [1] = symbol name + // [2] = symbol value + if (cols >= NOI_REC_COUNT_MATCH) { + + // If it matches either _s_egment or _l_ength records (first two chars) + // then add a record for it with the first two chars truncated + if ((strncmp(p_words[1], "l_", NOI_REC_S_L_CHK) == 0) || + (strncmp(p_words[1], "s_", NOI_REC_S_L_CHK) == 0)) { + noi_symbollist_add(p_words[1][0], p_words[1] + NOI_REC_S_L_CHK, p_words[2]); + } + // Otherwise check for records of interest and store them + else if (strcmp(p_words[1], "___overlay_count") == 0) { + overlay_count_addr = strtol(p_words[2], NULL, 16); + } + else if (strcmp(p_words[1], "___overlay_name") == 0) { + overlay_name_addr = strtol(p_words[2], NULL, 16); + } + } + } // end: valid min chars to process line + + } // end: while still lines to process + + fclose(noi_file); + + } // end: if valid file + else return (false); + + return true; +} diff --git a/gbdk/gbdk-support/makecom/noi_file.h b/gbdk/gbdk-support/makecom/noi_file.h new file mode 100644 index 00000000..b6d873b7 --- /dev/null +++ b/gbdk/gbdk-support/makecom/noi_file.h @@ -0,0 +1,40 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#ifndef _NOI_FILE_H +#define _NOI_FILE_H + +#include "list.h" + +#define MAX_STR_LEN 4096 +#define MAX_SPLIT_WORDS 4 + +#define NOI_REC_COUNT_MATCH 3 // "DEF ...symbol_name... ...symbol_value... + +#define ERR_NO_SYMBOLS_LEFT -1 +#define SYM_VAL_UNSET 0xFFFFFFFF + +#define NOI_REC_START_LEN 4 // length of "DEF " +#define NOI_REC_S_L_CHK 2 // length of "s_" or "l_" +#define NOI_REC_START 's' +#define NOI_REC_LENGTH 'l' + +typedef struct symbol_item { + char name[SYM_MAX_STR_LEN]; + uint32_t addr_start; + uint32_t length; + uint16_t bank_num; + uint32_t src_rom_addr; +} symbol_item; + +extern list_type symbol_list; +extern uint32_t overlay_count_addr; +extern uint32_t overlay_name_addr; + + +int noi_file_load_symbols(char * filename_in); +void noi_init(void); +void noi_cleanup(void); + +#endif // _NOI_FILE_H
\ No newline at end of file diff --git a/gbdk/gbdk-support/makecom/pascal/Makefile b/gbdk/gbdk-support/makecom/pascal/Makefile new file mode 100644 index 00000000..5a1d29ed --- /dev/null +++ b/gbdk/gbdk-support/makecom/pascal/Makefile @@ -0,0 +1,31 @@ +# bankcheck (auto bank tool) makefile + +ifndef TARGETDIR +TARGETDIR = /opt/gbdk +endif + +ifeq ($(OS),Windows_NT) + BUILD_OS := Windows_NT +else + BUILD_OS := $(shell uname -s) +endif + +# Target older macOS version than whatever build OS is for better compatibility +ifeq ($(BUILD_OS),Darwin) + export MACOSX_DEPLOYMENT_TARGET=10.10 +endif + +CC = +CFLAGS = +OBJ = +BIN = makecom + +all: $(BIN) + +$(BIN): makecom.dpr + fpc -B $< + +clean: + rm -f *.o $(BIN) *~ *.dof *.cfg *.dsk + rm -f *.exe + diff --git a/gbdk/gbdk-support/makecom/pascal/makecom.dpr b/gbdk/gbdk-support/makecom/pascal/makecom.dpr new file mode 100644 index 00000000..c2164095 --- /dev/null +++ b/gbdk/gbdk-support/makecom/pascal/makecom.dpr @@ -0,0 +1,184 @@ +{$APPTYPE CONSOLE} +{$IFDEF FPC} + {$MODE DELPHI} +{$ENDIF} +uses classes, sysutils, math; + +procedure ERROR(const msg: ansistring; const params: array of const); +begin + writeln(format(msg, params)); + writeln('USAGE: makecom <image.bin> [<image.noi>] <output.com>'); + halt(1); +end; + +procedure DecodeCommaText(const Value: ansistring; result: tStringList; adelimiter: ansichar); +var P, P1 : PAnsiChar; + S : ansistring; +begin + if assigned(result) then begin + result.BeginUpdate; + try + result.Clear; + P := PChar(Value); + while P^ in [#1..#31] do inc(P); + while P^ <> #0 do + begin + if P^ = '"' then + S := AnsiExtractQuotedStr(P, '"') + else + begin + P1 := P; + while (P^ >= ' ') and (P^ <> adelimiter) do inc(P); + SetString(S, P1, P - P1); + end; + result.Add(S); + while P^ in [#1..#31] do inc(P); + if P^ = adelimiter then + repeat + inc(P); + until not (P^ in [#1..#31]); + end; + finally + result.EndUpdate; + end; + end; +end; + +function load_symbols(const filename: ansistring; symbols: tStringList): boolean; +var s : textfile; + str : ansistring; + row : tStringList; +begin + result:= assigned(symbols); + if result then begin + assignfile(s, filename); reset(s); + try + row := tStringList.create; + try + while not eof(s) do begin + readln(s, str); + DecodeCommaText(str, row, ' '); + if (row[0] = 'DEF') then begin + symbols.Values[row[1]] := row[2]; + end; + end; + result:= true; + finally freeandnil(row); end; + finally closefile(s); end; + end; +end; + +function CopyData(banks: tList; bank: longint; image: tMemoryStream; source_ofs, dest_ofs: longint; len: longint): boolean; +var i : longint; + data : tMemoryStream; +begin + result:= (bank < 255); + if result then begin + if (banks.count <= bank) then + for i:= banks.count to bank do banks.Add(tMemoryStream.Create()); + data:= banks[bank]; + data.Seek(dest_ofs, soFromBeginning); + data.Write(pAnsiChar(image.Memory)[source_ofs], len); + end; +end; +procedure WriteData(banks: tList; const destname: ansistring); +var i : longint; + ovr : ansistring; +begin + if (banks.count > 0) then begin + with tMemoryStream(banks[0]) do try + writeln(format('writing program: %s', [destname])); + SaveToFile(destname); + finally free; end; + for i:= 1 to banks.count - 1 do + with tMemoryStream(banks[i]) do try + ovr:= ChangeFileExt(destname, format('.%.3d', [i])); + writeln(format('writing overlay: %s', [ovr])); + SaveToFile(ovr); + finally free; end; + end; +end; + +function Hex2Int(value: ansistring): longint; +begin + if (copy(value, 1, 2)) = '0x' then begin value[1]:= ' '; value[2]:= '$'; end; + result:= StrToIntDef(value, 0); +end; + +const section_names = '_CODE,_HOME,_BASE,_CODE_0,_INITIALIZER,_LIT,_GSINIT,_GSFINAL'; +var name_bin : ansistring; + name_noi : ansistring; + name_out : ansistring; + symbols : tStringList; + known : tStringList; + banks : tList; + source : tMemoryStream; + i, bank : longint; + name, v, l : ansistring; + addr, len : longint; +begin + if (paramcount() = 2) then begin + name_bin:= paramstr(1); name_noi:= changefileext(name_bin, '.noi'); name_out:= paramstr(2); + end else begin + if (paramcount() < 3) then ERROR('ERROR: Not sufficient parameters', []); + name_bin:= paramstr(1); name_noi:= paramstr(2); name_out:= paramstr(3); + end; + + if not fileexists(name_noi) then ERROR('ERROR: symbol file: "%s" not found', [name_noi]); + if not fileexists(name_bin) then ERROR('ERROR: binary image: "%s" not found', [name_bin]); + + known:= tStringList.create; + symbols := tStringList.create; + try + DecodeCommaText(section_names, known, ','); + if load_symbols(name_noi, symbols) then begin + source:= tMemoryStream.Create; + try + source.LoadFromFile(name_bin); + banks:= tList.Create; + with banks do try + for i:= 0 to symbols.count - 1 do begin + name:= symbols.Names[i]; + v:= symbols.Values[name]; + if (copy(name, 1, 2) = 's_') then begin + name:= copy(name, 3, length(name)); + l:= symbols.Values[format('l_%s',[name])]; + if length(l) > 0 then begin + addr:= Hex2Int(v); + len:= Hex2Int(l); + + if (len > 0) then begin + if (known.IndexOf(name) >= 0) then begin + CopyData(banks, 0, source, addr, math.max(0, addr - $100), len); + end; + if (copy(Name, 1, 6) = '_CODE_') then begin + bank:= addr shr 16; + CopyData(banks, bank, source, math.max(0, (addr and $ffff) - $4000) + $4000 * bank, 0, len); + end; + end; + end; + end; + end; + + if (banks.Count > 0) then begin + addr:= Hex2Int(symbols.Values['___overlay_count']); + if (addr > $100) then pAnsiChar(tMemoryStream(banks[0]).Memory)[addr - $100]:= chr(banks.Count - 1); + addr:= Hex2Int(symbols.Values['___overlay_name']); + if (addr > $100) then begin + name:= format('%-8.8s', [uppercase(changefileext(extractfilename(name_out), ''))]); + system.move(name[1], pAnsiChar(tMemoryStream(banks[0]).Memory)[addr - $100], 8); + end; + end; + + writeln('writing...'); + WriteData(banks, name_out); + writeln('done!'); + finally banks.free; end; + finally freeandnil(source); end; + end; + finally + freeandnil(symbols); + freeandnil(known); + end; + +end.
\ No newline at end of file diff --git a/gbdk/gbdk-support/makecom/path_ops.c b/gbdk/gbdk-support/makecom/path_ops.c new file mode 100644 index 00000000..974a4870 --- /dev/null +++ b/gbdk/gbdk-support/makecom/path_ops.c @@ -0,0 +1,145 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#include <stdio.h> +#include <string.h> +#include <stdbool.h> +#include "common.h" +#include "path_ops.h" + +const char kExtensionSeparator = '.'; +const char kPathSeparator = + +#ifdef _WIN32 + #ifndef _WIN32 + #define __WIN32__ + #endif +#endif + +#ifdef __WIN32__ + '\\'; +#else + '/'; +#endif + +const char kPathSeparator_unix = '/'; + + +void filename_replace_extension(char * filename, char * new_ext, size_t maxlen) { + + // Use a temp work string in case out and in filename are the same pointer + char temp[MAX_FILE_STR]; + char ext_sep[2] = {'\0'}; // default to empty string + + // Add leading . to path if needed + if (new_ext[0] != kExtensionSeparator) { + ext_sep[0] = kExtensionSeparator; + ext_sep[1] = '\0'; + } + + // Strip extension from filename, append new extension + filename_remove_extension(filename); + snprintf(temp, maxlen, "%s%s%s", filename, ext_sep, new_ext); + snprintf(filename, maxlen, "%s", temp); +} + + +void filename_replace_path(char * filename, char * new_path, size_t maxlen) { + + // Use a temp work string in case out and in filename are the same pointer + char temp[MAX_FILE_STR]; + char path_sep[2] = {'\0'}; // default to empty string + + // Add trailing slash to path if needed (Windows needs both for when running under linix like env) +#ifdef __WIN32__ + if (((new_path[(strlen(new_path)-1)] != kPathSeparator)) && + ((new_path[(strlen(new_path)-1)] != kPathSeparator_unix))) +#else + if ((new_path[(strlen(new_path)-1)] != kPathSeparator)) +#endif + { + path_sep[0] = kPathSeparator; + path_sep[1] = '\0'; + } + + // Strip path from path+filename, pre-pend new path + snprintf(temp, maxlen, "%s%s%s", new_path, path_sep, get_filename_from_path(filename)); + snprintf(filename, maxlen, "%s", temp); +} + + +const char * get_filename_from_path(const char * path) +{ + size_t i; + + // Returns string starting at last occurrance of path separator char + for(i = strlen(path) - 1; i; i--) { + + // Add trailing slash to path if needed (Windows needs both for when running under linix like env) + #ifdef __WIN32__ + if ((path[i] == kPathSeparator) || (path[i] == kPathSeparator_unix)) + #else + if (path[i] == kPathSeparator) + #endif + { + return &path[i+1]; + } + } + return path; +} + + +void filename_remove_extension(char * path) +{ + char * last_ext; + char * last_slash; + + // Find the last path separator if present + // Starting from here ensures that no path ".." characters + // get mistaken as extension delimiters. + last_slash = strrchr (path, kExtensionSeparator); + if (!last_slash) + last_slash = path; + + // Then check to see if there is an extension (starting with the last occurance of '.') + // (tries to remove *all* trailing extensions backward until the last slash) + last_ext = strrchr (last_slash, kExtensionSeparator); + while (last_ext) { + if (last_ext != NULL) { + // If an extension is found then overwrite it with a string terminator + *last_ext = '\0'; + } + last_ext = strrchr (last_slash, kExtensionSeparator); + } +} + + +bool get_path_without_filename(const char * path, char * path_only, uint32_t str_max) +{ + size_t i; + + if (strlen(path) + 1 > str_max) + return false; + + for(i = strlen(path) - 1; i; i--) { + + // Add trailing slash to path if needed (Windows needs both for when running under linix like env) + #ifdef __WIN32__ + if ((path[i] == kPathSeparator) || (path[i] == kPathSeparator_unix)) + #else + if (path[i] == kPathSeparator) + #endif + { + memcpy(path_only, path, i+1 ); + path_only[i+1] = '\0'; + return true; + } + } + + // memcpy(path_only, path, strlen(path)); + // No separater found, so no path + path_only[0] = '\0'; + return true; +} + diff --git a/gbdk/gbdk-support/makecom/path_ops.h b/gbdk/gbdk-support/makecom/path_ops.h new file mode 100644 index 00000000..2ae8e3a0 --- /dev/null +++ b/gbdk/gbdk-support/makecom/path_ops.h @@ -0,0 +1,15 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to <https://unlicense.org> +// bbbbbr 2020 + +#ifndef _PATH_OPS_H +#define _PATH_OPS_H + +void filename_replace_extension(char * filename, char * new_ext, size_t maxlen); +void filename_replace_path(char * filename, char * new_path, size_t maxlen); +const char * get_filename_from_path(const char * path); +void filename_remove_extension(char * path); +bool get_path_without_filename(const char * path, char * path_only, uint32_t str_max); + + +#endif // _PATH_OPS_H
\ No newline at end of file |
