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