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