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}