cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

gb.c (8915B)


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