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.