git.y1.nz

gbdk-2020

GameBoy Development Kit
download: https://git.y1.nz/archives/gbdk.tar.gz
README | Files | Log | Refs | LICENSE

gbdk-support/lcc/gb.c

      1 /* Unix */
      2 
      3 #include <stdio.h>
      4 #include <string.h>
      5 #include <stdlib.h>
      6 #include <stdbool.h>
      7 #include <assert.h>
      8 #include <ctype.h>
      9 
     10 #ifdef __WIN32__
     11 #include <windows.h>
     12 #endif
     13 
     14 #include "gb.h"
     15 #include "targets.h"
     16 
     17 #ifndef GBDKLIBDIR
     18 #define GBDKLIBDIR "\\gbdk\\"
     19 #endif
     20 
     21 extern char *progname;
     22 extern char * strsave(const char *);
     23 
     24 // Set default class as the first entry in classes (Game Boy)
     25 static CLASS *_class = &classes[0];
     26 
     27 static struct {
     28 	const char *name;
     29 	const char *val;
     30 } _tokens[] = {
     31 		// Expandable string tokens used in "CLASS" command strings
     32 		{ "port",		"sm83" },  // if default class is ever changed from Game Boy, this default (and plat) may need to be changed to match
     33 		{ "plat",		"gb" },
     34 		{ "sdccdir", 	"%bindir%"},
     35 
     36 		// In order for things such as "__SDCC_VERSION_MAJOR" to work with -E preprocess only,
     37 		// the preprocessor needs to be invoked via SDCC instead since that's where those
     38 		// settings are generated. (see sdcc --verbose -E ...)
     39 		// { "cpp",		"%sdccdir%sdcpp" },
     40 		// { "cppdefault", "-Wall -D__PORT_%port% -D__TARGET_%plat%"},
     41 		{ "cpp",		"%com%" },
     42 		{ "cppdefault",	"-E -D__PORT_%port% -D__TARGET_%plat% "},
     43 
     44 		{ "includedefault",	"-I%includedir%" },
     45 		{ "includedir", 	"%prefix%include" },
     46 		{ "prefix",		GBDKLIBDIR },
     47 		{ "comopt",		"--noinvariant --noinduction" },
     48 		{ "com",		"%sdccdir%sdcc" },
     49 		{ "comflag",	"-c"},
     50 		{ "comdefault",	"-m%port% --no-std-crt0 --fsigned-char --use-stdout --no-optsdcc-in-asm -D__PORT_%port% -D__TARGET_%plat% "},
     51 		/* asdsgb assembler defaults:
     52 			-p: disable pagination
     53 			-o: create object file
     54 			-g: make undef symbols global
     55 			-N: defer symbol resolving to link time (autobanking relies on this) [requires sdcc 12238+]
     56 		*/
     57 		{ "asdefault",	"-pogN -I%libdir%%plat%" },
     58 		{ "as_gb",		"%sdccdir%sdasgb" },
     59 		{ "as_z80",		"%sdccdir%sdasz80" },
     60 		{ "as_6500",	"%sdccdir%sdas6500" },
     61 		{ "bankpack",	"%bindir%bankpack" },
     62 		{ "ld_gb",		"%sdccdir%sdldgb" },
     63 		{ "ld_z80",		"%sdccdir%sdldz80" },
     64 		{ "ld_6808",	"%sdccdir%sdld6808" },
     65 		{ "ld",			"%sdccdir%sdld" },
     66 		{ "libdir",		"%prefix%lib/" },
     67 #ifndef GBDKBINDIR
     68 		{ "bindir",		"%prefix%bin/" },
     69 #else
     70 		{ "bindir",		GBDKBINDIR },
     71 #endif
     72 		{ "ihxcheck",	"%bindir%ihxcheck" },
     73 		{ "mkbin",		"%sdccdir%makebin" },
     74 		{ "crt0dir",	"%libdir%%plat%/crt0.o"},
     75 		{ "libs_include", "-k %libdir%%port%/ -l %port%.lib -k %libdir%%plat%/ -l %plat%.lib"},
     76 		{ "mkcom", "%sdccdir%makecom"},
     77 		{ "mknes", "%sdccdir%makenes"}
     78 };
     79 
     80 static char *getTokenVal(const char *key)
     81 {
     82 	int i;
     83 	for (i = 0; i < ARRAY_LEN(_tokens); i++) {
     84 		if (!strcmp(_tokens[i].name, key))
     85 			return strdup(_tokens[i].val);
     86 	}
     87 	assert(0);
     88 	return NULL;
     89 }
     90 
     91 static void setTokenVal(const char *key, const char *val)
     92 {
     93 	int i;
     94 	for (i = 0; i < ARRAY_LEN(_tokens); i++) {
     95 		if (!strcmp(_tokens[i].name, key)) {
     96 			_tokens[i].val = strdup(val);
     97 			return;
     98 		}
     99 	}
    100 	assert(0);
    101 }
    102 
    103 // Sets the local _class to a given Port/Platform from classes[]
    104 static int setClass(const char *port, const char *plat)
    105 {
    106 	int i;
    107 	for (i = 0; i < classes_count; i++) {
    108 		if (!strcmp(classes[i].port, port)) {
    109 			if (plat && classes[i].plat && !strcmp(classes[i].plat, plat)) {
    110 				_class = classes + i;
    111 				return 1;
    112 			}
    113 			else if (!classes[i].plat || !plat) {
    114 				_class = classes + i;
    115 				return 1;
    116 			}
    117 		}
    118 	}
    119 	return 0;
    120 }
    121 
    122 /* Algorithim
    123 	 while (chars in string)
    124 		if space, concat on end
    125 	if %
    126 		Copy off what we have sofar
    127 		Call ourself on value of token
    128 		Continue scanning
    129 */
    130 
    131 /* src is destroyed */
    132 static char **subBuildArgs(char **args, char *template)
    133 {
    134 	char *src = template;
    135 	char *last = src;
    136 	static int quoting = 0;
    137 
    138 	/* Shared buffer between calls of this function. */
    139 	static char buffer[128];
    140 	static int indent = 0;
    141 
    142 	indent++;
    143 	while (*src) {
    144 		if (isspace(*src) && !quoting) {
    145 			/* End of set - add in the command */
    146 			*src = '\0';
    147 			strcat(buffer, last);
    148 			*args = strdup(buffer);
    149 			buffer[0] = '\0';
    150 			args++;
    151 			last = src + 1;
    152 		}
    153 		else if (*src == '%') {
    154 			/* Again copy off what we already have */
    155 			*src = '\0';
    156 			strcat(buffer, last);
    157 			*src = '%';
    158 			src++;
    159 			last = src;
    160 			while (*src != '%') {
    161 				if (!*src) {
    162 					/* End of string without closing % */
    163 					assert(0);
    164 				}
    165 				src++;
    166 			}
    167 			*src = '\0';
    168 			/* And recurse :) */
    169 			args = subBuildArgs(args, getTokenVal(last));
    170 			*src = '%';
    171 			last = src + 1;
    172 		}
    173 		else if (*src == '\"') {
    174 			quoting = !quoting;
    175 		}
    176 		src++;
    177 	}
    178 	strcat(buffer, last);
    179 	if (indent == 1) {
    180 		*args = strdup(buffer);
    181 		args++;
    182 		buffer[0] = '\0';
    183 	}
    184 
    185 	indent--;
    186 	return args;
    187 }
    188 
    189 static void buildArgs(char **args, const char *template)
    190 {
    191 	char *s = strdup(template);
    192 	char **last;
    193 	last = subBuildArgs(args, s);
    194 	*last = NULL;
    195 }
    196 
    197 // TODO: This + suffix() is brittle and hard to read elsewhere. Rewrite it.
    198 //
    199 // If order is changed here, file type handling MUST be updated
    200 // in lcc.c: "switch (suffix(name, suffixes, 5)) {"
    201 //
    202 // suffix() interpretes this as each additional array item being
    203 // part of an inclusively larger set.
    204 char *suffixes[] = {
    205 	EXT_C,					// 0
    206 	EXT_I,			 		// 1
    207 	EXT_ASM ";" EXT_S,		// 2
    208 	EXT_O   ";" EXT_OBJ,	// 3
    209 	EXT_IHX,				// 4
    210 	EXT_GB,					// 5
    211 	0						// 6
    212 };
    213 
    214 char inputs[256] = "";
    215 
    216 // Todo: Move these into a struct
    217 // (Could use "CLASS" aside from const typing? It's mainly dupe of that anyway)
    218 char *cpp[256];
    219 char *include[256];
    220 char *com[256] = { "", "", "" };
    221 char *as[256];
    222 char *ihxcheck[256];
    223 char *ld[256];
    224 char *bankpack[256];
    225 char *mkbin[256];
    226 char *postproc[256];
    227 bool has_postproc_stage;
    228 char *rom_extension;
    229 arg_entry *llist0_defaults;
    230 int llist0_defaults_len = 0;
    231 
    232 
    233 const char *starts_with(const char *s1, const char *s2)
    234 {
    235 	if (!strncmp(s1, s2, strlen(s2))) {
    236 		return s1 + strlen(s2);
    237 	}
    238 	return NULL;
    239 }
    240 
    241 int option(char *arg) {
    242 	const char *tail;
    243 	if ((tail = starts_with(arg, "--prefix="))) {
    244 		/* Go through and set all of the paths */
    245 		setTokenVal("prefix", tail);
    246 		return 1;
    247 	}
    248 	else if ((tail = starts_with(arg, "--gbdklibdir="))) {
    249 		setTokenVal("libdir", tail);
    250 		return 1;
    251 	}
    252 	else if ((tail = starts_with(arg, "--gbdkincludedir="))) {
    253 		setTokenVal("includedir", tail);
    254 		return 1;
    255 	}
    256 	else if ((tail = starts_with(arg, "--sdccbindir="))) {
    257 		// Allow to easily run with external SDCC snapshot / release
    258 		setTokenVal("sdccdir", tail);
    259 		return 1;
    260 	}
    261 	else if ((tail = starts_with(arg, "-S"))) {
    262 		// -S is compile to ASM only
    263 		// When composing the compile stage, swap in of -S instead of default -c
    264 		setTokenVal("comflag", "-S");
    265 	}
    266 	else if ((tail = starts_with(arg, "-no-crt"))) {
    267 		// When composing link stage, clear out crt0dir path
    268 		setTokenVal("crt0dir", "");
    269 	}
    270 	else if ((tail = starts_with(arg, "-no-libs"))) {
    271 		// When composing link stage, clear out crt0dir path
    272 		setTokenVal("libs_include", "");
    273 	}
    274 	else if ((tail = starts_with(arg, "-m"))) {
    275 		char word_count = 0;
    276 		char * p_str = strtok( strsave(tail),":"); // Copy arg str so it doesn't get unmodified by strtok()
    277 		char * words[3]; // +1 in size of expected number of entries to detect excess
    278 
    279 		// Split string into words separated by ':' chars (expecting PORT and PLAT)
    280 		while (p_str != NULL) {
    281 			words[word_count++] = p_str;
    282 			p_str = strtok(NULL, ":");
    283 			if (word_count >= ARRAY_LEN(words)) break;
    284 		}
    285 
    286 		// Requires both PORT and PLAT, must match a valid setClass entry.
    287 		if (word_count == 2) {
    288 			// Error out and warn user when old gbz80 PORT name is used instead of sm83
    289 			if (!strcmp("gbz80", words[0])) {
    290 				fprintf(stderr, "Error: %s: old \"gbz80\" SDCC PORT name specified (in \"%s\"). Use \"sm83\" instead. "
    291 								"You must update your build settings.\n", progname, arg);
    292 				exit(-1);
    293 			}
    294 
    295 			setTokenVal("port", words[0]);
    296 			setTokenVal("plat", words[1]);
    297 			if (!setClass(words[0], words[1])) {
    298 				fprintf(stderr, "Error: %s: unrecognized PORT:PLATFORM from %s:%s\n", progname, words[0], words[1]);
    299 				exit(-1);
    300 			}
    301 		} else {
    302 			fprintf(stderr, "Error: -m requires both/only PORT and PLATFORM values (ex: -msm83:gb) : %s\n", arg);
    303 			exit(-1);
    304 		}
    305 
    306 		return 1;
    307 	}
    308 	return 0;
    309 }
    310 
    311 // Build the port/platform specific toolchain command strings
    312 // and apply any related settings
    313 void finalise(void)
    314 {
    315 	if (!_class->plat) {
    316 		setTokenVal("plat", _class->default_plat);
    317 	}
    318 	buildArgs(cpp, _class->cpp);
    319 	buildArgs(include, _class->include);
    320 	buildArgs(com, _class->com);
    321 	buildArgs(as, _class->as);
    322 	buildArgs(bankpack, _class->bankpack);
    323 	buildArgs(ld, _class->ld);
    324 	buildArgs(ihxcheck, _class->ihxcheck);
    325 	buildArgs(mkbin, _class->mkbin);
    326 	if (strlen(_class->postproc) != 0) {
    327 		buildArgs(postproc, _class->postproc);
    328 		has_postproc_stage = true;
    329 	} else {
    330 		has_postproc_stage = false;
    331 	}
    332 	rom_extension = strdup(_class->rom_extension);
    333 	llist0_defaults = _class->llist0_defaults;
    334 	llist0_defaults_len = _class->llist0_defaults_len;
    335 }
    336 
    337 void set_gbdk_dir(char* argv_0)
    338 {
    339 	char buf[1024 - 2]; // Path will get quoted below, so reserve two characters for them
    340 #ifdef __WIN32__
    341 	char slash = '\\';
    342 	if (GetModuleFileName(NULL,buf, sizeof(buf)) == 0)
    343 	{
    344 		return;
    345 	}
    346 #else
    347 	char slash = '/';
    348 	strncpy(buf, argv_0, sizeof(buf));
    349 	buf[sizeof(buf) - 1] = '\0';
    350 #endif
    351 
    352 	// Strip of the trailing GBDKDIR/bin/lcc.exe and use it as the prefix.
    353 	char *p = strrchr(buf, slash);
    354 	if (p) {
    355 		while(p != buf && *(p - 1) == slash) //Fixes https://github.com/Zal0/gbdk-2020/issues/29
    356 			-- p;
    357 		*p = '\0';
    358 
    359 		p = strrchr(buf, slash);
    360 		if (p) {
    361 			*++p = '\0';
    362 			char quotedBuf[1024];
    363 			snprintf(quotedBuf, sizeof(quotedBuf), "\"%s\"", buf);
    364 			setTokenVal("prefix", quotedBuf);
    365 		}
    366 	}
    367 }

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.