1 /* ======================================================================== */
2 /*  Title:    Legacy INTVPC BIN+CFG support                                 */
3 /*  Author:   J. Zbiciak, J. Tanner                                         */
4 /* ------------------------------------------------------------------------ */
5 /*  This module implements a memory peripheral with the semantics of        */
6 /*  INTVPC's BIN+CFG file, at least for the most part.  ECS paged ROM is    */
7 /*  supported indirectly by instantiating Paged ROMs from mem/mem.c.        */
8 /*                                                                          */
9 /*  The routines for reading BIN+CFG files are in bincfg/bincfg.h, not      */
10 /*  this file.                                                              */
11 /* ======================================================================== */
12 
13 
14 #include "config.h"
15 #include "periph/periph.h"
16 #include "mem/mem.h"
17 #include "metadata/metadata.h"
18 #include "lzoe/lzoe.h"
19 #include "file/file.h"
20 #include "cp1600/cp1600.h"
21 #include "bincfg.h"
22 #include "legacy.h"
23 
24 extern int jlp_accel_on;
25 
26 /* ======================================================================== */
27 /*  LEGACY_READ -- read from a legacy BIN+CFG.                              */
28 /* ======================================================================== */
legacy_read(periph_t * per,periph_t * ign,uint32_t addr,uint32_t data)29 uint32_t legacy_read (periph_t *per, periph_t *ign,
30                       uint32_t addr, uint32_t data)
31 {
32     legacy_t *const l = PERIPH_AS(legacy_t, per);
33     uint32_t mask;
34 
35     UNUSED(ign);
36     UNUSED(data);
37 
38     // Disallow reads to flat memory if JLP RAM / accelerators are on.
39     if (jlp_accel_on && addr >= 0x8000 && addr <= 0x9FFF)
40         return ~0U;
41 
42     // Disallow reads if range isn't marked readable.
43     if ((l->loc[addr].flags & BC_SPAN_R) == 0)
44         return ~0U;
45 
46     // Perform the read
47     mask = ~(~0u << l->loc[addr].width);
48     return l->loc[addr].data & mask;
49 }
50 
51 /* ======================================================================== */
52 /*  LEGACY_WRITE -- write to a legacy BIN+CFG.                              */
53 /* ======================================================================== */
legacy_write(periph_t * per,periph_t * ign,uint32_t addr,uint32_t data)54 void  legacy_write(periph_t *per, periph_t *ign, uint32_t addr, uint32_t data)
55 {
56     legacy_t *const l = PERIPH_AS(legacy_t, per);
57     uint32_t mask;
58 
59     UNUSED(ign);
60     UNUSED(data);
61 
62     // Disallow writes to flat memory if JLP RAM / accelerators are on.
63     if (jlp_accel_on && addr >= 0x8000 && addr < 0x9FFF) return;
64 
65     // Disallow writes if range isn't marked writeable
66     if ((l->loc[addr].flags & BC_SPAN_W) == 0)
67         return;
68 
69     // Perform the write
70     mask = ~(~0u << l->loc[addr].width);
71     l->loc[addr].data = data & mask;
72 }
73 
74 /* ======================================================================== */
75 /*  LEGACY_POKE  -- write to a legacy BIN+CFG, ignoring read-only status.   */
76 /* ======================================================================== */
legacy_poke(periph_t * per,periph_t * ign,uint32_t addr,uint32_t data)77 void legacy_poke (periph_t *per, periph_t *ign, uint32_t addr, uint32_t data)
78 {
79     legacy_t *const l = PERIPH_AS(legacy_t, per);
80     uint32_t mask;
81 
82     UNUSED(ign);
83     UNUSED(data);
84     if (jlp_accel_on && addr >= 0x8000 && addr <= 0x9FFF)
85         return;
86 
87     mask = ~(~0u << l->loc[addr].width);
88     l->loc[addr].data = data & mask;
89 }
90 
91 
92 /* ======================================================================== */
93 /*  LEGACY_APPLY_BINCFG -- Populates a legacy_t from a BIN+CFG.             */
94 /* ======================================================================== */
legacy_apply_bincfg(bc_cfgfile_t * bc,legacy_t * l,void * cpu,int flag_jlp_accel,int flag_jlp_flash,int rand_mem)95 LOCAL int legacy_apply_bincfg(bc_cfgfile_t *bc, legacy_t *l, void *cpu,
96                               int flag_jlp_accel, int flag_jlp_flash,
97                               int rand_mem)
98 {
99     uint32_t addr, ofs;
100     bc_memspan_t *span;
101     int num_ecs = 0;
102 
103     l->pg_rom = NULL;
104     l->bc     = bc;
105     l->loc    = CALLOC(struct legacy_loc_t, 65536);
106     if (!l->loc)
107         return -1;
108 
109     /* -------------------------------------------------------------------- */
110     /*  If bincfg has no metadata, grab some defaults.                      */
111     /* -------------------------------------------------------------------- */
112     if (!bc->metadata)
113         bc->metadata = default_game_metadata();
114 
115     /* -------------------------------------------------------------------- */
116     /*  Reconcile the JLP flags with the bincfg metadata.                   */
117     /* -------------------------------------------------------------------- */
118     if (flag_jlp_accel != -1)
119         bc->metadata->jlp_accel = (jlp_accel_t)flag_jlp_accel;
120 
121     if (flag_jlp_flash != -1)
122         bc->metadata->jlp_flash = flag_jlp_flash;
123 
124     l->jlp_accel = bc->metadata->jlp_accel;
125 
126     /* -------------------------------------------------------------------- */
127     /*  Traverse the memspan list, registering memory segments in legacy_t  */
128     /* -------------------------------------------------------------------- */
129     for (span = bc->span; span; span = (bc_memspan_t *)span->l.next)
130     {
131         /* ---------------------------------------------------------------- */
132         /*  If this span has an ECS page, skip it for now.  We'll come      */
133         /*  back to it in a minute.                                         */
134         /* ---------------------------------------------------------------- */
135         if (span->epage != BC_SPAN_NOPAGE || (span->flags & BC_SPAN_EP) != 0)
136         {
137             if ((span->flags & BC_SPAN_RAM) != BC_SPAN_WOM && span->epage < 16)
138                 num_ecs++;
139             continue;
140         }
141 
142         /* ---------------------------------------------------------------- */
143         /*  Sanity check addresses.                                         */
144         /* ---------------------------------------------------------------- */
145         if (span->s_addr > 0xFFFF || span->e_addr > 0xFFFF)
146         {
147             fprintf(stderr, "Address span %.8X-%.8X in CFG is illegal\n",
148                     span->s_addr, span->e_addr);
149             exit(1);
150         }
151 
152         /* ---------------------------------------------------------------- */
153         /*  Assertion:  If PRELOAD, then span->data.                        */
154         /* ---------------------------------------------------------------- */
155         assert(((span->flags & BC_SPAN_PL) == 0) || (span->data != 0));
156 
157         /* ---------------------------------------------------------------- */
158         /*  Add the segment to the legacy_t.                                */
159         /* ---------------------------------------------------------------- */
160         for (addr = span->s_addr, ofs = 0; addr <= span->e_addr; addr++, ofs++)
161         {
162             l->loc[addr].flags |= span->flags;
163 
164             if (span->data)
165                 l->loc[addr].data  = span->data[ofs];
166             else if (rand_mem && (span->flags & BC_SPAN_W))
167                 l->loc[addr].data  = rand_jz();
168 
169             if ((span->flags & BC_SPAN_PK) == 0)
170                 l->loc[addr].width = span->width;
171         }
172     }
173 
174     /* -------------------------------------------------------------------- */
175     /*  Now allocate ECS Paged ROMs/RAMs/WOMs as needed.                    */
176     /* -------------------------------------------------------------------- */
177     if (num_ecs > 0)
178     {
179         int pg_flags[16][16] = {{0}};
180         int pg_width[16][16] = {{0}};
181         uint16_t *const all_data = CALLOC(uint16_t, 4096 * 16 * 16);
182         bool *const all_init = CALLOC(bool, 4096 * 16 * 16);
183 
184         if (!all_data || !all_init)
185         {
186             fprintf(stderr, "legacy_bincfg_t: Out of memory\n");
187             exit(1);
188         }
189 
190         /* ---------------------------------------------------------------- */
191         /*  Make page number the major index, so consecutive chapters in    */
192         /*  the same page are consecutive in memory.                        */
193         /* ---------------------------------------------------------------- */
194         uint16_t *pg_data[16];
195         bool *pg_init[16];
196         for (int i = 0; i < 16; ++i)
197         {
198             pg_data[i] = all_data + i * 4096 * 16;
199             pg_init[i] = all_init + i * 4096 * 16;
200         }
201 
202         /* ---------------------------------------------------------------- */
203         /*  Step over all the spans, copying them into pg_data.  Track      */
204         /*  which words get initialized.  As a post-pass, we will fill the  */
205         /*  uninitialized locations with either 0xFFFF or random, based on  */
206         /*  whether it's ROM or RAM, and whether RAM randomization is on.   */
207         /* ---------------------------------------------------------------- */
208         for (span = bc->span; span; span = (bc_memspan_t *)span->l.next)
209         {
210             /* ------------------------------------------------------------ */
211             /*  Skip spans that are not ECS pages or are paged WOM.         */
212             /* ------------------------------------------------------------ */
213             if ( span->epage == BC_SPAN_NOPAGE ||
214                 (span->flags & BC_SPAN_EP) == 0 ||
215                 (span->flags & BC_SPAN_RAM) == BC_SPAN_WOM )
216             {
217                 continue;
218             }
219 
220             /* ------------------------------------------------------------ */
221             /*  Ignore out-of-range pages, and print a warning.             */
222             /* ------------------------------------------------------------ */
223             const int page = span->epage;
224             if (page >= 16)
225             {
226                 jzp_printf(
227                     "legacy_bincfg_t: Ignoring span $%.0X - $%.0X PAGE $%X."
228                     "  Page out of range.\n",
229                     span->s_addr, span->e_addr, span->epage);
230                 continue;
231             }
232 
233             /* ------------------------------------------------------------ */
234             /*  Skip out-of-range starting address, and truncate out of     */
235             /*  range ending addresses.                                     */
236             /* ------------------------------------------------------------ */
237             if (span->s_addr > 0xFFFF)
238             {
239                 jzp_printf(
240                     "legacy_bincfg_t: Ignoring span $%.0X - $%.0X PAGE $%X."
241                     "  Addresses of range.\n",
242                     span->s_addr, span->e_addr, span->epage);
243                 continue;
244             }
245             const uint32_t s_addr = span->s_addr;
246             const uint32_t e_addr =
247                 span->e_addr > 0xFFFF ? 0xFFFF : span->e_addr;
248             if (e_addr != span->e_addr)
249             {
250                 jzp_printf(
251                     "legacy_bincfg_t: Span $%.0X - $%.0X PAGE $%X:"
252                     "  Truncating span to $%.0X - $FFFF.\n",
253                     span->s_addr, span->e_addr, span->epage, span->s_addr);
254             }
255 
256             /* ------------------------------------------------------------ */
257             /*  Merge flags, and copy in any init data.                     */
258             /* ------------------------------------------------------------ */
259             const uint32_t s_chap = s_addr >> 12;
260             const uint32_t e_chap = e_addr >> 12;
261             for (uint32_t chap = s_chap; chap <= e_chap; chap++)
262             {
263                 const int s_flags = span->flags & BC_SPAN_RAM;
264                 const int s_width = span->width;
265 
266                 /* -------------------------------------------------------- */
267                 /*  Merge flags for overlapping ranges together, and warn   */
268                 /*  when we upgrade ROM to RAM.  Don't warn if we merge     */
269                 /*  ROM into an existing RAM as that's likely a macro poke. */
270                 /* -------------------------------------------------------- */
271                 if (pg_flags[page][chap] == BC_SPAN_ROM &&
272                     s_flags == BC_SPAN_RAM)
273                 {
274                     jzp_printf(
275                         "legacy_bincfg_t: Upgrading $%X000 - $%XFFF PAGE $%X "
276                         "from ROM to RAM.", chap, chap, page);
277                 }
278                 pg_flags[page][chap] |= s_flags;
279 
280                 /* -------------------------------------------------------- */
281                 /*  Don't override original width.  Most likely cause is a  */
282                 /*  macro poke into narrow RAM, which is decidedly rare.    */
283                 /* -------------------------------------------------------- */
284                 if (pg_width[page][chap] && pg_width[page][chap] != s_width)
285                 {
286                     jzp_printf(
287                         "legacy_bincfg_t: Warning, $%X000 - $%XFFF PAGE $%X "
288                         "Width mismatch: %d and %d.\n"
289                         "                 Keeping original width %d.\n",
290                         chap, chap, page, pg_width[page][chap], s_width);
291                 } else
292                 {
293                     pg_width[page][chap] = s_width;
294                 }
295             }
296 
297             if (span->data)
298             {
299                 for (addr = s_addr; addr <= e_addr; ++addr)
300                     pg_data[page][addr] = span->data[addr - s_addr];
301 
302                 for (addr = s_addr; addr <= e_addr; ++addr)
303                     pg_init[page][addr] = true;
304 
305                 /* Since we've claimed span->data, we can free it now. */
306                 free(span->data);
307                 span->data = NULL;
308             }
309         }
310 
311         /* ---------------------------------------------------------------- */
312         /*  Now post-pass, filling in 0xFFFF, 0x0000, or random.  Also      */
313         /*  count how many actual Mattel-style pages we actually have.      */
314         /* ---------------------------------------------------------------- */
315         num_ecs = 0;
316         for (int page = 0; page < 16; ++page)
317         {
318             for (int chap = 0; chap < 16; ++chap)
319             {
320                 if (!pg_flags[page][chap])
321                     continue;
322 
323                 num_ecs++;
324 
325                 const uint32_t s_addr = chap << 12;
326                 const uint32_t e_addr = s_addr | 0x0FFF;
327                 if (pg_flags[page][chap] == BC_SPAN_ROM)
328                 {
329                     for (addr = s_addr; addr <= e_addr; ++addr)
330                         if (!pg_init[page][addr])
331                             pg_data[page][addr] = 0xFFFF;
332                 } else if (!rand_mem)
333                 {
334                     for (addr = s_addr; addr <= e_addr; ++addr)
335                         if (!pg_init[page][addr])
336                             pg_data[page][addr] = 0x0000;
337                 } else
338                 {
339                     for (addr = s_addr; addr <= e_addr; ++addr)
340                         if (!pg_init[page][addr])
341                             pg_data[page][addr] = rand_jz();
342                 }
343             }
344         }
345 
346         /* ---------------------------------------------------------------- */
347         /*  Finally, allocate storage for paged memories and populate.      */
348         /* ---------------------------------------------------------------- */
349         l->pg_rom = CALLOC(mem_t, num_ecs);
350         l->npg_rom = num_ecs;
351 
352         num_ecs = 0;
353         bool failed = false;
354         for (int page = 0; page < 16 && !failed; ++page)
355         {
356             for (int chap = 0; chap < 16 && !failed; ++chap)
357             {
358                 if (!pg_flags[page][chap])
359                     continue;
360 
361                 const uint32_t s_addr = chap << 12;
362                 const int s_width = pg_width[page][chap];
363                 mem_t *const mem = l->pg_rom + num_ecs;
364                 uint16_t *image = CALLOC(uint16_t, 0x1000);
365                 if (!image)
366                 {
367                     failed = true;
368                     break;
369                 }
370                 memcpy(image, pg_data[page] + chap * 0x1000,
371                        0x1000 * sizeof(uint16_t));
372                 if (pg_flags[page][chap] == BC_SPAN_ROM)
373                 {
374                     if ( mem_make_prom( mem, s_width, s_addr, 12,
375                                         page, image, cpu ) )
376                         failed = true;
377                 } else
378                 {
379                     if ( mem_make_pram( mem, s_width, s_addr, 12,
380                                         page, image, cpu ) )
381                         failed = true;
382                 }
383                 if (!failed)
384                     num_ecs++;
385             }
386         }
387 
388         if (failed)
389         {
390             free(all_data);
391             free(all_init);
392             goto fail;
393         }
394     }
395 
396 
397 #if 0
398     if (num_ecs > 0)
399     {
400         num_ecs = 0;
401 
402         l->pg_rom = CALLOC(mem_t, 256); /* theoretical maximum */
403 
404         for (span = bc->span; span; span = (bc_memspan_t *)span->l.next)
405         {
406             uint32_t new_s_addr, new_e_addr, new_slen, old_slen;
407             uint32_t page_base, page_ofs;
408             uint16_t *new_data;
409 
410             /* ------------------------------------------------------------ */
411             /*  Skip spans that are not ECS pages.                          */
412             /* ------------------------------------------------------------ */
413             if ( span->epage == BC_SPAN_NOPAGE ||
414                 (span->flags & BC_SPAN_EP) == 0 )
415             {
416                 continue;
417             }
418 
419             /* ------------------------------------------------------------ */
420             /*  Skip spans that are write-only.                             */
421             /* ------------------------------------------------------------ */
422             if ( (span->flags & BC_SPAN_RAM) == BC_SPAN_WOM )
423                 continue;
424 
425             /* ------------------------------------------------------------ */
426             /*  Assertions:  If ECS Paged, then page >= 0.                  */
427             /* ------------------------------------------------------------ */
428             assert(span->epage != BC_SPAN_NOPAGE);
429 
430             /* ------------------------------------------------------------ */
431             /*  Realign the span to 4K boundaries and cut it up into 4K     */
432             /*  chunks.                                                     */
433             /* ------------------------------------------------------------ */
434             new_s_addr = span->s_addr & 0xF000;
435             new_e_addr = span->e_addr | 0x0FFF;
436             new_slen   = new_e_addr   - new_s_addr + 1;
437             old_slen   = span->e_addr - span->s_addr + 1;
438 
439             new_data   = CALLOC(uint16_t, new_slen);
440             memset( (void *)new_data, 0xFF, new_slen * sizeof(uint16_t) );
441 
442             if ( span->data )
443             {
444                 memcpy( new_data + span->s_addr - new_s_addr,
445                         span->data, old_slen * sizeof(uint16_t) );
446             } else
447             {
448                 for ( addr = span->s_addr; addr <= span->e_addr; addr++ )
449                     new_data[addr - span->s_addr] = rand_mem ? rand_jz() : 0;
450             }
451 
452             /* ------------------------------------------------------------ */
453             /*  Make a paged ROM out of each 4K segment.                    */
454             /* ------------------------------------------------------------ */
455             for ( page_ofs = 0,       page_base = new_s_addr;
456                                       page_base < new_e_addr;
457                   page_ofs += 0x1000, page_base += 0x1000 )
458             {
459                 uint16_t *image = CALLOC(uint16_t, 0x1000);
460                 memcpy( image, new_data + page_ofs, 0x1000 * sizeof(uint16_t) );
461                 mem_t *mem = l->pg_rom + num_ecs;
462 
463                 if ( (span->flags & BC_SPAN_RAM) == BC_SPAN_ROM )
464                 {
465                     if ( mem_make_prom( mem, span->width, page_base, 12,
466                                         span->epage, image, cpu ) )
467                     {
468                         free( new_data );
469                         goto fail;
470                     }
471                 } else
472                 {
473                     if ( mem_make_pram( mem, span->width, page_base, 12,
474                                         span->epage, image, cpu ) )
475                     {
476                         free( new_data );
477                         goto fail;
478                     }
479                 }
480 
481                 if ( l->jlp_accel > 0 &&
482                     (page_base == 0x8000 || page_base == 0x9000) )
483                     l->pg_rom[num_ecs].chk_jlp = 1;
484 
485                 num_ecs++;
486             }
487 
488             /* ------------------------------------------------------------ */
489             /*  Since we claimed span->data, null it out.                   */
490             /* ------------------------------------------------------------ */
491             free( new_data );
492             CONDFREE( span->data );
493             span->data = NULL;
494 
495         }
496     }
497     l->npg_rom = num_ecs;
498 #endif
499 
500     return 0;
501 
502 fail:
503     {
504         int i;
505 
506         if (l->pg_rom)
507             for (i = 0; i < num_ecs; i++)
508                 CONDFREE(l->pg_rom[num_ecs].image);
509 
510         CONDFREE(l->loc);
511         CONDFREE(l->pg_rom);
512     }
513     return -1;
514 }
515 
516 /* ======================================================================== */
517 /*  LEGACY_READ_BINCFG -- Reads a .BIN and optional .CFG file.              */
518 /* ======================================================================== */
legacy_read_bincfg(const char * bin_fn,const char * cfg_fn,legacy_t * l,void * cpu,int jlp_accel,int jlp_flash,int rand_mem)519 LOCAL int legacy_read_bincfg(const char *bin_fn, const char *cfg_fn,
520                              legacy_t *l, void *cpu, int jlp_accel,
521                              int jlp_flash, int rand_mem)
522 {
523     LZFILE *fc;
524     bc_cfgfile_t *bc;
525 
526     /* -------------------------------------------------------------------- */
527     /*  Read the .CFG file.  This process  open it, then parse it.          */
528     /*  Otherwise, we skip it -- lack of .CFG file is non-fatal.            */
529     /* -------------------------------------------------------------------- */
530     if (cfg_fn && (fc = lzoe_fopen(cfg_fn, "r")) != NULL)
531     {
532         bc = bc_parse_cfg(fc, bin_fn, cfg_fn);
533         lzoe_fclose(fc);
534     } else
535     {
536         bc = bc_parse_cfg(NULL, bin_fn, NULL);
537     }
538     if (!bc || !bc->span)
539     {
540         if (cfg_fn)
541             fprintf(stderr,
542                    "Could not generate configuration from this BIN+CFG:\n"
543                    "  \"%s\"\n  \"%s\"\n", bin_fn, cfg_fn);
544         else
545             fprintf(stderr,
546                    "Could not generate configuration from this BIN:\n"
547                    "  \"%s\"\n", bin_fn);
548 
549         return -1;
550     }
551 
552 #ifndef BC_NODOMACRO
553     /* -------------------------------------------------------------------- */
554     /*  Apply any statically safe macros.  Ignore errors.                   */
555     /* -------------------------------------------------------------------- */
556     bc_do_macros(bc, 0);
557 #endif
558 
559     /* -------------------------------------------------------------------- */
560     /*  Populate the config with corresponding BIN data.                    */
561     /* -------------------------------------------------------------------- */
562     if (bc_read_data(bc))
563     {
564         fprintf(stderr, "Error reading data for CFG file.\n");
565         goto err;
566     }
567 
568     /* -------------------------------------------------------------------- */
569     /*  Apply the configuration.  This generates the emulation image.       */
570     /* -------------------------------------------------------------------- */
571     if (legacy_apply_bincfg(bc, l, cpu, jlp_accel, jlp_flash, rand_mem))
572     {
573         fprintf(stderr, "Error applying CFG file\n");
574         goto err;
575     }
576 
577     return 0;
578 
579 err:
580 #ifndef BC_NOFREE
581     /* -------------------------------------------------------------------- */
582     /*  Discard the parsed config.                                          */
583     /* -------------------------------------------------------------------- */
584     bc_free_cfg(bc);
585     l->bc = NULL;
586 #endif
587     return -1;
588 
589 }
590 
591 /* ======================================================================== */
592 /*  LEGACY_PRINT_LOADING -- Print what files it's trying to load.           */
593 /* ======================================================================== */
legacy_print_loading(char * f1,char * f2)594 LOCAL void legacy_print_loading(char *f1, char *f2)
595 {
596     jzp_printf("Loading:\n");
597     if (f1 && file_exists(f1)) jzp_printf("  %s\n", f1);
598     if (f2 && file_exists(f2)) jzp_printf("  %s\n", f2);
599 }
600 
601 /* ======================================================================== */
602 /*  LEGACY_DTOR   -- Tear down a legacy_t, freeing its resources.           */
603 /* ======================================================================== */
legacy_dtor(periph_t * p)604 LOCAL void legacy_dtor(periph_t *p)
605 {
606     legacy_t *const l = PERIPH_AS(legacy_t, p);
607 
608     int i;
609 
610     if (l->pg_rom)
611         for (i = 0; i < l->npg_rom; i++)
612             CONDFREE(l->pg_rom[i].image);
613 
614     CONDFREE(l->pg_rom);
615     CONDFREE(l->loc);
616 
617     if (l->bc)
618         bc_free_cfg(l->bc);
619 }
620 
621 /* ======================================================================== */
622 /*  LEGACY_BINCFG -- Try to determine if a file is BIN+CFG or ROM, and      */
623 /*                   read it in if it is BIN+CFG.                           */
624 /*                                                                          */
625 /*  The return value from this function requires explanation.  If we        */
626 /*  figure out a .ROM file associated with this fname, we will a distinct   */
627 /*  char * that points to its filename.  If we determine the file is a      */
628 /*  BIN+CFG file pair, we will try to load it.  On success, we will return  */
629 /*  an exact copy of fname directly.  Otherwise, we will return NULL.       */
630 /* ======================================================================== */
legacy_bincfg(legacy_t * l,path_t * path,const char * fname_k,int * legacy_rom,void * cpu,int jlp_accel,int jlp_flash,int rand_mem)631 char *legacy_bincfg
632 (
633     legacy_t        *l,         /*  Legacy BIN+CFG structure            */
634     path_t          *path,      /*  Search path for games.              */
635     const char      *fname_k,   /*  Basename to use for CFG/BIN         */
636     int             *legacy_rom,
637     void            *cpu,
638     int             jlp_accel,
639     int             jlp_flash,
640     int             rand_mem
641 )
642 {
643     char *rom1_fn = NULL, *bin1_fn = NULL, *cfg1_fn = NULL;
644     char *rom2_fn = NULL, *bin2_fn = NULL, *cfg2_fn = NULL, *int2_fn = NULL;
645     char *rom3_fn = NULL;
646     char *itv2_fn = NULL;
647     char *p_rom1_fn = NULL, *p_bin1_fn = NULL, *p_cfg1_fn = NULL;
648     char *p_rom2_fn = NULL, *p_bin2_fn = NULL, *p_cfg2_fn = NULL;
649     char *p_rom3_fn = NULL;
650     char *p_itv2_fn = NULL, *p_int2_fn = NULL;
651     char *fname = strdup(fname_k);
652     int name_len = strlen(fname);
653     char *ext = strrchr(fname, '.');  /* look for an extension */
654     path_t dummy, *tmp;
655     char *fn;
656     int max_p_len = 1;
657     char sep[2] = { PATH_SEP, 0 };
658 
659     *legacy_rom = 0;
660 
661     /* -------------------------------------------------------------------- */
662     /*  Silly case:  If the filename ends in a '.', strip it off.           */
663     /* -------------------------------------------------------------------- */
664     if (ext && ext[1] == '\0')
665     {
666         *ext = '\0';
667         ext = NULL;
668     }
669 
670     /* -------------------------------------------------------------------- */
671     /*  Is it .ROM or .CC3?                                                 */
672     /* -------------------------------------------------------------------- */
673     if (ext && (stricmp(ext, ".rom")==0 ||
674                 stricmp(ext, ".cc3")==0))
675     {
676         rom1_fn = strdup(fname);
677     }
678     /* -------------------------------------------------------------------- */
679     /*  Is it .BIN or .INT or .ITV?                                         */
680     /* -------------------------------------------------------------------- */
681     else if (ext && (stricmp(ext, ".bin")==0 ||
682                      stricmp(ext, ".int")==0 ||
683                      stricmp(ext, ".itv")==0) )
684     {
685         char *s;
686 
687         bin1_fn = strdup(fname);
688         cfg1_fn = strdup(fname);
689         if (!cfg1_fn)
690         {
691             fprintf(stderr, "legacy_bincfg: Out of memory\n");
692             exit(1);
693         }
694 
695         s = cfg1_fn + name_len - 4;
696         strcpy(s, ".cfg");
697     }
698     /* -------------------------------------------------------------------- */
699     /*  In case those fail, have a backup plan.                             */
700     /*                                                                      */
701     /*  For instance, on the Mac version of "Intellivision Lives!", the     */
702     /*  ROM images are equivalent to a .BIN, but they have no extension.    */
703     /*  They do, occasionally, have a corresponding .CFG file, with ".cfg"  */
704     /*  as the extension.  (Pointed out by JJT.)                            */
705     /* -------------------------------------------------------------------- */
706     else
707     {
708         /* ---------------------------------------------------------------- */
709         /*  Handle the no-extension-but-matching-cfg case.                  */
710         /* ---------------------------------------------------------------- */
711         if (!ext)
712         {
713             bin1_fn = strdup(fname);
714             cfg1_fn = CALLOC(char, name_len+5);
715             if (!cfg1_fn)
716             {
717                 fprintf(stderr, "legacy_bincfg: Out of memory\n");
718                 exit(1);
719             }
720             snprintf(cfg1_fn, name_len + 5, "%s.cfg", fname);
721         }
722 
723         /* ---------------------------------------------------------------- */
724         /*  Also search for all known extensions appended to the file name. */
725         /*  This lets users type "jzintv foo" and it'll find "foo.rom" or   */
726         /*  "foo.bin" or whatever.                                          */
727         /* ---------------------------------------------------------------- */
728         bin2_fn = CALLOC(char, 6*(name_len + 5));
729         if (!bin2_fn)
730         {
731             fprintf(stderr, "legacy_bincfg: Out of memory\n");
732             exit(1);
733         }
734         cfg2_fn = bin2_fn + name_len + 5;
735         rom2_fn = cfg2_fn + name_len + 5;
736         int2_fn = rom2_fn + name_len + 5;
737         itv2_fn = int2_fn + name_len + 5;
738         rom3_fn = itv2_fn + name_len + 5;
739 
740         snprintf(bin2_fn, name_len + 5, "%s.bin", fname);
741         snprintf(cfg2_fn, name_len + 5, "%s.cfg", fname);
742         snprintf(rom2_fn, name_len + 5, "%s.rom", fname);
743         snprintf(int2_fn, name_len + 5, "%s.int", fname);
744         snprintf(itv2_fn, name_len + 5, "%s.itv", fname);
745         snprintf(rom3_fn, name_len + 5, "%s.cc3", fname);
746     }
747 
748     /* -------------------------------------------------------------------- */
749     /*  Now try out all of our options in each directory of the path.       */
750     /*  If the filename is an absolute path, short out the path search.     */
751     /* -------------------------------------------------------------------- */
752     if (!path)
753     {
754         path        = &dummy;
755         dummy.p_len = 1;
756         dummy.name  = ".";
757         dummy.next  = NULL;
758     }
759 
760     if (is_absolute_path(fname))
761     {
762         path        = &dummy;
763         dummy.p_len = 0;
764         dummy.name  = "";
765         dummy.next  = NULL;
766         sep[0]      = 0;
767     }
768 
769     for (tmp = path; tmp; tmp = tmp->next)
770         if (max_p_len < tmp->p_len)
771             max_p_len = tmp->p_len;
772 
773     p_rom1_fn = CALLOC(char, 9*(max_p_len + name_len + 7));
774 
775     if (!p_rom1_fn)
776     {
777         fprintf(stderr, "legacy_bincfg: Out of memory\n");
778         exit(1);
779     }
780 
781     p_bin1_fn = p_rom1_fn + max_p_len + name_len + 7;
782     p_cfg1_fn = p_bin1_fn + max_p_len + name_len + 7;
783     p_rom2_fn = p_cfg1_fn + max_p_len + name_len + 7;
784     p_bin2_fn = p_rom2_fn + max_p_len + name_len + 7;
785     p_cfg2_fn = p_bin2_fn + max_p_len + name_len + 7;
786     p_int2_fn = p_cfg2_fn + max_p_len + name_len + 7;
787     p_itv2_fn = p_int2_fn + max_p_len + name_len + 7;
788     p_rom3_fn = p_itv2_fn + max_p_len + name_len + 7;
789 
790     tmp = path;
791 
792     while (path)
793     {
794         /* ---------------------------------------------------------------- */
795         /*  Generate path-qualified versions of the filenames.              */
796         /* ---------------------------------------------------------------- */
797 #define N (max_p_len+name_len+7)
798         if (rom1_fn) snprintf(p_rom1_fn, N,"%s%s%s", path->name, sep, rom1_fn);
799         if (bin1_fn) snprintf(p_bin1_fn, N,"%s%s%s", path->name, sep, bin1_fn);
800         if (cfg1_fn) snprintf(p_cfg1_fn, N,"%s%s%s", path->name, sep, cfg1_fn);
801         if (rom2_fn) snprintf(p_rom2_fn, N,"%s%s%s", path->name, sep, rom2_fn);
802         if (bin2_fn) snprintf(p_bin2_fn, N,"%s%s%s", path->name, sep, bin2_fn);
803         if (cfg2_fn) snprintf(p_cfg2_fn, N,"%s%s%s", path->name, sep, cfg2_fn);
804         if (int2_fn) snprintf(p_int2_fn, N,"%s%s%s", path->name, sep, int2_fn);
805         if (itv2_fn) snprintf(p_itv2_fn, N,"%s%s%s", path->name, sep, itv2_fn);
806         if (rom3_fn) snprintf(p_rom3_fn, N,"%s%s%s", path->name, sep, rom3_fn);
807 
808         /* ---------------------------------------------------------------- */
809         /*  See if path-qualified instances of the files exist.             */
810         /* ---------------------------------------------------------------- */
811         if (rom1_fn && file_exists(p_rom1_fn))
812         {
813             legacy_print_loading(p_rom1_fn, NULL);
814             fn = strdup(p_rom1_fn);
815 
816             free(fname);
817             free(p_rom1_fn);
818             if (rom1_fn) free(rom1_fn);
819             if (bin1_fn) free(bin1_fn);
820             if (cfg1_fn) free(cfg1_fn);
821             if (bin2_fn) free(bin2_fn);  /* also frees cfg2/int2/itv2/rom2 */
822 
823             return fn;
824         }
825 
826         if (bin1_fn && file_exists(p_bin1_fn))
827         {
828             legacy_print_loading(p_bin1_fn, p_cfg1_fn);
829             fn = strdup(p_bin1_fn);
830 
831             if (legacy_read_bincfg(p_bin1_fn, p_cfg1_fn, l, cpu, jlp_accel,
832                                    jlp_flash, rand_mem))
833             {
834                 free(fname);
835                 free(p_rom1_fn);
836                 if (rom1_fn) free(rom1_fn);
837                 if (bin1_fn) free(bin1_fn);
838                 if (cfg1_fn) free(cfg1_fn);
839                 if (bin2_fn) free(bin2_fn); /*also frees cfg2/int2/itv2/rom2*/
840                 return NULL;
841             }
842 
843             goto finish;
844         }
845 
846         if (rom2_fn && file_exists(p_rom2_fn))
847         {
848             legacy_print_loading(p_rom2_fn, NULL);
849             fn = strdup(p_rom2_fn);
850 
851             free(fname);
852             free(p_rom1_fn);
853             if (rom1_fn) free(rom1_fn);
854             if (bin1_fn) free(bin1_fn);
855             if (cfg1_fn) free(cfg1_fn);
856             if (bin2_fn) free(bin2_fn); /*also frees cfg2/int2/itv2/rom2*/
857             return fn;
858         }
859 
860         if (rom3_fn && file_exists(p_rom3_fn))
861         {
862             legacy_print_loading(p_rom3_fn, NULL);
863             fn = strdup(p_rom3_fn);
864 
865             free(fname);
866             free(p_rom1_fn);
867             if (rom1_fn) free(rom1_fn);
868             if (bin1_fn) free(bin1_fn);
869             if (cfg1_fn) free(cfg1_fn);
870             if (bin2_fn) free(bin2_fn); /*also frees cfg2/int2/itv2/rom2*/
871             return fn;
872         }
873 
874         if (bin2_fn && file_exists(p_bin2_fn))
875         {
876             legacy_print_loading(p_bin2_fn, p_cfg2_fn);
877             fn = strdup(p_bin2_fn);
878 
879             if (legacy_read_bincfg(p_bin2_fn, p_cfg2_fn, l, cpu, jlp_accel,
880                                    jlp_flash, rand_mem))
881             {
882                 free(fname);
883                 free(p_rom1_fn);
884                 if (rom1_fn) free(rom1_fn);
885                 if (bin1_fn) free(bin1_fn);
886                 if (cfg1_fn) free(cfg1_fn);
887                 if (bin2_fn) free(bin2_fn); /*also frees cfg2/int2/itv2/rom2*/
888                 return NULL;
889             }
890 
891             goto finish;
892         }
893 
894         if (int2_fn && file_exists(p_int2_fn))
895         {
896             legacy_print_loading(p_int2_fn, p_cfg2_fn);
897             fn = strdup(p_int2_fn);
898 
899             if (legacy_read_bincfg(p_int2_fn, p_cfg2_fn, l, cpu, jlp_accel,
900                                    jlp_flash, rand_mem))
901             {
902                 free(fname);
903                 free(p_rom1_fn);
904                 if (rom1_fn) free(rom1_fn);
905                 if (bin1_fn) free(bin1_fn);
906                 if (cfg1_fn) free(cfg1_fn);
907                 if (bin2_fn) free(bin2_fn); /*also frees cfg2/int2/itv2/rom2*/
908                 return NULL;
909             }
910             goto finish;
911         }
912 
913         if (itv2_fn && file_exists(p_itv2_fn))
914         {
915             legacy_print_loading(p_itv2_fn, p_cfg2_fn);
916             fn = strdup(p_itv2_fn);
917 
918             if (legacy_read_bincfg(p_itv2_fn, p_cfg2_fn, l, cpu, jlp_accel,
919                                    jlp_flash, rand_mem))
920             {
921                 free(fname);
922                 free(p_rom1_fn);
923                 if (rom1_fn) free(rom1_fn);
924                 if (bin1_fn) free(bin1_fn);
925                 if (cfg1_fn) free(cfg1_fn);
926                 if (bin2_fn) free(bin2_fn); /*also frees cfg2/int2/itv2/rom2*/
927                 return NULL;
928             }
929 
930             goto finish;
931         }
932 
933         path = path->next;
934     }
935 
936     fprintf(stderr, "\nCould not find any of these candidate files:\n");
937     if (rom1_fn) fprintf(stderr, "  %s\n", rom1_fn);
938     if (bin1_fn) fprintf(stderr, "  %s\n", bin1_fn);
939     if (rom2_fn) fprintf(stderr, "  %s\n", rom2_fn);
940     if (rom3_fn) fprintf(stderr, "  %s\n", rom3_fn);
941     if (bin2_fn) fprintf(stderr, "  %s\n", bin2_fn);
942     if (int2_fn) fprintf(stderr, "  %s\n", int2_fn);
943     if (itv2_fn) fprintf(stderr, "  %s\n", itv2_fn);
944 
945     free(fname);
946     free(p_rom1_fn);
947     if (rom1_fn) free(rom1_fn);
948     if (bin1_fn) free(bin1_fn);
949     if (cfg1_fn) free(cfg1_fn);
950     if (bin2_fn) free(bin2_fn); /*also frees cfg2/int2/itv2/rom2*/
951 
952     dump_search_path(tmp);
953 
954     return NULL;
955 
956 finish:
957     free(fname);
958     *legacy_rom = 1;
959     free(p_rom1_fn);
960     if (rom1_fn) free(rom1_fn);
961     if (bin1_fn) free(bin1_fn);
962     if (cfg1_fn) free(cfg1_fn);
963     if (bin2_fn) free(bin2_fn); /*also frees cfg2/int2/itv2/rom2*/
964 
965     /* -------------------------------------------------------------------- */
966     /*  Set up peripheral function pointers to support reads of the right   */
967     /*  width.  Ignore writes and explicitly disallow ticks.                */
968     /* -------------------------------------------------------------------- */
969     l->periph.read        = legacy_read;
970     l->periph.write       = legacy_write;
971     l->periph.peek        = legacy_read;
972     l->periph.poke        = legacy_poke;
973     l->periph.dtor        = legacy_dtor;
974 
975     l->periph.tick        = NULL;
976     l->periph.min_tick    = ~0U;
977     l->periph.max_tick    = ~0U;
978     l->periph.addr_base   = 0;
979     l->periph.addr_mask   = 0xFFFF;
980 
981     return fn;
982 }
983 
984 /* ======================================================================== */
985 /*  LEGACY_REGISTER -- Actually registers the legacy ROMs.  Also frees      */
986 /*                     the saved bc_cfgfile_t.                              */
987 /* ======================================================================== */
legacy_register(legacy_t * l,periph_bus_t * bus,cp1600_t * cp)988 int legacy_register
989 (
990     legacy_t     *l,
991     periph_bus_t *bus,
992     cp1600_t     *cp
993 )
994 {
995     bc_cfgfile_t *bc = l->bc;
996     bc_memspan_t *span;
997     int num_ecs = 0, need_register, did_register = 0;
998     uint32_t addr;
999 
1000     /* -------------------------------------------------------------------- */
1001     /*  Register the legacy ROM.  Eventually, we might 'optimize' this as   */
1002     /*  I did for the Intellicart, where I have multiple periph_t's based   */
1003     /*  on what flags are set for each range.                               */
1004     /* -------------------------------------------------------------------- */
1005     for (span = bc->span; span; span = (bc_memspan_t *)span->l.next)
1006     {
1007 
1008         if ( span->epage != BC_SPAN_NOPAGE ||
1009             (span->flags & BC_SPAN_EP) != 0)
1010             continue;
1011 
1012         /* ---------------------------------------------------------------- */
1013         /*  Handle other segments directly.  If all the addresses in a      */
1014         /*  span are already mapped, skip this span.  We could/should       */
1015         /*  optimize this at some point to only register areas not yet      */
1016         /*  mapped, but I'm lazy right now.  The optimization would rarely  */
1017         /*  if ever kick in.                                                */
1018         /* ---------------------------------------------------------------- */
1019         need_register = 0;
1020         for (addr = span->s_addr; addr <= span->e_addr; addr++)
1021         {
1022             if (!(l->loc[addr].flags & LOC_MAPPED))
1023                 need_register = 1;
1024 
1025             l->loc[addr].flags |= LOC_MAPPED;
1026         }
1027 
1028         if (need_register)
1029         {
1030             periph_register(bus, &l->periph, span->s_addr, span->e_addr,
1031                             span->flags & BC_SPAN_PK ? "[macro poke]"
1032                                                      : "Legacy BIN+CFG");
1033             did_register = 1;
1034         }
1035 
1036         /* ---------------------------------------------------------------- */
1037         /*  Mark the span cacheable, with snoop if necessary.               */
1038         /* ---------------------------------------------------------------- */
1039         cp1600_cacheable(cp, span->s_addr, span->e_addr,
1040                          (span->flags & BC_SPAN_W) != 0);
1041     }
1042 
1043     /* -------------------------------------------------------------------- */
1044     /*  If no legacy segments got registered, register a dummy segment, so  */
1045     /*  our dtor gets called.  Otherwise we leak memory!                    */
1046     /* -------------------------------------------------------------------- */
1047     if (!did_register)
1048     {
1049         periph_register(bus, &l->periph, 0, 0, "[Legacy BIN+CFG]");
1050     }
1051 
1052     /* -------------------------------------------------------------------- */
1053     /*  Handle ECS-style pages differently.                                 */
1054     /* -------------------------------------------------------------------- */
1055     for (num_ecs = 0; num_ecs < l->npg_rom; num_ecs++)
1056     {
1057         char name[17];
1058         uint32_t s_addr = l->pg_rom[num_ecs].periph.addr_base;
1059         uint32_t e_addr = l->pg_rom[num_ecs].periph.addr_base + 0xFFF;
1060 
1061         snprintf(name, 17, "Paged ROM %d", num_ecs % 100000);
1062         periph_register(bus, &(l->pg_rom[num_ecs].periph),
1063                         s_addr, e_addr, name);
1064 
1065         cp1600_cacheable( cp, s_addr, e_addr, 0 );
1066         /* XXX:  for now paged ROM is never paged RAM.  Need to fix that.   */
1067         /* Note "snoopable" flag above set to 0.                            */
1068     }
1069 
1070 #ifndef BC_NOFREE
1071     /* -------------------------------------------------------------------- */
1072     /*  Discard the parsed config.                                          */
1073     /* -------------------------------------------------------------------- */
1074     bc_free_cfg(l->bc);
1075     l->bc = NULL;
1076 #endif
1077 
1078     assert(num_ecs == l->npg_rom);
1079 
1080     return 0;
1081 }
1082 
1083 
1084 /* ======================================================================== */
1085 /*  This program is free software; you can redistribute it and/or modify    */
1086 /*  it under the terms of the GNU General Public License as published by    */
1087 /*  the Free Software Foundation; either version 2 of the License, or       */
1088 /*  (at your option) any later version.                                     */
1089 /*                                                                          */
1090 /*  This program is distributed in the hope that it will be useful,         */
1091 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of          */
1092 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       */
1093 /*  General Public License for more details.                                */
1094 /*                                                                          */
1095 /*  You should have received a copy of the GNU General Public License along */
1096 /*  with this program; if not, write to the Free Software Foundation, Inc., */
1097 /*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.             */
1098 /* ------------------------------------------------------------------------ */
1099 /*           Copyright (c) 2003-+Inf, Joseph Zbiciak, John Tanner           */
1100 /* ======================================================================== */
1101