1 // license:BSD-3-Clause
2 // copyright-holders:Fabio Priuli
3 /***********************************************************************************************************
4
5 Mattel Intellivision cart emulation
6 (through slot devices)
7
8
9 This is a strange beast, because INTV carts had potentially access to
10 a *LOT* of memory ranges!
11 Quoting Joe Zbiciak's documentation for his emu (jzIntv):
12
13
14 The Intellivision leaves many addresses available to cartridges. However,
15 several address ranges come with caveats, such as interactions with other
16 devices in the system, or incompatibilities with various peripherals.
17
18 Below is a summary.
19
20 ADDRESSES NOTES
21 -------------- --------------------------------------------------------------
22 $0400 - $04FF RAM/ROM ok on all but Intellivision 2.
23 $0500 - $06FF RAM/ROM ok.
24 $0700 - $0CFF RAM/ROM ok if no Intellivoice.
25 $0D00 - $0FFF RAM/ROM ok.
26 $2000 - $2FFF RAM/ROM ok if no ECS.
27 $4000 - $47FF RAM/ROM ok if no ECS.
28 $4800 ROM ok. RAM ok only if boot ROM at $7000.
29 $4801 - $4FFF RAM/ROM ok.
30 $5000 - $5014 ROM ok. RAM ok only if boot ROM at $7000 or $4800.
31 $5015 - $6FFF RAM/ROM ok.
32 $7000 ROM ok if no ECS. RAM at $7000 confuses EXEC boot sequence.
33 $7001 - $77FF RAM/ROM ok if no ECS.
34 $7800 - $7FFF ROM ok if no ECS. Do not map RAM here due to GRAM alias.
35 $8000 - $8FFF RAM/ROM ok. Avoid STIC alias at $8000 - $803F.
36 $9000 - $B7FF RAM/ROM ok.
37 $B800 - $BFFF ROM ok. Do not map RAM here due to GRAM alias.
38 $C000 - $CFFF RAM/ROM ok. Avoid STIC alias at $C000 - $C03F.
39 $D000 - $DFFF RAM/ROM ok.
40 $E000 - $EFFF RAM/ROM ok if no ECS.
41 $F000 - $F7FF RAM/ROM ok.
42 $F800 - $FFFF ROM ok. Do not map RAM here due to GRAM alias.
43
44
45 We handle this, by always creating a 0x10000 wide ROM region to load the
46 cart image and exposing the following (long list of) read handlers:
47 read_rom04
48 read_rom20
49 read_rom40
50 read_rom48
51 read_rom50
52 read_rom60
53 read_rom70
54 read_rom80
55 read_rom90
56 read_roma0
57 read_romb0
58 read_romc0
59 read_romd0
60 read_rome0
61 read_romf0
62 Each pcb types will then use the correct ones for its wiring setup.
63
64 The BIN+CFG format introduced by INTVPC emulator includes metadata about where to
65 load ROM into memory in the CFG file, but we don't support it (because we don't parse
66 the CFG at all) and we rely instead on the intv.hsi metadata for fullpath loading of
67 these.
68 Alternatively, we support the .ROM format used by jzIntv.
69
70
71 TODO:
72 - Convert also the keyboard component to be a passthru slot device
73 - Merge some of the ROM accessor above, once it is clear which ones can be merged
74
75 ***********************************************************************************************************/
76
77
78 #include "emu.h"
79 #include "slot.h"
80 #include "hashfile.h"
81
82 #define INTELLIVOICE_MASK 0x02
83 #define ECS_MASK 0x01
84
85 //**************************************************************************
86 // GLOBAL VARIABLES
87 //**************************************************************************
88
89 DEFINE_DEVICE_TYPE(INTV_CART_SLOT, intv_cart_slot_device, "intv_cart_slot", "Intellivision Cartridge Slot")
90
91 //**************************************************************************
92 // Intellivision Cartridges Interface
93 //**************************************************************************
94
95 //-------------------------------------------------
96 // device_intv_cart_interface - constructor
97 //-------------------------------------------------
98
device_intv_cart_interface(const machine_config & mconfig,device_t & device)99 device_intv_cart_interface::device_intv_cart_interface(const machine_config &mconfig, device_t &device) :
100 device_interface(device, "intvcart"),
101 m_rom(nullptr),
102 m_rom_size(0)
103 {
104 }
105
106
107 //-------------------------------------------------
108 // ~device_intv_cart_interface - destructor
109 //-------------------------------------------------
110
~device_intv_cart_interface()111 device_intv_cart_interface::~device_intv_cart_interface()
112 {
113 }
114
115 //-------------------------------------------------
116 // rom_alloc - alloc the space for the cart
117 //-------------------------------------------------
118
rom_alloc(uint32_t size,const char * tag)119 void device_intv_cart_interface::rom_alloc(uint32_t size, const char *tag)
120 {
121 if (m_rom == nullptr)
122 {
123 m_rom = device().machine().memory().region_alloc(std::string(tag).append(INTVSLOT_ROM_REGION_TAG).c_str(), size, 1, ENDIANNESS_LITTLE)->base();
124 memset(m_rom, 0xff, size);
125 m_rom_size = size;
126 }
127 }
128
129
130 //-------------------------------------------------
131 // ram_alloc - alloc the space for the ram
132 //-------------------------------------------------
133
ram_alloc(uint32_t size)134 void device_intv_cart_interface::ram_alloc(uint32_t size)
135 {
136 m_ram.resize(size);
137 }
138
139
140 //**************************************************************************
141 // LIVE DEVICE
142 //**************************************************************************
143
144 //-------------------------------------------------
145 // intv_cart_slot_device - constructor
146 //-------------------------------------------------
intv_cart_slot_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)147 intv_cart_slot_device::intv_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
148 device_t(mconfig, INTV_CART_SLOT, tag, owner, clock),
149 device_image_interface(mconfig, *this),
150 device_single_card_slot_interface<device_intv_cart_interface>(mconfig, *this),
151 m_type(INTV_STD),
152 m_cart(nullptr)
153 {
154 }
155
156
157 //-------------------------------------------------
158 // intv_cart_slot_device - destructor
159 //-------------------------------------------------
160
~intv_cart_slot_device()161 intv_cart_slot_device::~intv_cart_slot_device()
162 {
163 }
164
165 //-------------------------------------------------
166 // device_start - device-specific startup
167 //-------------------------------------------------
168
device_start()169 void intv_cart_slot_device::device_start()
170 {
171 m_cart = get_card_device();
172 }
173
174
175 //-------------------------------------------------
176 // INTV PCB
177 //-------------------------------------------------
178
179 struct intv_slot
180 {
181 int pcb_id;
182 const char *slot_option;
183 };
184
185 // Here, we take the feature attribute from .xml (i.e. the PCB name) and we assign a unique ID to it
186 static const intv_slot slot_list[] =
187 {
188 { INTV_STD, "intv_rom" },
189 { INTV_RAM, "intv_ram" },
190 { INTV_GFACT, "intv_gfact" },
191 { INTV_WSMLB, "intv_wsmlb" },
192 { INTV_VOICE, "intv_voice" },
193 { INTV_ECS, "intv_ecs" },
194 { INTV_KEYCOMP, "intv_keycomp" }
195 };
196
intv_get_pcb_id(const char * slot)197 static int intv_get_pcb_id(const char *slot)
198 {
199 for (auto & elem : slot_list)
200 {
201 if (!core_stricmp(elem.slot_option, slot))
202 return elem.pcb_id;
203 }
204
205 return 0;
206 }
207
208 #if 1
intv_get_slot(int type)209 static const char *intv_get_slot(int type)
210 {
211 for (auto & elem : slot_list)
212 {
213 if (elem.pcb_id == type)
214 return elem.slot_option;
215 }
216
217 return "intv_rom";
218 }
219 #endif
220
221 /*-------------------------------------------------
222 call load
223 -------------------------------------------------*/
224
load_fullpath()225 image_init_result intv_cart_slot_device::load_fullpath()
226 {
227 uint8_t temp;
228 uint8_t num_segments;
229 uint8_t start_seg;
230 uint8_t end_seg;
231
232 uint32_t current_address;
233 uint32_t end_address;
234
235 uint8_t high_byte;
236 uint8_t low_byte;
237
238 uint8_t *ROM;
239
240 /* if it is in .rom format, we enter here */
241 if (is_filetype("rom"))
242 {
243 // header
244 fread(&temp, 1);
245 if (temp != 0xa8)
246 return image_init_result::FAIL;
247
248 fread(&num_segments, 1);
249
250 fread(&temp, 1);
251 if (temp != (num_segments ^ 0xff))
252 return image_init_result::FAIL;
253
254 m_cart->rom_alloc(0x20000, tag());
255 ROM = (uint8_t *)m_cart->get_rom_base();
256
257 for (int i = 0; i < num_segments; i++)
258 {
259 fread(&start_seg, 1);
260 current_address = start_seg * 0x100;
261
262 fread(&end_seg, 1);
263 end_address = end_seg * 0x100 + 0xff;
264
265 while (current_address <= end_address)
266 {
267 fread(&low_byte, 1);
268 ROM[(current_address << 1) + 1] = low_byte;
269 fread(&high_byte, 1);
270 ROM[current_address << 1] = high_byte;
271 current_address++;
272 }
273
274 // Here we should calculate and compare the CRC16...
275 fread(&temp, 1);
276 fread(&temp, 1);
277 }
278
279 // Access tables and fine address restriction tables are not supported ATM
280 for (int i = 0; i < (16 + 32 + 2); i++)
281 {
282 fread(&temp, 1);
283 }
284 return image_init_result::PASS;
285 }
286 /* otherwise, we load it as a .bin file, using extrainfo from intv.hsi in place of .cfg */
287 else
288 {
289 // This code is a blatant hack, due to impossibility to load a separate .cfg file in MESS.
290 // It shall be eventually replaced by the .xml loading
291
292 // extrainfo format
293 // 1. mapper number (to deal with bankswitch). no bankswitch is mapper 0 (most games).
294 // 2.->5. current images have at most 4 chunks of data. we store here block size and location to load
295 // (value & 0xf0) >> 4 is the location / 0x1000
296 // (value & 0x0f) is the size / 0x800
297 // 6. some images have a ram chunk. as above we store location and size in 8 bits
298 // 7. extra = 1 ECS, 2 Intellivoice
299 int start, size;
300 int mapper, rom[5], ram, extra;
301 std::string extrainfo;
302
303 m_cart->rom_alloc(0x20000, tag());
304 ROM = (uint8_t *)m_cart->get_rom_base();
305
306 if (!hashfile_extrainfo(*this, extrainfo))
307 {
308 // If no extrainfo, we assume a single 0x2000 chunk at 0x5000
309 for (int i = 0; i < 0x2000; i++ )
310 {
311 fread(&low_byte, 1);
312 ROM[((0x5000 + i) << 1) + 1] = low_byte;
313 fread(&high_byte, 1);
314 ROM[(0x5000 + i) << 1] = high_byte;
315 }
316 }
317 else
318 {
319 sscanf(extrainfo.c_str() ,"%d %d %d %d %d %d %d", &mapper, &rom[0], &rom[1], &rom[2],
320 &rom[3], &ram, &extra);
321 //printf("extrainfo: %d %d %d %d %d %d %d \n", mapper, rom[0], rom[1], rom[2], rom[3], ram, extra);
322
323 if (mapper)
324 logerror("Bankswitch not yet implemented!\n");
325
326 if (ram)
327 {
328 start = ((ram & 0xf0) >> 4) * 0x1000;
329 size = (ram & 0x0f) * 0x800;
330
331 if (start == 0xd000 && size == 0x800)
332 {
333 m_type = INTV_RAM;
334 m_cart->ram_alloc(0x800);
335 }
336 else if (start == 0x8800 && size == 0x800)
337 {
338 m_type = INTV_GFACT;
339 m_cart->ram_alloc(0x800);
340 }
341 else
342 printf("Unrecognized RAM setup [Start 0x%X - End 0x%X]. Please contact MESSdevs.\n", start, start + size);
343 }
344 if (extra & INTELLIVOICE_MASK)
345 {
346 printf("WARNING: This game requires emulation of the IntelliVoice module.\n");
347 }
348
349 if (extra & ECS_MASK)
350 {
351 printf("WARNING: This game requires emulation of the ECS module.\n");
352 }
353
354 for (int j = 0; j < 4; j++)
355 {
356 start = ((rom[j] & 0xf0) >> 4) * 0x1000;
357 size = (rom[j] & 0x0f) * 0x800;
358
359 // some cart has to be loaded to 0x4800, but none of the available ones goes to 0x4000.
360 // Hence, we use 0x04 << 4 in extrainfo (to reduce the stored values) and fix the value here.
361 if (start == 0x4000) start += 0x800;
362
363 // logerror("step %d: %d %d \n", j, start / 0x1000, size / 0x1000);
364
365 for (int i = 0; i < size; i++)
366 {
367 fread(&low_byte, 1);
368 ROM[((start + i) << 1) + 1] = low_byte;
369 fread(&high_byte, 1);
370 ROM[(start + i) << 1] = high_byte;
371 }
372 }
373 }
374
375 return image_init_result::PASS;
376 }
377 }
378
call_load()379 image_init_result intv_cart_slot_device::call_load()
380 {
381 if (m_cart)
382 {
383 if (!loaded_through_softlist())
384 return load_fullpath();
385 else
386 {
387 uint16_t offset[] = { 0x400, 0x2000, 0x4000, 0x4800, 0x5000, 0x6000, 0x7000, 0x8000, 0x8800, 0x9000, 0xa000, 0xb000, 0xc000, 0xd000, 0xe000, 0xf000};
388 const char* region_name[] = {"0400", "2000", "4000", "4800", "5000", "6000", "7000", "8000", "8800", "9000", "a000", "b000", "c000", "d000", "e000", "f000"};
389 const char *pcb_name = get_feature("slot");
390 bool extra_bank = false;
391
392 if (pcb_name)
393 m_type = intv_get_pcb_id(pcb_name);
394
395 // these two carts have paged roms, which does not work well with our 0x10000 rom region
396 // so if we are loading one of these, we allocate additional 0x2000 bytes for the paged bank
397 if (m_type == INTV_WSMLB)
398 extra_bank = true;
399
400 uint32_t size;
401 uint16_t address;
402 uint8_t *ROM, *region;
403
404 m_cart->rom_alloc(extra_bank ? 0x22000 : 0x20000, tag());
405 ROM = m_cart->get_rom_base();
406
407 for (int i = 0; i < 16; i++)
408 {
409 address = offset[i];
410 size = get_software_region_length(region_name[i]);
411 if (size)
412 {
413 region = get_software_region(region_name[i]);
414
415 for (int j = 0; j < size / 2; j++)
416 {
417 ROM[((address + j) << 1) + 1] = region[2 * j];
418 ROM[(address + j) << 1] = region[2 * j + 1];
419 }
420 }
421 }
422
423 if (m_type == INTV_RAM || m_type == INTV_GFACT || m_type == INTV_ECS)
424 m_cart->ram_alloc(get_software_region_length("ram"));
425
426 //printf("Type: %s\n", intv_get_slot(m_type));
427 return image_init_result::PASS;
428 }
429 }
430
431 return image_init_result::PASS;
432 }
433
434
435 /*-------------------------------------------------
436 get default card software
437 -------------------------------------------------*/
438
get_default_card_software(get_default_card_software_hook & hook) const439 std::string intv_cart_slot_device::get_default_card_software(get_default_card_software_hook &hook) const
440 {
441 if (hook.image_file())
442 {
443 const char *slot_string;
444 uint32_t len = hook.image_file()->size();
445 std::vector<uint8_t> rom(len);
446 int type = INTV_STD;
447
448 hook.image_file()->read(&rom[0], len);
449
450 if (rom[0] == 0xa8 && (rom[1] == (rom[2] ^ 0xff)))
451 {
452 // it's .ROM file, so that we don't have currently any way to distinguish RAM-equipped carts
453 }
454 else
455 {
456 // assume it's .BIN and try to use .hsi file to determine type (just RAM)
457 int start;
458 int mapper, rom[5], ram, extra;
459 std::string extrainfo;
460
461 if (hook.hashfile_extrainfo(extrainfo))
462 {
463 sscanf(extrainfo.c_str() ,"%d %d %d %d %d %d %d", &mapper, &rom[0], &rom[1], &rom[2],
464 &rom[3], &ram, &extra);
465
466 if (ram)
467 {
468 start = ((ram & 0xf0) >> 4) * 0x1000;
469 if (start == 0xd000)
470 type = INTV_RAM;
471 if (start == 0x8800)
472 type = INTV_GFACT;
473 }
474 }
475
476 }
477
478 slot_string = intv_get_slot(type);
479
480 //printf("type: %s\n", slot_string);
481
482 return std::string(slot_string);
483 }
484 return software_get_default_slot("intv_rom");
485 }
486
487 /*-------------------------------------------------
488 read_ay
489 -------------------------------------------------*/
490
read_ay(offs_t offset)491 uint16_t intv_cart_slot_device::read_ay(offs_t offset)
492 {
493 if (m_cart)
494 return m_cart->read_ay(offset);
495 else
496 return 0xffff;
497 }
498
499 /*-------------------------------------------------
500 write_ay
501 -------------------------------------------------*/
502
write_ay(offs_t offset,uint16_t data)503 void intv_cart_slot_device::write_ay(offs_t offset, uint16_t data)
504 {
505 if (m_cart)
506 m_cart->write_ay(offset, data);
507 }
508
509 /*-------------------------------------------------
510 read_speech
511 -------------------------------------------------*/
512
read_speech(offs_t offset)513 uint16_t intv_cart_slot_device::read_speech(offs_t offset)
514 {
515 if (m_cart)
516 return m_cart->read_speech(offset);
517 else
518 return 0xffff;
519 }
520
521 /*-------------------------------------------------
522 write_speech
523 -------------------------------------------------*/
524
write_speech(offs_t offset,uint16_t data)525 void intv_cart_slot_device::write_speech(offs_t offset, uint16_t data)
526 {
527 if (m_cart)
528 m_cart->write_speech(offset, data);
529 }
530
531
532
533 #include "bus/intv/rom.h"
534 #include "bus/intv/ecs.h"
535 //#include "bus/intv/keycomp.h"
536 #include "bus/intv/voice.h"
537
intv_cart(device_slot_interface & device)538 void intv_cart(device_slot_interface &device)
539 {
540 device.option_add_internal("intv_rom", INTV_ROM_STD);
541 device.option_add_internal("intv_ram", INTV_ROM_RAM);
542 device.option_add_internal("intv_gfact", INTV_ROM_GFACT);
543 device.option_add_internal("intv_wsmlb", INTV_ROM_WSMLB);
544 device.option_add_internal("intv_voice", INTV_ROM_VOICE);
545 device.option_add_internal("intv_ecs", INTV_ROM_ECS);
546 // device.option_add_internal("intv_keycomp", INTV_ROM_KEYCOMP);
547 }
548