1 /* ======================================================================== */
2 /*  BINCFG   -- Routines for reading a configuration file for the BIN+CFG   */
3 /*              file format.  Includes debug functions which can generate   */
4 /*              a new configuration file from the parsed config file.       */
5 /*                                                                          */
6 /*  Parser interface functions, intended to be called for reading a .CFG:   */
7 /*                                                                          */
8 /*  BC_PARSE_CFG  -- Reads a configuration file using the lexer/grammar.    */
9 /*  BC_READ_DATA  -- Reads ROM segments and attaches them to bc_cfgfile_t.  */
10 /*  BC_DO_MACROS  -- Applies macros that can be safely applied statically.  */
11 /*  BC_FREE_CFG   -- Frees all memory associated with a bc_cfgfile_t.       */
12 /*                                                                          */
13 /*  Structure printing functions for generating a .CFG from a parsed CFG.   */
14 /*  The following functions are compiled out if BC_NOPRINT is defined.      */
15 /*                                                                          */
16 /*  BC_PRINT_CFG  -- Chase through a bc_cfgfile_t structure and print out   */
17 /*                   what we find therein.  It calls the following helpers: */
18 /*                                                                          */
19 /*  BC_PRINT_DIAG    -- Print all the collected diagnostics attached to cfg */
20 /*  BC_PRINT_MACRO   -- Print the [macro] section                           */
21 /*  BC_PRINT_MACRO_T -- Print a single bc_macro_t                           */
22 /*  BC_PRINT_VARLIKE -- Print [var],[keys],[joystick] sections              */
23 /*  BC_PRINT_VAR_T   -- Print a single <name>,<value> tuple                 */
24 /*  BC_PRINT_MEMSPAN -- Print out all the memory span information.          */
25 /* ======================================================================== */
26 
27 #include "config.h"
28 #include "lzoe/lzoe.h"
29 #include "file/file.h"
30 #ifndef BC_NOMETADATA
31 #   include "metadata/metadata.h"
32 #   include "metadata/cfgvar_metadata.h"
33 #endif
34 #include "bincfg/bincfg.h"
35 #include "bincfg/bincfg_lex.h"
36 #include "bincfg/bincfg_grmr.tab.h"
37 
38 bc_cfgfile_t *bc_parsed_cfg = NULL;
39 
40 extern int bc_parse(void);  /* grrr... bison doesn't do this for us?!       */
41 
42 /* ======================================================================== */
43 /*  BC_PARSE_CFG  -- Invokes the lexer and grammar to parse the config.     */
44 /* ======================================================================== */
bc_parse_cfg(LZFILE * f,const char * const binfile,const char * const cfgfile)45 bc_cfgfile_t *bc_parse_cfg
46 (
47     LZFILE *f,
48     const char *const binfile,
49     const char *const cfgfile
50 )
51 {
52     bc_memspan_t *span, **prev;
53     int num_preload = 0, num_memattr = 0, need_default = 0, ma, pl;
54     bc_memspan_t **preloads, **memattrs;
55 
56     bc_parsed_cfg = NULL;
57 
58     /* -------------------------------------------------------------------- */
59     /*  Scan in the file, ignoring errors returned directly from bc_parse.  */
60     /*  All the diagnostics will be attached to whatever cfg we generate.   */
61     /* -------------------------------------------------------------------- */
62     if (f)
63     {
64         bc_restart( (FILE*)f ); /* register the file with the lexer.        */
65         bc_parse();             /* run the grammar.  It calls the bc_lex(). */
66     }
67 
68     /* -------------------------------------------------------------------- */
69     /*  If not parsing a configuration file, or the file was empty, just    */
70     /*  make an empty config as a starting point.                           */
71     /* -------------------------------------------------------------------- */
72     if (!bc_parsed_cfg)
73     {
74         bc_parsed_cfg = CALLOC(bc_cfgfile_t, 1);
75     }
76 
77     /* -------------------------------------------------------------------- */
78     /*  Leave if we get here without a valid config.                        */
79     /* -------------------------------------------------------------------- */
80     if (!bc_parsed_cfg)
81         return NULL;
82 
83     if (binfile) bc_parsed_cfg->binfile = strdup(binfile);
84     if (cfgfile) bc_parsed_cfg->cfgfile = strdup(cfgfile);
85 
86     /* -------------------------------------------------------------------- */
87     /*  Scan the memory spans counting up preload sections and non-preload  */
88     /*  (ie. memattr) sections.  We need to apply memattr spans to any      */
89     /*  preload spans they overlap, but before we do so, we need to         */
90     /*  allocate some storage.                                              */
91     /* -------------------------------------------------------------------- */
92     for (span = bc_parsed_cfg->span; span;
93          span = (bc_memspan_t*)(span->l.next))
94     {
95         if (span->flags & BC_SPAN_PL) num_preload++;
96         else                          num_memattr++;
97     }
98 
99     /* -------------------------------------------------------------------- */
100     /*  If there were no preload sections, we'll need a default config.     */
101     /*  The default config gets instantiated after we allocate arrays.      */
102     /* -------------------------------------------------------------------- */
103     if (num_preload == 0)
104     {
105         num_preload = 3;
106         need_default = 1;
107     }
108 
109     /* -------------------------------------------------------------------- */
110     /*  Allocate storage for the preload and memattr lists and fill them.   */
111     /* -------------------------------------------------------------------- */
112     preloads = CALLOC(bc_memspan_t*, num_preload);
113     memattrs = CALLOC(bc_memspan_t*, num_memattr);
114     assert((preloads && memattrs) || "Out of memory");
115     for (span = bc_parsed_cfg->span, ma = pl = 0; span;
116          span = (bc_memspan_t*)(span->l.next))
117     {
118         if (span->flags & BC_SPAN_PL) preloads[pl++] = span;
119         else                          memattrs[ma++] = span;
120     }
121     assert( pl == num_preload || (need_default && pl == 0));
122     assert( ma == num_memattr );
123 
124     /* -------------------------------------------------------------------- */
125     /*  If there were no preload sections, create a default mapping.        */
126     /* -------------------------------------------------------------------- */
127     if (need_default)
128     {
129         bc_memspan_t *s0, *s1, *s2;
130 
131         if (!(s0 = CALLOC(bc_memspan_t, 1)) ||
132             !(s1 = CALLOC(bc_memspan_t, 1)) ||
133             !(s2 = CALLOC(bc_memspan_t, 1)))
134         {
135             fprintf(stderr, "out of memory\n");
136             exit(1);
137         }
138 
139         s0->s_fofs = 0x0000;
140         s0->e_fofs = 0x1FFF;
141         s0->s_addr = 0x5000;
142         s0->e_addr = 0x6FFF;
143         s0->flags  = BC_SPAN_R | BC_SPAN_PL;
144         s0->width  = 16;
145         s0->epage  = BC_SPAN_NOPAGE;
146         s0->f_name = NULL;
147         s0->l.next = NULL;
148 
149         s1->s_fofs = 0x2000;
150         s1->e_fofs = 0x2FFF;
151         s1->s_addr = 0xD000;
152         s1->e_addr = 0xDFFF;
153         s1->flags  = BC_SPAN_R | BC_SPAN_PL;
154         s1->width  = 16;
155         s1->epage  = BC_SPAN_NOPAGE;
156         s1->f_name = NULL;
157         s1->l.next = NULL;
158 
159         s2->s_fofs = 0x3000;
160         s2->e_fofs = 0x3FFF;
161         s2->s_addr = 0xF000;
162         s2->e_addr = 0xFFFF;
163         s2->flags  = BC_SPAN_R | BC_SPAN_PL;
164         s2->width  = 16;
165         s2->epage  = BC_SPAN_NOPAGE;
166         s2->f_name = NULL;
167         s2->l.next = NULL;
168 
169         assert( preloads );
170         assert( num_preload == 3 );
171         preloads[0] = s0;
172         preloads[1] = s1;
173         preloads[2] = s2;
174     }
175 
176     /* -------------------------------------------------------------------- */
177     /*  Step through all of the memattr sections, applying their attribute  */
178     /*  flags to overlapping preload sections.  This is O(N^2), but who     */
179     /*  cares, as the total number of spans is at most dozens.  (Worse      */
180     /*  than O(N^2) if we have to split a span.)                            */
181     /* -------------------------------------------------------------------- */
182     for ( ma = 0 ; ma < num_memattr ; ma++ )
183     {
184         bc_memspan_t *m = memattrs[ma];
185 
186         for ( pl = 0 ; pl < num_preload ; pl++ )
187         {
188             bc_memspan_t *p = preloads[pl];
189 
190             /* Skip if spans don't overlap */
191             if (m->e_addr < p->s_addr) continue;
192             if (m->s_addr > p->e_addr) continue;
193             if (m->epage != p->epage)  continue;
194 
195             /* Is p completely inside m? No: split p. Fragments go to end.  */
196             if (p->s_addr < m->s_addr)
197             {
198                 int np = num_preload++;
199                 bc_memspan_t *n;
200                 preloads = (bc_memspan_t**)realloc((void*)preloads,
201                                num_preload * sizeof(bc_memspan_t*));
202                 assert(preloads || "Out of memory");
203 
204                 n = preloads[np] = CALLOC(bc_memspan_t, 1);
205                 assert(preloads[np] || "Out of memory");
206 
207                 *n = *p;
208 
209                 n->e_addr = m->s_addr - 1;
210                 p->s_addr = m->s_addr;
211                 p->s_fofs = p->e_fofs - p->e_addr + p->s_addr;
212                 n->e_fofs = n->s_fofs + n->e_addr - n->s_addr;
213             }
214 
215             if (p->e_addr > m->e_addr)
216             {
217                 int np = num_preload++;
218                 bc_memspan_t *n;
219                 preloads = (bc_memspan_t**)realloc((void*)preloads,
220                                num_preload * sizeof(bc_memspan_t*));
221                 assert(preloads || "Out of memory");
222 
223                 n = preloads[np] = CALLOC(bc_memspan_t, 1);
224                 assert(preloads[np] || "Out of memory");
225 
226                 *n = *p;
227 
228                 n->s_addr = m->e_addr + 1;
229                 p->e_addr = m->e_addr;
230                 n->s_fofs = n->e_fofs - n->e_addr + n->s_addr;
231                 p->e_fofs = p->s_fofs + p->e_addr - p->s_addr;
232             }
233 
234             /* Is m completely inside p? No: split m. Fragments go to end.  */
235             if (m->s_addr < p->s_addr)
236             {
237                 int nm = num_memattr++;
238                 bc_memspan_t *n;
239                 memattrs = (bc_memspan_t**)realloc((void*)memattrs,
240                                num_memattr * sizeof(bc_memspan_t*));
241                 assert(memattrs || "Out of memory");
242 
243                 n = memattrs[nm] = CALLOC(bc_memspan_t, 1);
244                 assert(memattrs[nm] || "Out of memory");
245 
246                 *n = *m;
247 
248                 n->e_addr = p->s_addr - 1;
249                 m->s_addr = p->s_addr;
250             }
251 
252             if (m->e_addr > p->e_addr)
253             {
254                 int nm = num_memattr++;
255                 bc_memspan_t *n;
256                 memattrs = (bc_memspan_t**)realloc((void*)memattrs,
257                                num_memattr * sizeof(bc_memspan_t*));
258                 assert(memattrs || "Out of memory");
259 
260                 n = memattrs[nm] = CALLOC(bc_memspan_t, 1);
261                 assert(memattrs[nm] || "Out of memory");
262 
263                 *n = *m;
264 
265                 n->s_addr = p->e_addr + 1;
266                 m->e_addr = p->e_addr;
267             }
268 
269             assert(m->s_addr == p->s_addr);
270             assert(m->e_addr == p->e_addr);
271 
272             /* Apply the memattr to the preload */
273             p->width = m->width;
274             p->flags = (m->flags | BC_SPAN_PL) & ~BC_SPAN_DEL;
275             m->flags |= BC_SPAN_DEL;
276         }
277     }
278 
279     /* -------------------------------------------------------------------- */
280     /*  Now reassemble the span list.  For memattr spans, only keep the     */
281     /*  ones not marked BC_SPAN_DEL, as those didn't overlap any preload.   */
282     /* -------------------------------------------------------------------- */
283     prev = &(bc_parsed_cfg->span);
284 
285     for (pl = 0; pl < num_preload; pl++)
286     {
287         *prev = preloads[pl];
288         prev = (bc_memspan_t**)&(preloads[pl]->l.next);
289         assert( (preloads[pl]->flags & BC_SPAN_DEL) == 0 );
290     }
291 
292     for (ma = 0; ma < num_memattr; ma++)
293     {
294         if (memattrs[ma]->flags & BC_SPAN_DEL)
295         {
296             free(memattrs[ma]);
297             continue;
298         }
299         *prev = memattrs[ma];
300         prev = (bc_memspan_t**)&(memattrs[ma]->l.next);
301     }
302     *prev = NULL;
303 
304     free(preloads);
305     free(memattrs);
306 
307     /* -------------------------------------------------------------------- */
308     /*  By default, assume no decoded metadata.                             */
309     /* -------------------------------------------------------------------- */
310     bc_parsed_cfg->metadata = NULL;
311 
312 #ifndef BC_NOMETADATA
313     /* -------------------------------------------------------------------- */
314     /*  Parse config variables into a metadata structure.                   */
315     /* -------------------------------------------------------------------- */
316     bc_parsed_cfg->metadata =
317         bc_parsed_cfg->vars ? game_metadata_from_cfgvars(bc_parsed_cfg->vars)
318                             : default_game_metadata();
319 #endif
320     return bc_parsed_cfg;
321 }
322 
323 /* ======================================================================== */
324 /*  BC_READ_HELPER -- helper function for bc_read_data.                     */
325 /* ======================================================================== */
bc_read_helper(char * f_name,uint16_t * buf,int width)326 LOCAL int bc_read_helper(char *f_name, uint16_t *buf, int width)
327 {
328     LZFILE *f;
329     int len;
330 
331     /* -------------------------------------------------------------------- */
332     /*  Open and read up to 64K words from a file.  Return how much we      */
333     /*  actually read from the file to the caller.                          */
334     /* -------------------------------------------------------------------- */
335     if ((f = lzoe_fopen(f_name, "rb")) == NULL)
336     {
337 
338         perror("fopen()");
339         fprintf(stderr, "Could not open binary file '%s' for reading.\n",
340                 f_name);
341         return -1;
342     }
343 
344     len = width > 8 ? file_read_rom16(f, BC_MAXBIN, buf)
345                     : file_read_rom8 (f, BC_MAXBIN, buf);
346 
347     if (len < 1)
348     {
349         fprintf(stderr, "Unable to read binary file '%s'\n", f_name);
350         return -1;
351     }
352 
353     lzoe_fclose(f);
354 
355     return len;
356 }
357 
358 /* ======================================================================== */
359 /*  BC_READ_DATA  -- Reads ROM segments and attaches them to bc_cfgfile_t.  */
360 /*                                                                          */
361 /*  This pass will adjust, or remove and free memspans that are partially   */
362 /*  or completely outside the bounds of the associated binfile.             */
363 /* ======================================================================== */
bc_read_data(bc_cfgfile_t * bc)364 int bc_read_data(bc_cfgfile_t *bc)
365 {
366     bc_memspan_t *span, *prev;
367     uint16_t *pbuf, *lbuf, *buf = NULL;
368     size_t plen, llen, len = 0;
369     int slen, i;
370 
371     /* -------------------------------------------------------------------- */
372     /*  Allocate a temporary buffer for the primary BIN file.  Note that    */
373     /*  we can't just parcel our BIN segments out of this buffer with       */
374     /*  pointer manipulation unless there is only 1 such segment.  This     */
375     /*  is due to the 'free()' semantics for the ->data field in each       */
376     /*  memspan.                                                            */
377     /* -------------------------------------------------------------------- */
378     if ((pbuf = CALLOC(uint16_t, BC_MAXBIN*2)) == NULL)
379     {
380         fprintf(stderr, "out of memory\n");
381         exit(1);
382     }
383     lbuf = pbuf + BC_MAXBIN;
384 
385     for (i = 0; i < BC_MAXBIN; i += 2)
386     {
387         pbuf[i + 0] = 0xDEAD;
388         pbuf[i + 1] = 0xBEEF;
389         lbuf[i + 0] = 0xDEAD;
390         lbuf[i + 1] = 0xBEEF;
391     }
392 
393     /* -------------------------------------------------------------------- */
394     /*  Load the primary file up front.                                     */
395     /* -------------------------------------------------------------------- */
396     if (!bc->binfile || !file_exists(bc->binfile))
397     {
398         plen = 0;
399     } else
400     {
401         int tmp;
402 
403         /* Assume primary binfile is width >= 9. */
404         tmp = bc_read_helper(bc->binfile, pbuf, 16);
405 
406         if (tmp < 0) { free(pbuf); return -1; }
407 
408         plen = (size_t)tmp;
409     }
410 
411     /* -------------------------------------------------------------------- */
412     /*  Chase down the spans, attaching ->data to each span.                */
413     /* -------------------------------------------------------------------- */
414     for (prev = NULL, span = bc->span; span;
415          prev = span, span = (bc_memspan_t *)(span->l.next))
416     {
417         bc_memspan_t dummy;
418 
419         /* ---------------------------------------------------------------- */
420         /*  Skip spans that already have data attached -- assume they are   */
421         /*  correct.  These are usually POKE spans.                         */
422         /* ---------------------------------------------------------------- */
423         if (span->data)
424             continue;
425 
426         assert((span->flags & BC_SPAN_PK) == 0);
427 
428         /* ---------------------------------------------------------------- */
429         /*  Skip spans that dopn't preload a data segment.  These would be  */
430         /*  banksw/memattr spans.                                           */
431         /* ---------------------------------------------------------------- */
432         if ((span->flags & BC_SPAN_PL) == 0)
433             continue;
434 
435         /* ---------------------------------------------------------------- */
436         /*  Load any file that might be associated with this span.          */
437         /* ---------------------------------------------------------------- */
438         if (span->f_name != NULL)
439         {
440             int tmp;
441 
442             tmp = bc_read_helper(span->f_name, lbuf, 16);
443 
444             if (tmp < 0) { free(pbuf); return -1; }
445 
446             llen = (size_t)tmp;
447             len = llen;
448             buf = lbuf;
449         } else if (span->f_name == NULL) /* true if primary-file span. */
450         {
451             len = plen;
452             buf = pbuf;
453         }
454 
455         /* ---------------------------------------------------------------- */
456         /*  Now attach data from the file to the memspan.  We handle spans  */
457         /*  that go outside the file specially, and differently depending   */
458         /*  on whether they're writeable.                                   */
459         /*   -- Non-writeable, entirely beyond EOF:  delete span            */
460         /*   -- Non-writeable, partially beyond EOF:  trim span             */
461         /*   -- Writeable, entirely beyond EOF:  drop preload flag          */
462         /*   -- Writeable, partially beyond EOF:  split and drop preload    */
463         /*      flag on part beyond EOF.                                    */
464         /* ---------------------------------------------------------------- */
465 
466         /* ---------------------------------------------------------------- */
467         /*   -- Non-writeable, entirely beyond EOF:  delete span            */
468         /* ---------------------------------------------------------------- */
469         if (span->s_fofs >= len && (span->flags & BC_SPAN_W) == 0)
470         {
471             ll_t *dead = (ll_t *)span;
472 
473             if (!prev)
474             {
475                 bc->span     = (bc_memspan_t *)span->l.next;
476                 dummy.l.next = span->l.next;
477                 span         = &dummy;
478             } else
479             {
480                 prev->l.next = span->l.next;
481                 span         = prev;
482             }
483 #ifndef BC_NOFREE
484             bc_free_memspan_t(dead, NULL);
485 #endif
486             continue;
487         }
488 
489         /* ---------------------------------------------------------------- */
490         /*   -- Non-writeable, partially beyond EOF:  trim span             */
491         /* ---------------------------------------------------------------- */
492         if (span->e_fofs >= len && (span->flags & BC_SPAN_W) == 0)
493             span->e_fofs = len - 1;
494 
495         /* ---------------------------------------------------------------- */
496         /*   -- Writeable, entirely beyond EOF:  drop preload flag          */
497         /* ---------------------------------------------------------------- */
498         if (span->s_fofs >= len && (span->flags & BC_SPAN_W) != 0)
499         {
500             span->flags &= ~BC_SPAN_PL;
501             continue;
502         }
503 
504         /* ---------------------------------------------------------------- */
505         /*   -- Writeable, partially beyond EOF:  split and drop preload    */
506         /*      flag on part beyond EOF.                                    */
507         /* ---------------------------------------------------------------- */
508         if (span->e_fofs >= len && (span->flags & BC_SPAN_W) != 0)
509         {
510             bc_memspan_t *part = CALLOC(bc_memspan_t, 1);
511             *part = *span;
512             part->flags &= BC_SPAN_PL;
513             part->s_fofs = 0;
514             part->e_fofs = 0;
515             part->s_addr = span->s_addr + len - span->s_fofs;
516             span->e_fofs = len - 1;
517             span->e_addr = span->s_addr + span->e_fofs - span->s_fofs;
518             part->l.next = (ll_t *)span;
519             prev->l.next = (ll_t *)part;
520         }
521 
522         /* ---------------------------------------------------------------- */
523         /*  Allocate span->data and copy over the data from the file.       */
524         /* ---------------------------------------------------------------- */
525         slen         = span->e_fofs - span->s_fofs + 1;
526         span->e_addr = span->s_addr + slen - 1;
527         span->data   = CALLOC(uint16_t, slen);
528 
529         if (!span->data) { fprintf(stderr, "out of memory\n"); exit(1); }
530 
531         assert(buf);
532         memcpy(span->data, buf + span->s_fofs, slen * sizeof(uint16_t));
533     }
534 
535     free(pbuf);
536     return 0;
537 }
538 
539 
540 
541 #ifndef BC_NODOMACRO /* BC_NODOMACRO if you don't want to interpret macros */
542 /* ======================================================================== */
543 /*  BC_DO_MACROS  -- Applies macros that can be safely applied statically.  */
544 /*                                                                          */
545 /*  I offer two modes here:                                                 */
546 /*                                                                          */
547 /*      1.  Execute up until the first macro we can't do statically, and    */
548 /*                                                                          */
549 /*      2.  Scan the macros, and execute them only if they're all safe      */
550 /*          to execute or ignore.  Special case:  We'll go ahead and        */
551 /*          execute a macro set that ends in a "run" command as long as     */
552 /*          it's the last macro in the list.                                */
553 /*                                                                          */
554 /*  Macros that are safe to execute statically:                             */
555 /*                                                                          */
556 /*      L <file> <width> <addr> 'L'oad <file> of <width> at <addr>.         */
557 /*      P <addr> <data>         'P'oke <data> at <addr>.                    */
558 /*                                                                          */
559 /*  Macros that are safe to treat as NOPs during this pass:                 */
560 /*                                                                          */
561 /*    <n/a>                     Null macro lines                            */
562 /*      A                       Shows the instructions 'A'head.             */
563 /*      I <addr>                'I'nspect data/code at <addr>.              */
564 /*      W <name> <list>         'W'atch a set of values with label <name>.  */
565 /*      V                       'V'iew emulation display                    */
566 /*                                                                          */
567 /*  Macros that are NOT safe to execute statically:                         */
568 /*                                                                          */
569 /*    [0-7] <value>             Set register <n> to <value>                 */
570 /*      B                       Run until next vertical 'B'lank.            */
571 /*      R                       'R'un emulation.                            */
572 /*      T <addr>                'T'race to <addr>.                          */
573 /*      O <addr>                Run t'O' <addr>.                            */
574 /*    <unk>                     Any unknown macros that are in the list.    */
575 /*                                                                          */
576 /*  We implement L and P by tacking additional ROM segments to the          */
577 /*  memspan list.  Load and Poke macros implicitly set "Preload" and        */
578 /*  "Read" attributes.  Load may also set 'Narrow' if the ROM is <= 8 bits  */
579 /*  wide.  Poke will set the "POKE" attribute.  No others are set.          */
580 /* ======================================================================== */
bc_do_macros(bc_cfgfile_t * cfg,int partial_ok)581 int  bc_do_macros(bc_cfgfile_t *cfg, int partial_ok)
582 {
583     bc_macro_t *macro;
584     bc_memspan_t *newspan = NULL;
585 
586     /* -------------------------------------------------------------------- */
587     /*  Trivial case:  Zero macros.                                         */
588     /* -------------------------------------------------------------------- */
589     if (!cfg->macro)
590         return 0;
591 
592     /* -------------------------------------------------------------------- */
593     /*  If given an all-or-nothing directive, prescan macro list to make    */
594     /*  sure it is acceptable.                                              */
595     /* -------------------------------------------------------------------- */
596     if (!partial_ok)
597     {
598         for (macro = bc_parsed_cfg->macro; macro;
599              macro = (bc_macro_t*)macro->l.next)
600         {
601             if (macro->cmd != BC_MAC_LOAD    &&
602                 macro->cmd != BC_MAC_POKE    &&
603                 macro->cmd != BC_MAC_NONE    &&
604                 macro->cmd != BC_MAC_AHEAD   &&
605                 macro->cmd != BC_MAC_INSPECT &&
606                 macro->cmd != BC_MAC_WATCH   &&
607                 macro->cmd != BC_MAC_VIEW)
608                 break;
609         }
610 
611         /* ---------------------------------------------------------------- */
612         /*  If we exited before end of list, we're at an unsupported macro. */
613         /*  If it's not a "run" macro at the end of the list, refuse it.    */
614         /* ---------------------------------------------------------------- */
615         if (macro != NULL &&
616             (macro->l.next != NULL || macro->cmd != BC_MAC_RUN))
617             return -1;
618     }
619 
620     /* -------------------------------------------------------------------- */
621     /*  Chase down the macro list looking for LOAD/POKE commands.           */
622     /* -------------------------------------------------------------------- */
623     for (macro = bc_parsed_cfg->macro; macro;
624          macro = (bc_macro_t*)macro->l.next)
625     {
626         if (macro->cmd == BC_MAC_LOAD || macro->cmd == BC_MAC_POKE)
627         {
628             newspan = CALLOC(bc_memspan_t, 1);
629             if (!newspan)
630             {
631                 fprintf(stderr, "like, out of memory or something\n");
632                 exit(1);
633             }
634 
635             newspan->l.next = NULL;
636 
637             /* Inefficient, but called very rarely. */
638             LL_CONCAT(bc_parsed_cfg->span, newspan, bc_memspan_t);
639         }
640 
641         switch (macro->cmd)
642         {
643             case BC_MAC_LOAD :
644             {
645                 newspan->s_fofs = 0;
646                 newspan->e_fofs = 0xFFFF;
647                 newspan->s_addr = macro->arg.load.addr;
648                 newspan->e_addr = 0; /* calculated on the fly. */
649                 newspan->width  = macro->arg.load.width;
650                 newspan->flags  = BC_SPAN_PL | BC_SPAN_R;
651                 newspan->epage  = BC_SPAN_NOPAGE;
652                 newspan->f_name = strdup(macro->arg.load.name);
653 
654                 if (newspan->width <= 8)
655                     newspan->flags |= BC_SPAN_N;
656 
657                 break;
658             }
659 
660             case BC_MAC_POKE :
661             {
662                 if (!(newspan->data = CALLOC(uint16_t, 1)))
663                 {
664                     fprintf(stderr, "out of memory\n");
665                     exit(1);
666                 }
667 
668                 newspan->s_fofs  = 0;
669                 newspan->e_fofs  = 0;
670                 newspan->s_addr  = macro->arg.poke.addr;
671                 newspan->e_addr  = macro->arg.poke.addr;
672                 newspan->width   = 16;
673                 newspan->flags   = BC_SPAN_PL | BC_SPAN_R | BC_SPAN_PK;
674                 newspan->epage   = macro->arg.poke.epage;
675                 newspan->f_name  = NULL;
676                 newspan->data[0] = macro->arg.poke.value;
677 
678                 if (macro->arg.poke.epage != BC_SPAN_NOPAGE)
679                     newspan->flags |= BC_SPAN_EP;
680 
681                 break;
682             }
683 
684             case BC_MAC_NONE:
685             case BC_MAC_AHEAD:
686             case BC_MAC_WATCH:
687             case BC_MAC_VIEW:
688             {
689                 /* ignore */
690                 break;
691             }
692 
693             default:
694             {
695                 macro = NULL; /* force loop to terminate. */
696                 break;
697             }
698         }
699         newspan = NULL;
700     }
701 
702 
703     return 0;
704 }
705 #endif
706 
707 
708 #ifndef BC_NOFREE /* BC_NOFREE if you have no intention of freeing a cfg */
709 /* ======================================================================== */
710 /*  BC_FREE_CFG   -- Release storage held by bc_cfgfile_t.                  */
711 /*                                                                          */
712 /*  Helper functions for FREE_CFG:                                          */
713 /*                                                                          */
714 /*  BC_FREE_MEMSPAN_T    -- Releases storage associated with bc_memspan_t.  */
715 /*  BC_FREE_MACRO_T      -- Releases storage associated with bc_macro_t.    */
716 /*  BC_FREE_DIAG_T       -- Releases storage associated with bc_diag_t      */
717 /* ======================================================================== */
718 #ifndef CONDFREE
719 # define CONDFREE(x) do { if (x) free(x); } while (0)
720 #endif
721 
722 /* ======================================================================== */
723 /*  BC_FREE_MEMSPAN_T    -- Releases storage associated with bc_memspan_t.  */
724 /* ======================================================================== */
bc_free_memspan_t(ll_t * l_mem,void * unused)725 void bc_free_memspan_t(ll_t *l_mem, void *unused)
726 {
727     bc_memspan_t *mem = (bc_memspan_t*)l_mem;
728     UNUSED(unused);
729 
730     CONDFREE(mem->f_name);
731     CONDFREE(mem->data);
732     free(mem);
733 }
734 
735 /* ======================================================================== */
736 /*  BC_FREE_MACRO_T      -- Releases storage associated with bc_macro_t.    */
737 /* ======================================================================== */
bc_free_macro_t(ll_t * l_mac,void * unused)738 void bc_free_macro_t(ll_t *l_mac, void *unused)
739 {
740     bc_macro_t *mac = (bc_macro_t *)l_mac;
741 
742     UNUSED(unused);
743 
744     switch (mac->cmd)
745     {
746         case BC_MAC_LOAD:   CONDFREE(mac->arg.load.name);   break;
747 
748         case BC_MAC_WATCH:  CONDFREE(mac->arg.watch.name);
749                             CONDFREE(mac->arg.watch.addr);  break;
750 
751         default: /* nothing */ ;
752     }
753     free(mac);
754 }
755 
756 /* ======================================================================== */
757 /*  BC_FREE_DIAG_T       -- Releases storage associated with bc_diag_t      */
758 /* ======================================================================== */
bc_free_diag_t(ll_t * l_diag,void * unused)759 void bc_free_diag_t(ll_t *l_diag, void *unused)
760 {
761     bc_diag_t *diag = (bc_diag_t *)l_diag;
762 
763     UNUSED(unused);
764 
765     CONDFREE(diag->sect);
766     CONDFREE(diag->msg);
767     free(diag);
768 }
769 
770 
771 /* ======================================================================== */
772 /*  BC_FREE_CFG   -- Release storage held by bc_cfgfile_t.                  */
773 /* ======================================================================== */
bc_free_cfg(bc_cfgfile_t * cfg)774 void bc_free_cfg(bc_cfgfile_t *cfg)
775 {
776     CONDFREE(cfg->cfgfile);
777     CONDFREE(cfg->binfile);
778 
779     if (cfg->span    ) LL_ACTON(cfg->span,  bc_free_memspan_t, NULL);
780     if (cfg->macro   ) LL_ACTON(cfg->macro, bc_free_macro_t,   NULL);
781     if (cfg->vars    ) free_cfg_var_list( cfg->vars     );
782     if (cfg->keys[0] ) free_cfg_var_list( cfg->keys[0]  );
783     if (cfg->keys[1] ) free_cfg_var_list( cfg->keys[1]  );
784     if (cfg->keys[2] ) free_cfg_var_list( cfg->keys[2]  );
785     if (cfg->keys[3] ) free_cfg_var_list( cfg->keys[3]  );
786     if (cfg->joystick) free_cfg_var_list( cfg->joystick );
787     if (cfg->diags   ) LL_ACTON(cfg->diags, bc_free_diag_t,    NULL);
788 
789 #ifndef BC_NOMETADATA
790     if (cfg->metadata) free_game_metadata( cfg->metadata );
791 #endif
792 
793     free(cfg);
794 }
795 #endif
796 
797 
798 #ifndef BC_NOPRINT
799 /* ======================================================================== */
800 /*  BC_PRINT_CFG  -- Chase through a bc_cfgfile_t structure and print out   */
801 /*                   what we find therein.                                  */
802 /*                                                                          */
803 /*  Helper functions for PRINT_CFG:                                         */
804 /*                                                                          */
805 /*  BC_PRINT_DIAG    -- Print all the collected diagnostics attached to cfg */
806 /*  BC_PRINT_MACRO   -- Print the [macro] section                           */
807 /*  BC_PRINT_VARLIKE -- Print [var],[keys],[joystick] sections              */
808 /*  BC_PRINT_MEMSPAN -- Print out all the memory span information.          */
809 /* ======================================================================== */
810 
811 /* ======================================================================== */
812 /*  BC_PRINT_DIAG    -- Print all the collected diagnostics attached to cfg */
813 /* ======================================================================== */
bc_print_diag(printer_t * RESTRICT const p,const char * RESTRICT const fname,const bc_diag_t * RESTRICT const diag,const int cmt)814 void bc_print_diag
815 (
816     printer_t       *RESTRICT const p,
817     const char      *RESTRICT const fname,
818     const bc_diag_t *RESTRICT const diag,
819     const int                       cmt
820 )
821 {
822     const bc_diag_t *RESTRICT d;
823     int tot_warn = 0, tot_err = 0;
824 
825     /* -------------------------------------------------------------------- */
826     /*  Step through the list and dump these out.  If cmt != 0, put a       */
827     /*  semicolon before each line.                                         */
828     /* -------------------------------------------------------------------- */
829     for (d = diag; d; d = (const bc_diag_t *)d->l.next)
830     {
831         p->fxn(p->opq, "%s%s:%d:%s %s: %s\015\012",
832                 cmt ? "; " : "", fname, d->line,
833                 d->sect ? d->sect : "<toplevel?>",
834                 d->type == BC_DIAG_WARNING ? "warning" : "error",
835                 d->msg  ? d->msg  : "out of memory?");
836         if (d->type == BC_DIAG_WARNING) tot_warn++; else tot_err++;
837     }
838 
839     /* -------------------------------------------------------------------- */
840     /*  If we had any warnings or errors, print summary information.        */
841     /* -------------------------------------------------------------------- */
842     if (tot_warn || tot_err)
843     {
844         p->fxn(p->opq, "%s%d warnings, %d errors\015\012",
845                 cmt ? "; " : "", tot_warn, tot_err);
846     }
847 }
848 
849 /* ======================================================================== */
850 /*  BC_PRINT_MACRO_T -- Print a single macro_t structure.                   */
851 /* ======================================================================== */
bc_print_macro_t(ll_t * const l_mac,void * RESTRICT const v_p)852 LOCAL void bc_print_macro_t(ll_t *const l_mac, void *RESTRICT const v_p)
853 {
854     const bc_macro_t *RESTRICT const mac = (bc_macro_t*)l_mac;
855     const printer_t *RESTRICT const p = (printer_t*)v_p;
856 
857     if (mac->quiet)
858         p->fxn(p->opq, "@");
859 
860     switch (mac->cmd)
861     {
862         case BC_MAC_NONE:       { p->fxn(p->opq, ";"); break; }
863         case BC_MAC_AHEAD:      { p->fxn(p->opq, "A"); break; }
864         case BC_MAC_BLANK:      { p->fxn(p->opq, "B"); break; }
865         case BC_MAC_RUN:        { p->fxn(p->opq, "R"); break; }
866         case BC_MAC_VIEW:       { p->fxn(p->opq, "V"); break; }
867 
868         case BC_MAC_REG:
869         {
870             p->fxn(p->opq, "%d $%.4X", mac->arg.reg.reg, mac->arg.reg.value);
871             break;
872         }
873 
874         case BC_MAC_INSPECT:
875         {
876             p->fxn(p->opq, "I $%.4X", mac->arg.inspect.addr);
877             break;
878         }
879 
880         case BC_MAC_POKE:
881         {
882             p->fxn(p->opq, "P $%.4X $%.4X",
883                     mac->arg.poke.addr, mac->arg.poke.value);
884             break;
885         }
886 
887         case BC_MAC_LOAD:
888         {
889             p->fxn(p->opq, "L %s %d $%.4X",
890                     cfg_quote_str( mac->arg.load.name ),
891                     mac->arg.load.width,
892                     mac->arg.load.addr);
893             break;
894         }
895 
896         case BC_MAC_RUNTO:
897         {
898             p->fxn(p->opq, "O $%.4X", mac->arg.runto.addr);
899             break;
900         }
901 
902         case BC_MAC_TRACE:
903         {
904             p->fxn(p->opq, "T $%.4X", mac->arg.runto.addr);
905             break;
906         }
907 
908         case BC_MAC_WATCH:
909         {
910             int i, lo, hi;
911 
912             p->fxn(p->opq, "W %s ", mac->arg.watch.name);
913             for (i = 0; i < mac->arg.watch.spans; i++)
914             {
915                 if (i)
916                     p->fxn(p->opq, ",");
917 
918                 lo = mac->arg.watch.addr[2*i + 0];
919                 hi = mac->arg.watch.addr[2*i + 1];
920 
921                 if (lo == hi) p->fxn(p->opq, "$%.4X", lo);
922                 else          p->fxn(p->opq, "$%.4X-$%.4X", lo, hi);
923             }
924             break;
925         }
926 
927         case BC_MAC_ERROR:
928         default:
929         {
930             p->fxn(p->opq, "; unknown macro\015\012");
931             break;
932         }
933     }
934 
935     p->fxn(p->opq, "\015\012");
936 }
937 
938 /* ======================================================================== */
939 /*  BC_PRINT_MACRO   -- Print the [macro] section                           */
940 /* ======================================================================== */
bc_print_macro(printer_t * RESTRICT const p,bc_macro_t * RESTRICT const mac)941 void bc_print_macro(printer_t  *RESTRICT const p,
942                     bc_macro_t *RESTRICT const mac)
943 {
944     /* -------------------------------------------------------------------- */
945     /*  Step through the macro list, regenerating each macro.               */
946     /* -------------------------------------------------------------------- */
947     p->fxn(p->opq, "\015\012[macro]\015\012");
948     LL_ACTON(mac, bc_print_macro_t, (void *)p);
949 }
950 
951 /* ======================================================================== */
952 /*  BC_PRINT_VARLIKE -- Print [var],[keys],[joystick] sections              */
953 /* ======================================================================== */
bc_print_varlike(printer_t * RESTRICT const p,cfg_var_t * RESTRICT const varlike,const char * RESTRICT const sectname)954 void bc_print_varlike
955 (
956     printer_t   *RESTRICT const p,
957     cfg_var_t   *RESTRICT const varlike,
958     const char  *RESTRICT const sectname
959 )
960 {
961     /* -------------------------------------------------------------------- */
962     /*  Real easy:  Just step thru the list and print all the tuples.       */
963     /* -------------------------------------------------------------------- */
964     p->fxn(p->opq, "\015\012%s\015\012", sectname);
965     print_cfg_var_list( varlike, p );
966 }
967 
968 /* ======================================================================== */
969 /*  BC_PRINT_MEMSPAN -- Print out all the memory span information.          */
970 /* ======================================================================== */
bc_print_memspan(printer_t * RESTRICT const p,const bc_memspan_t * RESTRICT const mem)971 void bc_print_memspan
972 (
973     printer_t           *RESTRICT const p,
974     const bc_memspan_t  *RESTRICT const mem
975 )
976 {
977     const bc_memspan_t *RESTRICT m;
978     int need_hdr;
979 
980     /* -------------------------------------------------------------------- */
981     /*  First print out a 'raw' list as a comment.                          */
982     /* -------------------------------------------------------------------- */
983     m = mem;
984     while (m)
985     {
986         p->fxn(p->opq,
987            "; $%.4X - $%.4X => $%.4X - $%.4X PAGE %X "
988            "FLAGS %c%c%c%c%c%c%c from \"%s\"\015\012",
989            m->s_fofs, m->e_fofs, m->s_addr, m->e_addr, m->epage,
990            m->flags & BC_SPAN_R  ? 'R' : '-',
991            m->flags & BC_SPAN_W  ? 'W' : '-',
992            m->flags & BC_SPAN_N  ? 'N' : '-',
993            m->flags & BC_SPAN_B  ? 'B' : '-',
994            m->flags & BC_SPAN_PL ? 'L' : '-',
995            m->flags & BC_SPAN_PK ? 'K' : '-',
996            m->flags & BC_SPAN_EP ? 'E' : '-',
997            m->f_name ? m->f_name : "(primary)"
998        );
999 
1000         m = (bc_memspan_t *)m->l.next;
1001     }
1002 
1003     /* -------------------------------------------------------------------- */
1004     /*  Next, try to print it out as CFG sections.  The CFG format is       */
1005     /*  kinda odd, but since we just parsed the config, it should be easy.  */
1006     /*                                                                      */
1007     /*  The following truth table indicates how the attributes map back     */
1008     /*  to different sections.                                              */
1009     /*                                                                      */
1010     /*  R W N B PL PK EP   Section       Format                             */
1011     /*  R - x x PL -  -    [mapping]     $xxxx - $xxxx = $xxxx              */
1012     /*  - W x x PL -  -    [mapping]     $xxxx - $xxxx = $xxxx WOM w        */
1013     /*  R W x x PL -  -    [mapping]     $xxxx - $xxxx = $xxxx RAM w        */
1014     /*  R - x x PL -  EP   [mapping]     $xxxx - $xxxx = $xxxx PAGE p       */
1015     /*  - W x x PL -  EP   [mapping]     $xxxx - $xxxx = $xxxx PAGE p WOM w */
1016     /*  R W x x PL -  EP   [mapping]     $xxxx - $xxxx = $xxxx PAGE p RAM w */
1017     /*  - - x x PL -  -    [preload]     $xxxx - $xxxx = $xxxx              */
1018     /*  x x x B x  -  x    [bankswitch]  $xxxx - $xxxx                      */
1019     /*  R - x x -  -  -    [memattr]     $xxxx - $xxxx = ROM d              */
1020     /*  - W x x -  -  -    [memattr]     $xxxx - $xxxx = WOM d              */
1021     /*  R W x x -  -  -    [memattr]     $xxxx - $xxxx = RAM d              */
1022     /*  R - x x -  -  EP   [memattr]     $xxxx - $xxxx = PAGE p ROM d       */
1023     /*  - W x x -  -  EP   [memattr]     $xxxx - $xxxx = PAGE p WOM d       */
1024     /*  R W x x -  -  EP   [memattr]     $xxxx - $xxxx = PAGE p RAM d       */
1025     /* -------------------------------------------------------------------- */
1026 
1027 
1028     /* -------------------------------------------------------------------- */
1029     /*  Get all the [mapping] sections.                                     */
1030     /* -------------------------------------------------------------------- */
1031     need_hdr = 1;
1032     for (m = mem; m; m = (bc_memspan_t *)m->l.next)
1033     {
1034         if (
1035             !(
1036                 (m->flags & BC_SPAN_PL) &&
1037                 (m->flags & (BC_SPAN_R | BC_SPAN_W))
1038             ) ||
1039             (m->flags & BC_SPAN_PK) ||
1040             (m->f_name != NULL))
1041             continue;
1042 
1043         if (need_hdr)
1044         {
1045             p->fxn(p->opq, "\015\012[mapping]\015\012");
1046             need_hdr = 0;
1047         }
1048 
1049         p->fxn(p->opq, "$%.4X - $%.4X = $%.4X",
1050                m->s_fofs, m->e_fofs, m->s_addr);
1051 
1052         if (m->flags & BC_SPAN_EP)
1053             p->fxn(p->opq, " PAGE %X", m->epage);
1054 
1055         switch (m->flags & (BC_SPAN_R | BC_SPAN_W))
1056         {
1057             case BC_SPAN_ROM:
1058                 if ( m->width != 16 )
1059                     p->fxn(p->opq, " ROM %d", m->width); /* Weird... */
1060                 break;
1061             case BC_SPAN_WOM:
1062                 p->fxn(p->opq, " WOM %d", m->width);
1063                 break;
1064             case BC_SPAN_RAM:
1065                 p->fxn(p->opq, " RAM %d", m->width);
1066                 break;
1067             default:
1068                 p->fxn(p->opq, " ; unknown!");
1069                 break;
1070         }
1071         p->fxn(p->opq, "\015\012");
1072     }
1073 
1074     /* -------------------------------------------------------------------- */
1075     /*  Get all the [preload] sections.                                     */
1076     /* -------------------------------------------------------------------- */
1077     need_hdr = 1;
1078     for (m = mem; m; m = (bc_memspan_t *)m->l.next)
1079     {
1080         if ((m->flags & (BC_SPAN_R|BC_SPAN_W|BC_SPAN_PL)) != (BC_SPAN_PL) ||
1081             (m->flags & BC_SPAN_PK) ||
1082             (m->f_name != NULL))
1083             continue;
1084 
1085         if (need_hdr)
1086         {
1087             p->fxn(p->opq, "\015\012[preload]\015\012");
1088             need_hdr = 0;
1089         }
1090 
1091         if (m->flags & BC_SPAN_EP)
1092             p->fxn(p->opq, "$%.4X - $%.4X = $%.4X PAGE %X\015\012",
1093                    m->s_fofs, m->e_fofs, m->s_addr, m->epage);
1094         else
1095             p->fxn(p->opq, "$%.4X - $%.4X = $%.4X\015\012",
1096                    m->s_fofs, m->e_fofs, m->s_addr);
1097     }
1098 
1099     /* -------------------------------------------------------------------- */
1100     /*  Get all the [bankswitch] sections.                                  */
1101     /* -------------------------------------------------------------------- */
1102     need_hdr = 1;
1103     for (m = mem; m; m = (bc_memspan_t *)m->l.next)
1104     {
1105         if ((m->flags & (BC_SPAN_B)) != (BC_SPAN_B) ||
1106             (m->flags & BC_SPAN_PK))
1107             continue;
1108 
1109         if (need_hdr)
1110         {
1111             p->fxn(p->opq, "\015\012[bankswitch]\015\012");
1112             need_hdr = 0;
1113         }
1114 
1115         p->fxn(p->opq, "$%.4X - $%.4X\015\012", m->s_addr, m->e_addr);
1116     }
1117 
1118 
1119     /* -------------------------------------------------------------------- */
1120     /*  Get all the [memattr] sections.                                     */
1121     /* -------------------------------------------------------------------- */
1122     need_hdr = 1;
1123     for (m = mem; m; m = (bc_memspan_t *)m->l.next)
1124     {
1125         if ((m->flags & (BC_SPAN_R | BC_SPAN_W)) == 0 ||
1126             (m->flags & BC_SPAN_PL) ||
1127             (m->flags & BC_SPAN_PK))
1128             continue;
1129 
1130         if (need_hdr)
1131         {
1132             p->fxn(p->opq, "\015\012[memattr]\015\012");
1133             need_hdr = 0;
1134         }
1135 
1136         p->fxn(p->opq, "$%.4X - $%.4X =", m->s_addr, m->e_addr);
1137 
1138         if (m->flags & BC_SPAN_EP)
1139             p->fxn(p->opq, " PAGE %X", m->epage);
1140 
1141         switch (m->flags & (BC_SPAN_R | BC_SPAN_W))
1142         {
1143             case BC_SPAN_ROM:
1144                 p->fxn(p->opq, " ROM %d", m->width);
1145                 break;
1146             case BC_SPAN_WOM:
1147                 p->fxn(p->opq, " WOM %d", m->width);
1148                 break;
1149             case BC_SPAN_RAM:
1150                 p->fxn(p->opq, " RAM %d", m->width);
1151                 break;
1152             default:
1153                 p->fxn(p->opq, " ; unknown!");
1154                 break;
1155         }
1156         p->fxn(p->opq, "\015\012");
1157     }
1158 }
1159 
1160 /* ======================================================================== */
1161 /*  BC_PRINT_CFG -- Chase through a bc_cfgfile_t structure and print out    */
1162 /*                  what we find therein.                                   */
1163 /* ======================================================================== */
bc_print_cfg(printer_t * RESTRICT const p,const bc_cfgfile_t * RESTRICT const bc)1164 void bc_print_cfg
1165 (
1166     printer_t           *RESTRICT const p,
1167     const bc_cfgfile_t  *RESTRICT const bc
1168 )
1169 {
1170     if (bc->diags)      bc_print_diag   (p, bc->cfgfile,  bc->diags, 1   );
1171     if (bc->span)       bc_print_memspan(p, bc->span                     );
1172     if (bc->macro)      bc_print_macro  (p, bc->macro                    );
1173     if (bc->vars)       bc_print_varlike(p, bc->vars,     "[vars]"       );
1174     if (bc->keys[0])    bc_print_varlike(p, bc->keys[0],  "[keys]"       );
1175     if (bc->keys[1])    bc_print_varlike(p, bc->keys[1],  "[capslock]"   );
1176     if (bc->keys[2])    bc_print_varlike(p, bc->keys[2],  "[numlock]"    );
1177     if (bc->keys[3])    bc_print_varlike(p, bc->keys[3],  "[scrolllock]" );
1178     if (bc->joystick)   bc_print_varlike(p, bc->joystick, "[joystick]"   );
1179 }
1180 
1181 #endif /* BC_NOPRINT */
1182 
1183 
1184 /* ======================================================================== */
1185 /*  This program is free software; you can redistribute it and/or modify    */
1186 /*  it under the terms of the GNU General Public License as published by    */
1187 /*  the Free Software Foundation; either version 2 of the License, or       */
1188 /*  (at your option) any later version.                                     */
1189 /*                                                                          */
1190 /*  This program is distributed in the hope that it will be useful,         */
1191 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of          */
1192 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       */
1193 /*  General Public License for more details.                                */
1194 /*                                                                          */
1195 /*  You should have received a copy of the GNU General Public License along */
1196 /*  with this program; if not, write to the Free Software Foundation, Inc., */
1197 /*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.             */
1198 /* ======================================================================== */
1199 /*                 Copyright (c) 2003-+Inf, Joseph Zbiciak                  */
1200 /* ======================================================================== */
1201